summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/i2c/busses/i2c-tegra.c134
-rw-r--r--include/linux/i2c-tegra.h11
2 files changed, 109 insertions, 36 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 3c94c4a81a55..10fdc96a338c 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -31,6 +31,7 @@
#include <asm/unaligned.h>
#include <mach/clk.h>
+#include <mach/pinmux.h>
#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
#define BYTES_PER_FIFO_WORD 4
@@ -120,10 +121,10 @@
*/
struct tegra_i2c_dev {
struct device *dev;
- struct i2c_adapter adapter;
struct clk *clk;
struct clk *i2c_clk;
struct resource *iomem;
+ struct rt_mutex dev_lock;
void __iomem *base;
int cont_id;
int irq;
@@ -134,8 +135,20 @@ struct tegra_i2c_dev {
u8 *msg_buf;
size_t msg_buf_remaining;
int msg_read;
- unsigned long bus_clk_rate;
bool is_suspended;
+ int bus_count;
+ const struct tegra_pingroup_config *last_mux;
+ int last_mux_len;
+ unsigned long last_bus_clk_rate;
+ struct tegra_i2c_bus busses[1];
+};
+
+struct tegra_i2c_bus {
+ struct tegra_i2c_dev *dev;
+ const struct tegra_pingroup_config *mux;
+ int mux_len;
+ unsigned long bus_clk_rate;
+ struct i2c_adapter adapter;
};
static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg)
@@ -353,7 +366,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
(0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
i2c_writel(i2c_dev, val, I2C_CNFG);
i2c_writel(i2c_dev, 0, I2C_INT_MASK);
- clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8);
+ clk_set_rate(i2c_dev->clk, i2c_dev->last_bus_clk_rate * 8);
if (!i2c_dev->is_dvc) {
u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
@@ -448,9 +461,10 @@ err:
return IRQ_HANDLED;
}
-static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
+static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
struct i2c_msg *msg, int stop)
{
+ struct tegra_i2c_dev *i2c_dev = i2c_bus->dev;
u32 packet_header;
u32 int_mask;
int ret;
@@ -529,21 +543,41 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
int num)
{
- struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ struct tegra_i2c_bus *i2c_bus = i2c_get_adapdata(adap);
+ struct tegra_i2c_dev *i2c_dev = i2c_bus->dev;
int i;
int ret = 0;
if (i2c_dev->is_suspended)
return -EBUSY;
+ rt_mutex_lock(&i2c_dev->dev_lock);
+
+ if (i2c_dev->last_mux != i2c_bus->mux) {
+ tegra_pinmux_set_safe_pinmux_table(i2c_dev->last_mux,
+ i2c_dev->last_mux_len);
+ tegra_pinmux_config_pinmux_table(i2c_bus->mux,
+ i2c_bus->mux_len);
+ i2c_dev->last_mux = i2c_bus->mux;
+ i2c_dev->last_mux_len = i2c_bus->mux_len;
+ }
+
+ if (i2c_dev->last_bus_clk != i2c_bus->bus_clk_rate) {
+ tegra_i2c_set_clk(i2c_dev, i2c_bus->bus_clk_rate);
+ i2c_dev->last_bus_clk = i2c_bus->bus_clk_rate;
+ }
+
clk_enable(i2c_dev->clk);
for (i = 0; i < num; i++) {
int stop = (i == (num - 1)) ? 1 : 0;
- ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], stop);
+ ret = tegra_i2c_xfer_msg(i2c_bus, &msgs[i], stop);
if (ret)
break;
}
clk_disable(i2c_dev->clk);
+
+ rt_mutex_unlock(&i2c_dev->dev_lock);
+
return ret ?: i;
}
@@ -560,7 +594,7 @@ static const struct i2c_algorithm tegra_i2c_algo = {
static int tegra_i2c_probe(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev;
- struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data;
+ struct tegra_i2c_platform_data *plat = pdev->dev.platform_data;
struct resource *res;
struct resource *iomem;
struct clk *clk;
@@ -568,8 +602,23 @@ static int tegra_i2c_probe(struct platform_device *pdev)
const unsigned int *prop;
void *base;
int irq;
+ int nbus;
+ int i = 0;
int ret = 0;
+ if (!plat) {
+ dev_err(&pdev->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ if (plat->bus_count <= 0 || plat->adapter_nr < 0) {
+ dev_err(&pdev->dev, "invalid platform data?\n");
+ return -ENODEV;
+ }
+
+ WARN_ON(plat->bus_count > TEGRA_I2C_MAX_BUS);
+ nbus = min(TEGRA_I2C_MAX_BUS, plat->bus_count);
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no mem resource\n");
@@ -609,7 +658,8 @@ static int tegra_i2c_probe(struct platform_device *pdev)
goto err_clk_put;
}
- i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev), GFP_KERNEL);
+ i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev) +
+ (nbus-1) * sizeof(struct tegra_i2c_bus), GFP_KERNEL);
if (!i2c_dev) {
ret = -ENOMEM;
goto err_i2c_clk_put;
@@ -619,24 +669,24 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->clk = clk;
i2c_dev->i2c_clk = i2c_clk;
i2c_dev->iomem = iomem;
- i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->irq = irq;
i2c_dev->cont_id = pdev->id;
i2c_dev->dev = &pdev->dev;
- i2c_dev->bus_clk_rate = 100000; /* default clock rate */
+ i2c_dev->last_bus_clk_rate = 100000; /* default clock rate */
if (pdata) {
- i2c_dev->bus_clk_rate = pdata->bus_clk_rate;
+ i2c_dev->last_bus_clk_rate = pdata->bus_clk_rate[0];
} else if (i2c_dev->dev->of_node) { /* if there is a device tree node ... */
+ /* TODO: DAN: this doesn't work for DT */
prop = of_get_property(i2c_dev->dev->of_node,
"clock-frequency", NULL);
if (prop)
- i2c_dev->bus_clk_rate = be32_to_cpup(prop);
+ i2c_dev->last_bus_clk_rate = be32_to_cpup(prop);
}
+ rt_mutex_init(&i2c_dev->dev_lock);
- if (pdev->id == 3)
- i2c_dev->is_dvc = 1;
+ i2c_dev->is_dvc = plat->is_dvc;
init_completion(&i2c_dev->msg_complete);
platform_set_drvdata(pdev, i2c_dev);
@@ -655,26 +705,38 @@ static int tegra_i2c_probe(struct platform_device *pdev)
clk_enable(i2c_dev->i2c_clk);
- i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
- i2c_dev->adapter.owner = THIS_MODULE;
- i2c_dev->adapter.class = I2C_CLASS_HWMON;
- strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter",
- sizeof(i2c_dev->adapter.name));
- i2c_dev->adapter.algo = &tegra_i2c_algo;
- i2c_dev->adapter.dev.parent = &pdev->dev;
- i2c_dev->adapter.nr = pdev->id;
- i2c_dev->adapter.dev.of_node = pdev->dev.of_node;
-
- ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
- if (ret) {
- dev_err(&pdev->dev, "Failed to add I2C adapter\n");
- goto err_free_irq;
+ for (i = 0; i < nbus; i++) {
+ struct tegra_i2c_bus *i2c_bus = &i2c_dev->busses[i];
+
+ i2c_bus->dev = i2c_dev;
+ i2c_bus->mux = plat->bus_mux[i];
+ i2c_bus->mux_len = plat->bus_mux_len[i];
+ i2c_bus->bus_clk_rate = plat->bus_clk_rate[i] ?: 100000;
+
+ i2c_bus->adapter.dev.of_node = pdev->dev.of_node;
+ i2c_bus->adapter.algo = &tegra_i2c_algo;
+ i2c_set_adapdata(&i2c_bus->adapter, i2c_bus);
+ i2c_bus->adapter.owner = THIS_MODULE;
+ i2c_bus->adapter.class = I2C_CLASS_HWMON;
+ strlcpy(i2c_bus->adapter.name, "Tegra I2C adapter",
+ sizeof(i2c_bus->adapter.name));
+ i2c_bus->adapter.dev.parent = &pdev->dev;
+ i2c_bus->adapter.nr = plat->adapter_nr + i;
+ ret = i2c_add_numbered_adapter(&i2c_bus->adapter);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add I2C adapter\n");
+ goto err_del_bus;
+ }
+ i2c_dev->bus_count++;
}
of_i2c_register_devices(&i2c_dev->adapter);
return 0;
-err_free_irq:
+
+err_del_bus:
+ while (i2c_dev->bus_count--)
+ i2c_del_adapter(&i2c_dev->busses[i2c_dev->bus_count].adapter);
free_irq(i2c_dev->irq, i2c_dev);
err_free:
kfree(i2c_dev);
@@ -692,7 +754,9 @@ err_iounmap:
static int tegra_i2c_remove(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
- i2c_del_adapter(&i2c_dev->adapter);
+ while (i2c_dev->bus_count--)
+ i2c_del_adapter(&i2c_dev->busses[i2c_dev->bus_count].adapter);
+
free_irq(i2c_dev->irq, i2c_dev);
clk_put(i2c_dev->i2c_clk);
clk_put(i2c_dev->clk);
@@ -708,9 +772,9 @@ static int tegra_i2c_suspend(struct platform_device *pdev, pm_message_t state)
{
struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
- i2c_lock_adapter(&i2c_dev->adapter);
+ rt_mutex_lock(&i2c_dev->dev_lock);
i2c_dev->is_suspended = true;
- i2c_unlock_adapter(&i2c_dev->adapter);
+ rt_mutex_unlock(&i2c_dev->dev_lock);
return 0;
}
@@ -720,18 +784,18 @@ static int tegra_i2c_resume(struct platform_device *pdev)
struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
int ret;
- i2c_lock_adapter(&i2c_dev->adapter);
+ rt_mutex_lock(&i2c_dev->dev_lock);
ret = tegra_i2c_init(i2c_dev);
if (ret) {
- i2c_unlock_adapter(&i2c_dev->adapter);
+ rt_mutex_unlock(&i2c_dev->dev_lock);
return ret;
}
i2c_dev->is_suspended = false;
- i2c_unlock_adapter(&i2c_dev->adapter);
+ rt_mutex_unlock(&i2c_dev->dev_lock);
return 0;
}
diff --git a/include/linux/i2c-tegra.h b/include/linux/i2c-tegra.h
index 9c85da49857a..6123502b8ddd 100644
--- a/include/linux/i2c-tegra.h
+++ b/include/linux/i2c-tegra.h
@@ -18,8 +18,17 @@
#ifndef _LINUX_I2C_TEGRA_H
#define _LINUX_I2C_TEGRA_H
+#include <mach/pinmux.h>
+
+#define TEGRA_I2C_MAX_BUS 3
+
struct tegra_i2c_platform_data {
- unsigned long bus_clk_rate;
+ int adapter_nr;
+ int bus_count;
+ const struct tegra_pingroup_config *bus_mux[TEGRA_I2C_MAX_BUS];
+ int bus_mux_len[TEGRA_I2C_MAX_BUS];
+ unsigned long bus_clk_rate[TEGRA_I2C_MAX_BUS];
+ bool is_dvc;
};
#endif /* _LINUX_I2C_TEGRA_H */