diff options
-rw-r--r-- | drivers/i2c/busses/i2c-tegra.c | 134 | ||||
-rw-r--r-- | include/linux/i2c-tegra.h | 11 |
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 */ |