aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2017-05-18 05:23:55 -0400
committerLinus Walleij <linus.walleij@linaro.org>2017-05-23 05:27:53 -0400
commitda6c2addf66d7ff7d0b090d6267d4292f951e4e6 (patch)
tree10f4412dc93b1774c49802bafa47988805e58baa
parent2a8209fa68236ad65363dba03db5dbced520268a (diff)
pinctrl: mxs: atomically switch mux and drive strength config
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> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r--drivers/pinctrl/freescale/pinctrl-mxs.c16
1 files changed, 12 insertions, 4 deletions
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,
194 return 0; 194 return 0;
195} 195}
196 196
197static void mxs_pinctrl_rmwl(u32 value, u32 mask, u8 shift, void __iomem *reg)
198{
199 u32 tmp;
200
201 tmp = readl(reg);
202 tmp &= ~(mask << shift);
203 tmp |= value << shift;
204 writel(tmp, reg);
205}
206
197static int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector, 207static int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
198 unsigned group) 208 unsigned group)
199{ 209{
@@ -211,8 +221,7 @@ static int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
211 reg += bank * 0x20 + pin / 16 * 0x10; 221 reg += bank * 0x20 + pin / 16 * 0x10;
212 shift = pin % 16 * 2; 222 shift = pin % 16 * 2;
213 223
214 writel(0x3 << shift, reg + CLR); 224 mxs_pinctrl_rmwl(g->muxsel[i], 0x3, shift, reg);
215 writel(g->muxsel[i] << shift, reg + SET);
216 } 225 }
217 226
218 return 0; 227 return 0;
@@ -279,8 +288,7 @@ static int mxs_pinconf_group_set(struct pinctrl_dev *pctldev,
279 /* mA */ 288 /* mA */
280 if (config & MA_PRESENT) { 289 if (config & MA_PRESENT) {
281 shift = pin % 8 * 4; 290 shift = pin % 8 * 4;
282 writel(0x3 << shift, reg + CLR); 291 mxs_pinctrl_rmwl(ma, 0x3, shift, reg);
283 writel(ma << shift, reg + SET);
284 } 292 }
285 293
286 /* vol */ 294 /* vol */