qcom-pm8008.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2021, The Linux Foundation. All rights reserved.
  4. */
  5. #include <linux/bitops.h>
  6. #include <linux/i2c.h>
  7. #include <linux/interrupt.h>
  8. #include <linux/irq.h>
  9. #include <linux/irqdomain.h>
  10. #include <linux/module.h>
  11. #include <linux/of_device.h>
  12. #include <linux/of_platform.h>
  13. #include <linux/pinctrl/consumer.h>
  14. #include <linux/regmap.h>
  15. #include <linux/slab.h>
  16. #include <dt-bindings/mfd/qcom-pm8008.h>
  17. #define I2C_INTR_STATUS_BASE 0x0550
  18. #define INT_RT_STS_OFFSET 0x10
  19. #define INT_SET_TYPE_OFFSET 0x11
  20. #define INT_POL_HIGH_OFFSET 0x12
  21. #define INT_POL_LOW_OFFSET 0x13
  22. #define INT_LATCHED_CLR_OFFSET 0x14
  23. #define INT_EN_SET_OFFSET 0x15
  24. #define INT_EN_CLR_OFFSET 0x16
  25. #define INT_LATCHED_STS_OFFSET 0x18
  26. enum {
  27. PM8008_MISC,
  28. PM8008_TEMP_ALARM,
  29. PM8008_GPIO1,
  30. PM8008_GPIO2,
  31. PM8008_NUM_PERIPHS,
  32. };
  33. #define PM8008_PERIPH_0_BASE 0x900
  34. #define PM8008_PERIPH_1_BASE 0x2400
  35. #define PM8008_PERIPH_2_BASE 0xC000
  36. #define PM8008_PERIPH_3_BASE 0xC100
  37. #define PM8008_TEMP_ALARM_ADDR PM8008_PERIPH_1_BASE
  38. #define PM8008_GPIO1_ADDR PM8008_PERIPH_2_BASE
  39. #define PM8008_GPIO2_ADDR PM8008_PERIPH_3_BASE
  40. #define PM8008_STATUS_BASE (PM8008_PERIPH_0_BASE | INT_LATCHED_STS_OFFSET)
  41. #define PM8008_MASK_BASE (PM8008_PERIPH_0_BASE | INT_EN_SET_OFFSET)
  42. #define PM8008_UNMASK_BASE (PM8008_PERIPH_0_BASE | INT_EN_CLR_OFFSET)
  43. #define PM8008_TYPE_BASE (PM8008_PERIPH_0_BASE | INT_SET_TYPE_OFFSET)
  44. #define PM8008_ACK_BASE (PM8008_PERIPH_0_BASE | INT_LATCHED_CLR_OFFSET)
  45. #define PM8008_POLARITY_HI_BASE (PM8008_PERIPH_0_BASE | INT_POL_HIGH_OFFSET)
  46. #define PM8008_POLARITY_LO_BASE (PM8008_PERIPH_0_BASE | INT_POL_LOW_OFFSET)
  47. #define PM8008_PERIPH_OFFSET(paddr) (paddr - PM8008_PERIPH_0_BASE)
  48. static unsigned int p0_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_0_BASE)};
  49. static unsigned int p1_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_1_BASE)};
  50. static unsigned int p2_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_2_BASE)};
  51. static unsigned int p3_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_3_BASE)};
  52. static struct regmap_irq_sub_irq_map pm8008_sub_reg_offsets[] = {
  53. REGMAP_IRQ_MAIN_REG_OFFSET(p0_offs),
  54. REGMAP_IRQ_MAIN_REG_OFFSET(p1_offs),
  55. REGMAP_IRQ_MAIN_REG_OFFSET(p2_offs),
  56. REGMAP_IRQ_MAIN_REG_OFFSET(p3_offs),
  57. };
  58. static unsigned int pm8008_virt_regs[] = {
  59. PM8008_POLARITY_HI_BASE,
  60. PM8008_POLARITY_LO_BASE,
  61. };
  62. enum {
  63. POLARITY_HI_INDEX,
  64. POLARITY_LO_INDEX,
  65. PM8008_NUM_VIRT_REGS,
  66. };
  67. static struct regmap_irq pm8008_irqs[] = {
  68. REGMAP_IRQ_REG(PM8008_IRQ_MISC_UVLO, PM8008_MISC, BIT(0)),
  69. REGMAP_IRQ_REG(PM8008_IRQ_MISC_OVLO, PM8008_MISC, BIT(1)),
  70. REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST2, PM8008_MISC, BIT(2)),
  71. REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST3, PM8008_MISC, BIT(3)),
  72. REGMAP_IRQ_REG(PM8008_IRQ_MISC_LDO_OCP, PM8008_MISC, BIT(4)),
  73. REGMAP_IRQ_REG(PM8008_IRQ_TEMP_ALARM, PM8008_TEMP_ALARM, BIT(0)),
  74. REGMAP_IRQ_REG(PM8008_IRQ_GPIO1, PM8008_GPIO1, BIT(0)),
  75. REGMAP_IRQ_REG(PM8008_IRQ_GPIO2, PM8008_GPIO2, BIT(0)),
  76. };
  77. static int pm8008_set_type_virt(unsigned int **virt_buf,
  78. unsigned int type, unsigned long hwirq,
  79. int reg)
  80. {
  81. switch (type) {
  82. case IRQ_TYPE_EDGE_FALLING:
  83. case IRQ_TYPE_LEVEL_LOW:
  84. virt_buf[POLARITY_HI_INDEX][reg] &= ~pm8008_irqs[hwirq].mask;
  85. virt_buf[POLARITY_LO_INDEX][reg] |= pm8008_irqs[hwirq].mask;
  86. break;
  87. case IRQ_TYPE_EDGE_RISING:
  88. case IRQ_TYPE_LEVEL_HIGH:
  89. virt_buf[POLARITY_HI_INDEX][reg] |= pm8008_irqs[hwirq].mask;
  90. virt_buf[POLARITY_LO_INDEX][reg] &= ~pm8008_irqs[hwirq].mask;
  91. break;
  92. case IRQ_TYPE_EDGE_BOTH:
  93. virt_buf[POLARITY_HI_INDEX][reg] |= pm8008_irqs[hwirq].mask;
  94. virt_buf[POLARITY_LO_INDEX][reg] |= pm8008_irqs[hwirq].mask;
  95. break;
  96. default:
  97. return -EINVAL;
  98. }
  99. return 0;
  100. }
  101. static struct regmap_irq_chip pm8008_irq_chip = {
  102. .name = "pm8008_irq",
  103. .main_status = I2C_INTR_STATUS_BASE,
  104. .num_main_regs = 1,
  105. .num_virt_regs = PM8008_NUM_VIRT_REGS,
  106. .irqs = pm8008_irqs,
  107. .num_irqs = ARRAY_SIZE(pm8008_irqs),
  108. .num_regs = PM8008_NUM_PERIPHS,
  109. .not_fixed_stride = true,
  110. .sub_reg_offsets = pm8008_sub_reg_offsets,
  111. .set_type_virt = pm8008_set_type_virt,
  112. .status_base = PM8008_STATUS_BASE,
  113. .mask_base = PM8008_MASK_BASE,
  114. .unmask_base = PM8008_UNMASK_BASE,
  115. .type_base = PM8008_TYPE_BASE,
  116. .ack_base = PM8008_ACK_BASE,
  117. .virt_reg_base = pm8008_virt_regs,
  118. .num_type_reg = PM8008_NUM_PERIPHS,
  119. };
  120. static struct regmap_config qcom_mfd_regmap_cfg = {
  121. .reg_bits = 16,
  122. .val_bits = 8,
  123. .max_register = 0xFFFF,
  124. };
  125. static int pm8008_init(struct regmap *regmap)
  126. {
  127. int rc;
  128. /*
  129. * Set TEMP_ALARM peripheral's TYPE so that the regmap-irq framework
  130. * reads this as the default value instead of zero, the HW default.
  131. * This is required to enable the writing of TYPE registers in
  132. * regmap_irq_sync_unlock().
  133. */
  134. rc = regmap_write(regmap, (PM8008_TEMP_ALARM_ADDR | INT_SET_TYPE_OFFSET), BIT(0));
  135. if (rc)
  136. return rc;
  137. /* Do the same for GPIO1 and GPIO2 peripherals */
  138. rc = regmap_write(regmap, (PM8008_GPIO1_ADDR | INT_SET_TYPE_OFFSET), BIT(0));
  139. if (rc)
  140. return rc;
  141. rc = regmap_write(regmap, (PM8008_GPIO2_ADDR | INT_SET_TYPE_OFFSET), BIT(0));
  142. return rc;
  143. }
  144. static int pm8008_probe_irq_peripherals(struct device *dev,
  145. struct regmap *regmap,
  146. int client_irq)
  147. {
  148. int rc, i;
  149. struct regmap_irq_type *type;
  150. struct regmap_irq_chip_data *irq_data;
  151. rc = pm8008_init(regmap);
  152. if (rc) {
  153. dev_err(dev, "Init failed: %d\n", rc);
  154. return rc;
  155. }
  156. for (i = 0; i < ARRAY_SIZE(pm8008_irqs); i++) {
  157. type = &pm8008_irqs[i].type;
  158. type->type_reg_offset = pm8008_irqs[i].reg_offset;
  159. type->type_rising_val = pm8008_irqs[i].mask;
  160. type->type_falling_val = pm8008_irqs[i].mask;
  161. type->type_level_high_val = 0;
  162. type->type_level_low_val = 0;
  163. if (type->type_reg_offset == PM8008_MISC)
  164. type->types_supported = IRQ_TYPE_EDGE_RISING;
  165. else
  166. type->types_supported = (IRQ_TYPE_EDGE_BOTH |
  167. IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW);
  168. }
  169. rc = devm_regmap_add_irq_chip(dev, regmap, client_irq,
  170. IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
  171. if (rc) {
  172. dev_err(dev, "Failed to add IRQ chip: %d\n", rc);
  173. return rc;
  174. }
  175. return 0;
  176. }
  177. static int pm8008_probe(struct i2c_client *client)
  178. {
  179. int rc;
  180. struct device *dev;
  181. struct regmap *regmap;
  182. dev = &client->dev;
  183. regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg);
  184. if (IS_ERR(regmap))
  185. return PTR_ERR(regmap);
  186. i2c_set_clientdata(client, regmap);
  187. if (of_property_read_bool(dev->of_node, "interrupt-controller")) {
  188. rc = pm8008_probe_irq_peripherals(dev, regmap, client->irq);
  189. if (rc)
  190. dev_err(dev, "Failed to probe irq periphs: %d\n", rc);
  191. }
  192. return devm_of_platform_populate(dev);
  193. }
  194. static const struct of_device_id pm8008_match[] = {
  195. { .compatible = "qcom,pm8008", },
  196. { },
  197. };
  198. MODULE_DEVICE_TABLE(of, pm8008_match);
  199. static struct i2c_driver pm8008_mfd_driver = {
  200. .driver = {
  201. .name = "pm8008",
  202. .of_match_table = pm8008_match,
  203. },
  204. .probe_new = pm8008_probe,
  205. };
  206. module_i2c_driver(pm8008_mfd_driver);
  207. MODULE_LICENSE("GPL v2");
  208. MODULE_ALIAS("i2c:qcom-pm8008");