motorola-cpcap.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /*
  2. * Motorola CPCAP PMIC core driver
  3. *
  4. * Copyright (C) 2016 Tony Lindgren <tony@atomide.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. */
  10. #include <linux/device.h>
  11. #include <linux/err.h>
  12. #include <linux/interrupt.h>
  13. #include <linux/irq.h>
  14. #include <linux/kernel.h>
  15. #include <linux/module.h>
  16. #include <linux/of_device.h>
  17. #include <linux/regmap.h>
  18. #include <linux/sysfs.h>
  19. #include <linux/mfd/motorola-cpcap.h>
  20. #include <linux/spi/spi.h>
  21. #define CPCAP_NR_IRQ_REG_BANKS 6
  22. #define CPCAP_NR_IRQ_CHIPS 3
  23. #define CPCAP_REGISTER_SIZE 4
  24. #define CPCAP_REGISTER_BITS 16
  25. struct cpcap_ddata {
  26. struct spi_device *spi;
  27. struct regmap_irq *irqs;
  28. struct regmap_irq_chip_data *irqdata[CPCAP_NR_IRQ_CHIPS];
  29. const struct regmap_config *regmap_conf;
  30. struct regmap *regmap;
  31. };
  32. static int cpcap_sense_irq(struct regmap *regmap, int irq)
  33. {
  34. int regnum = irq / CPCAP_REGISTER_BITS;
  35. int mask = BIT(irq % CPCAP_REGISTER_BITS);
  36. int reg = CPCAP_REG_INTS1 + (regnum * CPCAP_REGISTER_SIZE);
  37. int err, val;
  38. if (reg < CPCAP_REG_INTS1 || reg > CPCAP_REG_INTS4)
  39. return -EINVAL;
  40. err = regmap_read(regmap, reg, &val);
  41. if (err)
  42. return err;
  43. return !!(val & mask);
  44. }
  45. int cpcap_sense_virq(struct regmap *regmap, int virq)
  46. {
  47. struct regmap_irq_chip_data *d = irq_get_chip_data(virq);
  48. int irq_base = regmap_irq_chip_get_base(d);
  49. return cpcap_sense_irq(regmap, virq - irq_base);
  50. }
  51. EXPORT_SYMBOL_GPL(cpcap_sense_virq);
  52. static int cpcap_check_revision(struct cpcap_ddata *cpcap)
  53. {
  54. u16 vendor, rev;
  55. int ret;
  56. ret = cpcap_get_vendor(&cpcap->spi->dev, cpcap->regmap, &vendor);
  57. if (ret)
  58. return ret;
  59. ret = cpcap_get_revision(&cpcap->spi->dev, cpcap->regmap, &rev);
  60. if (ret)
  61. return ret;
  62. dev_info(&cpcap->spi->dev, "CPCAP vendor: %s rev: %i.%i (%x)\n",
  63. vendor == CPCAP_VENDOR_ST ? "ST" : "TI",
  64. CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev),
  65. rev);
  66. if (rev < CPCAP_REVISION_2_1) {
  67. dev_info(&cpcap->spi->dev,
  68. "Please add old CPCAP revision support as needed\n");
  69. return -ENODEV;
  70. }
  71. return 0;
  72. }
  73. /*
  74. * First two irq chips are the two private macro interrupt chips, the third
  75. * irq chip is for register banks 1 - 4 and is available for drivers to use.
  76. */
  77. static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = {
  78. {
  79. .name = "cpcap-m2",
  80. .num_regs = 1,
  81. .status_base = CPCAP_REG_MI1,
  82. .ack_base = CPCAP_REG_MI1,
  83. .mask_base = CPCAP_REG_MIM1,
  84. .use_ack = true,
  85. .ack_invert = true,
  86. },
  87. {
  88. .name = "cpcap-m2",
  89. .num_regs = 1,
  90. .status_base = CPCAP_REG_MI2,
  91. .ack_base = CPCAP_REG_MI2,
  92. .mask_base = CPCAP_REG_MIM2,
  93. .use_ack = true,
  94. .ack_invert = true,
  95. },
  96. {
  97. .name = "cpcap1-4",
  98. .num_regs = 4,
  99. .status_base = CPCAP_REG_INT1,
  100. .ack_base = CPCAP_REG_INT1,
  101. .mask_base = CPCAP_REG_INTM1,
  102. .use_ack = true,
  103. .ack_invert = true,
  104. },
  105. };
  106. static void cpcap_init_one_regmap_irq(struct cpcap_ddata *cpcap,
  107. struct regmap_irq *rirq,
  108. int irq_base, int irq)
  109. {
  110. unsigned int reg_offset;
  111. unsigned int bit, mask;
  112. reg_offset = irq - irq_base;
  113. reg_offset /= cpcap->regmap_conf->val_bits;
  114. reg_offset *= cpcap->regmap_conf->reg_stride;
  115. bit = irq % cpcap->regmap_conf->val_bits;
  116. mask = (1 << bit);
  117. rirq->reg_offset = reg_offset;
  118. rirq->mask = mask;
  119. }
  120. static int cpcap_init_irq_chip(struct cpcap_ddata *cpcap, int irq_chip,
  121. int irq_start, int nr_irqs)
  122. {
  123. struct regmap_irq_chip *chip = &cpcap_irq_chip[irq_chip];
  124. int i, ret;
  125. for (i = irq_start; i < irq_start + nr_irqs; i++) {
  126. struct regmap_irq *rirq = &cpcap->irqs[i];
  127. cpcap_init_one_regmap_irq(cpcap, rirq, irq_start, i);
  128. }
  129. chip->irqs = &cpcap->irqs[irq_start];
  130. chip->num_irqs = nr_irqs;
  131. chip->irq_drv_data = cpcap;
  132. ret = devm_regmap_add_irq_chip(&cpcap->spi->dev, cpcap->regmap,
  133. cpcap->spi->irq,
  134. irq_get_trigger_type(cpcap->spi->irq) |
  135. IRQF_SHARED, -1,
  136. chip, &cpcap->irqdata[irq_chip]);
  137. if (ret) {
  138. dev_err(&cpcap->spi->dev, "could not add irq chip %i: %i\n",
  139. irq_chip, ret);
  140. return ret;
  141. }
  142. return 0;
  143. }
  144. static int cpcap_init_irq(struct cpcap_ddata *cpcap)
  145. {
  146. int ret;
  147. cpcap->irqs = devm_kzalloc(&cpcap->spi->dev,
  148. array3_size(sizeof(*cpcap->irqs),
  149. CPCAP_NR_IRQ_REG_BANKS,
  150. cpcap->regmap_conf->val_bits),
  151. GFP_KERNEL);
  152. if (!cpcap->irqs)
  153. return -ENOMEM;
  154. ret = cpcap_init_irq_chip(cpcap, 0, 0, 16);
  155. if (ret)
  156. return ret;
  157. ret = cpcap_init_irq_chip(cpcap, 1, 16, 16);
  158. if (ret)
  159. return ret;
  160. ret = cpcap_init_irq_chip(cpcap, 2, 32, 64);
  161. if (ret)
  162. return ret;
  163. enable_irq_wake(cpcap->spi->irq);
  164. return 0;
  165. }
  166. static const struct of_device_id cpcap_of_match[] = {
  167. { .compatible = "motorola,cpcap", },
  168. { .compatible = "st,6556002", },
  169. {},
  170. };
  171. MODULE_DEVICE_TABLE(of, cpcap_of_match);
  172. static const struct regmap_config cpcap_regmap_config = {
  173. .reg_bits = 16,
  174. .reg_stride = 4,
  175. .pad_bits = 0,
  176. .val_bits = 16,
  177. .write_flag_mask = 0x8000,
  178. .max_register = CPCAP_REG_ST_TEST2,
  179. .cache_type = REGCACHE_NONE,
  180. .reg_format_endian = REGMAP_ENDIAN_LITTLE,
  181. .val_format_endian = REGMAP_ENDIAN_LITTLE,
  182. };
  183. static int cpcap_probe(struct spi_device *spi)
  184. {
  185. const struct of_device_id *match;
  186. struct cpcap_ddata *cpcap;
  187. int ret;
  188. match = of_match_device(of_match_ptr(cpcap_of_match), &spi->dev);
  189. if (!match)
  190. return -ENODEV;
  191. cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL);
  192. if (!cpcap)
  193. return -ENOMEM;
  194. cpcap->spi = spi;
  195. spi_set_drvdata(spi, cpcap);
  196. spi->bits_per_word = 16;
  197. spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
  198. ret = spi_setup(spi);
  199. if (ret)
  200. return ret;
  201. cpcap->regmap_conf = &cpcap_regmap_config;
  202. cpcap->regmap = devm_regmap_init_spi(spi, &cpcap_regmap_config);
  203. if (IS_ERR(cpcap->regmap)) {
  204. ret = PTR_ERR(cpcap->regmap);
  205. dev_err(&cpcap->spi->dev, "Failed to initialize regmap: %d\n",
  206. ret);
  207. return ret;
  208. }
  209. ret = cpcap_check_revision(cpcap);
  210. if (ret) {
  211. dev_err(&cpcap->spi->dev, "Failed to detect CPCAP: %i\n", ret);
  212. return ret;
  213. }
  214. ret = cpcap_init_irq(cpcap);
  215. if (ret)
  216. return ret;
  217. return devm_of_platform_populate(&cpcap->spi->dev);
  218. }
  219. static struct spi_driver cpcap_driver = {
  220. .driver = {
  221. .name = "cpcap-core",
  222. .of_match_table = cpcap_of_match,
  223. },
  224. .probe = cpcap_probe,
  225. };
  226. module_spi_driver(cpcap_driver);
  227. MODULE_ALIAS("platform:cpcap");
  228. MODULE_DESCRIPTION("CPCAP driver");
  229. MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
  230. MODULE_LICENSE("GPL v2");