diff mbox

pinctrl: mxs: atomically switch mux and drive strength config

Message ID 20170518092355.6735-1-u.kleine-koenig@pengutronix.de
State New
Headers show

Commit Message

Uwe Kleine-König May 18, 2017, 9:23 a.m. UTC
To set the mux mode of a pin two bits must be set. Up to now this is
implemented using the following idiom:

	writel(mask, reg + CLR);
	writel(value, reg + SET);

. This however results in the mux mode being 0 between the two writes.

On my machine there is an IC's reset pin connected to LCD_D20. The
bootloader configures this pin as GPIO output-high (i.e. not holding the
IC in reset). When Linux reconfigures the pin to GPIO the short time
LCD_D20 is muxed as LCD_D20 instead of GPIO_1_20 is enough to confuse
the connected IC.

The same problem is present for the pin's drive strength setting which is
reset to low drive strength before using the right value.

So instead of relying on the hardware to modify the register setting
using two writes implement the bit toggling using read-modify-write.

Fixes: 17723111e64f ("pinctrl: add pinctrl-mxs support")
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
 drivers/pinctrl/freescale/pinctrl-mxs.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

Comments

Shawn Guo May 21, 2017, 8:46 a.m. UTC | #1
On Thu, May 18, 2017 at 11:23:55AM +0200, Uwe Kleine-König wrote:
> To set the mux mode of a pin two bits must be set. Up to now this is
> implemented using the following idiom:
> 
> 	writel(mask, reg + CLR);
> 	writel(value, reg + SET);
> 
> . This however results in the mux mode being 0 between the two writes.
> 
> On my machine there is an IC's reset pin connected to LCD_D20. The
> bootloader configures this pin as GPIO output-high (i.e. not holding the
> IC in reset). When Linux reconfigures the pin to GPIO the short time
> LCD_D20 is muxed as LCD_D20 instead of GPIO_1_20 is enough to confuse
> the connected IC.
> 
> The same problem is present for the pin's drive strength setting which is
> reset to low drive strength before using the right value.
> 
> So instead of relying on the hardware to modify the register setting
> using two writes implement the bit toggling using read-modify-write.
> 
> Fixes: 17723111e64f ("pinctrl: add pinctrl-mxs support")
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

Acked-by: Shawn Guo <shawnguo@kernel.org>
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Linus Walleij May 23, 2017, 9:28 a.m. UTC | #2
On Thu, May 18, 2017 at 11:23 AM, Uwe Kleine-König
<u.kleine-koenig@pengutronix.de> wrote:

> To set the mux mode of a pin two bits must be set. Up to now this is
> implemented using the following idiom:
>
>         writel(mask, reg + CLR);
>         writel(value, reg + SET);
>
> . This however results in the mux mode being 0 between the two writes.
>
> On my machine there is an IC's reset pin connected to LCD_D20. The
> bootloader configures this pin as GPIO output-high (i.e. not holding the
> IC in reset). When Linux reconfigures the pin to GPIO the short time
> LCD_D20 is muxed as LCD_D20 instead of GPIO_1_20 is enough to confuse
> the connected IC.
>
> The same problem is present for the pin's drive strength setting which is
> reset to low drive strength before using the right value.
>
> So instead of relying on the hardware to modify the register setting
> using two writes implement the bit toggling using read-modify-write.
>
> Fixes: 17723111e64f ("pinctrl: add pinctrl-mxs support")
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

Patch applied for fixes with Shawn's ACK.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/pinctrl/freescale/pinctrl-mxs.c b/drivers/pinctrl/freescale/pinctrl-mxs.c
index 41b5b07d5a2b..6852010a6d70 100644
--- a/drivers/pinctrl/freescale/pinctrl-mxs.c
+++ b/drivers/pinctrl/freescale/pinctrl-mxs.c
@@ -194,6 +194,16 @@  static int mxs_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
 	return 0;
 }
 
+static void mxs_pinctrl_rmwl(u32 value, u32 mask, u8 shift, void __iomem *reg)
+{
+	u32 tmp;
+
+	tmp = readl(reg);
+	tmp &= ~(mask << shift);
+	tmp |= value << shift;
+	writel(tmp, reg);
+}
+
 static int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
 			       unsigned group)
 {
@@ -211,8 +221,7 @@  static int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
 		reg += bank * 0x20 + pin / 16 * 0x10;
 		shift = pin % 16 * 2;
 
-		writel(0x3 << shift, reg + CLR);
-		writel(g->muxsel[i] << shift, reg + SET);
+		mxs_pinctrl_rmwl(g->muxsel[i], 0x3, shift, reg);
 	}
 
 	return 0;
@@ -279,8 +288,7 @@  static int mxs_pinconf_group_set(struct pinctrl_dev *pctldev,
 			/* mA */
 			if (config & MA_PRESENT) {
 				shift = pin % 8 * 4;
-				writel(0x3 << shift, reg + CLR);
-				writel(ma << shift, reg + SET);
+				mxs_pinctrl_rmwl(ma, 0x3, shift, reg);
 			}
 
 			/* vol */