summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2013-08-13 15:48:28 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 13:40:28 -0700
commit4fa85e87e7f42efb89bc714049fd35e564400c9d (patch)
tree50d2ba70c312ae4a46e029f2aef511be1c26de11 /drivers/i2c
parent73669ec4d6922a746445481dfefaf6346452ee77 (diff)
i2c: tegra: Implement the bit banging method for i2c transfer after shutdown
Implement bit banging method of doing i2c transfer after shutdown call back get called. For bit banging method, driver will use bit algo for i2c transfer. Make this as optional and can be selected through platform data. bug 1213113 Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Reviewed-on: http://git-master/r/260041 (cherry picked from commit 74e591524d6dbff96d133a254e363ecee90094c9) Change-Id: Id68c53f9685ea93e2e16d5523619cf9254ef4dac Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Reviewed-on: http://git-master/r/261007 GVS: Gerrit_Virtual_Submit
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/Kconfig1
-rw-r--r--drivers/i2c/busses/i2c-tegra.c116
2 files changed, 117 insertions, 0 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 62b3fb2920fc..d2c4cb3521ac 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -711,6 +711,7 @@ config I2C_TEGRA
tristate "NVIDIA Tegra internal I2C controller"
depends on ARCH_TEGRA
select I2C_ALGO_BUSCLEAR
+ select I2C_ALGOBIT
help
If you say yes to this option, support will be included for the
I2C controller embedded in NVIDIA Tegra SOCs
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index ce3bfd719b4f..a69db1928618 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -23,6 +23,8 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/i2c-gpio.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
@@ -219,6 +221,10 @@ struct tegra_i2c_dev {
const struct tegra_i2c_chipdata *chipdata;
int scl_gpio;
int sda_gpio;
+ struct i2c_algo_bit_data bit_data;
+ const struct i2c_algorithm *bit_algo;
+ bool bit_banging_xfer_after_shutdown;
+ bool is_shutdown;
};
static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg)
@@ -284,6 +290,99 @@ static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data,
readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
}
+static inline void tegra_i2c_gpio_setscl(void *data, int state)
+{
+ struct tegra_i2c_dev *i2c_dev = data;
+
+ gpio_set_value(i2c_dev->scl_gpio, state);
+}
+
+static inline int tegra_i2c_gpio_getscl(void *data)
+{
+ struct tegra_i2c_dev *i2c_dev = data;
+
+ return gpio_get_value(i2c_dev->scl_gpio);
+}
+
+static inline void tegra_i2c_gpio_setsda(void *data, int state)
+{
+ struct tegra_i2c_dev *i2c_dev = data;
+
+ gpio_set_value(i2c_dev->sda_gpio, state);
+}
+
+static inline int tegra_i2c_gpio_getsda(void *data)
+{
+ struct tegra_i2c_dev *i2c_dev = data;
+
+ return gpio_get_value(i2c_dev->sda_gpio);
+}
+
+static int tegra_i2c_gpio_request(struct tegra_i2c_dev *i2c_dev)
+{
+ int ret;
+
+ ret = gpio_request_one(i2c_dev->scl_gpio,
+ GPIOF_OUT_INIT_HIGH | GPIOF_OPEN_DRAIN,
+ "i2c-gpio-scl");
+ if (ret < 0) {
+ dev_err(i2c_dev->dev, "GPIO request for gpio %d failed %d\n",
+ i2c_dev->scl_gpio, ret);
+ return ret;
+ }
+
+ ret = gpio_request_one(i2c_dev->sda_gpio,
+ GPIOF_OUT_INIT_HIGH | GPIOF_OPEN_DRAIN,
+ "i2c-gpio-sda");
+ if (ret < 0) {
+ dev_err(i2c_dev->dev, "GPIO request for gpio %d failed %d\n",
+ i2c_dev->sda_gpio, ret);
+ gpio_free(i2c_dev->scl_gpio);
+ return ret;
+ }
+ return ret;
+}
+
+static void tegra_i2c_gpio_free(struct tegra_i2c_dev *i2c_dev)
+{
+ gpio_free(i2c_dev->scl_gpio);
+ gpio_free(i2c_dev->sda_gpio);
+}
+
+static int tegra_i2c_gpio_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ int ret;
+
+ ret = tegra_i2c_gpio_request(i2c_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_dev->bit_algo->master_xfer(adap, msgs, num);
+ if (ret < 0)
+ dev_err(i2c_dev->dev, "i2c-bit-algo xfer failed %d\n", ret);
+
+ tegra_i2c_gpio_free(i2c_dev);
+ return ret;
+}
+
+static int tegra_i2c_gpio_init(struct tegra_i2c_dev *i2c_dev)
+{
+ struct i2c_algo_bit_data *bit_data = &i2c_dev->bit_data;
+
+ bit_data->setsda = tegra_i2c_gpio_setsda;
+ bit_data->getsda = tegra_i2c_gpio_getsda;
+ bit_data->setscl = tegra_i2c_gpio_setscl;
+ bit_data->getscl = tegra_i2c_gpio_getscl;
+ bit_data->data = i2c_dev;
+ bit_data->udelay = 5; /* 100KHz */
+ bit_data->timeout = HZ; /* 10 ms*/
+ i2c_dev->bit_algo = &i2c_bit_algo;
+ i2c_dev->adapter.algo_data = bit_data;
+ return 0;
+}
+
static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
{
u32 int_mask = i2c_readl(i2c_dev, I2C_INT_MASK);
@@ -1043,6 +1142,9 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
if (i2c_dev->is_suspended)
return -EBUSY;
+ if (i2c_dev->is_shutdown && i2c_dev->bit_banging_xfer_after_shutdown)
+ return tegra_i2c_gpio_xfer(adap, msgs, num);
+
i2c_dev->msgs = msgs;
i2c_dev->msgs_num = num;
@@ -1134,6 +1236,9 @@ static struct tegra_i2c_platform_data *parse_i2c_tegra_dt(
pdata->is_high_speed_enable = true;
}
+ if (of_find_property(np, "nvidia,bit-banging-xfer-after-shutdown", NULL))
+ pdata->bit_banging_xfer_after_shutdown = true;
+
pdata->scl_gpio = of_get_named_gpio(np, "scl-gpio", 0);
pdata->sda_gpio = of_get_named_gpio(np, "sda-gpio", 0);
pdata->is_dvc = of_device_is_compatible(np, "nvidia,tegra20-i2c-dvc");
@@ -1337,6 +1442,8 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->is_dvc = pdata->is_dvc;
i2c_dev->slave_addr = pdata->slave_addr;
i2c_dev->hs_master_code = pdata->hs_master_code;
+ i2c_dev->bit_banging_xfer_after_shutdown =
+ pdata->bit_banging_xfer_after_shutdown;
init_completion(&i2c_dev->msg_complete);
if (!i2c_dev->chipdata->has_xfer_complete_interrupt)
@@ -1391,6 +1498,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
of_i2c_register_devices(&i2c_dev->adapter);
pm_runtime_enable(&i2c_dev->adapter.dev);
+ tegra_i2c_gpio_init(i2c_dev);
return 0;
}
@@ -1408,6 +1516,13 @@ static int tegra_i2c_remove(struct platform_device *pdev)
return 0;
}
+static void tegra_i2c_shutdown(struct platform_device *pdev)
+{
+ struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ i2c_dev->is_shutdown = true;
+}
+
#ifdef CONFIG_PM_SLEEP
static int tegra_i2c_suspend_noirq(struct device *dev)
{
@@ -1462,6 +1577,7 @@ static const struct dev_pm_ops tegra_i2c_pm = {
static struct platform_driver tegra_i2c_driver = {
.probe = tegra_i2c_probe,
.remove = tegra_i2c_remove,
+ .shutdown = tegra_i2c_shutdown,
.id_table = tegra_i2c_devtype,
.driver = {
.name = "tegra-i2c",