aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@ti.com>2012-12-20 04:44:11 -0500
committerLinus Walleij <linus.walleij@linaro.org>2013-01-17 05:48:12 -0500
commitc111feabe2e200b15300d97107ffc1280bf8de2a (patch)
tree8776ec8a8887ae144f07de5b94fd7afa113c84bd
parent72c7901ef00925c6d0cc7ab69183a684908303bc (diff)
gpio: twl4030: Cache the direction and output states in private data
Use more coherent locking in the driver. Use bitfield to store the GPIO direction and if the pin is configured as output store the status also in a bitfiled. In this way we can just look at these bitfields when we need information about the pin status and only reach out to the chip when it is needed. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com> Acked-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r--drivers/gpio/gpio-twl4030.c100
1 files changed, 65 insertions, 35 deletions
diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c
index 4643f9cd0cae..4d330e36da1d 100644
--- a/drivers/gpio/gpio-twl4030.c
+++ b/drivers/gpio/gpio-twl4030.c
@@ -37,7 +37,6 @@
37 37
38#include <linux/i2c/twl.h> 38#include <linux/i2c/twl.h>
39 39
40
41/* 40/*
42 * The GPIO "subchip" supports 18 GPIOs which can be configured as 41 * The GPIO "subchip" supports 18 GPIOs which can be configured as
43 * inputs or outputs, with pullups or pulldowns on each pin. Each 42 * inputs or outputs, with pullups or pulldowns on each pin. Each
@@ -64,14 +63,15 @@
64/* Mask for GPIO registers when aggregated into a 32-bit integer */ 63/* Mask for GPIO registers when aggregated into a 32-bit integer */
65#define GPIO_32_MASK 0x0003ffff 64#define GPIO_32_MASK 0x0003ffff
66 65
67/* Data structures */
68static DEFINE_MUTEX(gpio_lock);
69
70struct gpio_twl4030_priv { 66struct gpio_twl4030_priv {
71 struct gpio_chip gpio_chip; 67 struct gpio_chip gpio_chip;
68 struct mutex mutex;
72 int irq_base; 69 int irq_base;
73 70
71 /* Bitfields for state caching */
74 unsigned int usage_count; 72 unsigned int usage_count;
73 unsigned int direction;
74 unsigned int out_state;
75}; 75};
76 76
77/*----------------------------------------------------------------------*/ 77/*----------------------------------------------------------------------*/
@@ -130,7 +130,7 @@ static inline int gpio_twl4030_read(u8 address)
130 130
131/*----------------------------------------------------------------------*/ 131/*----------------------------------------------------------------------*/
132 132
133static u8 cached_leden; /* protected by gpio_lock */ 133static u8 cached_leden;
134 134
135/* The LED lines are open drain outputs ... a FET pulls to GND, so an 135/* The LED lines are open drain outputs ... a FET pulls to GND, so an
136 * external pullup is needed. We could also expose the integrated PWM 136 * external pullup is needed. We could also expose the integrated PWM
@@ -144,14 +144,12 @@ static void twl4030_led_set_value(int led, int value)
144 if (led) 144 if (led)
145 mask <<= 1; 145 mask <<= 1;
146 146
147 mutex_lock(&gpio_lock);
148 if (value) 147 if (value)
149 cached_leden &= ~mask; 148 cached_leden &= ~mask;
150 else 149 else
151 cached_leden |= mask; 150 cached_leden |= mask;
152 status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, 151 status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
153 TWL4030_LED_LEDEN_REG); 152 TWL4030_LED_LEDEN_REG);
154 mutex_unlock(&gpio_lock);
155} 153}
156 154
157static int twl4030_set_gpio_direction(int gpio, int is_input) 155static int twl4030_set_gpio_direction(int gpio, int is_input)
@@ -162,7 +160,6 @@ static int twl4030_set_gpio_direction(int gpio, int is_input)
162 u8 base = REG_GPIODATADIR1 + d_bnk; 160 u8 base = REG_GPIODATADIR1 + d_bnk;
163 int ret = 0; 161 int ret = 0;
164 162
165 mutex_lock(&gpio_lock);
166 ret = gpio_twl4030_read(base); 163 ret = gpio_twl4030_read(base);
167 if (ret >= 0) { 164 if (ret >= 0) {
168 if (is_input) 165 if (is_input)
@@ -172,7 +169,6 @@ static int twl4030_set_gpio_direction(int gpio, int is_input)
172 169
173 ret = gpio_twl4030_write(base, reg); 170 ret = gpio_twl4030_write(base, reg);
174 } 171 }
175 mutex_unlock(&gpio_lock);
176 return ret; 172 return ret;
177} 173}
178 174
@@ -212,7 +208,7 @@ static int twl_request(struct gpio_chip *chip, unsigned offset)
212 struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); 208 struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
213 int status = 0; 209 int status = 0;
214 210
215 mutex_lock(&gpio_lock); 211 mutex_lock(&priv->mutex);
216 212
217 /* Support the two LED outputs as output-only GPIOs. */ 213 /* Support the two LED outputs as output-only GPIOs. */
218 if (offset >= TWL4030_GPIO_MAX) { 214 if (offset >= TWL4030_GPIO_MAX) {
@@ -271,7 +267,7 @@ done:
271 if (!status) 267 if (!status)
272 priv->usage_count |= BIT(offset); 268 priv->usage_count |= BIT(offset);
273 269
274 mutex_unlock(&gpio_lock); 270 mutex_unlock(&priv->mutex);
275 return status; 271 return status;
276} 272}
277 273
@@ -279,64 +275,96 @@ static void twl_free(struct gpio_chip *chip, unsigned offset)
279{ 275{
280 struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); 276 struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
281 277
278 mutex_lock(&priv->mutex);
282 if (offset >= TWL4030_GPIO_MAX) { 279 if (offset >= TWL4030_GPIO_MAX) {
283 twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); 280 twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1);
284 return; 281 goto out;
285 } 282 }
286 283
287 mutex_lock(&gpio_lock);
288
289 priv->usage_count &= ~BIT(offset); 284 priv->usage_count &= ~BIT(offset);
290 285
291 /* on last use, switch off GPIO module */ 286 /* on last use, switch off GPIO module */
292 if (!priv->usage_count) 287 if (!priv->usage_count)
293 gpio_twl4030_write(REG_GPIO_CTRL, 0x0); 288 gpio_twl4030_write(REG_GPIO_CTRL, 0x0);
294 289
295 mutex_unlock(&gpio_lock); 290out:
291 mutex_unlock(&priv->mutex);
296} 292}
297 293
298static int twl_direction_in(struct gpio_chip *chip, unsigned offset) 294static int twl_direction_in(struct gpio_chip *chip, unsigned offset)
299{ 295{
300 return (offset < TWL4030_GPIO_MAX) 296 struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
301 ? twl4030_set_gpio_direction(offset, 1) 297 int ret;
302 : -EINVAL; 298
299 mutex_lock(&priv->mutex);
300 if (offset < TWL4030_GPIO_MAX)
301 ret = twl4030_set_gpio_direction(offset, 1);
302 else
303 ret = -EINVAL;
304
305 if (!ret)
306 priv->direction &= ~BIT(offset);
307
308 mutex_unlock(&priv->mutex);
309
310 return ret;
303} 311}
304 312
305static int twl_get(struct gpio_chip *chip, unsigned offset) 313static int twl_get(struct gpio_chip *chip, unsigned offset)
306{ 314{
307 struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); 315 struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
316 int ret;
308 int status = 0; 317 int status = 0;
309 318
310 if (!(priv->usage_count & BIT(offset))) 319 mutex_lock(&priv->mutex);
311 return -EPERM; 320 if (!(priv->usage_count & BIT(offset))) {
321 ret = -EPERM;
322 goto out;
323 }
312 324
313 if (offset < TWL4030_GPIO_MAX) 325 if (priv->direction & BIT(offset))
314 status = twl4030_get_gpio_datain(offset); 326 status = priv->out_state & BIT(offset);
315 else if (offset == TWL4030_GPIO_MAX)
316 status = cached_leden & LEDEN_LEDAON;
317 else 327 else
318 status = cached_leden & LEDEN_LEDBON; 328 status = twl4030_get_gpio_datain(offset);
319 329
320 return (status < 0) ? 0 : status; 330 ret = (status <= 0) ? 0 : 1;
331out:
332 mutex_unlock(&priv->mutex);
333 return ret;
321} 334}
322 335
323static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) 336static void twl_set(struct gpio_chip *chip, unsigned offset, int value)
324{ 337{
325 if (offset < TWL4030_GPIO_MAX) { 338 struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
339
340 mutex_lock(&priv->mutex);
341 if (offset < TWL4030_GPIO_MAX)
326 twl4030_set_gpio_dataout(offset, value); 342 twl4030_set_gpio_dataout(offset, value);
327 return twl4030_set_gpio_direction(offset, 0); 343 else
328 } else {
329 twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); 344 twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value);
330 return 0; 345
331 } 346 if (value)
347 priv->out_state |= BIT(offset);
348 else
349 priv->out_state &= ~BIT(offset);
350
351 mutex_unlock(&priv->mutex);
332} 352}
333 353
334static void twl_set(struct gpio_chip *chip, unsigned offset, int value) 354static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value)
335{ 355{
356 struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
357
358 mutex_lock(&priv->mutex);
336 if (offset < TWL4030_GPIO_MAX) 359 if (offset < TWL4030_GPIO_MAX)
337 twl4030_set_gpio_dataout(offset, value); 360 twl4030_set_gpio_dataout(offset, value);
338 else 361
339 twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); 362 priv->direction |= BIT(offset);
363 mutex_unlock(&priv->mutex);
364
365 twl_set(chip, offset, value);
366
367 return 0;
340} 368}
341 369
342static int twl_to_irq(struct gpio_chip *chip, unsigned offset) 370static int twl_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -469,6 +497,8 @@ no_irqs:
469 priv->gpio_chip.ngpio = TWL4030_GPIO_MAX; 497 priv->gpio_chip.ngpio = TWL4030_GPIO_MAX;
470 priv->gpio_chip.dev = &pdev->dev; 498 priv->gpio_chip.dev = &pdev->dev;
471 499
500 mutex_init(&priv->mutex);
501
472 if (node) 502 if (node)
473 pdata = of_gpio_twl4030(&pdev->dev); 503 pdata = of_gpio_twl4030(&pdev->dev);
474 504