lp8788.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * TI LP8788 MFD - core interface
  4. *
  5. * Copyright 2012 Texas Instruments
  6. *
  7. * Author: Milo(Woogyom) Kim <[email protected]>
  8. */
  9. #include <linux/err.h>
  10. #include <linux/i2c.h>
  11. #include <linux/mfd/core.h>
  12. #include <linux/mfd/lp8788.h>
  13. #include <linux/module.h>
  14. #include <linux/slab.h>
  15. #define MAX_LP8788_REGISTERS 0xA2
  16. #define MFD_DEV_SIMPLE(_name) \
  17. { \
  18. .name = LP8788_DEV_##_name, \
  19. }
  20. #define MFD_DEV_WITH_ID(_name, _id) \
  21. { \
  22. .name = LP8788_DEV_##_name, \
  23. .id = _id, \
  24. }
  25. #define MFD_DEV_WITH_RESOURCE(_name, _resource, num_resource) \
  26. { \
  27. .name = LP8788_DEV_##_name, \
  28. .resources = _resource, \
  29. .num_resources = num_resource, \
  30. }
  31. static const struct resource chg_irqs[] = {
  32. /* Charger Interrupts */
  33. {
  34. .start = LP8788_INT_CHG_INPUT_STATE,
  35. .end = LP8788_INT_PRECHG_TIMEOUT,
  36. .name = LP8788_CHG_IRQ,
  37. .flags = IORESOURCE_IRQ,
  38. },
  39. /* Power Routing Switch Interrupts */
  40. {
  41. .start = LP8788_INT_ENTER_SYS_SUPPORT,
  42. .end = LP8788_INT_EXIT_SYS_SUPPORT,
  43. .name = LP8788_PRSW_IRQ,
  44. .flags = IORESOURCE_IRQ,
  45. },
  46. /* Battery Interrupts */
  47. {
  48. .start = LP8788_INT_BATT_LOW,
  49. .end = LP8788_INT_NO_BATT,
  50. .name = LP8788_BATT_IRQ,
  51. .flags = IORESOURCE_IRQ,
  52. },
  53. };
  54. static const struct resource rtc_irqs[] = {
  55. {
  56. .start = LP8788_INT_RTC_ALARM1,
  57. .end = LP8788_INT_RTC_ALARM2,
  58. .name = LP8788_ALM_IRQ,
  59. .flags = IORESOURCE_IRQ,
  60. },
  61. };
  62. static const struct mfd_cell lp8788_devs[] = {
  63. /* 4 bucks */
  64. MFD_DEV_WITH_ID(BUCK, 1),
  65. MFD_DEV_WITH_ID(BUCK, 2),
  66. MFD_DEV_WITH_ID(BUCK, 3),
  67. MFD_DEV_WITH_ID(BUCK, 4),
  68. /* 12 digital ldos */
  69. MFD_DEV_WITH_ID(DLDO, 1),
  70. MFD_DEV_WITH_ID(DLDO, 2),
  71. MFD_DEV_WITH_ID(DLDO, 3),
  72. MFD_DEV_WITH_ID(DLDO, 4),
  73. MFD_DEV_WITH_ID(DLDO, 5),
  74. MFD_DEV_WITH_ID(DLDO, 6),
  75. MFD_DEV_WITH_ID(DLDO, 7),
  76. MFD_DEV_WITH_ID(DLDO, 8),
  77. MFD_DEV_WITH_ID(DLDO, 9),
  78. MFD_DEV_WITH_ID(DLDO, 10),
  79. MFD_DEV_WITH_ID(DLDO, 11),
  80. MFD_DEV_WITH_ID(DLDO, 12),
  81. /* 10 analog ldos */
  82. MFD_DEV_WITH_ID(ALDO, 1),
  83. MFD_DEV_WITH_ID(ALDO, 2),
  84. MFD_DEV_WITH_ID(ALDO, 3),
  85. MFD_DEV_WITH_ID(ALDO, 4),
  86. MFD_DEV_WITH_ID(ALDO, 5),
  87. MFD_DEV_WITH_ID(ALDO, 6),
  88. MFD_DEV_WITH_ID(ALDO, 7),
  89. MFD_DEV_WITH_ID(ALDO, 8),
  90. MFD_DEV_WITH_ID(ALDO, 9),
  91. MFD_DEV_WITH_ID(ALDO, 10),
  92. /* ADC */
  93. MFD_DEV_SIMPLE(ADC),
  94. /* battery charger */
  95. MFD_DEV_WITH_RESOURCE(CHARGER, chg_irqs, ARRAY_SIZE(chg_irqs)),
  96. /* rtc */
  97. MFD_DEV_WITH_RESOURCE(RTC, rtc_irqs, ARRAY_SIZE(rtc_irqs)),
  98. /* backlight */
  99. MFD_DEV_SIMPLE(BACKLIGHT),
  100. /* current sink for vibrator */
  101. MFD_DEV_SIMPLE(VIBRATOR),
  102. /* current sink for keypad LED */
  103. MFD_DEV_SIMPLE(KEYLED),
  104. };
  105. int lp8788_read_byte(struct lp8788 *lp, u8 reg, u8 *data)
  106. {
  107. int ret;
  108. unsigned int val;
  109. ret = regmap_read(lp->regmap, reg, &val);
  110. if (ret < 0) {
  111. dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
  112. return ret;
  113. }
  114. *data = (u8)val;
  115. return 0;
  116. }
  117. EXPORT_SYMBOL_GPL(lp8788_read_byte);
  118. int lp8788_read_multi_bytes(struct lp8788 *lp, u8 reg, u8 *data, size_t count)
  119. {
  120. return regmap_bulk_read(lp->regmap, reg, data, count);
  121. }
  122. EXPORT_SYMBOL_GPL(lp8788_read_multi_bytes);
  123. int lp8788_write_byte(struct lp8788 *lp, u8 reg, u8 data)
  124. {
  125. return regmap_write(lp->regmap, reg, data);
  126. }
  127. EXPORT_SYMBOL_GPL(lp8788_write_byte);
  128. int lp8788_update_bits(struct lp8788 *lp, u8 reg, u8 mask, u8 data)
  129. {
  130. return regmap_update_bits(lp->regmap, reg, mask, data);
  131. }
  132. EXPORT_SYMBOL_GPL(lp8788_update_bits);
  133. static int lp8788_platform_init(struct lp8788 *lp)
  134. {
  135. struct lp8788_platform_data *pdata = lp->pdata;
  136. return (pdata && pdata->init_func) ? pdata->init_func(lp) : 0;
  137. }
  138. static const struct regmap_config lp8788_regmap_config = {
  139. .reg_bits = 8,
  140. .val_bits = 8,
  141. .max_register = MAX_LP8788_REGISTERS,
  142. };
  143. static int lp8788_probe(struct i2c_client *cl, const struct i2c_device_id *id)
  144. {
  145. struct lp8788 *lp;
  146. struct lp8788_platform_data *pdata = dev_get_platdata(&cl->dev);
  147. int ret;
  148. lp = devm_kzalloc(&cl->dev, sizeof(struct lp8788), GFP_KERNEL);
  149. if (!lp)
  150. return -ENOMEM;
  151. lp->regmap = devm_regmap_init_i2c(cl, &lp8788_regmap_config);
  152. if (IS_ERR(lp->regmap)) {
  153. ret = PTR_ERR(lp->regmap);
  154. dev_err(&cl->dev, "regmap init i2c err: %d\n", ret);
  155. return ret;
  156. }
  157. lp->pdata = pdata;
  158. lp->dev = &cl->dev;
  159. i2c_set_clientdata(cl, lp);
  160. ret = lp8788_platform_init(lp);
  161. if (ret)
  162. return ret;
  163. ret = lp8788_irq_init(lp, cl->irq);
  164. if (ret)
  165. return ret;
  166. ret = mfd_add_devices(lp->dev, -1, lp8788_devs,
  167. ARRAY_SIZE(lp8788_devs), NULL, 0, NULL);
  168. if (ret)
  169. goto err_exit_irq;
  170. return 0;
  171. err_exit_irq:
  172. lp8788_irq_exit(lp);
  173. return ret;
  174. }
  175. static void lp8788_remove(struct i2c_client *cl)
  176. {
  177. struct lp8788 *lp = i2c_get_clientdata(cl);
  178. mfd_remove_devices(lp->dev);
  179. lp8788_irq_exit(lp);
  180. }
  181. static const struct i2c_device_id lp8788_ids[] = {
  182. {"lp8788", 0},
  183. { }
  184. };
  185. MODULE_DEVICE_TABLE(i2c, lp8788_ids);
  186. static struct i2c_driver lp8788_driver = {
  187. .driver = {
  188. .name = "lp8788",
  189. },
  190. .probe = lp8788_probe,
  191. .remove = lp8788_remove,
  192. .id_table = lp8788_ids,
  193. };
  194. static int __init lp8788_init(void)
  195. {
  196. return i2c_add_driver(&lp8788_driver);
  197. }
  198. subsys_initcall(lp8788_init);
  199. static void __exit lp8788_exit(void)
  200. {
  201. i2c_del_driver(&lp8788_driver);
  202. }
  203. module_exit(lp8788_exit);
  204. MODULE_DESCRIPTION("TI LP8788 MFD Driver");
  205. MODULE_AUTHOR("Milo Kim");
  206. MODULE_LICENSE("GPL");