diff options
Diffstat (limited to 'drivers/mfd/tps6586x.c')
-rw-r--r-- | drivers/mfd/tps6586x.c | 225 |
1 files changed, 223 insertions, 2 deletions
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 4cde31e6a252..b4931ab34929 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,64 @@ | |||
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 | #define TPS658621A_VERSIONCRC 0x15 |
50 | #define TPS658621C_VERSIONCRC 0x2c | ||
51 | |||
52 | struct tps6586x_irq_data { | ||
53 | u8 mask_reg; | ||
54 | u8 mask_mask; | ||
55 | }; | ||
56 | |||
57 | #define TPS6586X_IRQ(_reg, _mask) \ | ||
58 | { \ | ||
59 | .mask_reg = (_reg) - TPS6586X_INT_MASK1, \ | ||
60 | .mask_mask = (_mask), \ | ||
61 | } | ||
62 | |||
63 | static const struct tps6586x_irq_data tps6586x_irqs[] = { | ||
64 | [TPS6586X_INT_PLDO_0] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 0), | ||
65 | [TPS6586X_INT_PLDO_1] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 1), | ||
66 | [TPS6586X_INT_PLDO_2] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 2), | ||
67 | [TPS6586X_INT_PLDO_3] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 3), | ||
68 | [TPS6586X_INT_PLDO_4] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 4), | ||
69 | [TPS6586X_INT_PLDO_5] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 5), | ||
70 | [TPS6586X_INT_PLDO_6] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 6), | ||
71 | [TPS6586X_INT_PLDO_7] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 7), | ||
72 | [TPS6586X_INT_COMP_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 0), | ||
73 | [TPS6586X_INT_ADC] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 1), | ||
74 | [TPS6586X_INT_PLDO_8] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 2), | ||
75 | [TPS6586X_INT_PLDO_9] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 3), | ||
76 | [TPS6586X_INT_PSM_0] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 4), | ||
77 | [TPS6586X_INT_PSM_1] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 5), | ||
78 | [TPS6586X_INT_PSM_2] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 6), | ||
79 | [TPS6586X_INT_PSM_3] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 7), | ||
80 | [TPS6586X_INT_RTC_ALM1] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 4), | ||
81 | [TPS6586X_INT_ACUSB_OVP] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 0x03), | ||
82 | [TPS6586X_INT_USB_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 2), | ||
83 | [TPS6586X_INT_AC_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 3), | ||
84 | [TPS6586X_INT_BAT_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 1 << 0), | ||
85 | [TPS6586X_INT_CHG_STAT] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 0xfc), | ||
86 | [TPS6586X_INT_CHG_TEMP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0x06), | ||
87 | [TPS6586X_INT_PP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0xf0), | ||
88 | [TPS6586X_INT_RESUME] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 5), | ||
89 | [TPS6586X_INT_LOW_SYS] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 6), | ||
90 | [TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1), | ||
91 | }; | ||
35 | 92 | ||
36 | struct tps6586x { | 93 | struct tps6586x { |
37 | struct mutex lock; | 94 | struct mutex lock; |
@@ -39,6 +96,12 @@ struct tps6586x { | |||
39 | struct i2c_client *client; | 96 | struct i2c_client *client; |
40 | 97 | ||
41 | struct gpio_chip gpio; | 98 | struct gpio_chip gpio; |
99 | struct irq_chip irq_chip; | ||
100 | struct mutex irq_lock; | ||
101 | int irq_base; | ||
102 | u32 irq_en; | ||
103 | u8 mask_cache[5]; | ||
104 | u8 mask_reg[5]; | ||
42 | }; | 105 | }; |
43 | 106 | ||
44 | static inline int __tps6586x_read(struct i2c_client *client, | 107 | static inline int __tps6586x_read(struct i2c_client *client, |
@@ -262,6 +325,129 @@ static int tps6586x_remove_subdevs(struct tps6586x *tps6586x) | |||
262 | return device_for_each_child(tps6586x->dev, NULL, __remove_subdev); | 325 | return device_for_each_child(tps6586x->dev, NULL, __remove_subdev); |
263 | } | 326 | } |
264 | 327 | ||
328 | static void tps6586x_irq_lock(unsigned int irq) | ||
329 | { | ||
330 | struct tps6586x *tps6586x = get_irq_chip_data(irq); | ||
331 | |||
332 | mutex_lock(&tps6586x->irq_lock); | ||
333 | } | ||
334 | |||
335 | static void tps6586x_irq_enable(unsigned int irq) | ||
336 | { | ||
337 | struct tps6586x *tps6586x = get_irq_chip_data(irq); | ||
338 | unsigned int __irq = irq - tps6586x->irq_base; | ||
339 | const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; | ||
340 | |||
341 | tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask; | ||
342 | tps6586x->irq_en |= (1 << __irq); | ||
343 | } | ||
344 | |||
345 | static void tps6586x_irq_disable(unsigned int irq) | ||
346 | { | ||
347 | struct tps6586x *tps6586x = get_irq_chip_data(irq); | ||
348 | |||
349 | unsigned int __irq = irq - tps6586x->irq_base; | ||
350 | const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; | ||
351 | |||
352 | tps6586x->mask_reg[data->mask_reg] |= data->mask_mask; | ||
353 | tps6586x->irq_en &= ~(1 << __irq); | ||
354 | } | ||
355 | |||
356 | static void tps6586x_irq_sync_unlock(unsigned int irq) | ||
357 | { | ||
358 | struct tps6586x *tps6586x = get_irq_chip_data(irq); | ||
359 | int i; | ||
360 | |||
361 | for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) { | ||
362 | if (tps6586x->mask_reg[i] != tps6586x->mask_cache[i]) { | ||
363 | if (!WARN_ON(tps6586x_write(tps6586x->dev, | ||
364 | TPS6586X_INT_MASK1 + i, | ||
365 | tps6586x->mask_reg[i]))) | ||
366 | tps6586x->mask_cache[i] = tps6586x->mask_reg[i]; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | mutex_unlock(&tps6586x->irq_lock); | ||
371 | } | ||
372 | |||
373 | static irqreturn_t tps6586x_irq(int irq, void *data) | ||
374 | { | ||
375 | struct tps6586x *tps6586x = data; | ||
376 | u32 acks; | ||
377 | int ret = 0; | ||
378 | |||
379 | ret = tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, | ||
380 | sizeof(acks), (uint8_t *)&acks); | ||
381 | |||
382 | if (ret < 0) { | ||
383 | dev_err(tps6586x->dev, "failed to read interrupt status\n"); | ||
384 | return IRQ_NONE; | ||
385 | } | ||
386 | |||
387 | acks = le32_to_cpu(acks); | ||
388 | |||
389 | while (acks) { | ||
390 | int i = __ffs(acks); | ||
391 | |||
392 | if (tps6586x->irq_en & (1 << i)) | ||
393 | handle_nested_irq(tps6586x->irq_base + i); | ||
394 | |||
395 | acks &= ~(1 << i); | ||
396 | } | ||
397 | |||
398 | return IRQ_HANDLED; | ||
399 | } | ||
400 | |||
401 | static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq, | ||
402 | int irq_base) | ||
403 | { | ||
404 | int i, ret; | ||
405 | u8 tmp[4]; | ||
406 | |||
407 | if (!irq_base) { | ||
408 | dev_warn(tps6586x->dev, "No interrupt support on IRQ base\n"); | ||
409 | return -EINVAL; | ||
410 | } | ||
411 | |||
412 | mutex_init(&tps6586x->irq_lock); | ||
413 | for (i = 0; i < 5; i++) { | ||
414 | tps6586x->mask_cache[i] = 0xff; | ||
415 | tps6586x->mask_reg[i] = 0xff; | ||
416 | tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff); | ||
417 | } | ||
418 | |||
419 | tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp); | ||
420 | |||
421 | tps6586x->irq_base = irq_base; | ||
422 | |||
423 | tps6586x->irq_chip.name = "tps6586x"; | ||
424 | tps6586x->irq_chip.enable = tps6586x_irq_enable; | ||
425 | tps6586x->irq_chip.disable = tps6586x_irq_disable; | ||
426 | tps6586x->irq_chip.bus_lock = tps6586x_irq_lock; | ||
427 | tps6586x->irq_chip.bus_sync_unlock = tps6586x_irq_sync_unlock; | ||
428 | |||
429 | for (i = 0; i < ARRAY_SIZE(tps6586x_irqs); i++) { | ||
430 | int __irq = i + tps6586x->irq_base; | ||
431 | set_irq_chip_data(__irq, tps6586x); | ||
432 | set_irq_chip_and_handler(__irq, &tps6586x->irq_chip, | ||
433 | handle_simple_irq); | ||
434 | set_irq_nested_thread(__irq, 1); | ||
435 | #ifdef CONFIG_ARM | ||
436 | set_irq_flags(__irq, IRQF_VALID); | ||
437 | #endif | ||
438 | } | ||
439 | |||
440 | ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT, | ||
441 | "tps6586x", tps6586x); | ||
442 | |||
443 | if (!ret) { | ||
444 | device_init_wakeup(tps6586x->dev, 1); | ||
445 | enable_irq_wake(irq); | ||
446 | } | ||
447 | |||
448 | return ret; | ||
449 | } | ||
450 | |||
265 | static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x, | 451 | static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x, |
266 | struct tps6586x_platform_data *pdata) | 452 | struct tps6586x_platform_data *pdata) |
267 | { | 453 | { |
@@ -273,13 +459,19 @@ static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x, | |||
273 | subdev = &pdata->subdevs[i]; | 459 | subdev = &pdata->subdevs[i]; |
274 | 460 | ||
275 | pdev = platform_device_alloc(subdev->name, subdev->id); | 461 | pdev = platform_device_alloc(subdev->name, subdev->id); |
462 | if (!pdev) { | ||
463 | ret = -ENOMEM; | ||
464 | goto failed; | ||
465 | } | ||
276 | 466 | ||
277 | pdev->dev.parent = tps6586x->dev; | 467 | pdev->dev.parent = tps6586x->dev; |
278 | pdev->dev.platform_data = subdev->platform_data; | 468 | pdev->dev.platform_data = subdev->platform_data; |
279 | 469 | ||
280 | ret = platform_device_add(pdev); | 470 | ret = platform_device_add(pdev); |
281 | if (ret) | 471 | if (ret) { |
472 | platform_device_put(pdev); | ||
282 | goto failed; | 473 | goto failed; |
474 | } | ||
283 | } | 475 | } |
284 | return 0; | 476 | return 0; |
285 | 477 | ||
@@ -306,7 +498,8 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, | |||
306 | return -EIO; | 498 | return -EIO; |
307 | } | 499 | } |
308 | 500 | ||
309 | if (ret != TPS658621A_VERSIONCRC) { | 501 | if ((ret != TPS658621A_VERSIONCRC) && |
502 | (ret != TPS658621C_VERSIONCRC)) { | ||
310 | dev_err(&client->dev, "Unsupported chip ID: %x\n", ret); | 503 | dev_err(&client->dev, "Unsupported chip ID: %x\n", ret); |
311 | return -ENODEV; | 504 | return -ENODEV; |
312 | } | 505 | } |
@@ -321,6 +514,15 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, | |||
321 | 514 | ||
322 | mutex_init(&tps6586x->lock); | 515 | mutex_init(&tps6586x->lock); |
323 | 516 | ||
517 | if (client->irq) { | ||
518 | ret = tps6586x_irq_init(tps6586x, client->irq, | ||
519 | pdata->irq_base); | ||
520 | if (ret) { | ||
521 | dev_err(&client->dev, "IRQ init failed: %d\n", ret); | ||
522 | goto err_irq_init; | ||
523 | } | ||
524 | } | ||
525 | |||
324 | ret = tps6586x_add_subdevs(tps6586x, pdata); | 526 | ret = tps6586x_add_subdevs(tps6586x, pdata); |
325 | if (ret) { | 527 | if (ret) { |
326 | dev_err(&client->dev, "add devices failed: %d\n", ret); | 528 | dev_err(&client->dev, "add devices failed: %d\n", ret); |
@@ -332,12 +534,31 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, | |||
332 | return 0; | 534 | return 0; |
333 | 535 | ||
334 | err_add_devs: | 536 | err_add_devs: |
537 | if (client->irq) | ||
538 | free_irq(client->irq, tps6586x); | ||
539 | err_irq_init: | ||
335 | kfree(tps6586x); | 540 | kfree(tps6586x); |
336 | return ret; | 541 | return ret; |
337 | } | 542 | } |
338 | 543 | ||
339 | static int __devexit tps6586x_i2c_remove(struct i2c_client *client) | 544 | static int __devexit tps6586x_i2c_remove(struct i2c_client *client) |
340 | { | 545 | { |
546 | struct tps6586x *tps6586x = i2c_get_clientdata(client); | ||
547 | struct tps6586x_platform_data *pdata = client->dev.platform_data; | ||
548 | int ret; | ||
549 | |||
550 | if (client->irq) | ||
551 | free_irq(client->irq, tps6586x); | ||
552 | |||
553 | if (pdata->gpio_base) { | ||
554 | ret = gpiochip_remove(&tps6586x->gpio); | ||
555 | if (ret) | ||
556 | dev_err(&client->dev, "Can't remove gpio chip: %d\n", | ||
557 | ret); | ||
558 | } | ||
559 | |||
560 | tps6586x_remove_subdevs(tps6586x); | ||
561 | kfree(tps6586x); | ||
341 | return 0; | 562 | return 0; |
342 | } | 563 | } |
343 | 564 | ||