atc260x-core.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Core support for ATC260x PMICs
  4. *
  5. * Copyright (C) 2019 Manivannan Sadhasivam <[email protected]>
  6. * Copyright (C) 2020 Cristian Ciocaltea <[email protected]>
  7. */
  8. #include <linux/interrupt.h>
  9. #include <linux/mfd/atc260x/core.h>
  10. #include <linux/mfd/core.h>
  11. #include <linux/module.h>
  12. #include <linux/of.h>
  13. #include <linux/of_device.h>
  14. #include <linux/regmap.h>
  15. #define ATC260X_CHIP_REV_MAX 31
  16. struct atc260x_init_regs {
  17. unsigned int cmu_devrst;
  18. unsigned int cmu_devrst_ints;
  19. unsigned int ints_msk;
  20. unsigned int pad_en;
  21. unsigned int pad_en_extirq;
  22. };
  23. static void regmap_lock_mutex(void *__mutex)
  24. {
  25. struct mutex *mutex = __mutex;
  26. /*
  27. * Using regmap within an atomic context (e.g. accessing a PMIC when
  28. * powering system down) is normally allowed only if the regmap type
  29. * is MMIO and the regcache type is either REGCACHE_NONE or
  30. * REGCACHE_FLAT. For slow buses like I2C and SPI, the regmap is
  31. * internally protected by a mutex which is acquired non-atomically.
  32. *
  33. * Let's improve this by using a customized locking scheme inspired
  34. * from I2C atomic transfer. See i2c_in_atomic_xfer_mode() for a
  35. * starting point.
  36. */
  37. if (system_state > SYSTEM_RUNNING && irqs_disabled())
  38. mutex_trylock(mutex);
  39. else
  40. mutex_lock(mutex);
  41. }
  42. static void regmap_unlock_mutex(void *__mutex)
  43. {
  44. struct mutex *mutex = __mutex;
  45. mutex_unlock(mutex);
  46. }
  47. static const struct regmap_config atc2603c_regmap_config = {
  48. .reg_bits = 8,
  49. .val_bits = 16,
  50. .max_register = ATC2603C_SADDR,
  51. .cache_type = REGCACHE_NONE,
  52. };
  53. static const struct regmap_config atc2609a_regmap_config = {
  54. .reg_bits = 8,
  55. .val_bits = 16,
  56. .max_register = ATC2609A_SADDR,
  57. .cache_type = REGCACHE_NONE,
  58. };
  59. static const struct regmap_irq atc2603c_regmap_irqs[] = {
  60. REGMAP_IRQ_REG(ATC2603C_IRQ_AUDIO, 0, ATC2603C_INTS_MSK_AUDIO),
  61. REGMAP_IRQ_REG(ATC2603C_IRQ_OV, 0, ATC2603C_INTS_MSK_OV),
  62. REGMAP_IRQ_REG(ATC2603C_IRQ_OC, 0, ATC2603C_INTS_MSK_OC),
  63. REGMAP_IRQ_REG(ATC2603C_IRQ_OT, 0, ATC2603C_INTS_MSK_OT),
  64. REGMAP_IRQ_REG(ATC2603C_IRQ_UV, 0, ATC2603C_INTS_MSK_UV),
  65. REGMAP_IRQ_REG(ATC2603C_IRQ_ALARM, 0, ATC2603C_INTS_MSK_ALARM),
  66. REGMAP_IRQ_REG(ATC2603C_IRQ_ONOFF, 0, ATC2603C_INTS_MSK_ONOFF),
  67. REGMAP_IRQ_REG(ATC2603C_IRQ_SGPIO, 0, ATC2603C_INTS_MSK_SGPIO),
  68. REGMAP_IRQ_REG(ATC2603C_IRQ_IR, 0, ATC2603C_INTS_MSK_IR),
  69. REGMAP_IRQ_REG(ATC2603C_IRQ_REMCON, 0, ATC2603C_INTS_MSK_REMCON),
  70. REGMAP_IRQ_REG(ATC2603C_IRQ_POWER_IN, 0, ATC2603C_INTS_MSK_POWERIN),
  71. };
  72. static const struct regmap_irq atc2609a_regmap_irqs[] = {
  73. REGMAP_IRQ_REG(ATC2609A_IRQ_AUDIO, 0, ATC2609A_INTS_MSK_AUDIO),
  74. REGMAP_IRQ_REG(ATC2609A_IRQ_OV, 0, ATC2609A_INTS_MSK_OV),
  75. REGMAP_IRQ_REG(ATC2609A_IRQ_OC, 0, ATC2609A_INTS_MSK_OC),
  76. REGMAP_IRQ_REG(ATC2609A_IRQ_OT, 0, ATC2609A_INTS_MSK_OT),
  77. REGMAP_IRQ_REG(ATC2609A_IRQ_UV, 0, ATC2609A_INTS_MSK_UV),
  78. REGMAP_IRQ_REG(ATC2609A_IRQ_ALARM, 0, ATC2609A_INTS_MSK_ALARM),
  79. REGMAP_IRQ_REG(ATC2609A_IRQ_ONOFF, 0, ATC2609A_INTS_MSK_ONOFF),
  80. REGMAP_IRQ_REG(ATC2609A_IRQ_WKUP, 0, ATC2609A_INTS_MSK_WKUP),
  81. REGMAP_IRQ_REG(ATC2609A_IRQ_IR, 0, ATC2609A_INTS_MSK_IR),
  82. REGMAP_IRQ_REG(ATC2609A_IRQ_REMCON, 0, ATC2609A_INTS_MSK_REMCON),
  83. REGMAP_IRQ_REG(ATC2609A_IRQ_POWER_IN, 0, ATC2609A_INTS_MSK_POWERIN),
  84. };
  85. static const struct regmap_irq_chip atc2603c_regmap_irq_chip = {
  86. .name = "atc2603c",
  87. .irqs = atc2603c_regmap_irqs,
  88. .num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs),
  89. .num_regs = 1,
  90. .status_base = ATC2603C_INTS_PD,
  91. .mask_base = ATC2603C_INTS_MSK,
  92. .mask_invert = true,
  93. };
  94. static const struct regmap_irq_chip atc2609a_regmap_irq_chip = {
  95. .name = "atc2609a",
  96. .irqs = atc2609a_regmap_irqs,
  97. .num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs),
  98. .num_regs = 1,
  99. .status_base = ATC2609A_INTS_PD,
  100. .mask_base = ATC2609A_INTS_MSK,
  101. .mask_invert = true,
  102. };
  103. static const struct resource atc2603c_onkey_resources[] = {
  104. DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF),
  105. };
  106. static const struct resource atc2609a_onkey_resources[] = {
  107. DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF),
  108. };
  109. static const struct mfd_cell atc2603c_mfd_cells[] = {
  110. { .name = "atc260x-regulator" },
  111. { .name = "atc260x-pwrc" },
  112. {
  113. .name = "atc260x-onkey",
  114. .num_resources = ARRAY_SIZE(atc2603c_onkey_resources),
  115. .resources = atc2603c_onkey_resources,
  116. },
  117. };
  118. static const struct mfd_cell atc2609a_mfd_cells[] = {
  119. { .name = "atc260x-regulator" },
  120. { .name = "atc260x-pwrc" },
  121. {
  122. .name = "atc260x-onkey",
  123. .num_resources = ARRAY_SIZE(atc2609a_onkey_resources),
  124. .resources = atc2609a_onkey_resources,
  125. },
  126. };
  127. static const struct atc260x_init_regs atc2603c_init_regs = {
  128. .cmu_devrst = ATC2603C_CMU_DEVRST,
  129. .cmu_devrst_ints = ATC2603C_CMU_DEVRST_INTS,
  130. .ints_msk = ATC2603C_INTS_MSK,
  131. .pad_en = ATC2603C_PAD_EN,
  132. .pad_en_extirq = ATC2603C_PAD_EN_EXTIRQ,
  133. };
  134. static const struct atc260x_init_regs atc2609a_init_regs = {
  135. .cmu_devrst = ATC2609A_CMU_DEVRST,
  136. .cmu_devrst_ints = ATC2609A_CMU_DEVRST_INTS,
  137. .ints_msk = ATC2609A_INTS_MSK,
  138. .pad_en = ATC2609A_PAD_EN,
  139. .pad_en_extirq = ATC2609A_PAD_EN_EXTIRQ,
  140. };
  141. static void atc260x_cmu_reset(struct atc260x *atc260x)
  142. {
  143. const struct atc260x_init_regs *regs = atc260x->init_regs;
  144. /* Assert reset */
  145. regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
  146. regs->cmu_devrst_ints, ~regs->cmu_devrst_ints);
  147. /* De-assert reset */
  148. regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
  149. regs->cmu_devrst_ints, regs->cmu_devrst_ints);
  150. }
  151. static void atc260x_dev_init(struct atc260x *atc260x)
  152. {
  153. const struct atc260x_init_regs *regs = atc260x->init_regs;
  154. /* Initialize interrupt block */
  155. atc260x_cmu_reset(atc260x);
  156. /* Disable all interrupt sources */
  157. regmap_write(atc260x->regmap, regs->ints_msk, 0);
  158. /* Enable EXTIRQ pad */
  159. regmap_update_bits(atc260x->regmap, regs->pad_en,
  160. regs->pad_en_extirq, regs->pad_en_extirq);
  161. }
  162. /**
  163. * atc260x_match_device(): Setup ATC260x variant related fields
  164. *
  165. * @atc260x: ATC260x device to setup (.dev field must be set)
  166. * @regmap_cfg: regmap config associated with this ATC260x device
  167. *
  168. * This lets the ATC260x core configure the MFD cells and register maps
  169. * for later use.
  170. */
  171. int atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_cfg)
  172. {
  173. struct device *dev = atc260x->dev;
  174. const void *of_data;
  175. of_data = of_device_get_match_data(dev);
  176. if (!of_data)
  177. return -ENODEV;
  178. atc260x->ic_type = (unsigned long)of_data;
  179. switch (atc260x->ic_type) {
  180. case ATC2603C:
  181. *regmap_cfg = atc2603c_regmap_config;
  182. atc260x->regmap_irq_chip = &atc2603c_regmap_irq_chip;
  183. atc260x->cells = atc2603c_mfd_cells;
  184. atc260x->nr_cells = ARRAY_SIZE(atc2603c_mfd_cells);
  185. atc260x->type_name = "atc2603c";
  186. atc260x->rev_reg = ATC2603C_CHIP_VER;
  187. atc260x->init_regs = &atc2603c_init_regs;
  188. break;
  189. case ATC2609A:
  190. *regmap_cfg = atc2609a_regmap_config;
  191. atc260x->regmap_irq_chip = &atc2609a_regmap_irq_chip;
  192. atc260x->cells = atc2609a_mfd_cells;
  193. atc260x->nr_cells = ARRAY_SIZE(atc2609a_mfd_cells);
  194. atc260x->type_name = "atc2609a";
  195. atc260x->rev_reg = ATC2609A_CHIP_VER;
  196. atc260x->init_regs = &atc2609a_init_regs;
  197. break;
  198. default:
  199. dev_err(dev, "Unsupported ATC260x device type: %u\n",
  200. atc260x->ic_type);
  201. return -EINVAL;
  202. }
  203. atc260x->regmap_mutex = devm_kzalloc(dev, sizeof(*atc260x->regmap_mutex),
  204. GFP_KERNEL);
  205. if (!atc260x->regmap_mutex)
  206. return -ENOMEM;
  207. mutex_init(atc260x->regmap_mutex);
  208. regmap_cfg->lock = regmap_lock_mutex,
  209. regmap_cfg->unlock = regmap_unlock_mutex,
  210. regmap_cfg->lock_arg = atc260x->regmap_mutex;
  211. return 0;
  212. }
  213. EXPORT_SYMBOL_GPL(atc260x_match_device);
  214. /**
  215. * atc260x_device_probe(): Probe a configured ATC260x device
  216. *
  217. * @atc260x: ATC260x device to probe (must be configured)
  218. *
  219. * This function lets the ATC260x core register the ATC260x MFD devices
  220. * and IRQCHIP. The ATC260x device passed in must be fully configured
  221. * with atc260x_match_device, its IRQ set, and regmap created.
  222. */
  223. int atc260x_device_probe(struct atc260x *atc260x)
  224. {
  225. struct device *dev = atc260x->dev;
  226. unsigned int chip_rev;
  227. int ret;
  228. if (!atc260x->irq) {
  229. dev_err(dev, "No interrupt support\n");
  230. return -EINVAL;
  231. }
  232. /* Initialize the hardware */
  233. atc260x_dev_init(atc260x);
  234. ret = regmap_read(atc260x->regmap, atc260x->rev_reg, &chip_rev);
  235. if (ret) {
  236. dev_err(dev, "Failed to get chip revision\n");
  237. return ret;
  238. }
  239. if (chip_rev > ATC260X_CHIP_REV_MAX) {
  240. dev_err(dev, "Unknown chip revision: %u\n", chip_rev);
  241. return -EINVAL;
  242. }
  243. atc260x->ic_ver = __ffs(chip_rev + 1U);
  244. dev_info(dev, "Detected chip type %s rev.%c\n",
  245. atc260x->type_name, 'A' + atc260x->ic_ver);
  246. ret = devm_regmap_add_irq_chip(dev, atc260x->regmap, atc260x->irq, IRQF_ONESHOT,
  247. -1, atc260x->regmap_irq_chip, &atc260x->irq_data);
  248. if (ret) {
  249. dev_err(dev, "Failed to add IRQ chip: %d\n", ret);
  250. return ret;
  251. }
  252. ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
  253. atc260x->cells, atc260x->nr_cells, NULL, 0,
  254. regmap_irq_get_domain(atc260x->irq_data));
  255. if (ret) {
  256. dev_err(dev, "Failed to add child devices: %d\n", ret);
  257. regmap_del_irq_chip(atc260x->irq, atc260x->irq_data);
  258. }
  259. return ret;
  260. }
  261. EXPORT_SYMBOL_GPL(atc260x_device_probe);
  262. MODULE_DESCRIPTION("ATC260x PMICs Core support");
  263. MODULE_AUTHOR("Manivannan Sadhasivam <[email protected]>");
  264. MODULE_AUTHOR("Cristian Ciocaltea <[email protected]>");
  265. MODULE_LICENSE("GPL");