lp8788.c 5.2 KB

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