From 9ac6cb5fbb1781d120ca0ad29d014d35c9c3f0c4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:17 +0100 Subject: i2c: add suspended flag and accessors for i2c adapters A few drivers open code the handling of suspended adapters. It could be handled by the core, though, to ensure generic handling. This patch adds the flag and accessor functions. The usage of these helpers is optional, though. See the kerneldoc in this patch. Using the new flag, we now reject further transfers if the adapter is already marked suspended. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- Documentation/i2c/fault-codes | 4 ++++ drivers/i2c/i2c-core-base.c | 3 +++ include/linux/i2c.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/Documentation/i2c/fault-codes b/Documentation/i2c/fault-codes index 47c25abb7d52..0cee0fc545b4 100644 --- a/Documentation/i2c/fault-codes +++ b/Documentation/i2c/fault-codes @@ -112,6 +112,10 @@ EPROTO case is when the length of an SMBus block data response (from the SMBus slave) is outside the range 1-32 bytes. +ESHUTDOWN + Returned when a transfer was requested using an adapter + which is already suspended. + ETIMEDOUT This is returned by drivers when an operation took too much time, and was aborted before it completed. diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 28460f6a60cc..926ca0a7477f 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1232,6 +1232,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) if (!adap->lock_ops) adap->lock_ops = &i2c_adapter_lock_ops; + adap->locked_flags = 0; rt_mutex_init(&adap->bus_lock); rt_mutex_init(&adap->mux_lock); mutex_init(&adap->userspace_clients_lock); @@ -1865,6 +1866,8 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (WARN_ON(!msgs || num < 1)) return -EINVAL; + if (WARN_ON(test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags))) + return -ESHUTDOWN; if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) return -EOPNOTSUPP; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 65b4eaed1d96..cba59d66c00d 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -680,6 +680,8 @@ struct i2c_adapter { int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ + unsigned long locked_flags; /* owned by the I2C core */ +#define I2C_ALF_IS_SUSPENDED 0 int nr; char name[48]; @@ -762,6 +764,38 @@ i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) adapter->lock_ops->unlock_bus(adapter, flags); } +/** + * i2c_mark_adapter_suspended - Report suspended state of the adapter to the core + * @adap: Adapter to mark as suspended + * + * When using this helper to mark an adapter as suspended, the core will reject + * further transfers to this adapter. The usage of this helper is optional but + * recommended for devices having distinct handlers for system suspend and + * runtime suspend. More complex devices are free to implement custom solutions + * to reject transfers when suspended. + */ +static inline void i2c_mark_adapter_suspended(struct i2c_adapter *adap) +{ + i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); + set_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags); + i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); +} + +/** + * i2c_mark_adapter_resumed - Report resumed state of the adapter to the core + * @adap: Adapter to mark as resumed + * + * When using this helper to mark an adapter as resumed, the core will allow + * further transfers to this adapter. See also further notes to + * @i2c_mark_adapter_suspended(). + */ +static inline void i2c_mark_adapter_resumed(struct i2c_adapter *adap) +{ + i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); + clear_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags); + i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); +} + /*flags for the client struct: */ #define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ #define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */ -- cgit v1.2.3-59-g8ed1b From 1adcc83e212c56afccdb633f5b3db020afbd0860 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:19 +0100 Subject: i2c: synquacer: remove unused is_suspended flag This flag was defined and checked but never set a value. Remove it. Signed-off-by: Wolfram Sang Acked-by: Ard Biesheuvel Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-synquacer.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c index 2184b7c3580e..d18b0941b71a 100644 --- a/drivers/i2c/busses/i2c-synquacer.c +++ b/drivers/i2c/busses/i2c-synquacer.c @@ -144,8 +144,6 @@ struct synquacer_i2c { u32 timeout_ms; enum i2c_state state; struct i2c_adapter adapter; - - bool is_suspended; }; static inline int is_lastmsg(struct synquacer_i2c *i2c) @@ -316,9 +314,6 @@ static int synquacer_i2c_doxfer(struct synquacer_i2c *i2c, unsigned long timeout; int ret; - if (i2c->is_suspended) - return -EBUSY; - synquacer_i2c_hw_init(i2c); bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR); if (bsr & SYNQUACER_I2C_BSR_BB) { -- cgit v1.2.3-59-g8ed1b From f2e0d28cba1e049ccf8aa11fc30c3d7fb9ab8278 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:20 +0100 Subject: i2c: brcmstb: use core helper to mark adapter suspended Rejecting transfers should be handled by the core. Signed-off-by: Wolfram Sang Reviewed-by: Kamal Dasu Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-brcmstb.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index 826d32049996..f4d862234980 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -170,7 +170,6 @@ struct brcmstb_i2c_dev { struct bsc_regs *bsc_regmap; struct i2c_adapter adapter; struct completion done; - bool is_suspended; u32 clk_freq_hz; int data_regsz; }; @@ -467,9 +466,6 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter, int xfersz = brcmstb_i2c_get_xfersz(dev); u32 cond, cond_per_msg; - if (dev->is_suspended) - return -EBUSY; - /* Loop through all messages */ for (i = 0; i < num; i++) { pmsg = &msgs[i]; @@ -689,10 +685,7 @@ static int brcmstb_i2c_suspend(struct device *dev) { struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - i2c_dev->is_suspended = true; - i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - + i2c_mark_adapter_suspended(&i2c_dev->adapter); return 0; } @@ -700,10 +693,8 @@ static int brcmstb_i2c_resume(struct device *dev) { struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); brcmstb_i2c_set_bsc_reg_defaults(i2c_dev); - i2c_dev->is_suspended = false; - i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + i2c_mark_adapter_resumed(&i2c_dev->adapter); return 0; } -- cgit v1.2.3-59-g8ed1b From 5601df355b63a8ddac21b2dfc3433d941a994c32 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:21 +0100 Subject: i2c: zx2967: use core helper to mark adapter suspended Rejecting transfers should be handled by the core. Also, this will ensure proper locking which was forgotten in this open coded version and make sure resume mark is set after enabling clocks (not before). Signed-off-by: Wolfram Sang Acked-by: Shawn Guo Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-zx2967.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-zx2967.c b/drivers/i2c/busses/i2c-zx2967.c index b8f9e020d80e..7b98d97da3c6 100644 --- a/drivers/i2c/busses/i2c-zx2967.c +++ b/drivers/i2c/busses/i2c-zx2967.c @@ -66,7 +66,6 @@ struct zx2967_i2c { int msg_rd; u8 *cur_trans; u8 access_cnt; - bool is_suspended; int error; }; @@ -313,9 +312,6 @@ static int zx2967_i2c_xfer(struct i2c_adapter *adap, int ret; int i; - if (i2c->is_suspended) - return -EBUSY; - zx2967_set_addr(i2c, msgs->addr); for (i = 0; i < num; i++) { @@ -470,7 +466,7 @@ static int __maybe_unused zx2967_i2c_suspend(struct device *dev) { struct zx2967_i2c *i2c = dev_get_drvdata(dev); - i2c->is_suspended = true; + i2c_mark_adapter_suspended(&i2c->adap); clk_disable_unprepare(i2c->clk); return 0; @@ -480,8 +476,8 @@ static int __maybe_unused zx2967_i2c_resume(struct device *dev) { struct zx2967_i2c *i2c = dev_get_drvdata(dev); - i2c->is_suspended = false; clk_prepare_enable(i2c->clk); + i2c_mark_adapter_resumed(&i2c->adap); return 0; } -- cgit v1.2.3-59-g8ed1b From 7b6b69984e0411daac4ba78c7dc6c6457c4aa5b4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:22 +0100 Subject: i2c: sprd: don't use pdev as variable name for struct device * The pointer to a device is usually named 'dev'. These 'pdev' here look much like copy&paste errors. Fix them to avoid confusion. Signed-off-by: Wolfram Sang Reviewed-by: Baolin Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sprd.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index a94e724f51dc..e266d8a713d9 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -586,40 +586,40 @@ static int sprd_i2c_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev) +static int __maybe_unused sprd_i2c_suspend_noirq(struct device *dev) { - struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); i2c_dev->is_suspended = true; i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - return pm_runtime_force_suspend(pdev); + return pm_runtime_force_suspend(dev); } -static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev) +static int __maybe_unused sprd_i2c_resume_noirq(struct device *dev) { - struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); i2c_dev->is_suspended = false; i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - return pm_runtime_force_resume(pdev); + return pm_runtime_force_resume(dev); } -static int __maybe_unused sprd_i2c_runtime_suspend(struct device *pdev) +static int __maybe_unused sprd_i2c_runtime_suspend(struct device *dev) { - struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); clk_disable_unprepare(i2c_dev->clk); return 0; } -static int __maybe_unused sprd_i2c_runtime_resume(struct device *pdev) +static int __maybe_unused sprd_i2c_runtime_resume(struct device *dev) { - struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); int ret; ret = clk_prepare_enable(i2c_dev->clk); -- cgit v1.2.3-59-g8ed1b From 5a7b81ff1b3e4493363c913e9f0affa904062798 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:23 +0100 Subject: i2c: sprd: use core helper to mark adapter suspended Rejecting transfers should be handled by the core. Signed-off-by: Wolfram Sang Reviewed-by: Baolin Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sprd.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index e266d8a713d9..961123529678 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -86,7 +86,6 @@ struct sprd_i2c { u32 count; int irq; int err; - bool is_suspended; }; static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count) @@ -284,9 +283,6 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct sprd_i2c *i2c_dev = i2c_adap->algo_data; int im, ret; - if (i2c_dev->is_suspended) - return -EBUSY; - ret = pm_runtime_get_sync(i2c_dev->dev); if (ret < 0) return ret; @@ -590,10 +586,7 @@ static int __maybe_unused sprd_i2c_suspend_noirq(struct device *dev) { struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - i2c_dev->is_suspended = true; - i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - + i2c_mark_adapter_suspended(&i2c_dev->adap); return pm_runtime_force_suspend(dev); } @@ -601,10 +594,7 @@ static int __maybe_unused sprd_i2c_resume_noirq(struct device *dev) { struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - i2c_dev->is_suspended = false; - i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - + i2c_mark_adapter_resumed(&i2c_dev->adap); return pm_runtime_force_resume(dev); } -- cgit v1.2.3-59-g8ed1b From d5c95bd6f3254e23736c48ff8f6c69c470b910a4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:24 +0100 Subject: i2c: exynos5: use core helper to mark adapter suspended Rejecting transfers should be handled by the core. Also, this will ensure proper locking which was forgotten in this open coded version. Signed-off-by: Wolfram Sang Tested-by: Marek Szyprowski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-exynos5.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index c1ce2299a76e..41de4ee409b6 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -183,7 +183,6 @@ enum i2c_type_exynos { struct exynos5_i2c { struct i2c_adapter adap; - unsigned int suspended:1; struct i2c_msg *msg; struct completion msg_complete; @@ -715,11 +714,6 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap, struct exynos5_i2c *i2c = adap->algo_data; int i, ret; - if (i2c->suspended) { - dev_err(i2c->dev, "HS-I2C is not initialized.\n"); - return -EIO; - } - ret = clk_enable(i2c->clk); if (ret) return ret; @@ -847,8 +841,7 @@ static int exynos5_i2c_suspend_noirq(struct device *dev) { struct exynos5_i2c *i2c = dev_get_drvdata(dev); - i2c->suspended = 1; - + i2c_mark_adapter_suspended(&i2c->adap); clk_unprepare(i2c->clk); return 0; @@ -871,7 +864,7 @@ static int exynos5_i2c_resume_noirq(struct device *dev) exynos5_i2c_init(i2c); clk_disable(i2c->clk); - i2c->suspended = 0; + i2c_mark_adapter_resumed(&i2c->adap); return 0; } -- cgit v1.2.3-59-g8ed1b From 2088716f90d160e735a469b8e1d0653facd74f16 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:25 +0100 Subject: i2c: s3c2410: use core helper to mark adapter suspended Rejecting transfers should be handled by the core. Also, this will ensure proper locking which was forgotten in this open coded version. Signed-off-by: Wolfram Sang Tested-by: Marek Szyprowski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-s3c2410.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 2f2e28d60ef5..53bc021f4a5a 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -104,7 +104,6 @@ enum s3c24xx_i2c_state { struct s3c24xx_i2c { wait_queue_head_t wait; kernel_ulong_t quirks; - unsigned int suspended:1; struct i2c_msg *msg; unsigned int msg_num; @@ -703,9 +702,6 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, unsigned long timeout; int ret; - if (i2c->suspended) - return -EIO; - ret = s3c24xx_i2c_set_master(i2c); if (ret != 0) { dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); @@ -1246,7 +1242,7 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev) { struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); - i2c->suspended = 1; + i2c_mark_adapter_suspended(&i2c->adap); if (!IS_ERR(i2c->sysreg)) regmap_read(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, &i2c->sys_i2c_cfg); @@ -1267,7 +1263,7 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev) return ret; s3c24xx_i2c_init(i2c); clk_disable(i2c->clk); - i2c->suspended = 0; + i2c_mark_adapter_resumed(&i2c->adap); return 0; } -- cgit v1.2.3-59-g8ed1b From 18569fa89a4db9ed6b5181624788a1574a9b6ed7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:26 +0100 Subject: i2c: rcar: add suspend/resume support Because the adapter will be set up before every transaction anyhow, we just need to mark it as suspended to the I2C core. Signed-off-by: Hiromitsu Yamasaki Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 254e6219e538..1d6390eed861 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -1017,10 +1017,35 @@ static int rcar_i2c_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int rcar_i2c_suspend(struct device *dev) +{ + struct rcar_i2c_priv *priv = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&priv->adap); + return 0; +} + +static int rcar_i2c_resume(struct device *dev) +{ + struct rcar_i2c_priv *priv = dev_get_drvdata(dev); + + i2c_mark_adapter_resumed(&priv->adap); + return 0; +} + +static SIMPLE_DEV_PM_OPS(rcar_i2c_pm_ops, rcar_i2c_suspend, rcar_i2c_resume); + +#define DEV_PM_OPS (&rcar_i2c_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + static struct platform_driver rcar_i2c_driver = { .driver = { .name = "i2c-rcar", .of_match_table = rcar_i2c_dt_ids, + .pm = DEV_PM_OPS, }, .probe = rcar_i2c_probe, .remove = rcar_i2c_remove, -- cgit v1.2.3-59-g8ed1b From 5b3a23a3cc94864aacc012532b36afa92a25b9c0 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 9 Jan 2019 06:56:49 +0100 Subject: i2c: imx: notify about real errors on dma i2c_imx_dma_request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At least on i.MX5x, the DMA events for I2C and SDHC use the same channel and there can only be a single user. So in this case there should be no message emitted that looks like an error if the I2C device doesn't have an assigned DMA channel. In contrast real problems that were only emitted at debug level before should be described at a higher level to be better visible and so understandable. Signed-off-by: Oleksij Rempel Acked-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index fa9ad53845d9..e28ef494dac8 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -285,9 +285,11 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, if (!dma) return; - dma->chan_tx = dma_request_slave_channel(dev, "tx"); - if (!dma->chan_tx) { - dev_dbg(dev, "can't request DMA tx channel\n"); + dma->chan_tx = dma_request_chan(dev, "tx"); + if (IS_ERR(dma->chan_tx)) { + ret = PTR_ERR(dma->chan_rx); + if (ret != -ENODEV && ret != -EPROBE_DEFER) + dev_err(dev, "can't request DMA tx channel (%d)\n", ret); goto fail_al; } @@ -298,13 +300,15 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma_sconfig.direction = DMA_MEM_TO_DEV; ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig); if (ret < 0) { - dev_dbg(dev, "can't configure tx channel\n"); + dev_err(dev, "can't configure tx channel (%d)\n", ret); goto fail_tx; } - dma->chan_rx = dma_request_slave_channel(dev, "rx"); - if (!dma->chan_rx) { - dev_dbg(dev, "can't request DMA rx channel\n"); + dma->chan_rx = dma_request_chan(dev, "rx"); + if (IS_ERR(dma->chan_rx)) { + ret = PTR_ERR(dma->chan_rx); + if (ret != -ENODEV && ret != -EPROBE_DEFER) + dev_err(dev, "can't request DMA rx channel (%d)\n", ret); goto fail_tx; } @@ -315,7 +319,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma_sconfig.direction = DMA_DEV_TO_MEM; ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig); if (ret < 0) { - dev_dbg(dev, "can't configure rx channel\n"); + dev_err(dev, "can't configure rx channel (%d)\n", ret); goto fail_rx; } @@ -332,7 +336,6 @@ fail_tx: dma_release_channel(dma->chan_tx); fail_al: devm_kfree(dev, dma); - dev_info(dev, "can't use DMA, using PIO instead.\n"); } static void i2c_imx_dma_callback(void *arg) -- cgit v1.2.3-59-g8ed1b From e1ab9a468e3b1636d60cebd0a778461270dde208 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 9 Jan 2019 06:56:50 +0100 Subject: i2c: imx: improve the error handling in i2c_imx_dma_request() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve the error handling in i2c_imx_dma_request() and let it return an error indication that the caller then can handle accordingly. Signed-off-by: Oleksij Rempel Acked-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index e28ef494dac8..09b124547669 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -273,8 +273,8 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx, } /* Functions for DMA support */ -static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, - dma_addr_t phy_addr) +static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, + dma_addr_t phy_addr) { struct imx_i2c_dma *dma; struct dma_slave_config dma_sconfig; @@ -283,7 +283,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); if (!dma) - return; + return -ENOMEM; dma->chan_tx = dma_request_chan(dev, "tx"); if (IS_ERR(dma->chan_tx)) { @@ -328,7 +328,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n", dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx)); - return; + return 0; fail_rx: dma_release_channel(dma->chan_rx); @@ -336,6 +336,8 @@ fail_tx: dma_release_channel(dma->chan_tx); fail_al: devm_kfree(dev, dma); + /* return successfully if there is no dma support */ + return ret == -ENODEV ? 0 : ret; } static void i2c_imx_dma_callback(void *arg) @@ -1163,11 +1165,13 @@ static int i2c_imx_probe(struct platform_device *pdev) dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res); dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n", i2c_imx->adapter.name); - dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); /* Init DMA config if supported */ - i2c_imx_dma_request(i2c_imx, phy_addr); + ret = i2c_imx_dma_request(i2c_imx, phy_addr); + if (ret < 0) + goto clk_notifier_unregister; + dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); return 0; /* Return OK */ clk_notifier_unregister: -- cgit v1.2.3-59-g8ed1b From b33a02aadcc6330a61e511240b634dc11112e65e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2019 17:24:55 +0200 Subject: i2c: acpi: Move I2C bits from acpi.h to i2c.h As discussed previously the best location for certain bus related bits, e.g. I2C, is its own realm of the headers. In order to uncontaminate acpi.h move the I2C bits to i2c.h. There is no functional change intended. Link: https://lkml.org/lkml/2018/11/28/744 Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Acked-by: Rafael J. Wysocki Signed-off-by: Wolfram Sang --- include/linux/acpi.h | 11 ----------- include/linux/i2c.h | 10 ++++++++++ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 87715f20b69a..13f5cb2c4763 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1061,17 +1061,6 @@ static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) } #endif -#if defined(CONFIG_ACPI) && IS_ENABLED(CONFIG_I2C) -bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, - struct acpi_resource_i2c_serialbus **i2c); -#else -static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, - struct acpi_resource_i2c_serialbus **i2c) -{ - return false; -} -#endif - /* Device properties */ #ifdef CONFIG_ACPI diff --git a/include/linux/i2c.h b/include/linux/i2c.h index cba59d66c00d..1f45331924d6 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -967,11 +967,21 @@ static inline int of_i2c_get_board_info(struct device *dev, #endif /* CONFIG_OF */ +struct acpi_resource; +struct acpi_resource_i2c_serialbus; + #if IS_ENABLED(CONFIG_ACPI) +bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, + struct acpi_resource_i2c_serialbus **i2c); u32 i2c_acpi_find_bus_speed(struct device *dev); struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, struct i2c_board_info *info); #else +static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, + struct acpi_resource_i2c_serialbus **i2c) +{ + return false; +} static inline u32 i2c_acpi_find_bus_speed(struct device *dev) { return 0; -- cgit v1.2.3-59-g8ed1b From 3d5b38684951564d07e3f4bb482ef9491f49ced1 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 17 Jan 2019 03:14:54 +0000 Subject: i2c: imx: Fix inconsistent IS_ERR and PTR_ERR in i2c_imx_dma_request() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the call to PTR_ERR to access the value just tested by IS_ERR. Fixes: 5b3a23a3cc94 ("i2c: imx: notify about real errors on dma i2c_imx_dma_request") Signed-off-by: YueHaibing Reviewed-by: Oleksij Rempel Reviewed-by: Uwe Kleine-König Acked-by: Esben Haabendal Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 09b124547669..42fed40198a0 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -287,7 +287,7 @@ static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma->chan_tx = dma_request_chan(dev, "tx"); if (IS_ERR(dma->chan_tx)) { - ret = PTR_ERR(dma->chan_rx); + ret = PTR_ERR(dma->chan_tx); if (ret != -ENODEV && ret != -EPROBE_DEFER) dev_err(dev, "can't request DMA tx channel (%d)\n", ret); goto fail_al; -- cgit v1.2.3-59-g8ed1b From 81d696c7c4ff8c981333159d072da65268bfe6d5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 22 Jan 2019 11:03:57 +0100 Subject: i2c: rcar: Fix clients using i2c from suspend callback When doing s2idle/s2ram on Salvator-X(S): WARNING: CPU: 2 PID: 971 at drivers/i2c/i2c-core-base.c:1869 __i2c_transfer+0x608/0x910 [...] Call trace: __i2c_transfer+0x608/0x910 i2c_smbus_xfer_emulated+0x158/0x5b0 __i2c_smbus_xfer+0x17c/0x818 i2c_smbus_xfer+0x64/0x98 i2c_smbus_read_byte_data+0x40/0x70 cs2000_bset.isra.1+0x2c/0x68 __cs2000_set_rate.constprop.7+0x80/0x148 cs2000_resume+0x18/0x20 dpm_run_callback+0x74/0x330 device_resume_early+0xd4/0x120 dpm_resume_early+0x158/0x4f8 suspend_devices_and_enter+0x36c/0xd98 [...] On second resume, the sound driver fails with: cs2000-cp 2-004f: pll lock failed rcar_sound ec500000.sound: can't use clk 1 As the CS2000 clock driver needs to send I2C messages during suspend, the I2C controller driver should be suspended later, and resumed earlier. Fix this by using the noirq sleep ops instead of the normal sleep ops, which are called after the late sleep ops, as used by the CS2000 clock driver. Fixes: 18569fa89a4db9ed ("i2c: rcar: add suspend/resume support") Signed-off-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 1d6390eed861..d6c0f50a6dab 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -1034,7 +1034,9 @@ static int rcar_i2c_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(rcar_i2c_pm_ops, rcar_i2c_suspend, rcar_i2c_resume); +static const struct dev_pm_ops rcar_i2c_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rcar_i2c_suspend, rcar_i2c_resume) +}; #define DEV_PM_OPS (&rcar_i2c_pm_ops) #else -- cgit v1.2.3-59-g8ed1b From 5b3e3b43b03bf84f1656c2f7afda6b73f945cc82 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:49 +0100 Subject: i2c: sh_mobile: simplify sending address for RX pd->pos won't be smaller than -1, so we can simplify the logic. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index a64f2ff3cb49..e18e3cedf817 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -392,13 +392,9 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) int real_pos; do { - if (pd->pos <= -1) { + if (sh_mobile_i2c_is_first_byte(pd)) { sh_mobile_i2c_get_data(pd, &data); - - if (sh_mobile_i2c_is_first_byte(pd)) - i2c_op(pd, OP_TX_FIRST, data); - else - i2c_op(pd, OP_TX, data); + i2c_op(pd, OP_TX_FIRST, data); break; } -- cgit v1.2.3-59-g8ed1b From 1f32fbdfc1e0af25c8f8572284c5d17d1cbcf9d7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:50 +0100 Subject: i2c: sh_mobile: remove get_data function It makes the code much easier comprehensible to explicitly code that the first byte will be client address and all the following bytes are the actual data. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index e18e3cedf817..27b57b376549 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -358,18 +358,6 @@ static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) return pd->pos == -1; } -static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd, - unsigned char *buf) -{ - switch (pd->pos) { - case -1: - *buf = i2c_8bit_addr_from_msg(pd->msg); - break; - default: - *buf = pd->msg->buf[pd->pos]; - } -} - static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) { unsigned char data; @@ -379,8 +367,13 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) return 1; } - sh_mobile_i2c_get_data(pd, &data); - i2c_op(pd, sh_mobile_i2c_is_first_byte(pd) ? OP_TX_FIRST : OP_TX, data); + if (sh_mobile_i2c_is_first_byte(pd)) { + data = i2c_8bit_addr_from_msg(pd->msg); + i2c_op(pd, OP_TX_FIRST, data); + } else { + data = pd->msg->buf[pd->pos]; + i2c_op(pd, OP_TX, data); + } pd->pos++; return 0; @@ -393,7 +386,7 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) do { if (sh_mobile_i2c_is_first_byte(pd)) { - sh_mobile_i2c_get_data(pd, &data); + data = i2c_8bit_addr_from_msg(pd->msg); i2c_op(pd, OP_TX_FIRST, data); break; } -- cgit v1.2.3-59-g8ed1b From d16f2bfea3cc3ebc8d1d45bc24d4f9f2c852dddd Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:51 +0100 Subject: i2c: sh_mobile: drop 'data' argument from i2c_op function It is clear that we always send the address in TX_FIRST and data in TX. No need to pass it from the caller. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 41 ++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 27b57b376549..ae5804c12998 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -303,13 +303,12 @@ static int sh_mobile_i2c_v2_init(struct sh_mobile_i2c_data *pd) return sh_mobile_i2c_check_timing(pd); } -static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, - enum sh_mobile_i2c_op op, unsigned char data) +static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, enum sh_mobile_i2c_op op) { unsigned char ret = 0; unsigned long flags; - dev_dbg(pd->dev, "op %d, data in 0x%02x\n", op, data); + dev_dbg(pd->dev, "op %d\n", op); spin_lock_irqsave(&pd->lock, flags); @@ -317,12 +316,12 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, case OP_START: /* issue start and trigger DTE interrupt */ iic_wr(pd, ICCR, ICCR_ICE | ICCR_TRS | ICCR_BBSY); break; - case OP_TX_FIRST: /* disable DTE interrupt and write data */ + case OP_TX_FIRST: /* disable DTE interrupt and write client address */ iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE); - iic_wr(pd, ICDR, data); + iic_wr(pd, ICDR, i2c_8bit_addr_from_msg(pd->msg)); break; case OP_TX: /* write data */ - iic_wr(pd, ICDR, data); + iic_wr(pd, ICDR, pd->msg->buf[pd->pos]); break; case OP_TX_STOP: /* issue a stop (or rep_start) */ iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS @@ -360,20 +359,15 @@ static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) { - unsigned char data; - if (pd->pos == pd->msg->len) { - i2c_op(pd, OP_TX_STOP, 0); + i2c_op(pd, OP_TX_STOP); return 1; } - if (sh_mobile_i2c_is_first_byte(pd)) { - data = i2c_8bit_addr_from_msg(pd->msg); - i2c_op(pd, OP_TX_FIRST, data); - } else { - data = pd->msg->buf[pd->pos]; - i2c_op(pd, OP_TX, data); - } + if (sh_mobile_i2c_is_first_byte(pd)) + i2c_op(pd, OP_TX_FIRST); + else + i2c_op(pd, OP_TX); pd->pos++; return 0; @@ -386,13 +380,12 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) do { if (sh_mobile_i2c_is_first_byte(pd)) { - data = i2c_8bit_addr_from_msg(pd->msg); - i2c_op(pd, OP_TX_FIRST, data); + i2c_op(pd, OP_TX_FIRST); break; } if (pd->pos == 0) { - i2c_op(pd, OP_TX_TO_RX, 0); + i2c_op(pd, OP_TX_TO_RX); break; } @@ -401,18 +394,18 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) if (pd->pos == pd->msg->len) { if (pd->stop_after_dma) { /* Simulate PIO end condition after DMA transfer */ - i2c_op(pd, OP_RX_STOP, 0); + i2c_op(pd, OP_RX_STOP); pd->pos++; break; } if (real_pos < 0) { - i2c_op(pd, OP_RX_STOP, 0); + i2c_op(pd, OP_RX_STOP); break; } - data = i2c_op(pd, OP_RX_STOP_DATA, 0); + data = i2c_op(pd, OP_RX_STOP_DATA); } else if (real_pos >= 0) { - data = i2c_op(pd, OP_RX, 0); + data = i2c_op(pd, OP_RX); } if (real_pos >= 0) @@ -687,7 +680,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, start_ch(pd, msg, do_start); if (do_start) - i2c_op(pd, OP_START, 0); + i2c_op(pd, OP_START); /* The interrupt handler takes care of the rest... */ timeout = wait_event_timeout(pd->wait, -- cgit v1.2.3-59-g8ed1b From e659f36d46904b5e0e860401c0454fcf6025f419 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:52 +0100 Subject: i2c: sh_mobile: remove is_first_byte function All state machines deal with pd->pos values. This helper function is an exception and makes it only more confusing. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index ae5804c12998..ef9101cde29f 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -352,11 +352,6 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, enum sh_mobile_i2c_op return ret; } -static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) -{ - return pd->pos == -1; -} - static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) { if (pd->pos == pd->msg->len) { @@ -364,7 +359,7 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) return 1; } - if (sh_mobile_i2c_is_first_byte(pd)) + if (pd->pos == -1) i2c_op(pd, OP_TX_FIRST); else i2c_op(pd, OP_TX); @@ -379,7 +374,7 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) int real_pos; do { - if (sh_mobile_i2c_is_first_byte(pd)) { + if (pd->pos == -1) { i2c_op(pd, OP_TX_FIRST); break; } -- cgit v1.2.3-59-g8ed1b From 0130e3bfa9fa102252ad318d0f5608cc09822baa Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:53 +0100 Subject: i2c: sh_mobile: replace break; with if-block In preparation to remove the do-while-loop. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index ef9101cde29f..ab6969ed7eff 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -394,11 +394,10 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) break; } - if (real_pos < 0) { + if (real_pos < 0) i2c_op(pd, OP_RX_STOP); - break; - } - data = i2c_op(pd, OP_RX_STOP_DATA); + else + data = i2c_op(pd, OP_RX_STOP_DATA); } else if (real_pos >= 0) { data = i2c_op(pd, OP_RX); } -- cgit v1.2.3-59-g8ed1b From 63c524600353a9c2d097041863a9ab36ed2db79b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:54 +0100 Subject: i2c: sh_mobile: refactor rx isr Remove the do_while loop which was just there to have an easy exit with "break;" and replace it with if-else-blocks which should make the state machine clearer. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 50 ++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index ab6969ed7eff..dd4df961d8eb 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -373,39 +373,31 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) unsigned char data; int real_pos; - do { - if (pd->pos == -1) { - i2c_op(pd, OP_TX_FIRST); - break; - } - - if (pd->pos == 0) { - i2c_op(pd, OP_TX_TO_RX); - break; - } - - real_pos = pd->pos - 2; + real_pos = pd->pos - 2; - if (pd->pos == pd->msg->len) { - if (pd->stop_after_dma) { - /* Simulate PIO end condition after DMA transfer */ - i2c_op(pd, OP_RX_STOP); - pd->pos++; - break; - } - - if (real_pos < 0) - i2c_op(pd, OP_RX_STOP); - else - data = i2c_op(pd, OP_RX_STOP_DATA); - } else if (real_pos >= 0) { - data = i2c_op(pd, OP_RX); + if (pd->pos == -1) { + i2c_op(pd, OP_TX_FIRST); + } else if (pd->pos == 0) { + i2c_op(pd, OP_TX_TO_RX); + } else if (pd->pos == pd->msg->len) { + if (pd->stop_after_dma) { + /* Simulate PIO end condition after DMA transfer */ + i2c_op(pd, OP_RX_STOP); + pd->pos++; + goto done; } - if (real_pos >= 0) - pd->msg->buf[real_pos] = data; - } while (0); + if (real_pos < 0) + i2c_op(pd, OP_RX_STOP); + else + data = i2c_op(pd, OP_RX_STOP_DATA); + } else if (real_pos >= 0) { + data = i2c_op(pd, OP_RX); + } + if (real_pos >= 0) + pd->msg->buf[real_pos] = data; + done: pd->pos++; return pd->pos == (pd->msg->len + 2); } -- cgit v1.2.3-59-g8ed1b From 9d899ed4009976c5cb26196afa6d28b82d78010b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:55 +0100 Subject: i2c: sh_mobile: update copyright and comments Update copyright years and add Renesas to it. Add/update comments to make driver easier to understand. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index dd4df961d8eb..554bd79e0891 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -2,8 +2,7 @@ /* * SuperH Mobile I2C Controller * - * Copyright (C) 2014 Wolfram Sang - * + * Copyright (C) 2014-19 Wolfram Sang * Copyright (C) 2008 Magnus Damm * * Portions of the code based on out-of-tree driver i2c-sh7343.c @@ -373,6 +372,7 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) unsigned char data; int real_pos; + /* switch from TX (address) to RX (data) adds two interrupts */ real_pos = pd->pos - 2; if (pd->pos == -1) { @@ -717,8 +717,7 @@ static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = { }; /* - * r8a7740 chip has lasting errata on I2C I/O pad reset. - * this is work-around for it. + * r8a7740 has an errata regarding I2C I/O pad reset needing this workaround. */ static int sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd) { -- cgit v1.2.3-59-g8ed1b From 44783efdfed190088066d4c3470766c28da38a21 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Wed, 16 Jan 2019 11:39:43 -0800 Subject: i2c: aspeed: Remove hard-coded bus timeout value setting This commit removes hard-coded bus timeout value setting so that it can be set by i2c-core-base. Signed-off-by: Jae Hyun Yoo Reviewed-by: Joel Stanley Reviewed-by: Brendan Higgins Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-aspeed.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 8dc9161ced38..833b6b6a4c7e 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -930,7 +930,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) init_completion(&bus->cmd_complete); bus->adap.owner = THIS_MODULE; bus->adap.retries = 0; - bus->adap.timeout = 5 * HZ; bus->adap.algo = &aspeed_i2c_algo; bus->adap.dev.parent = &pdev->dev; bus->adap.dev.of_node = pdev->dev.of_node; -- cgit v1.2.3-59-g8ed1b From e814e688413aabd7b0d75e2a8ed1caa472951dec Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 25 Jan 2019 14:11:42 +0100 Subject: i2c: of: Try to find an I2C adapter matching the parent If an I2C adapter doesn't match the provided device tree node, also try matching the parent's device tree node. This allows finding an adapter based on the device node of the parent device that was used to register it. This fixes a regression on Tegra124-based Chromebooks (Nyan) where the eDP controller registers an I2C adapter that is used to read to EDID. After commit 993a815dcbb2 ("dt-bindings: panel: Add missing .txt suffix") this stopped working because the I2C adapter could no longer be found. The approach in this patch fixes the regression without introducing the issues that the above commit solved. Fixes: 17ab7806de0c ("drm: don't link DP aux i2c adapter to the hardware device node") Signed-off-by: Thierry Reding Tested-by: Tristan Bastian Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-of.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c index 6cb7ad608bcd..0f01cdba9d2c 100644 --- a/drivers/i2c/i2c-core-of.c +++ b/drivers/i2c/i2c-core-of.c @@ -121,6 +121,17 @@ static int of_dev_node_match(struct device *dev, void *data) return dev->of_node == data; } +static int of_dev_or_parent_node_match(struct device *dev, void *data) +{ + if (dev->of_node == data) + return 1; + + if (dev->parent) + return dev->parent->of_node == data; + + return 0; +} + /* must call put_device() when done with returned i2c_client device */ struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) { @@ -145,7 +156,8 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) struct device *dev; struct i2c_adapter *adapter; - dev = bus_find_device(&i2c_bus_type, NULL, node, of_dev_node_match); + dev = bus_find_device(&i2c_bus_type, NULL, node, + of_dev_or_parent_node_match); if (!dev) return NULL; -- cgit v1.2.3-59-g8ed1b From 9f21ef41a5c55c8d6f3c2f8a8773126f698f2a7d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 28 Jan 2019 23:25:33 +0100 Subject: i2c: Add DT bindings for Xscale I2C masters This adds a device tree binding for Intel XScale I2C masters. We define compatible strings for the iop3xx and ixp4xx chip families. Signed-off-by: Linus Walleij Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/i2c-xscale.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-xscale.txt diff --git a/Documentation/devicetree/bindings/i2c/i2c-xscale.txt b/Documentation/devicetree/bindings/i2c/i2c-xscale.txt new file mode 100644 index 000000000000..dcc8390e0d24 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-xscale.txt @@ -0,0 +1,20 @@ +i2c Controller on XScale platforms such as IOP3xx and IXP4xx + +Required properties: +- compatible : Must be one of + "intel,iop3xx-i2c" + "intel,ixp4xx-i2c"; +- reg +- #address-cells = <1>; +- #size-cells = <0>; + +Optional properties: +- Child nodes conforming to i2c bus binding + +Example: + +i2c@c8011000 { + compatible = "intel,ixp4xx-i2c"; + reg = <0xc8011000 0x18>; + interrupts = <33 IRQ_TYPE_LEVEL_LOW>; +}; -- cgit v1.2.3-59-g8ed1b From cac28ac2a87c38a026d6d7601b06fe16f41ed751 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 28 Jan 2019 23:25:34 +0100 Subject: i2c: iop3xx: Add device tree probing This adds device tree probing support for the IOP3xx I2C master. Signed-off-by: Linus Walleij Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-iop3xx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index 85cbe4b55578..a34cb3848280 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -471,6 +471,7 @@ iop3xx_i2c_probe(struct platform_device *pdev) new_adapter->owner = THIS_MODULE; new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; new_adapter->dev.parent = &pdev->dev; + new_adapter->dev.of_node = pdev->dev.of_node; new_adapter->nr = pdev->id; /* @@ -508,12 +509,19 @@ out: return ret; } +static const struct of_device_id i2c_iop3xx_match[] = { + { .compatible = "intel,iop3xx-i2c", }, + { .compatible = "intel,ixp4xx-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_iop3xx_match); static struct platform_driver iop3xx_i2c_driver = { .probe = iop3xx_i2c_probe, .remove = iop3xx_i2c_remove, .driver = { .name = "IOP3xx-I2C", + .of_match_table = i2c_iop3xx_match, }, }; -- cgit v1.2.3-59-g8ed1b From 2292822e1576c89191a65c3d0da584d75d3c033f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 19 Jan 2019 13:16:53 +0100 Subject: i2c: algo-bit: include main i2c header We are using symbols from it, so we should include it directly. Found after sorting includes in a driver. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- include/linux/i2c-algo-bit.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/i2c-algo-bit.h b/include/linux/i2c-algo-bit.h index 63904ba6887e..d64cebc6e65a 100644 --- a/include/linux/i2c-algo-bit.h +++ b/include/linux/i2c-algo-bit.h @@ -25,6 +25,8 @@ #ifndef _LINUX_I2C_ALGO_BIT_H #define _LINUX_I2C_ALGO_BIT_H +#include + /* --- Defines for bit-adapters --------------------------------------- */ /* * This struct contains the hw-dependent functions of bit-style adapters to -- cgit v1.2.3-59-g8ed1b From 738ac0679b969776a638daf2cfb5011049d467da Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 19 Jan 2019 13:16:54 +0100 Subject: i2c: algo-bit: convert to SPDX header And use kernel style for the remaining comments in the header. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/algos/i2c-algo-bit.c | 25 ++++++++----------------- include/linux/i2c-algo-bit.h | 31 ++++++++----------------------- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index c33dcfb87993..5e5990a83da5 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -1,21 +1,12 @@ -/* ------------------------------------------------------------------------- - * i2c-algo-bit.c i2c driver algorithms for bit-shift adapters - * ------------------------------------------------------------------------- +// SPDX-License-Identifier: GPL-2.0+ +/* + * i2c-algo-bit.c: i2c driver algorithms for bit-shift adapters + * * Copyright (C) 1995-2000 Simon G. Vogl - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - * ------------------------------------------------------------------------- */ - -/* With some changes from Frodo Looijaard , Kyösti Mälkki - and Jean Delvare */ + * + * With some changes from Frodo Looijaard , Kyösti Mälkki + * and Jean Delvare + */ #include #include diff --git a/include/linux/i2c-algo-bit.h b/include/linux/i2c-algo-bit.h index d64cebc6e65a..69045df78e2d 100644 --- a/include/linux/i2c-algo-bit.h +++ b/include/linux/i2c-algo-bit.h @@ -1,26 +1,11 @@ -/* ------------------------------------------------------------------------- */ -/* i2c-algo-bit.h i2c driver algorithms for bit-shift adapters */ -/* ------------------------------------------------------------------------- */ -/* Copyright (C) 1995-99 Simon G. Vogl - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - MA 02110-1301 USA. */ -/* ------------------------------------------------------------------------- */ - -/* With some changes from Kyösti Mälkki and even - Frodo Looijaard */ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * i2c-algo-bit.h: i2c driver algorithms for bit-shift adapters + * + * Copyright (C) 1995-99 Simon G. Vogl + * With some changes from Kyösti Mälkki and even + * Frodo Looijaard + */ #ifndef _LINUX_I2C_ALGO_BIT_H #define _LINUX_I2C_ALGO_BIT_H -- cgit v1.2.3-59-g8ed1b From 2f8aa465b918fee7d148617d451cffeee1fdd934 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 19 Jan 2019 13:16:55 +0100 Subject: i2c: gpio: sort includes Less risk for duplications when adding new ones. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-gpio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index c008d209f0b8..33f9d7ed1223 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -9,15 +9,15 @@ */ #include #include -#include +#include #include -#include +#include #include #include -#include -#include -#include #include +#include +#include +#include struct i2c_gpio_private_data { struct gpio_desc *sda; -- cgit v1.2.3-59-g8ed1b From 70ae5669b5b3f357a5c1d64152508506624a5c5d Mon Sep 17 00:00:00 2001 From: qii wang Date: Mon, 21 Jan 2019 15:59:29 +0800 Subject: dt-bindings: i2c: Add Mediatek MT7629 i2c binding Add MT7629 i2c binding to binding file. Signed-off-by: qii wang Reviewed-by: Rob Herring Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/i2c-mtk.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/i2c/i2c-mtk.txt b/Documentation/devicetree/bindings/i2c/i2c-mtk.txt index e199695b1c96..ee4c32454198 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mtk.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-mtk.txt @@ -10,6 +10,7 @@ Required properties: "mediatek,mt6589-i2c": for MediaTek MT6589 "mediatek,mt7622-i2c": for MediaTek MT7622 "mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623 + "mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629 "mediatek,mt8173-i2c": for MediaTek MT8173 - reg: physical base address of the controller and dma base, length of memory mapped region. -- cgit v1.2.3-59-g8ed1b From 93caa0dab77cb4bd6c8efd82042f5a6b73d2b87e Mon Sep 17 00:00:00 2001 From: qii wang Date: Mon, 21 Jan 2019 15:59:30 +0800 Subject: i2c: mediatek: speeds is replaced by macros definitions Different speeds have been defined by macros, so we use macros definitions. Signed-off-by: qii wang Reviewed-by: Nicolas Boichat Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index a74ef76705e0..7396449703a1 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -456,7 +456,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, control_reg = readw(i2c->base + OFFSET_CONTROL) & ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS); - if ((i2c->speed_hz > 400000) || (left_num >= 1)) + if ((i2c->speed_hz > MAX_FS_MODE_SPEED) || (left_num >= 1)) control_reg |= I2C_CONTROL_RS; if (i2c->op == I2C_MASTER_WRRD) @@ -465,7 +465,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, writew(control_reg, i2c->base + OFFSET_CONTROL); /* set start condition */ - if (i2c->speed_hz <= 100000) + if (i2c->speed_hz <= I2C_DEFAULT_SPEED) writew(I2C_ST_START_CON, i2c->base + OFFSET_EXT_CONF); else writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF); -- cgit v1.2.3-59-g8ed1b From 0056a54b335335f0231a80ecf81d03095037437e Mon Sep 17 00:00:00 2001 From: qii wang Date: Mon, 21 Jan 2019 15:59:31 +0800 Subject: i2c: mediatek: remove completion_done() Completion_done() is useless when we don't use its return value, so we remove it. Signed-off-by: qii wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 7396449703a1..660de1ee68ed 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -642,8 +642,6 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, return -ETIMEDOUT; } - completion_done(&i2c->msg_complete); - if (i2c->irq_stat & (I2C_HS_NACKERR | I2C_ACKERR)) { dev_dbg(i2c->dev, "addr: %x, transfer ACK error\n", msgs->addr); mtk_i2c_init_hw(i2c); -- cgit v1.2.3-59-g8ed1b From c6324fadeac80da60ab99d22cfe19873a31dd30d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 21 Jan 2019 15:16:56 +0100 Subject: i2c: gpio: fault-injector: sort debugfs files alphabetically There is no value in chronological sorting. Make it easier to follow for humans. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-gpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 33f9d7ed1223..362d3b9dd3b6 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -181,12 +181,12 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev) if (!priv->debug_dir) return; - debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); - debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir, priv, &fops_incomplete_addr_phase); debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir, priv, &fops_incomplete_write_byte); + debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); + debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); } static void i2c_gpio_fault_injector_exit(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 05bd07280d5f70920c3619998b3a79742e4cde30 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 21 Jan 2019 15:16:57 +0100 Subject: i2c: gpio: fault-injector: better SPHINX style in docs We don't use SPHINX currently in I2C documentation; but preparing the I2C fault injector docs already is a good idea since it makes it easier to read. Especially as new stuff is going to be added soon. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- Documentation/i2c/gpio-fault-injection | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/i2c/gpio-fault-injection b/Documentation/i2c/gpio-fault-injection index a4ce62090fd5..1a44e3edc0c4 100644 --- a/Documentation/i2c/gpio-fault-injection +++ b/Documentation/i2c/gpio-fault-injection @@ -1,3 +1,4 @@ +========================= Linux I2C fault injection ========================= @@ -13,6 +14,9 @@ mounted at /sys/kernel/debug. There will be a separate subdirectory per GPIO driven I2C bus. Each subdirectory will contain files to trigger the fault injection. They will be described now along with their intended use-cases. +Wire states +=========== + "scl" ----- @@ -34,10 +38,10 @@ I2C specification version 4, section 3.1.16) using the helpers of the Linux I2C core (see 'struct bus_recovery_info'). However, the bus recovery will not succeed because SDA is still pinned low until you manually release it again with "echo 1 > sda". A test with an automatic release can be done with the -following class of fault injectors. +"incomplete transfers" class of fault injectors. -Introduction to incomplete transfers ------------------------------------- +Incomplete transfers +==================== The following fault injectors create situations where SDA will be held low by a device. Bus recovery should be able to fix these situations. But please note: -- cgit v1.2.3-59-g8ed1b From d0051ca584609bd4100ece6ed9f04ae0430dd23b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 21 Jan 2019 18:07:59 +0100 Subject: i2c: rcar: comment maintenance Update copyright years and remove a comment which grew incorrect and useless over time. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index d6c0f50a6dab..498ba4b87833 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -2,8 +2,8 @@ /* * Driver for the Renesas R-Car I2C unit * - * Copyright (C) 2014-15 Wolfram Sang - * Copyright (C) 2011-2015 Renesas Electronics Corporation + * Copyright (C) 2014-19 Wolfram Sang + * Copyright (C) 2011-2019 Renesas Electronics Corporation * * Copyright (C) 2012-14 Renesas Solutions Corp. * Kuninori Morimoto @@ -355,9 +355,6 @@ static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv) rcar_i2c_prepare_msg(priv); } -/* - * interrupt functions - */ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv) { struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE -- cgit v1.2.3-59-g8ed1b From 2e5a662de36a92a95b5939273468b01785dc41ec Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 6 Feb 2019 08:16:51 +0100 Subject: i2c: cbus-gpio: Switch to use GPIO descriptors This augments the CBUS GPIO I2C driver to use GPIO descriptors for clock, sel and data. We drop the platform data that was only used for carrying GPIO numbers and use machine descriptor tables instead. Signed-off-by: Linus Walleij Tested-by: Aaro Koskinen Acked-by: Tony Lindgren Signed-off-by: Wolfram Sang --- arch/arm/mach-omap1/board-nokia770.c | 18 ++++--- drivers/i2c/busses/i2c-cbus-gpio.c | 80 +++++++++++------------------ include/linux/platform_data/i2c-cbus-gpio.h | 27 ---------- 3 files changed, 40 insertions(+), 85 deletions(-) delete mode 100644 include/linux/platform_data/i2c-cbus-gpio.h diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c index eb41db78cd47..10848f573d37 100644 --- a/arch/arm/mach-omap1/board-nokia770.c +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #include #include @@ -217,18 +217,19 @@ static inline void nokia770_mmc_init(void) #endif #if IS_ENABLED(CONFIG_I2C_CBUS_GPIO) -static struct i2c_cbus_platform_data nokia770_cbus_data = { - .clk_gpio = OMAP_MPUIO(9), - .dat_gpio = OMAP_MPUIO(10), - .sel_gpio = OMAP_MPUIO(11), +static struct gpiod_lookup_table nokia770_cbus_gpio_table = { + .dev_id = "i2c-cbus-gpio.2", + .table = { + GPIO_LOOKUP_IDX("mpuio", 9, NULL, 0, 0), /* clk */ + GPIO_LOOKUP_IDX("mpuio", 10, NULL, 1, 0), /* dat */ + GPIO_LOOKUP_IDX("mpuio", 11, NULL, 2, 0), /* sel */ + { }, + }, }; static struct platform_device nokia770_cbus_device = { .name = "i2c-cbus-gpio", .id = 2, - .dev = { - .platform_data = &nokia770_cbus_data, - }, }; static struct i2c_board_info nokia770_i2c_board_info_2[] __initdata = { @@ -257,6 +258,7 @@ static void __init nokia770_cbus_init(void) nokia770_i2c_board_info_2[1].irq = gpio_to_irq(tahvo_irq_gpio); i2c_register_board_info(2, nokia770_i2c_board_info_2, ARRAY_SIZE(nokia770_i2c_board_info_2)); + gpiod_add_lookup_table(&nokia770_cbus_gpio_table); platform_device_register(&nokia770_cbus_device); } #else /* CONFIG_I2C_CBUS_GPIO */ diff --git a/drivers/i2c/busses/i2c-cbus-gpio.c b/drivers/i2c/busses/i2c-cbus-gpio.c index b4f91e48948a..72df563477b1 100644 --- a/drivers/i2c/busses/i2c-cbus-gpio.c +++ b/drivers/i2c/busses/i2c-cbus-gpio.c @@ -18,16 +18,14 @@ #include #include -#include #include #include #include #include #include -#include +#include #include #include -#include /* * Bit counts are derived from Nokia implementation. These should be checked @@ -39,9 +37,9 @@ struct cbus_host { spinlock_t lock; /* host lock */ struct device *dev; - int clk_gpio; - int dat_gpio; - int sel_gpio; + struct gpio_desc *clk; + struct gpio_desc *dat; + struct gpio_desc *sel; }; /** @@ -51,9 +49,9 @@ struct cbus_host { */ static void cbus_send_bit(struct cbus_host *host, unsigned bit) { - gpio_set_value(host->dat_gpio, bit ? 1 : 0); - gpio_set_value(host->clk_gpio, 1); - gpio_set_value(host->clk_gpio, 0); + gpiod_set_value(host->dat, bit ? 1 : 0); + gpiod_set_value(host->clk, 1); + gpiod_set_value(host->clk, 0); } /** @@ -78,9 +76,9 @@ static int cbus_receive_bit(struct cbus_host *host) { int ret; - gpio_set_value(host->clk_gpio, 1); - ret = gpio_get_value(host->dat_gpio); - gpio_set_value(host->clk_gpio, 0); + gpiod_set_value(host->clk, 1); + ret = gpiod_get_value(host->dat); + gpiod_set_value(host->clk, 0); return ret; } @@ -123,10 +121,10 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev, spin_lock_irqsave(&host->lock, flags); /* Reset state and start of transfer, SEL stays down during transfer */ - gpio_set_value(host->sel_gpio, 0); + gpiod_set_value(host->sel, 0); /* Set the DAT pin to output */ - gpio_direction_output(host->dat_gpio, 1); + gpiod_direction_output(host->dat, 1); /* Send the device address */ cbus_send_data(host, dev, CBUS_ADDR_BITS); @@ -141,12 +139,12 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev, cbus_send_data(host, data, 16); ret = 0; } else { - ret = gpio_direction_input(host->dat_gpio); + ret = gpiod_direction_input(host->dat); if (ret) { dev_dbg(host->dev, "failed setting direction\n"); goto out; } - gpio_set_value(host->clk_gpio, 1); + gpiod_set_value(host->clk, 1); ret = cbus_receive_word(host); if (ret < 0) { @@ -156,9 +154,9 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev, } /* Indicate end of transfer, SEL goes up until next transfer */ - gpio_set_value(host->sel_gpio, 1); - gpio_set_value(host->clk_gpio, 1); - gpio_set_value(host->clk_gpio, 0); + gpiod_set_value(host->sel, 1); + gpiod_set_value(host->clk, 1); + gpiod_set_value(host->clk, 0); out: spin_unlock_irqrestore(&host->lock, flags); @@ -214,7 +212,6 @@ static int cbus_i2c_probe(struct platform_device *pdev) { struct i2c_adapter *adapter; struct cbus_host *chost; - int ret; adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter), GFP_KERNEL); @@ -225,22 +222,20 @@ static int cbus_i2c_probe(struct platform_device *pdev) if (!chost) return -ENOMEM; - if (pdev->dev.of_node) { - struct device_node *dnode = pdev->dev.of_node; - if (of_gpio_count(dnode) != 3) - return -ENODEV; - chost->clk_gpio = of_get_gpio(dnode, 0); - chost->dat_gpio = of_get_gpio(dnode, 1); - chost->sel_gpio = of_get_gpio(dnode, 2); - } else if (dev_get_platdata(&pdev->dev)) { - struct i2c_cbus_platform_data *pdata = - dev_get_platdata(&pdev->dev); - chost->clk_gpio = pdata->clk_gpio; - chost->dat_gpio = pdata->dat_gpio; - chost->sel_gpio = pdata->sel_gpio; - } else { + if (gpiod_count(&pdev->dev, NULL) != 3) return -ENODEV; - } + chost->clk = devm_gpiod_get_index(&pdev->dev, NULL, 0, GPIOD_OUT_LOW); + if (IS_ERR(chost->clk)) + return PTR_ERR(chost->clk); + chost->dat = devm_gpiod_get_index(&pdev->dev, NULL, 1, GPIOD_IN); + if (IS_ERR(chost->dat)) + return PTR_ERR(chost->dat); + chost->sel = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_OUT_HIGH); + if (IS_ERR(chost->sel)) + return PTR_ERR(chost->sel); + gpiod_set_consumer_name(chost->clk, "CBUS clk"); + gpiod_set_consumer_name(chost->dat, "CBUS dat"); + gpiod_set_consumer_name(chost->sel, "CBUS sel"); adapter->owner = THIS_MODULE; adapter->class = I2C_CLASS_HWMON; @@ -254,21 +249,6 @@ static int cbus_i2c_probe(struct platform_device *pdev) spin_lock_init(&chost->lock); chost->dev = &pdev->dev; - ret = devm_gpio_request_one(&pdev->dev, chost->clk_gpio, - GPIOF_OUT_INIT_LOW, "CBUS clk"); - if (ret) - return ret; - - ret = devm_gpio_request_one(&pdev->dev, chost->dat_gpio, GPIOF_IN, - "CBUS data"); - if (ret) - return ret; - - ret = devm_gpio_request_one(&pdev->dev, chost->sel_gpio, - GPIOF_OUT_INIT_HIGH, "CBUS sel"); - if (ret) - return ret; - i2c_set_adapdata(adapter, chost); platform_set_drvdata(pdev, adapter); diff --git a/include/linux/platform_data/i2c-cbus-gpio.h b/include/linux/platform_data/i2c-cbus-gpio.h deleted file mode 100644 index 6faa992a9502..000000000000 --- a/include/linux/platform_data/i2c-cbus-gpio.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * i2c-cbus-gpio.h - CBUS I2C platform_data definition - * - * Copyright (C) 2004-2009 Nokia Corporation - * - * Written by Felipe Balbi and Aaro Koskinen. - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file "COPYING" in the main directory of this - * archive for more details. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __INCLUDE_LINUX_I2C_CBUS_GPIO_H -#define __INCLUDE_LINUX_I2C_CBUS_GPIO_H - -struct i2c_cbus_platform_data { - int dat_gpio; - int clk_gpio; - int sel_gpio; -}; - -#endif /* __INCLUDE_LINUX_I2C_CBUS_GPIO_H */ -- cgit v1.2.3-59-g8ed1b From 43116a0d9a533af24728ea7898c1d984446caa58 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 4 Feb 2019 23:00:09 +0100 Subject: i2c: sh_mobile: sort compatible entries Makes it easier to add new ones. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 554bd79e0891..0e834d97ca51 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -773,11 +773,11 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config }, - { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config }, - { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a77990", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config }, + { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config }, + { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config }, { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, {}, }; -- cgit v1.2.3-59-g8ed1b From e8a27567509b2439b3d8e5937a11483fb25c0f0c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 4 Feb 2019 23:00:10 +0100 Subject: i2c: sh_mobile: use new clock calculation formulas for Gen3 We could finally measure the clock on an Ebisu board. The new formula gives way better results, i.e. 100kHz instead of 106kHz and 400kHz instead of 387kHz. Switch to these formulas for all Gen3 SoCs. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 0e834d97ca51..7ef3b095f7ae 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -773,11 +773,11 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config }, - { .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config }, - { .compatible = "renesas,iic-r8a77990", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7795", .data = &v2_freq_calc_dt_config }, + { .compatible = "renesas,iic-r8a77990", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config }, { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config }, - { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config }, + { .compatible = "renesas,rcar-gen3-iic", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, {}, }; -- cgit v1.2.3-59-g8ed1b From 8fbd9b08b73e6de17b958a107e69a998faba18fa Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 5 Feb 2019 18:00:46 +0100 Subject: i2c: sh_mobile: use new clock calculation formulas for Gen2 We measured the clock on a Lager and an Ebisu board. The new formula gives better results for both. So after Gen3, switch to this formula for all Gen2 SoCs. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 7ef3b095f7ae..8777af4c695e 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -769,14 +769,14 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config }, { .compatible = "renesas,iic-r8a774c0", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7790", .data = &v2_freq_calc_dt_config }, - { .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config }, - { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config }, - { .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config }, - { .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7791", .data = &v2_freq_calc_dt_config }, + { .compatible = "renesas,iic-r8a7792", .data = &v2_freq_calc_dt_config }, + { .compatible = "renesas,iic-r8a7793", .data = &v2_freq_calc_dt_config }, + { .compatible = "renesas,iic-r8a7794", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,iic-r8a7795", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,iic-r8a77990", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config }, - { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config }, + { .compatible = "renesas,rcar-gen2-iic", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,rcar-gen3-iic", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, {}, -- cgit v1.2.3-59-g8ed1b From 60c1d5605b67218cc600588af59260c0d1144495 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 5 Feb 2019 13:58:41 +0100 Subject: i2c: gpio: merge two very similar comments I think it is clear enough if we have the explanation once and make it clear it is applicable for both SCL and SDA. Reword it a little with the help of Simon's native language skills :) Signed-off-by: Simon Horman Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-gpio.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 362d3b9dd3b6..2d532cc042f5 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -286,11 +286,11 @@ static int i2c_gpio_probe(struct platform_device *pdev) /* * First get the GPIO pins; if it fails, we'll defer the probe. - * If the SDA line is marked from platform data or device tree as - * "open drain" it means something outside of our control is making - * this line being handled as open drain, and we should just handle - * it as any other output. Else we enforce open drain as this is - * required for an I2C bus. + * If the SCL/SDA lines are marked "open drain" by platform data or + * device tree then this means that something outside of our control is + * marking these lines to be handled as open drain, and we should just + * handle them as we handle any other output. Else we enforce open + * drain as this is required for an I2C bus. */ if (pdata->sda_is_open_drain) gflags = GPIOD_OUT_HIGH; @@ -300,13 +300,6 @@ static int i2c_gpio_probe(struct platform_device *pdev) if (IS_ERR(priv->sda)) return PTR_ERR(priv->sda); - /* - * If the SCL line is marked from platform data or device tree as - * "open drain" it means something outside of our control is making - * this line being handled as open drain, and we should just handle - * it as any other output. Else we enforce open drain as this is - * required for an I2C bus. - */ if (pdata->scl_is_open_drain) gflags = GPIOD_OUT_HIGH; else -- cgit v1.2.3-59-g8ed1b From 18769445ca55c2277d29e35ffda7e9a206b1758f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 5 Feb 2019 14:37:25 +0100 Subject: i2c: rcar: refactor TCYC handling The latest documentation made it clear that we need to initialize the TCYC value independently of DMA. The old code used TCYC06 (wrongly) for non-DMA transfers. The new code sets TCYC up independently from DMA. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 498ba4b87833..dd52a068b140 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -39,8 +39,8 @@ #define ICSAR 0x1C /* slave address */ #define ICMAR 0x20 /* master address */ #define ICRXTX 0x24 /* data port */ -#define ICDMAER 0x3c /* DMA enable */ -#define ICFBSCR 0x38 /* first bit setup cycle */ +#define ICFBSCR 0x38 /* first bit setup cycle (Gen3) */ +#define ICDMAER 0x3c /* DMA enable (Gen3) */ /* ICSCR */ #define SDBS (1 << 3) /* slave data buffer select */ @@ -83,7 +83,6 @@ #define TMDMAE (1 << 0) /* DMA Master Transmitted Enable */ /* ICFBSCR */ -#define TCYC06 0x04 /* 6*Tcyc delay 1st bit between SDA and SCL */ #define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */ @@ -212,6 +211,10 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv) rcar_i2c_write(priv, ICMSR, 0); /* start clock */ rcar_i2c_write(priv, ICCCR, priv->icccr); + + if (priv->devtype == I2C_RCAR_GEN3) + rcar_i2c_write(priv, ICFBSCR, TCYC17); + } static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv) @@ -363,9 +366,6 @@ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv) /* Disable DMA Master Received/Transmitted */ rcar_i2c_write(priv, ICDMAER, 0); - /* Reset default delay */ - rcar_i2c_write(priv, ICFBSCR, TCYC06); - dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg), sg_dma_len(&priv->sg), priv->dma_direction); @@ -461,9 +461,6 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv) return; } - /* Set delay for DMA operations */ - rcar_i2c_write(priv, ICFBSCR, TCYC17); - /* Enable DMA Master Received/Transmitted */ if (read) rcar_i2c_write(priv, ICDMAER, RMDMAE); -- cgit v1.2.3-59-g8ed1b From 4fa882c9f628b312d697cfcefaa6e973ce8ece3e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 7 Aug 2018 12:07:43 +0200 Subject: eeprom: at24: remove at24_platform_data There are no more users of at24_platform_data. Remove the relevant header and modify the driver code to not use it anymore. Signed-off-by: Bartosz Golaszewski --- MAINTAINERS | 1 - drivers/misc/eeprom/at24.c | 162 +++++++++++++++++-------------------- include/linux/platform_data/at24.h | 60 -------------- 3 files changed, 75 insertions(+), 148 deletions(-) delete mode 100644 include/linux/platform_data/at24.h diff --git a/MAINTAINERS b/MAINTAINERS index 9919840d54cd..d901919dd475 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2503,7 +2503,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git S: Maintained F: Documentation/devicetree/bindings/eeprom/at24.txt F: drivers/misc/eeprom/at24.c -F: include/linux/platform_data/at24.h ATA OVER ETHERNET (AOE) DRIVER M: "Ed L. Cashin" diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index ddfcf4ade7bf..b806a403ca46 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -22,10 +22,24 @@ #include #include #include -#include #include #include +/* Address pointer is 16 bit. */ +#define AT24_FLAG_ADDR16 BIT(7) +/* sysfs-entry will be read-only. */ +#define AT24_FLAG_READONLY BIT(6) +/* sysfs-entry will be world-readable. */ +#define AT24_FLAG_IRUGO BIT(5) +/* Take always 8 addresses (24c00). */ +#define AT24_FLAG_TAKE8ADDR BIT(4) +/* Factory-programmed serial number. */ +#define AT24_FLAG_SERIAL BIT(3) +/* Factory-programmed mac address. */ +#define AT24_FLAG_MAC BIT(2) +/* Does not auto-rollover reads to the next slave address. */ +#define AT24_FLAG_NO_RDROL BIT(1) + /* * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. * Differences between different vendor product lines (like Atmel AT24C or @@ -107,10 +121,6 @@ module_param_named(write_timeout, at24_write_timeout, uint, 0); MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); struct at24_chip_data { - /* - * these fields mirror their equivalents in - * struct at24_platform_data - */ u32 byte_len; u8 flags; }; @@ -471,63 +481,11 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) return 0; } -static void at24_properties_to_pdata(struct device *dev, - struct at24_platform_data *chip) -{ - int err; - u32 val; - - if (device_property_present(dev, "read-only")) - chip->flags |= AT24_FLAG_READONLY; - if (device_property_present(dev, "no-read-rollover")) - chip->flags |= AT24_FLAG_NO_RDROL; - - err = device_property_read_u32(dev, "address-width", &val); - if (!err) { - switch (val) { - case 8: - if (chip->flags & AT24_FLAG_ADDR16) - dev_warn(dev, "Override address width to be 8, while default is 16\n"); - chip->flags &= ~AT24_FLAG_ADDR16; - break; - case 16: - chip->flags |= AT24_FLAG_ADDR16; - break; - default: - dev_warn(dev, "Bad \"address-width\" property: %u\n", - val); - } - } - - err = device_property_read_u32(dev, "size", &val); - if (!err) - chip->byte_len = val; - - err = device_property_read_u32(dev, "pagesize", &val); - if (!err) { - chip->page_size = val; - } else { - /* - * This is slow, but we can't know all eeproms, so we better - * play safe. Specifying custom eeprom-types via platform_data - * is recommended anyhow. - */ - chip->page_size = 1; - } -} - -static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata) +static const struct at24_chip_data *at24_get_chip_data(struct device *dev) { struct device_node *of_node = dev->of_node; const struct at24_chip_data *cdata; const struct i2c_device_id *id; - struct at24_platform_data *pd; - - pd = dev_get_platdata(dev); - if (pd) { - memcpy(pdata, pd, sizeof(*pdata)); - return 0; - } id = i2c_match_id(at24_ids, to_i2c_client(dev)); @@ -544,13 +502,9 @@ static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata) cdata = acpi_device_get_match_data(dev); if (!cdata) - return -ENODEV; + return ERR_PTR(-ENODEV); - pdata->byte_len = cdata->byte_len; - pdata->flags = cdata->flags; - at24_properties_to_pdata(dev, pdata); - - return 0; + return cdata; } static void at24_remove_dummy_clients(struct at24_data *at24) @@ -619,7 +573,8 @@ static int at24_probe(struct i2c_client *client) { struct regmap_config regmap_config = { }; struct nvmem_config nvmem_config = { }; - struct at24_platform_data pdata = { }; + u32 byte_len, page_size, flags, addrw; + const struct at24_chip_data *cdata; struct device *dev = &client->dev; bool i2c_fn_i2c, i2c_fn_block; unsigned int i, num_addresses; @@ -634,35 +589,72 @@ static int at24_probe(struct i2c_client *client) i2c_fn_block = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK); - err = at24_get_pdata(dev, &pdata); + cdata = at24_get_chip_data(dev); + if (IS_ERR(cdata)) + return PTR_ERR(cdata); + + err = device_property_read_u32(dev, "pagesize", &page_size); if (err) - return err; + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + page_size = 1; + + flags = cdata->flags; + if (device_property_present(dev, "read-only")) + flags |= AT24_FLAG_READONLY; + if (device_property_present(dev, "no-read-rollover")) + flags |= AT24_FLAG_NO_RDROL; + + err = device_property_read_u32(dev, "address-width", &addrw); + if (!err) { + switch (addrw) { + case 8: + if (flags & AT24_FLAG_ADDR16) + dev_warn(dev, + "Override address width to be 8, while default is 16\n"); + flags &= ~AT24_FLAG_ADDR16; + break; + case 16: + flags |= AT24_FLAG_ADDR16; + break; + default: + dev_warn(dev, "Bad \"address-width\" property: %u\n", + addrw); + } + } + + err = device_property_read_u32(dev, "size", &byte_len); + if (err) + byte_len = cdata->byte_len; if (!i2c_fn_i2c && !i2c_fn_block) - pdata.page_size = 1; + page_size = 1; - if (!pdata.page_size) { + if (!page_size) { dev_err(dev, "page_size must not be 0!\n"); return -EINVAL; } - if (!is_power_of_2(pdata.page_size)) + if (!is_power_of_2(page_size)) dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); - if (pdata.flags & AT24_FLAG_TAKE8ADDR) + if (flags & AT24_FLAG_TAKE8ADDR) num_addresses = 8; else - num_addresses = DIV_ROUND_UP(pdata.byte_len, - (pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256); + num_addresses = DIV_ROUND_UP(byte_len, + (flags & AT24_FLAG_ADDR16) ? 65536 : 256); - if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) { + if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) { dev_err(dev, "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); return -EINVAL; } regmap_config.val_bits = 8; - regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8; + regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8; regmap_config.disable_locking = true; regmap = devm_regmap_init_i2c(client, ®map_config); @@ -675,11 +667,11 @@ static int at24_probe(struct i2c_client *client) return -ENOMEM; mutex_init(&at24->lock); - at24->byte_len = pdata.byte_len; - at24->page_size = pdata.page_size; - at24->flags = pdata.flags; + at24->byte_len = byte_len; + at24->page_size = page_size; + at24->flags = flags; at24->num_addresses = num_addresses; - at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len); + at24->offset_adj = at24_get_offset_adj(flags, byte_len); at24->client[0].client = client; at24->client[0].regmap = regmap; @@ -687,10 +679,10 @@ static int at24_probe(struct i2c_client *client) if (IS_ERR(at24->wp_gpio)) return PTR_ERR(at24->wp_gpio); - writable = !(pdata.flags & AT24_FLAG_READONLY); + writable = !(flags & AT24_FLAG_READONLY); if (writable) { at24->write_max = min_t(unsigned int, - pdata.page_size, at24_io_limit); + page_size, at24_io_limit); if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX) at24->write_max = I2C_SMBUS_BLOCK_MAX; } @@ -733,7 +725,7 @@ static int at24_probe(struct i2c_client *client) nvmem_config.priv = at24; nvmem_config.stride = 1; nvmem_config.word_size = 1; - nvmem_config.size = pdata.byte_len; + nvmem_config.size = byte_len; at24->nvmem = devm_nvmem_register(dev, &nvmem_config); if (IS_ERR(at24->nvmem)) { @@ -742,13 +734,9 @@ static int at24_probe(struct i2c_client *client) } dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n", - pdata.byte_len, client->name, + byte_len, client->name, writable ? "writable" : "read-only", at24->write_max); - /* export data to kernel code */ - if (pdata.setup) - pdata.setup(at24->nvmem, pdata.context); - return 0; err_clients: diff --git a/include/linux/platform_data/at24.h b/include/linux/platform_data/at24.h deleted file mode 100644 index 63507ff464ee..000000000000 --- a/include/linux/platform_data/at24.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * at24.h - platform_data for the at24 (generic eeprom) driver - * (C) Copyright 2008 by Pengutronix - * (C) Copyright 2012 by Wolfram Sang - * same license as the driver - */ - -#ifndef _LINUX_AT24_H -#define _LINUX_AT24_H - -#include -#include -#include - -/** - * struct at24_platform_data - data to set up at24 (generic eeprom) driver - * @byte_len: size of eeprom in byte - * @page_size: number of byte which can be written in one go - * @flags: tunable options, check AT24_FLAG_* defines - * @setup: an optional callback invoked after eeprom is probed; enables kernel - code to access eeprom via nvmem, see example - * @context: optional parameter passed to setup() - * - * If you set up a custom eeprom type, please double-check the parameters. - * Especially page_size needs extra care, as you risk data loss if your value - * is bigger than what the chip actually supports! - * - * An example in pseudo code for a setup() callback: - * - * void get_mac_addr(struct nvmem_device *nvmem, void *context) - * { - * u8 *mac_addr = ethernet_pdata->mac_addr; - * off_t offset = context; - * - * // Read MAC addr from EEPROM - * if (nvmem_device_read(nvmem, offset, ETH_ALEN, mac_addr) == ETH_ALEN) - * pr_info("Read MAC addr from EEPROM: %pM\n", mac_addr); - * } - * - * This function pointer and context can now be set up in at24_platform_data. - */ - -struct at24_platform_data { - u32 byte_len; /* size (sum of all addr) */ - u16 page_size; /* for writes */ - u8 flags; -#define AT24_FLAG_ADDR16 BIT(7) /* address pointer is 16 bit */ -#define AT24_FLAG_READONLY BIT(6) /* sysfs-entry will be read-only */ -#define AT24_FLAG_IRUGO BIT(5) /* sysfs-entry will be world-readable */ -#define AT24_FLAG_TAKE8ADDR BIT(4) /* take always 8 addresses (24c00) */ -#define AT24_FLAG_SERIAL BIT(3) /* factory-programmed serial number */ -#define AT24_FLAG_MAC BIT(2) /* factory-programmed mac address */ -#define AT24_FLAG_NO_RDROL BIT(1) /* does not auto-rollover reads to */ - /* the next slave address */ - - void (*setup)(struct nvmem_device *nvmem, void *context); - void *context; -}; - -#endif /* _LINUX_AT24_H */ -- cgit v1.2.3-59-g8ed1b From e7224a116f0dac593233143849f25bc095f8d2cc Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 5 Feb 2019 14:03:44 +0100 Subject: dt-bindings: at24: add the 'num-addresses' property Currently the at24 driver only creates additional i2c dummies for atmel,24c00 and it's hard-coded. Some other chips (like for example Microchip's 24AA02T) also take more slave addresses despite being otherwise compatible with already supported variants. Add a new property to the device tree binding document that defines the total number of i2c slave addresses taken by the device. The addresses are counted starting from the one in the reg property. Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/eeprom/at24.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/eeprom/at24.txt b/Documentation/devicetree/bindings/eeprom/at24.txt index f9a7c984274c..0e456bbc1213 100644 --- a/Documentation/devicetree/bindings/eeprom/at24.txt +++ b/Documentation/devicetree/bindings/eeprom/at24.txt @@ -75,6 +75,8 @@ Optional properties: - address-width: number of address bits (one of 8, 16). + - num-addresses: total number of i2c slave addresses this device takes + Example: eeprom@52 { @@ -82,4 +84,5 @@ eeprom@52 { reg = <0x52>; pagesize = <32>; wp-gpios = <&gpio1 3 0>; + num-addresses = <8>; }; -- cgit v1.2.3-59-g8ed1b From 950bcbbe31548636d24e49b3abaf3780204f7fe8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 5 Feb 2019 14:18:25 +0100 Subject: eeprom: at24: implement support for 'num-addresses' property If the device node defines 'num-addresses', let it override the default behavior. Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index b806a403ca46..63aa541c9608 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -641,11 +641,14 @@ static int at24_probe(struct i2c_client *client) if (!is_power_of_2(page_size)) dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); - if (flags & AT24_FLAG_TAKE8ADDR) - num_addresses = 8; - else - num_addresses = DIV_ROUND_UP(byte_len, - (flags & AT24_FLAG_ADDR16) ? 65536 : 256); + err = device_property_read_u32(dev, "num-addresses", &num_addresses); + if (err) { + if (flags & AT24_FLAG_TAKE8ADDR) + num_addresses = 8; + else + num_addresses = DIV_ROUND_UP(byte_len, + (flags & AT24_FLAG_ADDR16) ? 65536 : 256); + } if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) { dev_err(dev, -- cgit v1.2.3-59-g8ed1b From ca8655483c8849953b993196ad6adc9370a75d66 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:42 -0800 Subject: i2c: tegra: sort all the include headers alphabetically This patch sorts all the include headers alphabetically for the I2C Tegra driver. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index c77adbbea0c7..79c6aa87499b 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -6,24 +6,21 @@ * Author: Colin Cross */ -#include -#include -#include #include +#include #include #include -#include +#include #include -#include -#include -#include +#include +#include +#include #include -#include +#include #include +#include #include -#include - -#include +#include #define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000)) #define BYTES_PER_FIFO_WORD 4 -- cgit v1.2.3-59-g8ed1b From ce9562424501dee2ce44e6f22d8c9e82917f40d1 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:43 -0800 Subject: i2c: tegra: add bus clear Master Support Bus clear feature of Tegra I2C controller helps to recover from bus hang when I2C master loses the bus arbitration due to the slave device holding SDA LOW continuously for some unknown reasons. Per I2C specification, the device that held the bus LOW should release it within 9 clock pulses. During bus clear operation, Tegra I2C controller sends 9 clock pulses and terminates the transaction with STOP condition. Upon successful bus clear operation, bus goes to idle state and driver retries the transaction. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 81 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 79c6aa87499b..a46446dbc48a 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -51,6 +51,7 @@ #define I2C_FIFO_STATUS_RX_SHIFT 0 #define I2C_INT_MASK 0x064 #define I2C_INT_STATUS 0x068 +#define I2C_INT_BUS_CLR_DONE BIT(11) #define I2C_INT_PACKET_XFER_COMPLETE BIT(7) #define I2C_INT_ALL_PACKETS_XFER_COMPLETE BIT(6) #define I2C_INT_TX_FIFO_OVERFLOW BIT(5) @@ -93,6 +94,15 @@ #define I2C_HEADER_MASTER_ADDR_SHIFT 12 #define I2C_HEADER_SLAVE_ADDR_SHIFT 1 +#define I2C_BUS_CLEAR_CNFG 0x084 +#define I2C_BC_SCLK_THRESHOLD 9 +#define I2C_BC_SCLK_THRESHOLD_SHIFT 16 +#define I2C_BC_STOP_COND BIT(2) +#define I2C_BC_TERMINATE BIT(1) +#define I2C_BC_ENABLE BIT(0) +#define I2C_BUS_CLEAR_STATUS 0x088 +#define I2C_BC_STATUS BIT(0) + #define I2C_CONFIG_LOAD 0x08C #define I2C_MSTR_CONFIG_LOAD BIT(0) #define I2C_SLV_CONFIG_LOAD BIT(1) @@ -154,6 +164,8 @@ enum msg_end_type { * be transferred in one go. * @quirks: i2c adapter quirks for limiting write/read transfer size and not * allowing 0 length transfers. + * @supports_bus_clear: Bus Clear support to recover from bus hang during + * SDA stuck low from device for some unknown reasons. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -167,6 +179,7 @@ struct tegra_i2c_hw_feature { bool has_slcg_override_reg; bool has_mst_fifo; const struct i2c_adapter_quirks *quirks; + bool supports_bus_clear; }; /** @@ -640,6 +653,13 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) goto err; } + /* + * I2C transfer is terminated during the bus clear so skip + * processing the other interrupts. + */ + if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE)) + goto err; + if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { if (i2c_dev->msg_buf_remaining) tegra_i2c_empty_rx_fifo(i2c_dev); @@ -668,6 +688,8 @@ err: tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ | I2C_INT_RX_FIFO_DATA_REQ); + if (i2c_dev->hw->supports_bus_clear) + tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); i2c_writel(i2c_dev, status, I2C_INT_STATUS); if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); @@ -678,6 +700,44 @@ done: return IRQ_HANDLED; } +static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap) +{ + struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + int err; + unsigned long time_left; + u32 reg; + + reinit_completion(&i2c_dev->msg_complete); + reg = (I2C_BC_SCLK_THRESHOLD << I2C_BC_SCLK_THRESHOLD_SHIFT) | + I2C_BC_STOP_COND | I2C_BC_TERMINATE; + i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG); + if (i2c_dev->hw->has_config_load_reg) { + err = tegra_i2c_wait_for_config_load(i2c_dev); + if (err) + return err; + } + + reg |= I2C_BC_ENABLE; + i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG); + tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); + + time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, + TEGRA_I2C_TIMEOUT); + if (time_left == 0) { + dev_err(i2c_dev->dev, "timed out for bus clear\n"); + return -ETIMEDOUT; + } + + reg = i2c_readl(i2c_dev, I2C_BUS_CLEAR_STATUS); + if (!(reg & I2C_BC_STATUS)) { + dev_err(i2c_dev->dev, + "un-recovered arbitration lost\n"); + return -EIO; + } + + return -EAGAIN; +} + static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, struct i2c_msg *msg, enum msg_end_type end_state) { @@ -759,6 +819,13 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, return 0; tegra_i2c_init(i2c_dev); + /* start recovery upon arbitration loss in single master mode */ + if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) { + if (!i2c_dev->is_multimaster_mode) + return i2c_recover_bus(&i2c_dev->adapter); + return -EAGAIN; + } + if (i2c_dev->msg_err == I2C_ERR_NO_ACK) { if (msg->flags & I2C_M_IGNORE_NAK) return 0; @@ -841,6 +908,10 @@ static const struct i2c_adapter_quirks tegra194_i2c_quirks = { .flags = I2C_AQ_NO_ZERO_LEN, }; +static struct i2c_bus_recovery_info tegra_i2c_recovery_info = { + .recover_bus = tegra_i2c_issue_bus_clear, +}; + static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_continue_xfer_support = false, .has_per_pkt_xfer_complete_irq = false, @@ -853,6 +924,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_slcg_override_reg = false, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = false, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -867,6 +939,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_slcg_override_reg = false, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = false, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -881,6 +954,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_slcg_override_reg = false, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -895,6 +969,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_slcg_override_reg = true, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -909,6 +984,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_slcg_override_reg = true, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -923,6 +999,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_slcg_override_reg = true, .has_mst_fifo = true, .quirks = &tegra194_i2c_quirks, + .supports_bus_clear = true, }; /* Match table for of_platform binding */ @@ -974,6 +1051,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->base = base; i2c_dev->div_clk = div_clk; i2c_dev->adapter.algo = &tegra_i2c_algo; + i2c_dev->adapter.retries = 1; i2c_dev->irq = irq; i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; @@ -1051,6 +1129,9 @@ static int tegra_i2c_probe(struct platform_device *pdev) } } + if (i2c_dev->hw->supports_bus_clear) + i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info; + ret = tegra_i2c_init(i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to initialize i2c controller\n"); -- cgit v1.2.3-59-g8ed1b From f4e3f4ae1d9c9330de355f432b69952e8cef650c Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:44 -0800 Subject: i2c: tegra: fix maximum transfer size Tegra186 and prior supports maximum 4K bytes per packet transfer including 12 bytes of packet header. This patch fixes max write length limit to account packet header size for transfers. Cc: stable@vger.kernel.org # 4.4+ Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index a46446dbc48a..036cab795426 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -901,7 +901,7 @@ static const struct i2c_algorithm tegra_i2c_algo = { static const struct i2c_adapter_quirks tegra_i2c_quirks = { .flags = I2C_AQ_NO_ZERO_LEN, .max_read_len = 4096, - .max_write_len = 4096, + .max_write_len = 4096 - 12, }; static const struct i2c_adapter_quirks tegra194_i2c_quirks = { -- cgit v1.2.3-59-g8ed1b From b03ff2a23359d0dd6f0a1516c6a9e9c4760ed230 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:45 -0800 Subject: i2c: tegra: update maximum transfer size Tegra194 supports maximum 64K bytes per packet including 12 bytes of packet header irrespective of PIO or DMA mode transfer. This patch updates Tegra194 max write length to account for packet header size for transfers. Cc: stable@vger.kernel.org # 4.20+ Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 036cab795426..f8265bd5d02c 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -125,6 +125,9 @@ #define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000 #define I2C_MST_FIFO_STATUS_TX_SHIFT 16 +/* Packet header size in bytes */ +#define I2C_PACKET_HEADER_SIZE 12 + /* * msg_end_type: The bus control which need to be send at end of transfer. * @MSG_END_STOP: Send stop pulse at end of transfer. @@ -900,12 +903,13 @@ static const struct i2c_algorithm tegra_i2c_algo = { /* payload size is only 12 bit */ static const struct i2c_adapter_quirks tegra_i2c_quirks = { .flags = I2C_AQ_NO_ZERO_LEN, - .max_read_len = 4096, - .max_write_len = 4096 - 12, + .max_read_len = SZ_4K, + .max_write_len = SZ_4K - I2C_PACKET_HEADER_SIZE, }; static const struct i2c_adapter_quirks tegra194_i2c_quirks = { .flags = I2C_AQ_NO_ZERO_LEN, + .max_write_len = SZ_64K - I2C_PACKET_HEADER_SIZE, }; static struct i2c_bus_recovery_info tegra_i2c_recovery_info = { -- cgit v1.2.3-59-g8ed1b From 86c92b9965ff1758952cd0d6c5f19eeeef291eea Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:46 -0800 Subject: i2c: tegra: Add DMA support This patch adds DMA support for Tegra I2C. Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for transfer size of the max FIFO depth and DMA mode is used for transfer size higher than max FIFO depth to save CPU overhead. PIO mode needs full intervention of CPU to fill or empty FIFO's and also need to service multiple data requests interrupt for the same transaction. This adds delay between data bytes of the same transfer when CPU is fully loaded and some slave devices has internal timeout for no bus activity and stops transaction to avoid bus hang. DMA mode is helpful in such cases. DMA mode is also helpful for Large transfers during downloading or uploading FW over I2C to some external devices. Tegra210 and prior Tegra chips use APBDMA driver which is replaced with GPCDMA on Tegra186 and Tegra194. This patch uses has_apb_dma flag in hw_feature to differentiate DMA driver change between Tegra chipset. APBDMA driver is registered from module-init level and this patch also has a change to register I2C driver at module-init level rather than subsys-init to avoid deferring I2C probe till APBDMA driver is registered. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Tested-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 432 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 383 insertions(+), 49 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index f8265bd5d02c..3b923411c9a7 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include #include @@ -42,8 +44,8 @@ #define I2C_FIFO_CONTROL 0x05c #define I2C_FIFO_CONTROL_TX_FLUSH BIT(1) #define I2C_FIFO_CONTROL_RX_FLUSH BIT(0) -#define I2C_FIFO_CONTROL_TX_TRIG_SHIFT 5 -#define I2C_FIFO_CONTROL_RX_TRIG_SHIFT 2 +#define I2C_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 5) +#define I2C_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 2) #define I2C_FIFO_STATUS 0x060 #define I2C_FIFO_STATUS_TX_MASK 0xF0 #define I2C_FIFO_STATUS_TX_SHIFT 4 @@ -128,6 +130,13 @@ /* Packet header size in bytes */ #define I2C_PACKET_HEADER_SIZE 12 +/* + * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode, + * above this, controller will use DMA to fill FIFO. + * MAX PIO len is 20 bytes excluding packet header. + */ +#define I2C_PIO_MODE_MAX_LEN 32 + /* * msg_end_type: The bus control which need to be send at end of transfer. * @MSG_END_STOP: Send stop pulse at end of transfer. @@ -169,6 +178,7 @@ enum msg_end_type { * allowing 0 length transfers. * @supports_bus_clear: Bus Clear support to recover from bus hang during * SDA stuck low from device for some unknown reasons. + * @has_apb_dma: Support of APBDMA on corresponding Tegra chip. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -183,6 +193,7 @@ struct tegra_i2c_hw_feature { bool has_mst_fifo; const struct i2c_adapter_quirks *quirks; bool supports_bus_clear; + bool has_apb_dma; }; /** @@ -194,6 +205,7 @@ struct tegra_i2c_hw_feature { * @fast_clk: clock reference for fast clock of I2C controller * @rst: reset control for the I2C controller * @base: ioremapped registers cookie + * @base_phys: physical base address of the I2C controller * @cont_id: I2C controller ID, used for packet header * @irq: IRQ number of transfer complete interrupt * @irq_disabled: used to track whether or not the interrupt is enabled @@ -207,6 +219,13 @@ struct tegra_i2c_hw_feature { * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes * @is_multimaster_mode: track if I2C controller is in multi-master mode * @xfer_lock: lock to serialize transfer submission and processing + * @tx_dma_chan: DMA transmit channel + * @rx_dma_chan: DMA receive channel + * @dma_phys: handle to DMA resources + * @dma_buf: pointer to allocated DMA buffer + * @dma_buf_size: DMA buffer size + * @is_curr_dma_xfer: indicates active DMA transfer + * @dma_complete: DMA completion notifier */ struct tegra_i2c_dev { struct device *dev; @@ -216,6 +235,7 @@ struct tegra_i2c_dev { struct clk *fast_clk; struct reset_control *rst; void __iomem *base; + phys_addr_t base_phys; int cont_id; int irq; bool irq_disabled; @@ -229,6 +249,13 @@ struct tegra_i2c_dev { u16 clk_divisor_non_hs_mode; bool is_multimaster_mode; spinlock_t xfer_lock; + struct dma_chan *tx_dma_chan; + struct dma_chan *rx_dma_chan; + dma_addr_t dma_phys; + u32 *dma_buf; + unsigned int dma_buf_size; + bool is_curr_dma_xfer; + struct completion dma_complete; }; static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, @@ -297,6 +324,109 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) i2c_writel(i2c_dev, int_mask, I2C_INT_MASK); } +static void tegra_i2c_dma_complete(void *args) +{ + struct tegra_i2c_dev *i2c_dev = args; + + complete(&i2c_dev->dma_complete); +} + +static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len) +{ + struct dma_async_tx_descriptor *dma_desc; + enum dma_transfer_direction dir; + struct dma_chan *chan; + + dev_dbg(i2c_dev->dev, "starting DMA for length: %zu\n", len); + reinit_completion(&i2c_dev->dma_complete); + dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan; + dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys, + len, dir, DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!dma_desc) { + dev_err(i2c_dev->dev, "failed to get DMA descriptor\n"); + return -EINVAL; + } + + dma_desc->callback = tegra_i2c_dma_complete; + dma_desc->callback_param = i2c_dev; + dmaengine_submit(dma_desc); + dma_async_issue_pending(chan); + return 0; +} + +static void tegra_i2c_release_dma(struct tegra_i2c_dev *i2c_dev) +{ + if (i2c_dev->dma_buf) { + dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size, + i2c_dev->dma_buf, i2c_dev->dma_phys); + i2c_dev->dma_buf = NULL; + } + + if (i2c_dev->tx_dma_chan) { + dma_release_channel(i2c_dev->tx_dma_chan); + i2c_dev->tx_dma_chan = NULL; + } + + if (i2c_dev->rx_dma_chan) { + dma_release_channel(i2c_dev->rx_dma_chan); + i2c_dev->rx_dma_chan = NULL; + } +} + +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) +{ + struct dma_chan *chan; + u32 *dma_buf; + dma_addr_t dma_phys; + int err; + + if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA) || + !i2c_dev->hw->has_apb_dma) { + err = -ENODEV; + goto err_out; + } + + chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx"); + if (IS_ERR(chan)) { + err = PTR_ERR(chan); + goto err_out; + } + + i2c_dev->rx_dma_chan = chan; + + chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx"); + if (IS_ERR(chan)) { + err = PTR_ERR(chan); + goto err_out; + } + + i2c_dev->tx_dma_chan = chan; + + dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size, + &dma_phys, GFP_KERNEL | __GFP_NOWARN); + if (!dma_buf) { + dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n"); + err = -ENOMEM; + goto err_out; + } + + i2c_dev->dma_buf = dma_buf; + i2c_dev->dma_phys = dma_phys; + return 0; + +err_out: + tegra_i2c_release_dma(i2c_dev); + if (err != -EPROBE_DEFER) { + dev_err(i2c_dev->dev, "cannot use DMA: %d\n", err); + dev_err(i2c_dev->dev, "fallbacking to PIO\n"); + return 0; + } + + return err; +} + static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev) { unsigned long timeout = jiffies + HZ; @@ -574,16 +704,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2); } - if (i2c_dev->hw->has_mst_fifo) { - val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) | - I2C_MST_FIFO_CONTROL_RX_TRIG(1); - i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL); - } else { - val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT | - 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; - i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); - } - err = tegra_i2c_flush_fifos(i2c_dev); if (err) goto err; @@ -663,25 +783,37 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE)) goto err; - if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { - if (i2c_dev->msg_buf_remaining) - tegra_i2c_empty_rx_fifo(i2c_dev); - else - BUG(); - } + if (!i2c_dev->is_curr_dma_xfer) { + if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { + if (i2c_dev->msg_buf_remaining) + tegra_i2c_empty_rx_fifo(i2c_dev); + else + BUG(); + } - if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) { - if (i2c_dev->msg_buf_remaining) - tegra_i2c_fill_tx_fifo(i2c_dev); - else - tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ); + if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) { + if (i2c_dev->msg_buf_remaining) + tegra_i2c_fill_tx_fifo(i2c_dev); + else + tegra_i2c_mask_irq(i2c_dev, + I2C_INT_TX_FIFO_DATA_REQ); + } } i2c_writel(i2c_dev, status, I2C_INT_STATUS); if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + /* + * During message read XFER_COMPLETE interrupt is triggered prior to + * DMA completion and during message write XFER_COMPLETE interrupt is + * triggered after DMA completion. + * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer. + * so forcing msg_buf_remaining to 0 in DMA mode. + */ if (status & I2C_INT_PACKET_XFER_COMPLETE) { + if (i2c_dev->is_curr_dma_xfer) + i2c_dev->msg_buf_remaining = 0; BUG_ON(i2c_dev->msg_buf_remaining); complete(&i2c_dev->msg_complete); } @@ -697,12 +829,91 @@ err: if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + if (i2c_dev->is_curr_dma_xfer) { + if (i2c_dev->msg_read) + dmaengine_terminate_async(i2c_dev->rx_dma_chan); + else + dmaengine_terminate_async(i2c_dev->tx_dma_chan); + + complete(&i2c_dev->dma_complete); + } + complete(&i2c_dev->msg_complete); done: spin_unlock(&i2c_dev->xfer_lock); return IRQ_HANDLED; } +static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev, + size_t len) +{ + u32 val, reg; + u8 dma_burst; + struct dma_slave_config slv_config = {0}; + struct dma_chan *chan; + int ret; + unsigned long reg_offset; + + if (i2c_dev->hw->has_mst_fifo) + reg = I2C_MST_FIFO_CONTROL; + else + reg = I2C_FIFO_CONTROL; + + if (i2c_dev->is_curr_dma_xfer) { + if (len & 0xF) + dma_burst = 1; + else if (len & 0x10) + dma_burst = 4; + else + dma_burst = 8; + + if (i2c_dev->msg_read) { + chan = i2c_dev->rx_dma_chan; + reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_RX_FIFO); + slv_config.src_addr = i2c_dev->base_phys + reg_offset; + slv_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slv_config.src_maxburst = dma_burst; + + if (i2c_dev->hw->has_mst_fifo) + val = I2C_MST_FIFO_CONTROL_RX_TRIG(dma_burst); + else + val = I2C_FIFO_CONTROL_RX_TRIG(dma_burst); + } else { + chan = i2c_dev->tx_dma_chan; + reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_TX_FIFO); + slv_config.dst_addr = i2c_dev->base_phys + reg_offset; + slv_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slv_config.dst_maxburst = dma_burst; + + if (i2c_dev->hw->has_mst_fifo) + val = I2C_MST_FIFO_CONTROL_TX_TRIG(dma_burst); + else + val = I2C_FIFO_CONTROL_TX_TRIG(dma_burst); + } + + slv_config.device_fc = true; + ret = dmaengine_slave_config(chan, &slv_config); + if (ret < 0) { + dev_err(i2c_dev->dev, "DMA slave config failed: %d\n", + ret); + dev_err(i2c_dev->dev, "fallbacking to PIO\n"); + tegra_i2c_release_dma(i2c_dev); + i2c_dev->is_curr_dma_xfer = false; + } else { + goto out; + } + } + + if (i2c_dev->hw->has_mst_fifo) + val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) | + I2C_MST_FIFO_CONTROL_RX_TRIG(1); + else + val = I2C_FIFO_CONTROL_TX_TRIG(8) | + I2C_FIFO_CONTROL_RX_TRIG(1); +out: + i2c_writel(i2c_dev, val, reg); +} + static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap) { struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap); @@ -748,6 +959,10 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, u32 int_mask; unsigned long time_left; unsigned long flags; + size_t xfer_size; + u32 *buffer = NULL; + int err = 0; + bool dma; tegra_i2c_flush_fifos(i2c_dev); @@ -757,19 +972,57 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, i2c_dev->msg_read = (msg->flags & I2C_M_RD); reinit_completion(&i2c_dev->msg_complete); + if (i2c_dev->msg_read) + xfer_size = msg->len; + else + xfer_size = msg->len + I2C_PACKET_HEADER_SIZE; + + xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD); + i2c_dev->is_curr_dma_xfer = (xfer_size > I2C_PIO_MODE_MAX_LEN) && + i2c_dev->dma_buf; + tegra_i2c_config_fifo_trig(i2c_dev, xfer_size); + dma = i2c_dev->is_curr_dma_xfer; spin_lock_irqsave(&i2c_dev->xfer_lock, flags); int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; tegra_i2c_unmask_irq(i2c_dev, int_mask); + if (dma) { + if (i2c_dev->msg_read) { + dma_sync_single_for_device(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_FROM_DEVICE); + err = tegra_i2c_dma_submit(i2c_dev, xfer_size); + if (err < 0) { + dev_err(i2c_dev->dev, + "starting RX DMA failed, err %d\n", + err); + goto unlock; + } + + } else { + dma_sync_single_for_cpu(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_TO_DEVICE); + buffer = i2c_dev->dma_buf; + } + } packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | PACKET_HEADER0_PROTOCOL_I2C | (i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) | (1 << PACKET_HEADER0_PACKET_ID_SHIFT); - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + if (dma && !i2c_dev->msg_read) + *buffer++ = packet_header; + else + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); packet_header = msg->len - 1; - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + if (dma && !i2c_dev->msg_read) + *buffer++ = packet_header; + else + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); packet_header = I2C_HEADER_IE_ENABLE; if (end_state == MSG_END_CONTINUE) @@ -786,23 +1039,77 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, packet_header |= I2C_HEADER_CONT_ON_NAK; if (msg->flags & I2C_M_RD) packet_header |= I2C_HEADER_READ; - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); - - if (!(msg->flags & I2C_M_RD)) - tegra_i2c_fill_tx_fifo(i2c_dev); + if (dma && !i2c_dev->msg_read) + *buffer++ = packet_header; + else + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + + if (!i2c_dev->msg_read) { + if (dma) { + memcpy(buffer, msg->buf, msg->len); + dma_sync_single_for_device(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_TO_DEVICE); + err = tegra_i2c_dma_submit(i2c_dev, xfer_size); + if (err < 0) { + dev_err(i2c_dev->dev, + "starting TX DMA failed, err %d\n", + err); + goto unlock; + } + } else { + tegra_i2c_fill_tx_fifo(i2c_dev); + } + } if (i2c_dev->hw->has_per_pkt_xfer_complete_irq) int_mask |= I2C_INT_PACKET_XFER_COMPLETE; - if (msg->flags & I2C_M_RD) - int_mask |= I2C_INT_RX_FIFO_DATA_REQ; - else if (i2c_dev->msg_buf_remaining) - int_mask |= I2C_INT_TX_FIFO_DATA_REQ; + if (!dma) { + if (msg->flags & I2C_M_RD) + int_mask |= I2C_INT_RX_FIFO_DATA_REQ; + else if (i2c_dev->msg_buf_remaining) + int_mask |= I2C_INT_TX_FIFO_DATA_REQ; + } tegra_i2c_unmask_irq(i2c_dev, int_mask); - spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags); dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n", i2c_readl(i2c_dev, I2C_INT_MASK)); +unlock: + spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags); + + if (dma) { + if (err) + return err; + + time_left = wait_for_completion_timeout( + &i2c_dev->dma_complete, + TEGRA_I2C_TIMEOUT); + if (time_left == 0) { + dev_err(i2c_dev->dev, "DMA transfer timeout\n"); + dmaengine_terminate_sync(i2c_dev->msg_read ? + i2c_dev->rx_dma_chan : + i2c_dev->tx_dma_chan); + tegra_i2c_init(i2c_dev); + return -ETIMEDOUT; + } + + if (i2c_dev->msg_read && i2c_dev->msg_err == I2C_ERR_NONE) { + dma_sync_single_for_cpu(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_FROM_DEVICE); + memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, + msg->len); + } + + if (i2c_dev->msg_err != I2C_ERR_NONE) + dmaengine_synchronize(i2c_dev->msg_read ? + i2c_dev->rx_dma_chan : + i2c_dev->tx_dma_chan); + } + time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT); tegra_i2c_mask_irq(i2c_dev, int_mask); @@ -818,6 +1125,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, time_left, completion_done(&i2c_dev->msg_complete), i2c_dev->msg_err); + i2c_dev->is_curr_dma_xfer = false; if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) return 0; @@ -929,6 +1237,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -944,6 +1253,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -959,6 +1269,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -974,6 +1285,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -989,6 +1301,23 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = true, +}; + +static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .has_single_clk_source = true, + .clk_divisor_hs_mode = 1, + .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_fast_plus_mode = 0x10, + .has_config_load_reg = true, + .has_multi_master_mode = true, + .has_slcg_override_reg = true, + .has_mst_fifo = true, + .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, + .has_apb_dma = false, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -1004,11 +1333,13 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_mst_fifo = true, .quirks = &tegra194_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = false, }; /* Match table for of_platform binding */ static const struct of_device_id tegra_i2c_of_match[] = { { .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, }, + { .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, }, { .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, }, { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, }, { .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, }, @@ -1026,11 +1357,13 @@ static int tegra_i2c_probe(struct platform_device *pdev) struct clk *div_clk; struct clk *fast_clk; void __iomem *base; + phys_addr_t base_phys; int irq; int ret = 0; int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base_phys = res->start; base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); @@ -1053,6 +1386,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) return -ENOMEM; i2c_dev->base = base; + i2c_dev->base_phys = base_phys; i2c_dev->div_clk = div_clk; i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->adapter.retries = 1; @@ -1072,7 +1406,10 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node, "nvidia,tegra20-i2c-dvc"); i2c_dev->adapter.quirks = i2c_dev->hw->quirks; + i2c_dev->dma_buf_size = i2c_dev->adapter.quirks->max_write_len + + I2C_PACKET_HEADER_SIZE; init_completion(&i2c_dev->msg_complete); + init_completion(&i2c_dev->dma_complete); spin_lock_init(&i2c_dev->xfer_lock); if (!i2c_dev->hw->has_single_clk_source) { @@ -1136,17 +1473,21 @@ static int tegra_i2c_probe(struct platform_device *pdev) if (i2c_dev->hw->supports_bus_clear) i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info; + ret = tegra_i2c_init_dma(i2c_dev); + if (ret < 0) + goto disable_div_clk; + ret = tegra_i2c_init(i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to initialize i2c controller\n"); - goto disable_div_clk; + goto release_dma; } ret = devm_request_irq(&pdev->dev, i2c_dev->irq, tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq); - goto disable_div_clk; + goto release_dma; } i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); @@ -1160,10 +1501,13 @@ static int tegra_i2c_probe(struct platform_device *pdev) ret = i2c_add_numbered_adapter(&i2c_dev->adapter); if (ret) - goto disable_div_clk; + goto release_dma; return 0; +release_dma: + tegra_i2c_release_dma(i2c_dev); + disable_div_clk: if (i2c_dev->is_multimaster_mode) clk_disable(i2c_dev->div_clk); @@ -1200,6 +1544,7 @@ static int tegra_i2c_remove(struct platform_device *pdev) if (!i2c_dev->hw->has_single_clk_source) clk_unprepare(i2c_dev->fast_clk); + tegra_i2c_release_dma(i2c_dev); return 0; } @@ -1223,18 +1568,7 @@ static struct platform_driver tegra_i2c_driver = { }, }; -static int __init tegra_i2c_init_driver(void) -{ - return platform_driver_register(&tegra_i2c_driver); -} - -static void __exit tegra_i2c_exit_driver(void) -{ - platform_driver_unregister(&tegra_i2c_driver); -} - -subsys_initcall(tegra_i2c_init_driver); -module_exit(tegra_i2c_exit_driver); +module_platform_driver(tegra_i2c_driver); MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver"); MODULE_AUTHOR("Colin Cross"); -- cgit v1.2.3-59-g8ed1b From 80d40626cc76f65f2699573a95ecc90a31dcf50e Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:47 -0800 Subject: i2c: tegra: update transfer timeout Tegra194 allows max of 64K bytes and Tegra186 and prior allows max of 4K bytes of transfer per packet. one sec timeout is not enough for transfers more than 10K bytes at STD bus rate. This patch updates I2C transfer timeout based on the transfer size and I2C bus rate to allow enough time during max transfer size at lower bus speed. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 3b923411c9a7..cfecd81789c3 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -24,7 +24,6 @@ #include #include -#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000)) #define BYTES_PER_FIFO_WORD 4 #define I2C_CNFG 0x000 @@ -936,7 +935,7 @@ static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap) tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, - TEGRA_I2C_TIMEOUT); + msecs_to_jiffies(50)); if (time_left == 0) { dev_err(i2c_dev->dev, "timed out for bus clear\n"); return -ETIMEDOUT; @@ -963,6 +962,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, u32 *buffer = NULL; int err = 0; bool dma; + u16 xfer_time = 100; tegra_i2c_flush_fifos(i2c_dev); @@ -982,6 +982,12 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, i2c_dev->dma_buf; tegra_i2c_config_fifo_trig(i2c_dev, xfer_size); dma = i2c_dev->is_curr_dma_xfer; + /* + * Transfer time in mSec = Total bits / transfer rate + * Total bits = 9 bits per byte (including ACK bit) + Start & stop bits + */ + xfer_time += DIV_ROUND_CLOSEST(((xfer_size * 9) + 2) * MSEC_PER_SEC, + i2c_dev->bus_clk_rate); spin_lock_irqsave(&i2c_dev->xfer_lock, flags); int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; @@ -1085,7 +1091,7 @@ unlock: time_left = wait_for_completion_timeout( &i2c_dev->dma_complete, - TEGRA_I2C_TIMEOUT); + msecs_to_jiffies(xfer_time)); if (time_left == 0) { dev_err(i2c_dev->dev, "DMA transfer timeout\n"); dmaengine_terminate_sync(i2c_dev->msg_read ? @@ -1111,7 +1117,7 @@ unlock: } time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, - TEGRA_I2C_TIMEOUT); + msecs_to_jiffies(xfer_time)); tegra_i2c_mask_irq(i2c_dev, int_mask); if (time_left == 0) { @@ -1390,6 +1396,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->div_clk = div_clk; i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->adapter.retries = 1; + i2c_dev->adapter.timeout = 6 * HZ; i2c_dev->irq = irq; i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; -- cgit v1.2.3-59-g8ed1b From 0940d24912e9256fdf172f84c54ffd91680f05d0 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:48 -0800 Subject: i2c: tegra: add i2c interface timing support This patch adds I2C interface timing registers support for proper bus rate configuration along with meeting the I2C spec setup and hold times based on the tuning performed on Tegra210, Tegra186 and Tegra194 platforms. I2C_INTERFACE_TIMING_0 register contains TLOW and THIGH field and Tegra I2C controller design uses them as a part of internal clock divisor. I2C_INTERFACE_TIMING_1 register contains the setup and hold times for start and stop conditions. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Tested-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 189 ++++++++++++++++++++++++++++++++++------- 1 file changed, 159 insertions(+), 30 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index cfecd81789c3..a4cd79c9f7a7 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -126,6 +126,15 @@ #define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000 #define I2C_MST_FIFO_STATUS_TX_SHIFT 16 +#define I2C_INTERFACE_TIMING_0 0x94 +#define I2C_THIGH_SHIFT 8 +#define I2C_INTERFACE_TIMING_1 0x98 + +#define I2C_STANDARD_MODE 100000 +#define I2C_FAST_MODE 400000 +#define I2C_FAST_PLUS_MODE 1000000 +#define I2C_HS_MODE 3500000 + /* Packet header size in bytes */ #define I2C_PACKET_HEADER_SIZE 12 @@ -160,7 +169,10 @@ enum msg_end_type { * @has_config_load_reg: Has the config load register to load the new * configuration. * @clk_divisor_hs_mode: Clock divisor in HS mode. - * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is + * @clk_divisor_std_mode: Clock divisor in standard mode. It is + * applicable if there is no fast clock source i.e. single clock + * source. + * @clk_divisor_fast_mode: Clock divisor in fast mode. It is * applicable if there is no fast clock source i.e. single clock * source. * @clk_divisor_fast_plus_mode: Clock divisor in fast mode plus. It is @@ -178,6 +190,18 @@ enum msg_end_type { * @supports_bus_clear: Bus Clear support to recover from bus hang during * SDA stuck low from device for some unknown reasons. * @has_apb_dma: Support of APBDMA on corresponding Tegra chip. + * @tlow_std_mode: Low period of the clock in standard mode. + * @thigh_std_mode: High period of the clock in standard mode. + * @tlow_fast_fastplus_mode: Low period of the clock in fast/fast-plus modes. + * @thigh_fast_fastplus_mode: High period of the clock in fast/fast-plus modes. + * @setup_hold_time_std_mode: Setup and hold time for start and stop conditions + * in standard mode. + * @setup_hold_time_fast_fast_plus_mode: Setup and hold time for start and stop + * conditions in fast/fast-plus modes. + * @setup_hold_time_hs_mode: Setup and hold time for start and stop conditions + * in HS mode. + * @has_interface_timing_reg: Has interface timing register to program the tuned + * timing settings. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -185,7 +209,8 @@ struct tegra_i2c_hw_feature { bool has_single_clk_source; bool has_config_load_reg; int clk_divisor_hs_mode; - int clk_divisor_std_fast_mode; + int clk_divisor_std_mode; + int clk_divisor_fast_mode; u16 clk_divisor_fast_plus_mode; bool has_multi_master_mode; bool has_slcg_override_reg; @@ -193,6 +218,14 @@ struct tegra_i2c_hw_feature { const struct i2c_adapter_quirks *quirks; bool supports_bus_clear; bool has_apb_dma; + u8 tlow_std_mode; + u8 thigh_std_mode; + u8 tlow_fast_fastplus_mode; + u8 thigh_fast_fastplus_mode; + u32 setup_hold_time_std_mode; + u32 setup_hold_time_fast_fast_plus_mode; + u32 setup_hold_time_hs_mode; + bool has_interface_timing_reg; }; /** @@ -660,11 +693,13 @@ static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev) return 0; } -static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) +static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev, bool clk_reinit) { u32 val; int err; - u32 clk_divisor; + u32 clk_divisor, clk_multiplier; + u32 tsu_thd = 0; + u8 tlow, thigh; err = pm_runtime_get_sync(i2c_dev->dev); if (err < 0) { @@ -694,6 +729,41 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT; i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR); + if (i2c_dev->bus_clk_rate > I2C_STANDARD_MODE && + i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE) { + tlow = i2c_dev->hw->tlow_fast_fastplus_mode; + thigh = i2c_dev->hw->thigh_fast_fastplus_mode; + tsu_thd = i2c_dev->hw->setup_hold_time_fast_fast_plus_mode; + } else { + tlow = i2c_dev->hw->tlow_std_mode; + thigh = i2c_dev->hw->thigh_std_mode; + tsu_thd = i2c_dev->hw->setup_hold_time_std_mode; + } + + if (i2c_dev->hw->has_interface_timing_reg) { + val = (thigh << I2C_THIGH_SHIFT) | tlow; + i2c_writel(i2c_dev, val, I2C_INTERFACE_TIMING_0); + } + + /* + * configure setup and hold times only when tsu_thd is non-zero. + * otherwise, preserve the chip default values + */ + if (i2c_dev->hw->has_interface_timing_reg && tsu_thd) + i2c_writel(i2c_dev, tsu_thd, I2C_INTERFACE_TIMING_1); + + if (!clk_reinit) { + clk_multiplier = (tlow + thigh + 2); + clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1); + err = clk_set_rate(i2c_dev->div_clk, + i2c_dev->bus_clk_rate * clk_multiplier); + if (err) { + dev_err(i2c_dev->dev, + "failed changing clock rate: %d\n", err); + goto err; + } + } + if (!i2c_dev->is_dvc) { u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG); @@ -1097,7 +1167,7 @@ unlock: dmaengine_terminate_sync(i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan); - tegra_i2c_init(i2c_dev); + tegra_i2c_init(i2c_dev, true); return -ETIMEDOUT; } @@ -1123,7 +1193,7 @@ unlock: if (time_left == 0) { dev_err(i2c_dev->dev, "i2c transfer timed out\n"); - tegra_i2c_init(i2c_dev); + tegra_i2c_init(i2c_dev, true); return -ETIMEDOUT; } @@ -1135,7 +1205,7 @@ unlock: if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) return 0; - tegra_i2c_init(i2c_dev); + tegra_i2c_init(i2c_dev, true); /* start recovery upon arbitration loss in single master mode */ if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) { if (!i2c_dev->is_multimaster_mode) @@ -1235,7 +1305,8 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_per_pkt_xfer_complete_irq = false, .has_single_clk_source = false, .clk_divisor_hs_mode = 3, - .clk_divisor_std_fast_mode = 0, + .clk_divisor_std_mode = 0, + .clk_divisor_fast_mode = 0, .clk_divisor_fast_plus_mode = 0, .has_config_load_reg = false, .has_multi_master_mode = false, @@ -1244,6 +1315,14 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -1251,7 +1330,8 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_per_pkt_xfer_complete_irq = false, .has_single_clk_source = false, .clk_divisor_hs_mode = 3, - .clk_divisor_std_fast_mode = 0, + .clk_divisor_std_mode = 0, + .clk_divisor_fast_mode = 0, .clk_divisor_fast_plus_mode = 0, .has_config_load_reg = false, .has_multi_master_mode = false, @@ -1260,6 +1340,14 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -1267,7 +1355,8 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x19, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = false, .has_multi_master_mode = false, @@ -1276,6 +1365,14 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -1283,7 +1380,8 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x19, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, .has_multi_master_mode = false, @@ -1292,6 +1390,14 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = true, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -1299,7 +1405,8 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x19, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, .has_multi_master_mode = true, @@ -1308,6 +1415,14 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0, + .setup_hold_time_fast_fast_plus_mode = 0, + .setup_hold_time_hs_mode = 0, + .has_interface_timing_reg = true, }; static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { @@ -1315,7 +1430,8 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x16, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, .has_multi_master_mode = true, @@ -1324,6 +1440,14 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = false, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x3, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0, + .setup_hold_time_fast_fast_plus_mode = 0, + .setup_hold_time_hs_mode = 0, + .has_interface_timing_reg = true, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -1331,8 +1455,9 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, - .clk_divisor_fast_plus_mode = 0x10, + .clk_divisor_std_mode = 0x4f, + .clk_divisor_fast_mode = 0x3c, + .clk_divisor_fast_plus_mode = 0x16, .has_config_load_reg = true, .has_multi_master_mode = true, .has_slcg_override_reg = true, @@ -1340,6 +1465,14 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .quirks = &tegra194_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = false, + .tlow_std_mode = 0x8, + .thigh_std_mode = 0x7, + .tlow_fast_fastplus_mode = 0x2, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x08080808, + .setup_hold_time_fast_fast_plus_mode = 0x02020202, + .setup_hold_time_hs_mode = 0x090909, + .has_interface_timing_reg = true, }; /* Match table for of_platform binding */ @@ -1366,7 +1499,6 @@ static int tegra_i2c_probe(struct platform_device *pdev) phys_addr_t base_phys; int irq; int ret = 0; - int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base_phys = res->start; @@ -1438,20 +1570,17 @@ static int tegra_i2c_probe(struct platform_device *pdev) } } - i2c_dev->clk_divisor_non_hs_mode = - i2c_dev->hw->clk_divisor_std_fast_mode; - if (i2c_dev->hw->clk_divisor_fast_plus_mode && - (i2c_dev->bus_clk_rate == 1000000)) + if (i2c_dev->bus_clk_rate > I2C_FAST_MODE && + i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE) i2c_dev->clk_divisor_non_hs_mode = - i2c_dev->hw->clk_divisor_fast_plus_mode; - - clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1); - ret = clk_set_rate(i2c_dev->div_clk, - i2c_dev->bus_clk_rate * clk_multiplier); - if (ret) { - dev_err(i2c_dev->dev, "Clock rate change failed %d\n", ret); - goto unprepare_fast_clk; - } + i2c_dev->hw->clk_divisor_fast_plus_mode; + else if (i2c_dev->bus_clk_rate > I2C_STANDARD_MODE && + i2c_dev->bus_clk_rate <= I2C_FAST_MODE) + i2c_dev->clk_divisor_non_hs_mode = + i2c_dev->hw->clk_divisor_fast_mode; + else + i2c_dev->clk_divisor_non_hs_mode = + i2c_dev->hw->clk_divisor_std_mode; ret = clk_prepare(i2c_dev->div_clk); if (ret < 0) { @@ -1484,7 +1613,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) if (ret < 0) goto disable_div_clk; - ret = tegra_i2c_init(i2c_dev); + ret = tegra_i2c_init(i2c_dev, false); if (ret) { dev_err(&pdev->dev, "Failed to initialize i2c controller\n"); goto release_dma; -- cgit v1.2.3-59-g8ed1b From e7663ef5ae0f02e3b902eb0305dec981333eb3e1 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Feb 2019 09:51:30 +0100 Subject: i2c: ocores: stop transfer on timeout Detecting a timeout is ok, but we also need to assert a STOP command on the bus in order to prevent it from generating interrupts when there are no on going transfers. Example: very long transmission. 1. ocores_xfer: START a transfer 2. ocores_isr : handle byte by byte the transfer 3. ocores_xfer: goes in timeout [[bugfix here]] 4. ocores_xfer: return to I2C subsystem and to the I2C driver 5. I2C driver : it may clean up the i2c_msg memory 6. ocores_isr : receives another interrupt (pending bytes to be transferred) but the i2c_msg memory is invalid now So, since the transfer was too long, we have to detect the timeout and STOP the transfer. Another point is that we have a critical region here. When handling the timeout condition we may have a running IRQ handler. For this reason I introduce a spinlock. In order to make easier to understan locking I have: - added a new function to handle timeout - modified the current ocores_process() function in order to be protected by the new spinlock Like this it is obvious at first sight that this locking serializes the execution of ocores_process() and ocores_process_timeout() Signed-off-by: Federico Vaga Reviewed-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 54 ++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 87f9caacba85..aa852028d8c1 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -25,7 +25,12 @@ #include #include #include +#include +/** + * @process_lock: protect I2C transfer process. + * ocores_process() and ocores_process_timeout() can't run in parallel. + */ struct ocores_i2c { void __iomem *base; u32 reg_shift; @@ -36,6 +41,7 @@ struct ocores_i2c { int pos; int nmsgs; int state; /* see STATE_ */ + spinlock_t process_lock; struct clk *clk; int ip_clock_khz; int bus_clock_khz; @@ -141,19 +147,26 @@ static void ocores_process(struct ocores_i2c *i2c) { struct i2c_msg *msg = i2c->msg; u8 stat = oc_getreg(i2c, OCI2C_STATUS); + unsigned long flags; + + /* + * If we spin here is because we are in timeout, so we are going + * to be in STATE_ERROR. See ocores_process_timeout() + */ + spin_lock_irqsave(&i2c->process_lock, flags); if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) { /* stop has been sent */ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); wake_up(&i2c->wait); - return; + goto out; } /* error? */ if (stat & OCI2C_STAT_ARBLOST) { i2c->state = STATE_ERROR; oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); - return; + goto out; } if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) { @@ -163,7 +176,7 @@ static void ocores_process(struct ocores_i2c *i2c) if (stat & OCI2C_STAT_NACK) { i2c->state = STATE_ERROR; oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); - return; + goto out; } } else msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); @@ -184,14 +197,14 @@ static void ocores_process(struct ocores_i2c *i2c) oc_setreg(i2c, OCI2C_DATA, addr); oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); - return; + goto out; } else i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; } else { i2c->state = STATE_DONE; oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); - return; + goto out; } } @@ -202,6 +215,9 @@ static void ocores_process(struct ocores_i2c *i2c) oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]); oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE); } + +out: + spin_unlock_irqrestore(&i2c->process_lock, flags); } static irqreturn_t ocores_isr(int irq, void *dev_id) @@ -213,9 +229,24 @@ static irqreturn_t ocores_isr(int irq, void *dev_id) return IRQ_HANDLED; } +/** + * Process timeout event + * @i2c: ocores I2C device instance + */ +static void ocores_process_timeout(struct ocores_i2c *i2c) +{ + unsigned long flags; + + spin_lock_irqsave(&i2c->process_lock, flags); + i2c->state = STATE_ERROR; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + spin_unlock_irqrestore(&i2c->process_lock, flags); +} + static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct ocores_i2c *i2c = i2c_get_adapdata(adap); + int ret; i2c->msg = msgs; i2c->pos = 0; @@ -225,11 +256,14 @@ static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg)); oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); - if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || - (i2c->state == STATE_DONE), HZ)) - return (i2c->state == STATE_DONE) ? num : -EIO; - else + ret = wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || + (i2c->state == STATE_DONE), HZ); + if (ret == 0) { + ocores_process_timeout(i2c); return -ETIMEDOUT; + } + + return (i2c->state == STATE_DONE) ? num : -EIO; } static int ocores_init(struct device *dev, struct ocores_i2c *i2c) @@ -422,6 +456,8 @@ static int ocores_i2c_probe(struct platform_device *pdev) if (!i2c) return -ENOMEM; + spin_lock_init(&i2c->process_lock); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2c->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(i2c->base)) -- cgit v1.2.3-59-g8ed1b From 2dc9834688ae3ba057d46e60525a11ec146ae3df Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Feb 2019 09:51:31 +0100 Subject: i2c: ocores: do not handle IRQ if IF is not set If the Interrupt Flag (IF) is not set, we should not handle the IRQ: - the line can be shared with other devices - it can be a spurious interrupt To avoid reading twice the status register, the ocores_process() function expects it to be read by the caller. Signed-off-by: Federico Vaga Acked-by: Peter Korsgaard Reviewed-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index aa852028d8c1..fcc25582c648 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -143,10 +143,9 @@ static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) return i2c->getreg(i2c, reg); } -static void ocores_process(struct ocores_i2c *i2c) +static void ocores_process(struct ocores_i2c *i2c, u8 stat) { struct i2c_msg *msg = i2c->msg; - u8 stat = oc_getreg(i2c, OCI2C_STATUS); unsigned long flags; /* @@ -223,8 +222,12 @@ out: static irqreturn_t ocores_isr(int irq, void *dev_id) { struct ocores_i2c *i2c = dev_id; + u8 stat = oc_getreg(i2c, OCI2C_STATUS); + + if (!(stat & OCI2C_STAT_IF)) + return IRQ_NONE; - ocores_process(i2c); + ocores_process(i2c, stat); return IRQ_HANDLED; } -- cgit v1.2.3-59-g8ed1b From 69c8c0c0efa84cc7c95ef774e97cac237b0f6d36 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Feb 2019 09:51:32 +0100 Subject: i2c: ocores: add polling interface This driver assumes that an interrupt line is always available for the I2C master. This is not always the case and this patch adds support for a polling version. Report from Andrew Lunn: I did some timing tests for this. On my box, we request a udelay of 80uS. The kernel actually delays for about 79uS. We then spin in ocores_wait() for an additional 10-11uS, which is 3 to 4 iterations. There are actually 9 bits on the wire, not 8, since there is an ACK/NACK bit after the actual data transfer. So i changed the delay to (9 * 1000) / i2c->bus_clock_khz. That resulted in ocores_wait() mostly not looping at all. But for reading an 4K AT24 EEPROM, it increased the read time by 10ms, from 424ms to 434ms. So we should probably keep with 8. Signed-off-by: Federico Vaga Tested-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 182 +++++++++++++++++++++++++++++++++++----- 1 file changed, 161 insertions(+), 21 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index fcc25582c648..5dea7b9ab7e5 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -26,6 +27,9 @@ #include #include #include +#include + +#define OCORES_FLAG_POLL BIT(0) /** * @process_lock: protect I2C transfer process. @@ -35,6 +39,7 @@ struct ocores_i2c { void __iomem *base; u32 reg_shift; u32 reg_io_width; + unsigned long flags; wait_queue_head_t wait; struct i2c_adapter adap; struct i2c_msg *msg; @@ -246,10 +251,116 @@ static void ocores_process_timeout(struct ocores_i2c *i2c) spin_unlock_irqrestore(&i2c->process_lock, flags); } -static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +/** + * Wait until something change in a given register + * @i2c: ocores I2C device instance + * @reg: register to query + * @mask: bitmask to apply on register value + * @val: expected result + * @timeout: timeout in jiffies + * + * Timeout is necessary to avoid to stay here forever when the chip + * does not answer correctly. + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_wait(struct ocores_i2c *i2c, + int reg, u8 mask, u8 val, + const unsigned long timeout) +{ + unsigned long j; + + j = jiffies + timeout; + while (1) { + u8 status = oc_getreg(i2c, reg); + + if ((status & mask) == val) + break; + + if (time_after(jiffies, j)) + return -ETIMEDOUT; + } + return 0; +} + +/** + * Wait until is possible to process some data + * @i2c: ocores I2C device instance + * + * Used when the device is in polling mode (interrupts disabled). + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_poll_wait(struct ocores_i2c *i2c) +{ + u8 mask; + int err; + + if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) { + /* transfer is over */ + mask = OCI2C_STAT_BUSY; + } else { + /* on going transfer */ + mask = OCI2C_STAT_TIP; + /* + * We wait for the data to be transferred (8bit), + * then we start polling on the ACK/NACK bit + */ + udelay((8 * 1000) / i2c->bus_clock_khz); + } + + /* + * once we are here we expect to get the expected result immediately + * so if after 1ms we timeout then something is broken. + */ + err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, msecs_to_jiffies(1)); + if (err) + dev_warn(i2c->adap.dev.parent, + "%s: STATUS timeout, bit 0x%x did not clear in 1ms\n", + __func__, mask); + return err; +} + +/** + * It handles an IRQ-less transfer + * @i2c: ocores I2C device instance + * + * Even if IRQ are disabled, the I2C OpenCore IP behavior is exactly the same + * (only that IRQ are not produced). This means that we can re-use entirely + * ocores_isr(), we just add our polling code around it. + * + * It can run in atomic context + */ +static void ocores_process_polling(struct ocores_i2c *i2c) +{ + while (1) { + irqreturn_t ret; + int err; + + err = ocores_poll_wait(i2c); + if (err) { + i2c->state = STATE_ERROR; + break; /* timeout */ + } + + ret = ocores_isr(-1, i2c); + if (ret == IRQ_NONE) + break; /* all messages have been transferred */ + } +} + +static int ocores_xfer_core(struct ocores_i2c *i2c, + struct i2c_msg *msgs, int num, + bool polling) { - struct ocores_i2c *i2c = i2c_get_adapdata(adap); int ret; + u8 ctrl; + + ctrl = oc_getreg(i2c, OCI2C_CONTROL); + if (polling) + oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~OCI2C_CTRL_IEN); + else + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN); i2c->msg = msgs; i2c->pos = 0; @@ -259,16 +370,37 @@ static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg)); oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); - ret = wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || - (i2c->state == STATE_DONE), HZ); - if (ret == 0) { - ocores_process_timeout(i2c); - return -ETIMEDOUT; + if (polling) { + ocores_process_polling(i2c); + } else { + ret = wait_event_timeout(i2c->wait, + (i2c->state == STATE_ERROR) || + (i2c->state == STATE_DONE), HZ); + if (ret == 0) { + ocores_process_timeout(i2c); + return -ETIMEDOUT; + } } return (i2c->state == STATE_DONE) ? num : -EIO; } +static int ocores_xfer_polling(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, true); +} + +static int ocores_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct ocores_i2c *i2c = i2c_get_adapdata(adap); + + if (i2c->flags & OCORES_FLAG_POLL) + return ocores_xfer_polling(adap, msgs, num); + return ocores_xfer_core(i2c, msgs, num, false); +} + static int ocores_init(struct device *dev, struct ocores_i2c *i2c) { int prescale; @@ -276,7 +408,8 @@ static int ocores_init(struct device *dev, struct ocores_i2c *i2c) u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); /* make sure the device is disabled */ - oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); prescale = (i2c->ip_clock_khz / (5 * i2c->bus_clock_khz)) - 1; prescale = clamp(prescale, 0, 0xffff); @@ -294,7 +427,7 @@ static int ocores_init(struct device *dev, struct ocores_i2c *i2c) /* Init the device */ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); - oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_EN); return 0; } @@ -451,10 +584,6 @@ static int ocores_i2c_probe(struct platform_device *pdev) int ret; int i; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; @@ -509,18 +638,29 @@ static int ocores_i2c_probe(struct platform_device *pdev) } } + init_waitqueue_head(&i2c->wait); + + irq = platform_get_irq(pdev, 0); + if (irq == -ENXIO) { + i2c->flags |= OCORES_FLAG_POLL; + } else { + if (irq < 0) + return irq; + } + + if (!(i2c->flags & OCORES_FLAG_POLL)) { + ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0, + pdev->name, i2c); + if (ret) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + goto err_clk; + } + } + ret = ocores_init(&pdev->dev, i2c); if (ret) goto err_clk; - init_waitqueue_head(&i2c->wait); - ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0, - pdev->name, i2c); - if (ret) { - dev_err(&pdev->dev, "Cannot claim IRQ\n"); - goto err_clk; - } - /* hook up driver to tree */ platform_set_drvdata(pdev, i2c); i2c->adap = ocores_adapter; -- cgit v1.2.3-59-g8ed1b From 2c7e4928b35660e2147d14d5e42849c22f44b55f Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Feb 2019 09:51:33 +0100 Subject: i2c: ocores: add SPDX tag It adds the SPDX tag and it removes the old text about the GPLv2. Signed-off-by: Federico Vaga Reviewed-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 5 +---- include/linux/platform_data/i2c-ocores.h | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 5dea7b9ab7e5..78085a88d866 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * i2c-ocores.c: I2C bus driver for OpenCores I2C controller * (https://opencores.org/project/i2c/overview) @@ -6,10 +7,6 @@ * * Support for the GRLIB port of the controller by * Andreas Larsson - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/include/linux/platform_data/i2c-ocores.h b/include/linux/platform_data/i2c-ocores.h index 113d6b12f650..8c416ff8affd 100644 --- a/include/linux/platform_data/i2c-ocores.h +++ b/include/linux/platform_data/i2c-ocores.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * i2c-ocores.h - definitions for the i2c-ocores interface * * Peter Korsgaard - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _LINUX_I2C_OCORES_H -- cgit v1.2.3-59-g8ed1b From fac9c29fc7d5b57724b88f54d27e11b20404a2ec Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Feb 2019 09:51:34 +0100 Subject: i2c: ocores: checkpatch fixes Miscellaneous style fixes from checkpatch Signed-off-by: Federico Vaga Reviewed-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 78085a88d866..b32d67c90d67 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -179,8 +179,9 @@ static void ocores_process(struct ocores_i2c *i2c, u8 stat) oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); goto out; } - } else + } else { msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); + } /* end of msg? */ if (i2c->pos == msg->len) { @@ -197,11 +198,11 @@ static void ocores_process(struct ocores_i2c *i2c, u8 stat) i2c->state = STATE_START; oc_setreg(i2c, OCI2C_DATA, addr); - oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); goto out; - } else - i2c->state = (msg->flags & I2C_M_RD) - ? STATE_READ : STATE_WRITE; + } + i2c->state = (msg->flags & I2C_M_RD) + ? STATE_READ : STATE_WRITE; } else { i2c->state = STATE_DONE; oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); @@ -461,13 +462,16 @@ static const struct of_device_id ocores_i2c_match[] = { MODULE_DEVICE_TABLE(of, ocores_i2c_match); #ifdef CONFIG_OF -/* Read and write functions for the GRLIB port of the controller. Registers are +/* + * Read and write functions for the GRLIB port of the controller. Registers are * 32-bit big endian and the PRELOW and PREHIGH registers are merged into one - * register. The subsequent registers has their offset decreased accordingly. */ + * register. The subsequent registers have their offsets decreased accordingly. + */ static u8 oc_getreg_grlib(struct ocores_i2c *i2c, int reg) { u32 rd; int rreg = reg; + if (reg != OCI2C_PRELOW) rreg--; rd = ioread32be(i2c->base + (rreg << i2c->reg_shift)); @@ -481,6 +485,7 @@ static void oc_setreg_grlib(struct ocores_i2c *i2c, int reg, u8 value) { u32 curr, wr; int rreg = reg; + if (reg != OCI2C_PRELOW) rreg--; if (reg == OCI2C_PRELOW || reg == OCI2C_PREHIGH) { @@ -569,7 +574,7 @@ static int ocores_i2c_of_probe(struct platform_device *pdev, return 0; } #else -#define ocores_i2c_of_probe(pdev,i2c) -ENODEV +#define ocores_i2c_of_probe(pdev, i2c) -ENODEV #endif static int ocores_i2c_probe(struct platform_device *pdev) @@ -686,10 +691,11 @@ err_clk: static int ocores_i2c_remove(struct platform_device *pdev) { struct ocores_i2c *i2c = platform_get_drvdata(pdev); + u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); /* disable i2c logic */ - oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL) - & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); /* remove adapter & data */ i2c_del_adapter(&i2c->adap); @@ -707,7 +713,8 @@ static int ocores_i2c_suspend(struct device *dev) u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); /* make sure the device is disabled */ - oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); if (!IS_ERR(i2c->clk)) clk_disable_unprepare(i2c->clk); -- cgit v1.2.3-59-g8ed1b From 809445d4b7fa7101d9434182bebbd8c84d7e4b65 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 14 Feb 2019 04:24:49 +0100 Subject: i2c: ocores: Add support for IO mapper registers. Some implementations of the OCORES i2c bus master use IO mapped registers. Add support for getting the IO registers from the platform data, and register accessor functions. Signed-off-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index b32d67c90d67..0d90a82a2c03 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -34,6 +34,7 @@ */ struct ocores_i2c { void __iomem *base; + int iobase; u32 reg_shift; u32 reg_io_width; unsigned long flags; @@ -135,6 +136,16 @@ static inline u8 oc_getreg_32be(struct ocores_i2c *i2c, int reg) return ioread32be(i2c->base + (reg << i2c->reg_shift)); } +static void oc_setreg_io_8(struct ocores_i2c *i2c, int reg, u8 value) +{ + outb(value, i2c->iobase + reg); +} + +static inline u8 oc_getreg_io_8(struct ocores_i2c *i2c, int reg) +{ + return inb(i2c->iobase + reg); +} + static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) { i2c->setreg(i2c, reg, value); @@ -593,9 +604,24 @@ static int ocores_i2c_probe(struct platform_device *pdev) spin_lock_init(&i2c->process_lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - i2c->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(i2c->base)) - return PTR_ERR(i2c->base); + if (res) { + i2c->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + } else { + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -EINVAL; + i2c->iobase = res->start; + if (!devm_request_region(&pdev->dev, res->start, + resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, "Can't get I/O resource.\n"); + return -EBUSY; + } + i2c->setreg = oc_setreg_io_8; + i2c->getreg = oc_getreg_io_8; + } pdata = dev_get_platdata(&pdev->dev); if (pdata) { -- cgit v1.2.3-59-g8ed1b From 237b5f66e1ed8a58662f29bcd04442953cdb8b55 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 14 Feb 2019 04:24:50 +0100 Subject: i2c: ocores: Add support for bus clock via platform data Add the I2C bus clock speed to the platform data structure. If not set, default to 100KHz as before. Signed-off-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 5 ++++- include/linux/platform_data/i2c-ocores.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 0d90a82a2c03..4eea18689e99 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -628,7 +628,10 @@ static int ocores_i2c_probe(struct platform_device *pdev) i2c->reg_shift = pdata->reg_shift; i2c->reg_io_width = pdata->reg_io_width; i2c->ip_clock_khz = pdata->clock_khz; - i2c->bus_clock_khz = 100; + if (pdata->bus_khz) + i2c->bus_clock_khz = pdata->bus_khz; + else + i2c->bus_clock_khz = 100; } else { ret = ocores_i2c_of_probe(pdev, i2c); if (ret) diff --git a/include/linux/platform_data/i2c-ocores.h b/include/linux/platform_data/i2c-ocores.h index 8c416ff8affd..e6326cbafe59 100644 --- a/include/linux/platform_data/i2c-ocores.h +++ b/include/linux/platform_data/i2c-ocores.h @@ -12,6 +12,7 @@ struct ocores_i2c_platform_data { u32 reg_shift; /* register offset shift value */ u32 reg_io_width; /* register io read/write width */ u32 clock_khz; /* input clock in kHz */ + u32 bus_khz; /* bus clock in kHz */ bool big_endian; /* registers are big endian */ u8 num_devices; /* number of devices in the devices list */ struct i2c_board_info const *devices; /* devices connected to the bus */ -- cgit v1.2.3-59-g8ed1b From d8434c31378d5f2009741fa171bd1143c77a6e4a Mon Sep 17 00:00:00 2001 From: John Sperbeck Date: Tue, 12 Feb 2019 19:40:57 -0800 Subject: i2c: core-smbus: don't trace smbus_reply data on errors If an smbus transfer fails, there's no guarantee that the output buffer was written. So, avoid trying to show the output buffer when tracing after an error. This was 'mostly harmless', but would trip up kasan checking if left-over cruft in byte 0 is a large length, causing us to read from unwritten memory. Signed-off-by: John Sperbeck Reviewed-by: Steven Rostedt (VMware) Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-smbus.c | 2 +- include/trace/events/smbus.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 9cd66cabb84f..132119112596 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -585,7 +585,7 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, trace: /* If enabled, the reply tracepoint is conditional on read_write. */ trace_smbus_reply(adapter, addr, flags, read_write, - command, protocol, data); + command, protocol, data, res); trace_smbus_result(adapter, addr, flags, read_write, command, protocol, res); diff --git a/include/trace/events/smbus.h b/include/trace/events/smbus.h index d2fb6e1d3e10..a4892a187842 100644 --- a/include/trace/events/smbus.h +++ b/include/trace/events/smbus.h @@ -138,9 +138,9 @@ TRACE_EVENT_CONDITION(smbus_reply, TP_PROTO(const struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, - const union i2c_smbus_data *data), - TP_ARGS(adap, addr, flags, read_write, command, protocol, data), - TP_CONDITION(read_write == I2C_SMBUS_READ), + const union i2c_smbus_data *data, int res), + TP_ARGS(adap, addr, flags, read_write, command, protocol, data, res), + TP_CONDITION(res >= 0 && read_write == I2C_SMBUS_READ), TP_STRUCT__entry( __field(int, adapter_nr ) __field(__u16, addr ) -- cgit v1.2.3-59-g8ed1b From 2e57b7cebb988a27cee44626ae91424e73823bfb Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Mon, 11 Feb 2019 10:54:44 -0800 Subject: i2c: aspeed: Add multi-master use case support In multi-master environment, this driver's master cannot know exactly when a peer master sends data to this driver's slave so cases can be happened that this master tries sending data through the master_xfer function but slave data from a peer master is still being processed or slave xfer is started by a peer immediately after it queues a master command. To support multi-master use cases properly, this H/W provides arbitration in physical level and it provides priority based command handling too to avoid conflicts in multi-master environment, means that if a master and a slave events happen at the same time, H/W will handle a higher priority event first and a pending event will be handled when bus comes back to the idle state. To support this H/W feature properly, this patch adds the 'pending' state of master and its handling code so that the pending master xfer can be continued after slave operation properly. Signed-off-by: Jae Hyun Yoo Reviewed-by: Brendan Higgins Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-aspeed.c | 119 +++++++++++++++++++++++++++++++--------- 1 file changed, 93 insertions(+), 26 deletions(-) diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 833b6b6a4c7e..6c8b38fd6e64 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -117,6 +117,7 @@ enum aspeed_i2c_master_state { ASPEED_I2C_MASTER_INACTIVE, + ASPEED_I2C_MASTER_PENDING, ASPEED_I2C_MASTER_START, ASPEED_I2C_MASTER_TX_FIRST, ASPEED_I2C_MASTER_TX, @@ -126,12 +127,13 @@ enum aspeed_i2c_master_state { }; enum aspeed_i2c_slave_state { - ASPEED_I2C_SLAVE_STOP, + ASPEED_I2C_SLAVE_INACTIVE, ASPEED_I2C_SLAVE_START, ASPEED_I2C_SLAVE_READ_REQUESTED, ASPEED_I2C_SLAVE_READ_PROCESSED, ASPEED_I2C_SLAVE_WRITE_REQUESTED, ASPEED_I2C_SLAVE_WRITE_RECEIVED, + ASPEED_I2C_SLAVE_STOP, }; struct aspeed_i2c_bus { @@ -156,6 +158,8 @@ struct aspeed_i2c_bus { int cmd_err; /* Protected only by i2c_lock_bus */ int master_xfer_result; + /* Multi-master */ + bool multi_master; #if IS_ENABLED(CONFIG_I2C_SLAVE) struct i2c_client *slave; enum aspeed_i2c_slave_state slave_state; @@ -251,7 +255,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) } /* Slave is not currently active, irq was for someone else. */ - if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) + if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) return irq_handled; dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", @@ -277,16 +281,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; bus->slave_state = ASPEED_I2C_SLAVE_STOP; } - if (irq_status & ASPEED_I2CD_INTR_TX_NAK) { + if (irq_status & ASPEED_I2CD_INTR_TX_NAK && + bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { irq_handled |= ASPEED_I2CD_INTR_TX_NAK; bus->slave_state = ASPEED_I2C_SLAVE_STOP; } - if (irq_status & ASPEED_I2CD_INTR_TX_ACK) - irq_handled |= ASPEED_I2CD_INTR_TX_ACK; switch (bus->slave_state) { case ASPEED_I2C_SLAVE_READ_REQUESTED: - if (irq_status & ASPEED_I2CD_INTR_TX_ACK) + if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK)) dev_err(bus->dev, "Unexpected ACK on read request.\n"); bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); @@ -294,9 +297,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); break; case ASPEED_I2C_SLAVE_READ_PROCESSED: - if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK)) + if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { dev_err(bus->dev, "Expected ACK after processed read.\n"); + break; + } + irq_handled |= ASPEED_I2CD_INTR_TX_ACK; i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); @@ -310,10 +316,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) break; case ASPEED_I2C_SLAVE_STOP: i2c_slave_event(slave, I2C_SLAVE_STOP, &value); + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; + break; + case ASPEED_I2C_SLAVE_START: + /* Slave was just started. Waiting for the next event. */; break; default: - dev_err(bus->dev, "unhandled slave_state: %d\n", + dev_err(bus->dev, "unknown slave_state: %d\n", bus->slave_state); + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; break; } @@ -329,6 +340,17 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) u8 slave_addr = i2c_8bit_addr_from_msg(msg); bus->master_state = ASPEED_I2C_MASTER_START; + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* + * If it's requested in the middle of a slave session, set the master + * state to 'pending' then H/W will continue handling this master + * command when the bus comes back to the idle state. + */ + if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) + bus->master_state = ASPEED_I2C_MASTER_PENDING; +#endif /* CONFIG_I2C_SLAVE */ + bus->buf_index = 0; if (msg->flags & I2C_M_RD) { @@ -384,10 +406,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) bus->master_state = ASPEED_I2C_MASTER_INACTIVE; irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; goto out_complete; - } else { - /* Master is not currently active, irq was for someone else. */ - if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE) - goto out_no_complete; } /* @@ -399,11 +417,32 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) if (ret) { dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", irq_status); - bus->cmd_err = ret; - bus->master_state = ASPEED_I2C_MASTER_INACTIVE; irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); - goto out_complete; + if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { + bus->cmd_err = ret; + bus->master_state = ASPEED_I2C_MASTER_INACTIVE; + goto out_complete; + } + } + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* + * A pending master command will be started by H/W when the bus comes + * back to idle state after completing a slave operation so change the + * master state from 'pending' to 'start' at here if slave is inactive. + */ + if (bus->master_state == ASPEED_I2C_MASTER_PENDING) { + if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) + goto out_no_complete; + + bus->master_state = ASPEED_I2C_MASTER_START; } +#endif /* CONFIG_I2C_SLAVE */ + + /* Master is not currently active, irq was for someone else. */ + if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE || + bus->master_state == ASPEED_I2C_MASTER_PENDING) + goto out_no_complete; /* We are in an invalid state; reset bus to a known state. */ if (!bus->msgs) { @@ -423,6 +462,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) * then update the state and handle the new state below. */ if (bus->master_state == ASPEED_I2C_MASTER_START) { +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* + * If a peer master starts a xfer immediately after it queues a + * master command, change its state to 'pending' then H/W will + * continue the queued master xfer just after completing the + * slave mode session. + */ + if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) { + bus->master_state = ASPEED_I2C_MASTER_PENDING; + dev_dbg(bus->dev, + "master goes pending due to a slave start\n"); + goto out_no_complete; + } +#endif /* CONFIG_I2C_SLAVE */ if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) { bus->cmd_err = -ENXIO; @@ -566,7 +619,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) * interrupt bits. Each case needs to be handled using corresponding * handlers depending on the current state. */ - if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { + if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE && + bus->master_state != ASPEED_I2C_MASTER_PENDING) { irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); irq_remaining &= ~irq_handled; if (irq_remaining) @@ -601,15 +655,16 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, { struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); unsigned long time_left, flags; - int ret = 0; spin_lock_irqsave(&bus->lock, flags); bus->cmd_err = 0; - /* If bus is busy, attempt recovery. We assume a single master - * environment. - */ - if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) { + /* If bus is busy in a single master environment, attempt recovery. */ + if (!bus->multi_master && + (readl(bus->base + ASPEED_I2C_CMD_REG) & + ASPEED_I2CD_BUS_BUSY_STS)) { + int ret; + spin_unlock_irqrestore(&bus->lock, flags); ret = aspeed_i2c_recover_bus(bus); if (ret) @@ -629,10 +684,20 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, time_left = wait_for_completion_timeout(&bus->cmd_complete, bus->adap.timeout); - if (time_left == 0) + if (time_left == 0) { + /* + * If timed out and bus is still busy in a multi master + * environment, attempt recovery at here. + */ + if (bus->multi_master && + (readl(bus->base + ASPEED_I2C_CMD_REG) & + ASPEED_I2CD_BUS_BUSY_STS)) + aspeed_i2c_recover_bus(bus); + return -ETIMEDOUT; - else - return bus->master_xfer_result; + } + + return bus->master_xfer_result; } static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) @@ -672,7 +737,7 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client) __aspeed_i2c_reg_slave(bus, client->addr); bus->slave = client; - bus->slave_state = ASPEED_I2C_SLAVE_STOP; + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; spin_unlock_irqrestore(&bus->lock, flags); return 0; @@ -827,7 +892,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, if (ret < 0) return ret; - if (!of_property_read_bool(pdev->dev.of_node, "multi-master")) + if (of_property_read_bool(pdev->dev.of_node, "multi-master")) + bus->multi_master = true; + else fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; /* Enable Master Mode */ -- cgit v1.2.3-59-g8ed1b From 8a6d508a8055ea2ef8477e649662287e57727f54 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Tue, 12 Feb 2019 14:06:57 +0800 Subject: i2c: expand minor range when registering chrdev region Actually, total amount of available minor number for a single major is MINORMASK + 1. So expand minor range when registering chrdev region. Signed-off-by: Chengguang Xu [wsa: fixed typo in commit message] Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index ccd76c71af09..3f7b9af11137 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -52,7 +52,7 @@ struct i2c_dev { struct cdev cdev; }; -#define I2C_MINORS MINORMASK +#define I2C_MINORS (MINORMASK + 1) static LIST_HEAD(i2c_dev_list); static DEFINE_SPINLOCK(i2c_dev_list_lock); -- cgit v1.2.3-59-g8ed1b From bb0e9b1d2a1f933b4d899cae039363297270198d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 15 Feb 2019 15:31:26 +0000 Subject: i2c: tegra: change phrasing, "fallbacking" to "falling back" The phrasing in two dev_err messages is using fallbacking which os less understandable than "falling back", so fix this up. Signed-off-by: Colin Ian King Acked-by: Thierry Reding Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index a4cd79c9f7a7..5a403c3ab66c 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -452,7 +452,7 @@ err_out: tegra_i2c_release_dma(i2c_dev); if (err != -EPROBE_DEFER) { dev_err(i2c_dev->dev, "cannot use DMA: %d\n", err); - dev_err(i2c_dev->dev, "fallbacking to PIO\n"); + dev_err(i2c_dev->dev, "falling back to PIO\n"); return 0; } @@ -965,7 +965,7 @@ static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev, if (ret < 0) { dev_err(i2c_dev->dev, "DMA slave config failed: %d\n", ret); - dev_err(i2c_dev->dev, "fallbacking to PIO\n"); + dev_err(i2c_dev->dev, "falling back to PIO\n"); tegra_i2c_release_dma(i2c_dev); i2c_dev->is_curr_dma_xfer = false; } else { -- cgit v1.2.3-59-g8ed1b From 9ffc125cd4f953d1e3ea6104a809717e417ed057 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 19 Feb 2019 09:28:51 -0800 Subject: i2c: tegra: remove master fifo support on tegra186 Tegra186 does not have master FIFO control register and instead uses FIFO control register like prior Tegra chipset. This patch fixes this and prevents crashing during boot when accessing FIFO control registers. Acked-by: Thierry Reding Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 5a403c3ab66c..9d097ad2f6db 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -1436,7 +1436,7 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .has_config_load_reg = true, .has_multi_master_mode = true, .has_slcg_override_reg = true, - .has_mst_fifo = true, + .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = false, -- cgit v1.2.3-59-g8ed1b From 6b9932bc28fd61b83a8cb86bf0de1a86e5c18fa3 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 19 Feb 2019 09:28:52 -0800 Subject: i2c: tegra: remove multi-master support Multi-master support is defeatured on Tegra210 and Tegra186 due to known bugs. This patch removes multi-master support for Tegra210 and Tegra186 I2C HW feature. Acked-by: Thierry Reding Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 9d097ad2f6db..31ff67015255 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -1409,7 +1409,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, - .has_multi_master_mode = true, + .has_multi_master_mode = false, .has_slcg_override_reg = true, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, @@ -1434,7 +1434,7 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, - .has_multi_master_mode = true, + .has_multi_master_mode = false, .has_slcg_override_reg = true, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, -- cgit v1.2.3-59-g8ed1b From 63e57b6f191db99ffdd0dc6c7b9ee6b2cf7abb04 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 19 Feb 2019 17:39:45 +0100 Subject: i2c: gpio: fault-injector: add 'lose_arbitration' injector Add a fault injector simulating 'arbitration lost' from multi-master setups. Read the docs for its usage. A helper function for future fault injectors using SCL interrupts is created to achieve this. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- Documentation/i2c/gpio-fault-injection | 25 ++++++++++++ drivers/i2c/busses/i2c-gpio.c | 74 ++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/Documentation/i2c/gpio-fault-injection b/Documentation/i2c/gpio-fault-injection index 1a44e3edc0c4..1f1bb96a64bd 100644 --- a/Documentation/i2c/gpio-fault-injection +++ b/Documentation/i2c/gpio-fault-injection @@ -83,3 +83,28 @@ This is why bus recovery (up to 9 clock pulses) must either check SDA or send additional STOP conditions to ensure the bus has been released. Otherwise random data will be written to a device! +Lost arbitration +================ + +Here, we want to simulate the condition where the master under test loses the +bus arbitration against another master in a multi-master setup. + +"lose_arbitration" +------------------ + +This file is write only and you need to write the duration of the arbitration +intereference (in µs, maximum is 100ms). The calling process will then sleep +and wait for the next bus clock. The process is interruptible, though. + +Arbitration lost is achieved by waiting for SCL going down by the master under +test and then pulling SDA low for some time. So, the I2C address sent out +should be corrupted and that should be detected properly. That means that the +address sent out should have a lot of '1' bits to be able to detect corruption. +There doesn't need to be a device at this address because arbitration lost +should be detected beforehand. Also note, that SCL going down is monitored +using interrupts, so the interrupt latency might cause the first bits to be not +corrupted. A good starting point for using this fault injector on an otherwise +idle bus is: + +# echo 200 > lose_arbitration & +# i2cget -y 0x3f diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 2d532cc042f5..76e43783f50f 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -7,12 +7,14 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -27,6 +29,9 @@ struct i2c_gpio_private_data { struct i2c_gpio_platform_data pdata; #ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR struct dentry *debug_dir; + /* these must be protected by bus lock */ + struct completion scl_irq_completion; + u64 scl_irq_data; #endif }; @@ -162,6 +167,70 @@ static int fops_incomplete_write_byte_set(void *data, u64 addr) } DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n"); +static int i2c_gpio_fi_act_on_scl_irq(struct i2c_gpio_private_data *priv, + irqreturn_t handler(int, void*)) +{ + int ret, irq = gpiod_to_irq(priv->scl); + + if (irq < 0) + return irq; + + i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); + + ret = gpiod_direction_input(priv->scl); + if (ret) + goto unlock; + + reinit_completion(&priv->scl_irq_completion); + + ret = request_irq(irq, handler, IRQF_TRIGGER_FALLING, + "i2c_gpio_fault_injector_scl_irq", priv); + if (ret) + goto output; + + wait_for_completion_interruptible(&priv->scl_irq_completion); + + free_irq(irq, priv); + output: + ret = gpiod_direction_output(priv->scl, 1) ?: ret; + unlock: + i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); + + return ret; +} + +static irqreturn_t lose_arbitration_irq(int irq, void *dev_id) +{ + struct i2c_gpio_private_data *priv = dev_id; + + setsda(&priv->bit_data, 0); + udelay(priv->scl_irq_data); + setsda(&priv->bit_data, 1); + + complete(&priv->scl_irq_completion); + + return IRQ_HANDLED; +} + +static int fops_lose_arbitration_set(void *data, u64 duration) +{ + struct i2c_gpio_private_data *priv = data; + + if (duration > 100 * 1000) + return -EINVAL; + + priv->scl_irq_data = duration; + /* + * Interrupt on falling SCL. This ensures that the master under test has + * really started the transfer. Interrupt on falling SDA did only + * exercise 'bus busy' detection on some HW but not 'arbitration lost'. + * Note that the interrupt latency may cause the first bits to be + * transmitted correctly. + */ + return i2c_gpio_fi_act_on_scl_irq(priv, lose_arbitration_irq); +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_lose_arbitration, NULL, fops_lose_arbitration_set, "%llu\n"); + static void i2c_gpio_fault_injector_init(struct platform_device *pdev) { struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev); @@ -181,10 +250,15 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev) if (!priv->debug_dir) return; + init_completion(&priv->scl_irq_completion); + debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir, priv, &fops_incomplete_addr_phase); debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir, priv, &fops_incomplete_write_byte); + if (priv->bit_data.getscl) + debugfs_create_file_unsafe("lose_arbitration", 0200, priv->debug_dir, + priv, &fops_lose_arbitration); debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); } -- cgit v1.2.3-59-g8ed1b From bb6bdd51c838e8d046a84502f12619de4fd58d69 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 19 Feb 2019 17:39:46 +0100 Subject: i2c: gpio: fault-injector: add 'inject_panic' injector Add a fault injector simulating a Kernel panic happening after starting a transfer. Read the docs for its usage. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- Documentation/i2c/gpio-fault-injection | 26 ++++++++++++++++++++++++++ drivers/i2c/busses/i2c-gpio.c | 31 ++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Documentation/i2c/gpio-fault-injection b/Documentation/i2c/gpio-fault-injection index 1f1bb96a64bd..c87f416d53dd 100644 --- a/Documentation/i2c/gpio-fault-injection +++ b/Documentation/i2c/gpio-fault-injection @@ -108,3 +108,29 @@ idle bus is: # echo 200 > lose_arbitration & # i2cget -y 0x3f + +Panic during transfer +===================== + +This fault injector will create a Kernel panic once the master under test +started a transfer. This usually means that the state machine of the bus master +driver will be ungracefully interrupted and the bus may end up in an unusual +state. Use this to check if your shutdown/reboot/boot code can handle this +scenario. + +"inject_panic" +-------------- + +This file is write only and you need to write the delay between the detected +start of a transmission and the induced Kernel panic (in µs, maximum is 100ms). +The calling process will then sleep and wait for the next bus clock. The +process is interruptible, though. + +Start of a transfer is detected by waiting for SCL going down by the master +under test. A good starting point for using this fault injector is: + +# echo 0 > inject_panic & +# i2cget -y + +Note that there doesn't need to be a device listening to the address you are +using. Results may vary depending on that, though. diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 76e43783f50f..bba5c4627de3 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -231,6 +231,32 @@ static int fops_lose_arbitration_set(void *data, u64 duration) } DEFINE_DEBUGFS_ATTRIBUTE(fops_lose_arbitration, NULL, fops_lose_arbitration_set, "%llu\n"); +static irqreturn_t inject_panic_irq(int irq, void *dev_id) +{ + struct i2c_gpio_private_data *priv = dev_id; + + udelay(priv->scl_irq_data); + panic("I2C fault injector induced panic"); + + return IRQ_HANDLED; +} + +static int fops_inject_panic_set(void *data, u64 duration) +{ + struct i2c_gpio_private_data *priv = data; + + if (duration > 100 * 1000) + return -EINVAL; + + priv->scl_irq_data = duration; + /* + * Interrupt on falling SCL. This ensures that the master under test has + * really started the transfer. + */ + return i2c_gpio_fi_act_on_scl_irq(priv, inject_panic_irq); +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_inject_panic, NULL, fops_inject_panic_set, "%llu\n"); + static void i2c_gpio_fault_injector_init(struct platform_device *pdev) { struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev); @@ -256,9 +282,12 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev) priv, &fops_incomplete_addr_phase); debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir, priv, &fops_incomplete_write_byte); - if (priv->bit_data.getscl) + if (priv->bit_data.getscl) { + debugfs_create_file_unsafe("inject_panic", 0200, priv->debug_dir, + priv, &fops_inject_panic); debugfs_create_file_unsafe("lose_arbitration", 0200, priv->debug_dir, priv, &fops_lose_arbitration); + } debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); } -- cgit v1.2.3-59-g8ed1b From 89328b1b81858f724c6e3716eadf2a280f748ba0 Mon Sep 17 00:00:00 2001 From: Jonathan Hunter Date: Thu, 21 Feb 2019 15:00:38 +0000 Subject: i2c: tegra: Only display error messages if DMA setup fails Commit 86c92b9965ff ("i2c: tegra: Add DMA support") added DMA support to the Tegra I2C driver for Tegra devices that support the APB DMA controller. One side-effect of this change is that even for Tegra devices that do not have an APB DMA controller and hence, cannot support DMA tranfers for I2C transactions, the following error messages are still displayed ... ERR KERN tegra-i2c 31c0000.i2c: cannot use DMA: -19 ERR KERN tegra-i2c 31c0000.i2c: falling back to PIO There is no point displaying the above messages for devices that do not have an APB DMA controller and so fix this by returning from the tegra_i2c_init_dma() function if 'has_apb_dma' is not true. Furthermore, if CONFIG_TEGRA20_APB_DMA is not set, then rather than printing an error message, print an debug message as for whatever reason this could be intentional. Fixes: 86c92b9965ff ("i2c: tegra: Add DMA support") Signed-off-by: Jonathan Hunter Reviewed-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 31ff67015255..ebaa78d17d6e 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -414,10 +414,12 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) dma_addr_t dma_phys; int err; - if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA) || - !i2c_dev->hw->has_apb_dma) { - err = -ENODEV; - goto err_out; + if (!i2c_dev->hw->has_apb_dma) + return 0; + + if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA)) { + dev_dbg(i2c_dev->dev, "Support for APB DMA not enabled!\n"); + return 0; } chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx"); -- cgit v1.2.3-59-g8ed1b From 2751541555382dfa7661bcfaac3ee0fac49f505d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 22 Feb 2019 14:08:40 +0100 Subject: i2c: designware: Do not allow i2c_dw_xfer() calls while suspended On most Intel Bay- and Cherry-Trail systems the PMIC is connected over I2C and the PMIC is accessed through various means by the _PS0 and _PS3 ACPI methods (power on / off methods) of various devices. This leads to suspend/resume ordering problems where a device may be resumed and get its _PS0 method executed before the I2C controller is resumed. On Cherry Trail this leads to errors like these: i2c_designware 808622C1:06: controller timed out ACPI Error: AE_ERROR, Returned by Handler for [UserDefinedRegion] ACPI Error: Method parse/execution failed \_SB.P18W._ON, AE_ERROR video LNXVIDEO:00: Failed to change power state to D0 But on Bay Trail this caused I2C reads to seem to succeed, but they end up returning wrong data, which ends up getting written back by the typical read-modify-write cycle done to turn on various power-resources. Debugging the problems caused by this silent data corruption is quite nasty. This commit adds a check which disallows i2c_dw_xfer() calls to happen until the controller's resume method has completed. Which turns the silent data corruption into getting these errors in dmesg instead: i2c_designware 80860F41:04: Error i2c_dw_xfer call while suspended ACPI Error: AE_ERROR, Returned by Handler for [UserDefinedRegion] ACPI Error: Method parse/execution failed \_SB.PCI0.GFX0._PS0, AE_ERROR Which is much better. Note the above errors are an example of issues which this patch will help to debug, the actual fix requires fixing the suspend order and this has been fixed by a different commit. Note the setting / clearing of the suspended flag in the suspend / resume methods is NOT protected by i2c_lock_bus(). This is intentional as these methods get called from i2c_dw_xfer() (through pm_runtime_get/put) a nd i2c_dw_xfer() is called with the i2c_bus_lock held, so otherwise we would deadlock. This means that there is a theoretical race between a non runtime suspend and the suspended check in i2c_dw_xfer(), this is not a problem since normally we should not hit the race and this check is primarily a debugging tool so hitting the check if there are suspend/resume ordering problems does not need to be 100% reliable. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-core.h | 2 ++ drivers/i2c/busses/i2c-designware-master.c | 6 ++++++ drivers/i2c/busses/i2c-designware-pcidrv.c | 7 ++++++- drivers/i2c/busses/i2c-designware-platdrv.c | 3 +++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index b4a0b2b99a78..6b4ef1d38fb2 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -215,6 +215,7 @@ * @disable_int: function to disable all interrupts * @init: function to initialize the I2C hardware * @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE + * @suspended: set to true if the controller is suspended * * HCNT and LCNT parameters can be used if the platform knows more accurate * values than the one computed based only on the input clock frequency. @@ -270,6 +271,7 @@ struct dw_i2c_dev { int (*set_sda_hold_time)(struct dw_i2c_dev *dev); int mode; struct i2c_bus_recovery_info rinfo; + bool suspended; }; #define ACCESS_SWAP 0x00000001 diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 8d1bc44d2530..bb8e3f149979 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -426,6 +426,12 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) pm_runtime_get_sync(dev->dev); + if (dev->suspended) { + dev_err(dev->dev, "Error %s call while suspended\n", __func__); + ret = -ESHUTDOWN; + goto done_nolock; + } + reinit_completion(&dev->cmd_complete); dev->msgs = msgs; dev->msgs_num = num; diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index d50f80487214..76810deb2de6 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -176,6 +176,7 @@ static int i2c_dw_pci_suspend(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); struct dw_i2c_dev *i_dev = pci_get_drvdata(pdev); + i_dev->suspended = true; i_dev->disable(i_dev); return 0; @@ -185,8 +186,12 @@ static int i2c_dw_pci_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct dw_i2c_dev *i_dev = pci_get_drvdata(pdev); + int ret; - return i_dev->init(i_dev); + ret = i_dev->init(i_dev); + i_dev->suspended = false; + + return ret; } #endif diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 9eaac3be1f63..ead5e7de3e4d 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -454,6 +454,8 @@ static int dw_i2c_plat_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); + i_dev->suspended = true; + if (i_dev->shared_with_punit) return 0; @@ -471,6 +473,7 @@ static int dw_i2c_plat_resume(struct device *dev) i2c_dw_prepare_clk(i_dev, true); i_dev->init(i_dev); + i_dev->suspended = false; return 0; } -- cgit v1.2.3-59-g8ed1b From 088a8a7fb408d77b5c3029114f7ed457ed3ee715 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 11 Feb 2019 11:37:28 +0100 Subject: i2c: ocores: turn incomplete kdoc into a comment gcc complains, rightfully so, I think: drivers/i2c/busses/i2c-ocores.c:32: warning: Cannot understand * @process_lock: protect I2C transfer process. on line 32 - I thought it was a doc line Make it a simple comment. Signed-off-by: Wolfram Sang Reviewed-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 4eea18689e99..4e1a077fb688 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -28,9 +28,9 @@ #define OCORES_FLAG_POLL BIT(0) -/** - * @process_lock: protect I2C transfer process. - * ocores_process() and ocores_process_timeout() can't run in parallel. +/* + * 'process_lock' exists because ocores_process() and ocores_process_timeout() + * can't run in parallel. */ struct ocores_i2c { void __iomem *base; -- cgit v1.2.3-59-g8ed1b From 93b6604c5a669d84e45fe5129294875bf82eb1ff Mon Sep 17 00:00:00 2001 From: Jim Broadus Date: Tue, 19 Feb 2019 11:30:27 -0800 Subject: i2c: Allow recovery of the initial IRQ by an I2C client device. A previous change allowed I2C client devices to discover new IRQs upon reprobe by clearing the IRQ in i2c_device_remove. However, if an IRQ was assigned in i2c_new_device, that information is lost. For example, the touchscreen and trackpad devices on a Dell Inspiron laptop are I2C devices whose IRQs are defined by ACPI extended IRQ types. The client device structures are initialized during an ACPI walk. After removing the i2c_hid device, modprobe fails. This change caches the initial IRQ value in i2c_new_device and then resets the client device IRQ to the initial value in i2c_device_remove. Fixes: 6f108dd70d30 ("i2c: Clear client->irq in i2c_device_remove") Signed-off-by: Jim Broadus Reviewed-by: Benjamin Tissoires Reviewed-by: Charles Keepax [wsa: this is an easy to backport fix for the regression. We will refactor the code to handle irq assignments better in general.] Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 9 +++++---- include/linux/i2c.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 926ca0a7477f..cb6c5cb0df0b 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -430,7 +430,7 @@ static int i2c_device_remove(struct device *dev) dev_pm_clear_wake_irq(&client->dev); device_init_wakeup(&client->dev, false); - client->irq = 0; + client->irq = client->init_irq; return status; } @@ -741,10 +741,11 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->flags = info->flags; client->addr = info->addr; - client->irq = info->irq; - if (!client->irq) - client->irq = i2c_dev_irq_from_resources(info->resources, + client->init_irq = info->irq; + if (!client->init_irq) + client->init_irq = i2c_dev_irq_from_resources(info->resources, info->num_resources); + client->irq = client->init_irq; strlcpy(client->name, info->type, sizeof(client->name)); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 1f45331924d6..383510b4f083 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -333,6 +333,7 @@ struct i2c_client { char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on */ struct device dev; /* the device structure */ + int init_irq; /* irq set at initialization */ int irq; /* irq issued by device */ struct list_head detected; #if IS_ENABLED(CONFIG_I2C_SLAVE) -- cgit v1.2.3-59-g8ed1b