From patchwork Fri Nov 17 18:05:37 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Stach X-Patchwork-Id: 839085 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3ydmHz5Ft1z9s7G for ; Sat, 18 Nov 2017 05:05:49 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1161231AbdKQSFm (ORCPT ); Fri, 17 Nov 2017 13:05:42 -0500 Received: from metis.ext.4.pengutronix.de ([92.198.50.35]:49519 "EHLO metis.ext.4.pengutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753820AbdKQSFl (ORCPT ); Fri, 17 Nov 2017 13:05:41 -0500 Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7] helo=dude.pengutronix.de.) by metis.ext.pengutronix.de with esmtp (Exim 4.84_2) (envelope-from ) id 1eFl1H-0000mZ-NU; Fri, 17 Nov 2017 19:05:39 +0100 From: Lucas Stach To: Wolfram Sang Cc: linux-i2c@vger.kernel.org, kernel@pengutronix.de, patchwork-lst@pengutronix.de, Lukas Rusak , Chris Healy Subject: [PATCH 1/3] i2c: imx: use clk notifier for rate changes Date: Fri, 17 Nov 2017 19:05:37 +0100 Message-Id: <20171117180539.4984-2-l.stach@pengutronix.de> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171117180539.4984-1-l.stach@pengutronix.de> References: <20171117180539.4984-1-l.stach@pengutronix.de> X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: l.stach@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-i2c@vger.kernel.org Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org Instead of repeatedly calling clk_get_rate for each transfer, register a clock notifier to update the cached divider value each time the clock rate actually changes. Signed-off-by: Lucas Stach --- drivers/i2c/busses/i2c-imx.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 54a47b40546f..800c1f390308 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -467,15 +467,14 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx) return 0; } -static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx) +static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, + unsigned int i2c_clk_rate) { struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div; - unsigned int i2c_clk_rate; unsigned int div; int i; /* Divider value calculation */ - i2c_clk_rate = clk_get_rate(i2c_imx->clk); if (i2c_imx->cur_clk == i2c_clk_rate) return; @@ -510,6 +509,24 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx) #endif } +static int i2c_imx_clk_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct clk_notifier_data *ndata = data; + struct imx_i2c_struct *i2c_imx = container_of(&ndata->clk, + struct imx_i2c_struct, + clk); + + if (action & POST_RATE_CHANGE) + i2c_imx_set_clk(i2c_imx, ndata->new_rate); + + return NOTIFY_OK; +} + +static struct notifier_block i2c_imx_clk_notifier = { + .notifier_call = i2c_imx_clk_notifier_call, +}; + static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) { unsigned int temp = 0; @@ -517,8 +534,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - i2c_imx_set_clk(i2c_imx); - imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR); /* Enable I2C controller */ imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR); @@ -1131,6 +1146,8 @@ static int i2c_imx_probe(struct platform_device *pdev) "clock-frequency", &i2c_imx->bitrate); if (ret < 0 && pdata && pdata->bitrate) i2c_imx->bitrate = pdata->bitrate; + clk_notifier_register(i2c_imx->clk, &i2c_imx_clk_notifier); + i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk)); /* Set up chip registers to defaults */ imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, @@ -1141,12 +1158,12 @@ static int i2c_imx_probe(struct platform_device *pdev) ret = i2c_imx_init_recovery_info(i2c_imx, pdev); /* Give it another chance if pinctrl used is not ready yet */ if (ret == -EPROBE_DEFER) - goto rpm_disable; + goto clk_notifier_unregister; /* Add I2C adapter */ ret = i2c_add_numbered_adapter(&i2c_imx->adapter); if (ret < 0) - goto rpm_disable; + goto clk_notifier_unregister; pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); @@ -1162,6 +1179,8 @@ static int i2c_imx_probe(struct platform_device *pdev) return 0; /* Return OK */ +clk_notifier_unregister: + clk_notifier_unregister(i2c_imx->clk, &i2c_imx_clk_notifier); rpm_disable: pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1195,6 +1214,7 @@ static int i2c_imx_remove(struct platform_device *pdev) imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR); imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR); + clk_notifier_unregister(i2c_imx->clk, &i2c_imx_clk_notifier); clk_disable_unprepare(i2c_imx->clk); pm_runtime_put_noidle(&pdev->dev); From patchwork Fri Nov 17 18:05:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Stach X-Patchwork-Id: 839087 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3ydmJ96XQhz9s7f for ; Sat, 18 Nov 2017 05:06:01 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1161213AbdKQSFn (ORCPT ); Fri, 17 Nov 2017 13:05:43 -0500 Received: from metis.ext.4.pengutronix.de ([92.198.50.35]:53557 "EHLO metis.ext.4.pengutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1161078AbdKQSFl (ORCPT ); Fri, 17 Nov 2017 13:05:41 -0500 Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7] helo=dude.pengutronix.de.) by metis.ext.pengutronix.de with esmtp (Exim 4.84_2) (envelope-from ) id 1eFl1H-0000mZ-OK; Fri, 17 Nov 2017 19:05:39 +0100 From: Lucas Stach To: Wolfram Sang Cc: linux-i2c@vger.kernel.org, kernel@pengutronix.de, patchwork-lst@pengutronix.de, Lukas Rusak , Chris Healy Subject: [PATCH 2/3] i2c: add runtime PM handling to core Date: Fri, 17 Nov 2017 19:05:38 +0100 Message-Id: <20171117180539.4984-3-l.stach@pengutronix.de> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171117180539.4984-1-l.stach@pengutronix.de> References: <20171117180539.4984-1-l.stach@pengutronix.de> X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: l.stach@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-i2c@vger.kernel.org Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org This allows to simplify the drivers if they opt to use the runtime PM provided by the core. Even more importantly it allows to move the runtime PM handling out of the i2c bus lock. As runtime PM may need to take other locks itself (i.e. when manipulating clocks), holding the bus lock may lead to deadlocks, due to different lock ordering. Signed-off-by: Lucas Stach --- drivers/i2c/i2c-core-base.c | 18 ++++++++++++++++-- include/linux/i2c.h | 1 + 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 56e46581b84b..3c971711171c 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1931,11 +1931,19 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) } #endif + if (adap->auto_rpm) { + ret = pm_runtime_get_sync(adap->dev.parent); + if (ret < 0) + return ret; + } + if (in_atomic() || irqs_disabled()) { ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); - if (!ret) + if (!ret) { /* I2C activity is ongoing. */ - return -EAGAIN; + ret = -EAGAIN; + goto out_rpm; + } } else { i2c_lock_bus(adap, I2C_LOCK_SEGMENT); } @@ -1943,6 +1951,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) ret = __i2c_transfer(adap, msgs, num); i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); +out_rpm: + if (adap->auto_rpm) { + pm_runtime_mark_last_busy(adap->dev.parent); + pm_runtime_put_autosuspend(adap->dev.parent); + } + return ret; } else { dev_dbg(&adap->dev, "I2C level transfers not supported\n"); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index d501d3956f13..6bc3d1ed554e 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -585,6 +585,7 @@ struct i2c_adapter { int nr; char name[48]; struct completion dev_released; + bool auto_rpm; /* runtime PM managed by core */ struct mutex userspace_clients_lock; struct list_head userspace_clients; From patchwork Fri Nov 17 18:05:39 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Stach X-Patchwork-Id: 839088 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3ydmJ52zsqz9s7M for ; Sat, 18 Nov 2017 05:05:57 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1161233AbdKQSFn (ORCPT ); Fri, 17 Nov 2017 13:05:43 -0500 Received: from metis.ext.4.pengutronix.de ([92.198.50.35]:59443 "EHLO metis.ext.4.pengutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1161213AbdKQSFl (ORCPT ); Fri, 17 Nov 2017 13:05:41 -0500 Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7] helo=dude.pengutronix.de.) by metis.ext.pengutronix.de with esmtp (Exim 4.84_2) (envelope-from ) id 1eFl1H-0000mZ-R6; Fri, 17 Nov 2017 19:05:39 +0100 From: Lucas Stach To: Wolfram Sang Cc: linux-i2c@vger.kernel.org, kernel@pengutronix.de, patchwork-lst@pengutronix.de, Lukas Rusak , Chris Healy Subject: [PATCH 3/3] i2c: imx: use runtime PM provided by core Date: Fri, 17 Nov 2017 19:05:39 +0100 Message-Id: <20171117180539.4984-4-l.stach@pengutronix.de> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171117180539.4984-1-l.stach@pengutronix.de> References: <20171117180539.4984-1-l.stach@pengutronix.de> X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: l.stach@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-i2c@vger.kernel.org Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org Simplifies the driver and fixes a deadlock, which has been observed in the wild, due to the i2c bus lock and the clk framework clk_prepare lock being taken in different order by multiple i2c devices. Signed-off-by: Lucas Stach --- drivers/i2c/busses/i2c-imx.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 800c1f390308..95da40e74847 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -904,10 +904,6 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent); - if (result < 0) - goto out; - /* Start I2C transfer */ result = i2c_imx_start(i2c_imx); if (result) { @@ -971,10 +967,6 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, /* Stop I2C transfer */ i2c_imx_stop(i2c_imx); - pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent); - pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent); - -out: dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__, (result < 0) ? "error" : "success msg", (result < 0) ? result : num); @@ -1099,6 +1091,7 @@ static int i2c_imx_probe(struct platform_device *pdev) i2c_imx->adapter.dev.parent = &pdev->dev; i2c_imx->adapter.nr = pdev->id; i2c_imx->adapter.dev.of_node = pdev->dev.of_node; + i2c_imx->adapter.auto_rpm = true; i2c_imx->base = base; /* Get I2C clock */