lp8788-irq.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * TI LP8788 MFD - interrupt handler
  4. *
  5. * Copyright 2012 Texas Instruments
  6. *
  7. * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
  8. */
  9. #include <linux/delay.h>
  10. #include <linux/err.h>
  11. #include <linux/interrupt.h>
  12. #include <linux/irq.h>
  13. #include <linux/irqdomain.h>
  14. #include <linux/device.h>
  15. #include <linux/mfd/lp8788.h>
  16. #include <linux/module.h>
  17. #include <linux/slab.h>
  18. /* register address */
  19. #define LP8788_INT_1 0x00
  20. #define LP8788_INTEN_1 0x03
  21. #define BASE_INTEN_ADDR LP8788_INTEN_1
  22. #define SIZE_REG 8
  23. #define NUM_REGS 3
  24. /*
  25. * struct lp8788_irq_data
  26. * @lp : used for accessing to lp8788 registers
  27. * @irq_lock : mutex for enabling/disabling the interrupt
  28. * @domain : IRQ domain for handling nested interrupt
  29. * @enabled : status of enabled interrupt
  30. */
  31. struct lp8788_irq_data {
  32. struct lp8788 *lp;
  33. struct mutex irq_lock;
  34. struct irq_domain *domain;
  35. int enabled[LP8788_INT_MAX];
  36. };
  37. static inline u8 _irq_to_addr(enum lp8788_int_id id)
  38. {
  39. return id / SIZE_REG;
  40. }
  41. static inline u8 _irq_to_enable_addr(enum lp8788_int_id id)
  42. {
  43. return _irq_to_addr(id) + BASE_INTEN_ADDR;
  44. }
  45. static inline u8 _irq_to_mask(enum lp8788_int_id id)
  46. {
  47. return 1 << (id % SIZE_REG);
  48. }
  49. static inline u8 _irq_to_val(enum lp8788_int_id id, int enable)
  50. {
  51. return enable << (id % SIZE_REG);
  52. }
  53. static void lp8788_irq_enable(struct irq_data *data)
  54. {
  55. struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
  56. irqd->enabled[data->hwirq] = 1;
  57. }
  58. static void lp8788_irq_disable(struct irq_data *data)
  59. {
  60. struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
  61. irqd->enabled[data->hwirq] = 0;
  62. }
  63. static void lp8788_irq_bus_lock(struct irq_data *data)
  64. {
  65. struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
  66. mutex_lock(&irqd->irq_lock);
  67. }
  68. static void lp8788_irq_bus_sync_unlock(struct irq_data *data)
  69. {
  70. struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
  71. enum lp8788_int_id irq = data->hwirq;
  72. u8 addr, mask, val;
  73. addr = _irq_to_enable_addr(irq);
  74. mask = _irq_to_mask(irq);
  75. val = _irq_to_val(irq, irqd->enabled[irq]);
  76. lp8788_update_bits(irqd->lp, addr, mask, val);
  77. mutex_unlock(&irqd->irq_lock);
  78. }
  79. static struct irq_chip lp8788_irq_chip = {
  80. .name = "lp8788",
  81. .irq_enable = lp8788_irq_enable,
  82. .irq_disable = lp8788_irq_disable,
  83. .irq_bus_lock = lp8788_irq_bus_lock,
  84. .irq_bus_sync_unlock = lp8788_irq_bus_sync_unlock,
  85. };
  86. static irqreturn_t lp8788_irq_handler(int irq, void *ptr)
  87. {
  88. struct lp8788_irq_data *irqd = ptr;
  89. struct lp8788 *lp = irqd->lp;
  90. u8 status[NUM_REGS], addr, mask;
  91. bool handled = false;
  92. int i;
  93. if (lp8788_read_multi_bytes(lp, LP8788_INT_1, status, NUM_REGS))
  94. return IRQ_NONE;
  95. for (i = 0 ; i < LP8788_INT_MAX ; i++) {
  96. addr = _irq_to_addr(i);
  97. mask = _irq_to_mask(i);
  98. /* reporting only if the irq is enabled */
  99. if (status[addr] & mask) {
  100. handle_nested_irq(irq_find_mapping(irqd->domain, i));
  101. handled = true;
  102. }
  103. }
  104. return handled ? IRQ_HANDLED : IRQ_NONE;
  105. }
  106. static int lp8788_irq_map(struct irq_domain *d, unsigned int virq,
  107. irq_hw_number_t hwirq)
  108. {
  109. struct lp8788_irq_data *irqd = d->host_data;
  110. struct irq_chip *chip = &lp8788_irq_chip;
  111. irq_set_chip_data(virq, irqd);
  112. irq_set_chip_and_handler(virq, chip, handle_edge_irq);
  113. irq_set_nested_thread(virq, 1);
  114. irq_set_noprobe(virq);
  115. return 0;
  116. }
  117. static const struct irq_domain_ops lp8788_domain_ops = {
  118. .map = lp8788_irq_map,
  119. };
  120. int lp8788_irq_init(struct lp8788 *lp, int irq)
  121. {
  122. struct lp8788_irq_data *irqd;
  123. int ret;
  124. if (irq <= 0) {
  125. dev_warn(lp->dev, "invalid irq number: %d\n", irq);
  126. return 0;
  127. }
  128. irqd = devm_kzalloc(lp->dev, sizeof(*irqd), GFP_KERNEL);
  129. if (!irqd)
  130. return -ENOMEM;
  131. irqd->lp = lp;
  132. irqd->domain = irq_domain_add_linear(lp->dev->of_node, LP8788_INT_MAX,
  133. &lp8788_domain_ops, irqd);
  134. if (!irqd->domain) {
  135. dev_err(lp->dev, "failed to add irq domain err\n");
  136. return -EINVAL;
  137. }
  138. lp->irqdm = irqd->domain;
  139. mutex_init(&irqd->irq_lock);
  140. ret = request_threaded_irq(irq, NULL, lp8788_irq_handler,
  141. IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
  142. "lp8788-irq", irqd);
  143. if (ret) {
  144. dev_err(lp->dev, "failed to create a thread for IRQ_N\n");
  145. return ret;
  146. }
  147. lp->irq = irq;
  148. return 0;
  149. }
  150. void lp8788_irq_exit(struct lp8788 *lp)
  151. {
  152. if (lp->irq)
  153. free_irq(lp->irq, lp->irqdm);
  154. }