diff options
Diffstat (limited to 'drivers/mfd/tps6586x.c')
-rw-r--r-- | drivers/mfd/tps6586x.c | 267 |
1 files changed, 244 insertions, 23 deletions
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 4cde31e6a252..bba26d96c240 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c | |||
@@ -15,6 +15,8 @@ | |||
15 | * published by the Free Software Foundation. | 15 | * published by the Free Software Foundation. |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/irq.h> | ||
18 | #include <linux/kernel.h> | 20 | #include <linux/kernel.h> |
19 | #include <linux/module.h> | 21 | #include <linux/module.h> |
20 | #include <linux/mutex.h> | 22 | #include <linux/mutex.h> |
@@ -29,9 +31,62 @@ | |||
29 | #define TPS6586X_GPIOSET1 0x5d | 31 | #define TPS6586X_GPIOSET1 0x5d |
30 | #define TPS6586X_GPIOSET2 0x5e | 32 | #define TPS6586X_GPIOSET2 0x5e |
31 | 33 | ||
34 | /* interrupt control registers */ | ||
35 | #define TPS6586X_INT_ACK1 0xb5 | ||
36 | #define TPS6586X_INT_ACK2 0xb6 | ||
37 | #define TPS6586X_INT_ACK3 0xb7 | ||
38 | #define TPS6586X_INT_ACK4 0xb8 | ||
39 | |||
40 | /* interrupt mask registers */ | ||
41 | #define TPS6586X_INT_MASK1 0xb0 | ||
42 | #define TPS6586X_INT_MASK2 0xb1 | ||
43 | #define TPS6586X_INT_MASK3 0xb2 | ||
44 | #define TPS6586X_INT_MASK4 0xb3 | ||
45 | #define TPS6586X_INT_MASK5 0xb4 | ||
46 | |||
32 | /* device id */ | 47 | /* device id */ |
33 | #define TPS6586X_VERSIONCRC 0xcd | 48 | #define TPS6586X_VERSIONCRC 0xcd |
34 | #define TPS658621A_VERSIONCRC 0x15 | 49 | |
50 | struct tps6586x_irq_data { | ||
51 | u8 mask_reg; | ||
52 | u8 mask_mask; | ||
53 | }; | ||
54 | |||
55 | #define TPS6586X_IRQ(_reg, _mask) \ | ||
56 | { \ | ||
57 | .mask_reg = (_reg) - TPS6586X_INT_MASK1, \ | ||
58 | .mask_mask = (_mask), \ | ||
59 | } | ||
60 | |||
61 | static const struct tps6586x_irq_data tps6586x_irqs[] = { | ||
62 | [TPS6586X_INT_PLDO_0] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 0), | ||
63 | [TPS6586X_INT_PLDO_1] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 1), | ||
64 | [TPS6586X_INT_PLDO_2] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 2), | ||
65 | [TPS6586X_INT_PLDO_3] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 3), | ||
66 | [TPS6586X_INT_PLDO_4] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 4), | ||
67 | [TPS6586X_INT_PLDO_5] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 5), | ||
68 | [TPS6586X_INT_PLDO_6] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 6), | ||
69 | [TPS6586X_INT_PLDO_7] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 7), | ||
70 | [TPS6586X_INT_COMP_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 0), | ||
71 | [TPS6586X_INT_ADC] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 1), | ||
72 | [TPS6586X_INT_PLDO_8] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 2), | ||
73 | [TPS6586X_INT_PLDO_9] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 3), | ||
74 | [TPS6586X_INT_PSM_0] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 4), | ||
75 | [TPS6586X_INT_PSM_1] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 5), | ||
76 | [TPS6586X_INT_PSM_2] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 6), | ||
77 | [TPS6586X_INT_PSM_3] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 7), | ||
78 | [TPS6586X_INT_RTC_ALM1] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 4), | ||
79 | [TPS6586X_INT_ACUSB_OVP] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 0x03), | ||
80 | [TPS6586X_INT_USB_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 2), | ||
81 | [TPS6586X_INT_AC_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 3), | ||
82 | [TPS6586X_INT_BAT_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 1 << 0), | ||
83 | [TPS6586X_INT_CHG_STAT] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 0xfc), | ||
84 | [TPS6586X_INT_CHG_TEMP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0x06), | ||
85 | [TPS6586X_INT_PP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0xf0), | ||
86 | [TPS6586X_INT_RESUME] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 5), | ||
87 | [TPS6586X_INT_LOW_SYS] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 6), | ||
88 | [TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1), | ||
89 | }; | ||
35 | 90 | ||
36 | struct tps6586x { | 91 | struct tps6586x { |
37 | struct mutex lock; | 92 | struct mutex lock; |
@@ -39,6 +94,12 @@ struct tps6586x { | |||
39 | struct i2c_client *client; | 94 | struct i2c_client *client; |
40 | 95 | ||
41 | struct gpio_chip gpio; | 96 | struct gpio_chip gpio; |
97 | struct irq_chip irq_chip; | ||
98 | struct mutex irq_lock; | ||
99 | int irq_base; | ||
100 | u32 irq_en; | ||
101 | u8 mask_cache[5]; | ||
102 | u8 mask_reg[5]; | ||
42 | }; | 103 | }; |
43 | 104 | ||
44 | static inline int __tps6586x_read(struct i2c_client *client, | 105 | static inline int __tps6586x_read(struct i2c_client *client, |
@@ -89,12 +150,12 @@ static inline int __tps6586x_write(struct i2c_client *client, | |||
89 | static inline int __tps6586x_writes(struct i2c_client *client, int reg, | 150 | static inline int __tps6586x_writes(struct i2c_client *client, int reg, |
90 | int len, uint8_t *val) | 151 | int len, uint8_t *val) |
91 | { | 152 | { |
92 | int ret; | 153 | int ret, i; |
93 | 154 | ||
94 | ret = i2c_smbus_write_i2c_block_data(client, reg, len, val); | 155 | for (i = 0; i < len; i++) { |
95 | if (ret < 0) { | 156 | ret = __tps6586x_write(client, reg + i, *(val + i)); |
96 | dev_err(&client->dev, "failed writings to 0x%02x\n", reg); | 157 | if (ret < 0) |
97 | return ret; | 158 | return ret; |
98 | } | 159 | } |
99 | 160 | ||
100 | return 0; | 161 | return 0; |
@@ -209,8 +270,8 @@ static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset, | |||
209 | { | 270 | { |
210 | struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio); | 271 | struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio); |
211 | 272 | ||
212 | __tps6586x_write(tps6586x->client, TPS6586X_GPIOSET2, | 273 | tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET2, |
213 | value << offset); | 274 | value << offset, 1 << offset); |
214 | } | 275 | } |
215 | 276 | ||
216 | static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, | 277 | static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, |
@@ -227,12 +288,10 @@ static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, | |||
227 | return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask); | 288 | return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask); |
228 | } | 289 | } |
229 | 290 | ||
230 | static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) | 291 | static int tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) |
231 | { | 292 | { |
232 | int ret; | ||
233 | |||
234 | if (!gpio_base) | 293 | if (!gpio_base) |
235 | return; | 294 | return 0; |
236 | 295 | ||
237 | tps6586x->gpio.owner = THIS_MODULE; | 296 | tps6586x->gpio.owner = THIS_MODULE; |
238 | tps6586x->gpio.label = tps6586x->client->name; | 297 | tps6586x->gpio.label = tps6586x->client->name; |
@@ -246,9 +305,7 @@ static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) | |||
246 | tps6586x->gpio.set = tps6586x_gpio_set; | 305 | tps6586x->gpio.set = tps6586x_gpio_set; |
247 | tps6586x->gpio.get = tps6586x_gpio_get; | 306 | tps6586x->gpio.get = tps6586x_gpio_get; |
248 | 307 | ||
249 | ret = gpiochip_add(&tps6586x->gpio); | 308 | return gpiochip_add(&tps6586x->gpio); |
250 | if (ret) | ||
251 | dev_warn(tps6586x->dev, "GPIO registration failed: %d\n", ret); | ||
252 | } | 309 | } |
253 | 310 | ||
254 | static int __remove_subdev(struct device *dev, void *unused) | 311 | static int __remove_subdev(struct device *dev, void *unused) |
@@ -262,6 +319,129 @@ static int tps6586x_remove_subdevs(struct tps6586x *tps6586x) | |||
262 | return device_for_each_child(tps6586x->dev, NULL, __remove_subdev); | 319 | return device_for_each_child(tps6586x->dev, NULL, __remove_subdev); |
263 | } | 320 | } |
264 | 321 | ||
322 | static void tps6586x_irq_lock(struct irq_data *data) | ||
323 | { | ||
324 | struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data); | ||
325 | |||
326 | mutex_lock(&tps6586x->irq_lock); | ||
327 | } | ||
328 | |||
329 | static void tps6586x_irq_enable(struct irq_data *irq_data) | ||
330 | { | ||
331 | struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); | ||
332 | unsigned int __irq = irq_data->irq - tps6586x->irq_base; | ||
333 | const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; | ||
334 | |||
335 | tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask; | ||
336 | tps6586x->irq_en |= (1 << __irq); | ||
337 | } | ||
338 | |||
339 | static void tps6586x_irq_disable(struct irq_data *irq_data) | ||
340 | { | ||
341 | struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); | ||
342 | |||
343 | unsigned int __irq = irq_data->irq - tps6586x->irq_base; | ||
344 | const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; | ||
345 | |||
346 | tps6586x->mask_reg[data->mask_reg] |= data->mask_mask; | ||
347 | tps6586x->irq_en &= ~(1 << __irq); | ||
348 | } | ||
349 | |||
350 | static void tps6586x_irq_sync_unlock(struct irq_data *data) | ||
351 | { | ||
352 | struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data); | ||
353 | int i; | ||
354 | |||
355 | for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) { | ||
356 | if (tps6586x->mask_reg[i] != tps6586x->mask_cache[i]) { | ||
357 | if (!WARN_ON(tps6586x_write(tps6586x->dev, | ||
358 | TPS6586X_INT_MASK1 + i, | ||
359 | tps6586x->mask_reg[i]))) | ||
360 | tps6586x->mask_cache[i] = tps6586x->mask_reg[i]; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | mutex_unlock(&tps6586x->irq_lock); | ||
365 | } | ||
366 | |||
367 | static irqreturn_t tps6586x_irq(int irq, void *data) | ||
368 | { | ||
369 | struct tps6586x *tps6586x = data; | ||
370 | u32 acks; | ||
371 | int ret = 0; | ||
372 | |||
373 | ret = tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, | ||
374 | sizeof(acks), (uint8_t *)&acks); | ||
375 | |||
376 | if (ret < 0) { | ||
377 | dev_err(tps6586x->dev, "failed to read interrupt status\n"); | ||
378 | return IRQ_NONE; | ||
379 | } | ||
380 | |||
381 | acks = le32_to_cpu(acks); | ||
382 | |||
383 | while (acks) { | ||
384 | int i = __ffs(acks); | ||
385 | |||
386 | if (tps6586x->irq_en & (1 << i)) | ||
387 | handle_nested_irq(tps6586x->irq_base + i); | ||
388 | |||
389 | acks &= ~(1 << i); | ||
390 | } | ||
391 | |||
392 | return IRQ_HANDLED; | ||
393 | } | ||
394 | |||
395 | static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq, | ||
396 | int irq_base) | ||
397 | { | ||
398 | int i, ret; | ||
399 | u8 tmp[4]; | ||
400 | |||
401 | if (!irq_base) { | ||
402 | dev_warn(tps6586x->dev, "No interrupt support on IRQ base\n"); | ||
403 | return -EINVAL; | ||
404 | } | ||
405 | |||
406 | mutex_init(&tps6586x->irq_lock); | ||
407 | for (i = 0; i < 5; i++) { | ||
408 | tps6586x->mask_cache[i] = 0xff; | ||
409 | tps6586x->mask_reg[i] = 0xff; | ||
410 | tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff); | ||
411 | } | ||
412 | |||
413 | tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp); | ||
414 | |||
415 | tps6586x->irq_base = irq_base; | ||
416 | |||
417 | tps6586x->irq_chip.name = "tps6586x"; | ||
418 | tps6586x->irq_chip.irq_enable = tps6586x_irq_enable; | ||
419 | tps6586x->irq_chip.irq_disable = tps6586x_irq_disable; | ||
420 | tps6586x->irq_chip.irq_bus_lock = tps6586x_irq_lock; | ||
421 | tps6586x->irq_chip.irq_bus_sync_unlock = tps6586x_irq_sync_unlock; | ||
422 | |||
423 | for (i = 0; i < ARRAY_SIZE(tps6586x_irqs); i++) { | ||
424 | int __irq = i + tps6586x->irq_base; | ||
425 | irq_set_chip_data(__irq, tps6586x); | ||
426 | irq_set_chip_and_handler(__irq, &tps6586x->irq_chip, | ||
427 | handle_simple_irq); | ||
428 | irq_set_nested_thread(__irq, 1); | ||
429 | #ifdef CONFIG_ARM | ||
430 | set_irq_flags(__irq, IRQF_VALID); | ||
431 | #endif | ||
432 | } | ||
433 | |||
434 | ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT, | ||
435 | "tps6586x", tps6586x); | ||
436 | |||
437 | if (!ret) { | ||
438 | device_init_wakeup(tps6586x->dev, 1); | ||
439 | enable_irq_wake(irq); | ||
440 | } | ||
441 | |||
442 | return ret; | ||
443 | } | ||
444 | |||
265 | static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x, | 445 | static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x, |
266 | struct tps6586x_platform_data *pdata) | 446 | struct tps6586x_platform_data *pdata) |
267 | { | 447 | { |
@@ -273,13 +453,19 @@ static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x, | |||
273 | subdev = &pdata->subdevs[i]; | 453 | subdev = &pdata->subdevs[i]; |
274 | 454 | ||
275 | pdev = platform_device_alloc(subdev->name, subdev->id); | 455 | pdev = platform_device_alloc(subdev->name, subdev->id); |
456 | if (!pdev) { | ||
457 | ret = -ENOMEM; | ||
458 | goto failed; | ||
459 | } | ||
276 | 460 | ||
277 | pdev->dev.parent = tps6586x->dev; | 461 | pdev->dev.parent = tps6586x->dev; |
278 | pdev->dev.platform_data = subdev->platform_data; | 462 | pdev->dev.platform_data = subdev->platform_data; |
279 | 463 | ||
280 | ret = platform_device_add(pdev); | 464 | ret = platform_device_add(pdev); |
281 | if (ret) | 465 | if (ret) { |
466 | platform_device_put(pdev); | ||
282 | goto failed; | 467 | goto failed; |
468 | } | ||
283 | } | 469 | } |
284 | return 0; | 470 | return 0; |
285 | 471 | ||
@@ -306,10 +492,7 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, | |||
306 | return -EIO; | 492 | return -EIO; |
307 | } | 493 | } |
308 | 494 | ||
309 | if (ret != TPS658621A_VERSIONCRC) { | 495 | dev_info(&client->dev, "VERSIONCRC is %02x\n", ret); |
310 | dev_err(&client->dev, "Unsupported chip ID: %x\n", ret); | ||
311 | return -ENODEV; | ||
312 | } | ||
313 | 496 | ||
314 | tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL); | 497 | tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL); |
315 | if (tps6586x == NULL) | 498 | if (tps6586x == NULL) |
@@ -321,23 +504,62 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, | |||
321 | 504 | ||
322 | mutex_init(&tps6586x->lock); | 505 | mutex_init(&tps6586x->lock); |
323 | 506 | ||
507 | if (client->irq) { | ||
508 | ret = tps6586x_irq_init(tps6586x, client->irq, | ||
509 | pdata->irq_base); | ||
510 | if (ret) { | ||
511 | dev_err(&client->dev, "IRQ init failed: %d\n", ret); | ||
512 | goto err_irq_init; | ||
513 | } | ||
514 | } | ||
515 | |||
516 | ret = tps6586x_gpio_init(tps6586x, pdata->gpio_base); | ||
517 | if (ret) { | ||
518 | dev_err(&client->dev, "GPIO registration failed: %d\n", ret); | ||
519 | goto err_gpio_init; | ||
520 | } | ||
521 | |||
324 | ret = tps6586x_add_subdevs(tps6586x, pdata); | 522 | ret = tps6586x_add_subdevs(tps6586x, pdata); |
325 | if (ret) { | 523 | if (ret) { |
326 | dev_err(&client->dev, "add devices failed: %d\n", ret); | 524 | dev_err(&client->dev, "add devices failed: %d\n", ret); |
327 | goto err_add_devs; | 525 | goto err_add_devs; |
328 | } | 526 | } |
329 | 527 | ||
330 | tps6586x_gpio_init(tps6586x, pdata->gpio_base); | ||
331 | |||
332 | return 0; | 528 | return 0; |
333 | 529 | ||
334 | err_add_devs: | 530 | err_add_devs: |
531 | if (pdata->gpio_base) { | ||
532 | ret = gpiochip_remove(&tps6586x->gpio); | ||
533 | if (ret) | ||
534 | dev_err(&client->dev, "Can't remove gpio chip: %d\n", | ||
535 | ret); | ||
536 | } | ||
537 | err_gpio_init: | ||
538 | if (client->irq) | ||
539 | free_irq(client->irq, tps6586x); | ||
540 | err_irq_init: | ||
335 | kfree(tps6586x); | 541 | kfree(tps6586x); |
336 | return ret; | 542 | return ret; |
337 | } | 543 | } |
338 | 544 | ||
339 | static int __devexit tps6586x_i2c_remove(struct i2c_client *client) | 545 | static int __devexit tps6586x_i2c_remove(struct i2c_client *client) |
340 | { | 546 | { |
547 | struct tps6586x *tps6586x = i2c_get_clientdata(client); | ||
548 | struct tps6586x_platform_data *pdata = client->dev.platform_data; | ||
549 | int ret; | ||
550 | |||
551 | if (client->irq) | ||
552 | free_irq(client->irq, tps6586x); | ||
553 | |||
554 | if (pdata->gpio_base) { | ||
555 | ret = gpiochip_remove(&tps6586x->gpio); | ||
556 | if (ret) | ||
557 | dev_err(&client->dev, "Can't remove gpio chip: %d\n", | ||
558 | ret); | ||
559 | } | ||
560 | |||
561 | tps6586x_remove_subdevs(tps6586x); | ||
562 | kfree(tps6586x); | ||
341 | return 0; | 563 | return 0; |
342 | } | 564 | } |
343 | 565 | ||
@@ -372,4 +594,3 @@ module_exit(tps6586x_exit); | |||
372 | MODULE_DESCRIPTION("TPS6586X core driver"); | 594 | MODULE_DESCRIPTION("TPS6586X core driver"); |
373 | MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); | 595 | MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); |
374 | MODULE_LICENSE("GPL"); | 596 | MODULE_LICENSE("GPL"); |
375 | |||