diff options
-rw-r--r-- | drivers/gpio/gpio-twl4030.c | 100 |
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 */ | ||
68 | static DEFINE_MUTEX(gpio_lock); | ||
69 | |||
70 | struct gpio_twl4030_priv { | 66 | struct 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 | ||
133 | static u8 cached_leden; /* protected by gpio_lock */ | 133 | static 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 | ||
157 | static int twl4030_set_gpio_direction(int gpio, int is_input) | 155 | static 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); | 290 | out: |
291 | mutex_unlock(&priv->mutex); | ||
296 | } | 292 | } |
297 | 293 | ||
298 | static int twl_direction_in(struct gpio_chip *chip, unsigned offset) | 294 | static 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 | ||
305 | static int twl_get(struct gpio_chip *chip, unsigned offset) | 313 | static 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; |
331 | out: | ||
332 | mutex_unlock(&priv->mutex); | ||
333 | return ret; | ||
321 | } | 334 | } |
322 | 335 | ||
323 | static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) | 336 | static 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 | ||
334 | static void twl_set(struct gpio_chip *chip, unsigned offset, int value) | 354 | static 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 | ||
342 | static int twl_to_irq(struct gpio_chip *chip, unsigned offset) | 370 | static 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 | ||