0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. From b5bc51d44485c7ce0ca180a8c5de11a206f686e8 Mon Sep 17 00:00:00 2001
  2. From: Josh Cartwright <[email protected]>
  3. Date: Wed, 12 Feb 2014 13:44:25 -0600
  4. Subject: [PATCH 057/182] spmi: pmic_arb: add support for interrupt handling
  5. The Qualcomm PMIC Arbiter, in addition to being a basic SPMI controller,
  6. also implements interrupt handling for slave devices. Note, this is
  7. outside the scope of SPMI, as SPMI leaves interrupt handling completely
  8. unspecified.
  9. Extend the driver to provide a irq_chip implementation and chained irq
  10. handling which allows for these interrupts to be used.
  11. Cc: Thomas Gleixner <[email protected]>
  12. Signed-off-by: Josh Cartwright <[email protected]>
  13. Signed-off-by: Greg Kroah-Hartman <[email protected]>
  14. ---
  15. drivers/spmi/Kconfig | 1 +
  16. drivers/spmi/spmi-pmic-arb.c | 377 +++++++++++++++++++++++++++++++++++++++++-
  17. 2 files changed, 376 insertions(+), 2 deletions(-)
  18. --- a/drivers/spmi/Kconfig
  19. +++ b/drivers/spmi/Kconfig
  20. @@ -13,6 +13,7 @@ if SPMI
  21. config SPMI_MSM_PMIC_ARB
  22. tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)"
  23. depends on ARM
  24. + depends on IRQ_DOMAIN
  25. depends on ARCH_MSM || COMPILE_TEST
  26. default ARCH_MSM
  27. help
  28. --- a/drivers/spmi/spmi-pmic-arb.c
  29. +++ b/drivers/spmi/spmi-pmic-arb.c
  30. @@ -13,6 +13,9 @@
  31. #include <linux/err.h>
  32. #include <linux/interrupt.h>
  33. #include <linux/io.h>
  34. +#include <linux/irqchip/chained_irq.h>
  35. +#include <linux/irqdomain.h>
  36. +#include <linux/irq.h>
  37. #include <linux/kernel.h>
  38. #include <linux/module.h>
  39. #include <linux/of.h>
  40. @@ -103,6 +106,14 @@ enum pmic_arb_cmd_op_code {
  41. * @cnfg: address of the PMIC Arbiter configuration registers.
  42. * @lock: lock to synchronize accesses.
  43. * @channel: which channel to use for accesses.
  44. + * @irq: PMIC ARB interrupt.
  45. + * @ee: the current Execution Environment
  46. + * @min_apid: minimum APID (used for bounding IRQ search)
  47. + * @max_apid: maximum APID
  48. + * @mapping_table: in-memory copy of PPID -> APID mapping table.
  49. + * @domain: irq domain object for PMIC IRQ domain
  50. + * @spmic: SPMI controller object
  51. + * @apid_to_ppid: cached mapping from APID to PPID
  52. */
  53. struct spmi_pmic_arb_dev {
  54. void __iomem *base;
  55. @@ -110,6 +121,14 @@ struct spmi_pmic_arb_dev {
  56. void __iomem *cnfg;
  57. raw_spinlock_t lock;
  58. u8 channel;
  59. + int irq;
  60. + u8 ee;
  61. + u8 min_apid;
  62. + u8 max_apid;
  63. + u32 mapping_table[SPMI_MAPPING_TABLE_LEN];
  64. + struct irq_domain *domain;
  65. + struct spmi_controller *spmic;
  66. + u16 apid_to_ppid[256];
  67. };
  68. static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset)
  69. @@ -306,12 +325,316 @@ static int pmic_arb_write_cmd(struct spm
  70. return rc;
  71. }
  72. +enum qpnpint_regs {
  73. + QPNPINT_REG_RT_STS = 0x10,
  74. + QPNPINT_REG_SET_TYPE = 0x11,
  75. + QPNPINT_REG_POLARITY_HIGH = 0x12,
  76. + QPNPINT_REG_POLARITY_LOW = 0x13,
  77. + QPNPINT_REG_LATCHED_CLR = 0x14,
  78. + QPNPINT_REG_EN_SET = 0x15,
  79. + QPNPINT_REG_EN_CLR = 0x16,
  80. + QPNPINT_REG_LATCHED_STS = 0x18,
  81. +};
  82. +
  83. +struct spmi_pmic_arb_qpnpint_type {
  84. + u8 type; /* 1 -> edge */
  85. + u8 polarity_high;
  86. + u8 polarity_low;
  87. +} __packed;
  88. +
  89. +/* Simplified accessor functions for irqchip callbacks */
  90. +static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
  91. + size_t len)
  92. +{
  93. + struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
  94. + u8 sid = d->hwirq >> 24;
  95. + u8 per = d->hwirq >> 16;
  96. +
  97. + if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid,
  98. + (per << 8) + reg, buf, len))
  99. + dev_err_ratelimited(&pa->spmic->dev,
  100. + "failed irqchip transaction on %x\n",
  101. + d->irq);
  102. +}
  103. +
  104. +static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
  105. +{
  106. + struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
  107. + u8 sid = d->hwirq >> 24;
  108. + u8 per = d->hwirq >> 16;
  109. +
  110. + if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid,
  111. + (per << 8) + reg, buf, len))
  112. + dev_err_ratelimited(&pa->spmic->dev,
  113. + "failed irqchip transaction on %x\n",
  114. + d->irq);
  115. +}
  116. +
  117. +static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid)
  118. +{
  119. + unsigned int irq;
  120. + u32 status;
  121. + int id;
  122. +
  123. + status = readl_relaxed(pa->intr + SPMI_PIC_IRQ_STATUS(apid));
  124. + while (status) {
  125. + id = ffs(status) - 1;
  126. + status &= ~(1 << id);
  127. + irq = irq_find_mapping(pa->domain,
  128. + pa->apid_to_ppid[apid] << 16
  129. + | id << 8
  130. + | apid);
  131. + generic_handle_irq(irq);
  132. + }
  133. +}
  134. +
  135. +static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc)
  136. +{
  137. + struct spmi_pmic_arb_dev *pa = irq_get_handler_data(irq);
  138. + struct irq_chip *chip = irq_get_chip(irq);
  139. + void __iomem *intr = pa->intr;
  140. + int first = pa->min_apid >> 5;
  141. + int last = pa->max_apid >> 5;
  142. + u32 status;
  143. + int i, id;
  144. +
  145. + chained_irq_enter(chip, desc);
  146. +
  147. + for (i = first; i <= last; ++i) {
  148. + status = readl_relaxed(intr +
  149. + SPMI_PIC_OWNER_ACC_STATUS(pa->ee, i));
  150. + while (status) {
  151. + id = ffs(status) - 1;
  152. + status &= ~(1 << id);
  153. + periph_interrupt(pa, id + i * 32);
  154. + }
  155. + }
  156. +
  157. + chained_irq_exit(chip, desc);
  158. +}
  159. +
  160. +static void qpnpint_irq_ack(struct irq_data *d)
  161. +{
  162. + struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
  163. + u8 irq = d->hwirq >> 8;
  164. + u8 apid = d->hwirq;
  165. + unsigned long flags;
  166. + u8 data;
  167. +
  168. + raw_spin_lock_irqsave(&pa->lock, flags);
  169. + writel_relaxed(1 << irq, pa->intr + SPMI_PIC_IRQ_CLEAR(apid));
  170. + raw_spin_unlock_irqrestore(&pa->lock, flags);
  171. +
  172. + data = 1 << irq;
  173. + qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
  174. +}
  175. +
  176. +static void qpnpint_irq_mask(struct irq_data *d)
  177. +{
  178. + struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
  179. + u8 irq = d->hwirq >> 8;
  180. + u8 apid = d->hwirq;
  181. + unsigned long flags;
  182. + u32 status;
  183. + u8 data;
  184. +
  185. + raw_spin_lock_irqsave(&pa->lock, flags);
  186. + status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid));
  187. + if (status & SPMI_PIC_ACC_ENABLE_BIT) {
  188. + status = status & ~SPMI_PIC_ACC_ENABLE_BIT;
  189. + writel_relaxed(status, pa->intr + SPMI_PIC_ACC_ENABLE(apid));
  190. + }
  191. + raw_spin_unlock_irqrestore(&pa->lock, flags);
  192. +
  193. + data = 1 << irq;
  194. + qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1);
  195. +}
  196. +
  197. +static void qpnpint_irq_unmask(struct irq_data *d)
  198. +{
  199. + struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
  200. + u8 irq = d->hwirq >> 8;
  201. + u8 apid = d->hwirq;
  202. + unsigned long flags;
  203. + u32 status;
  204. + u8 data;
  205. +
  206. + raw_spin_lock_irqsave(&pa->lock, flags);
  207. + status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid));
  208. + if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) {
  209. + writel_relaxed(status | SPMI_PIC_ACC_ENABLE_BIT,
  210. + pa->intr + SPMI_PIC_ACC_ENABLE(apid));
  211. + }
  212. + raw_spin_unlock_irqrestore(&pa->lock, flags);
  213. +
  214. + data = 1 << irq;
  215. + qpnpint_spmi_write(d, QPNPINT_REG_EN_SET, &data, 1);
  216. +}
  217. +
  218. +static void qpnpint_irq_enable(struct irq_data *d)
  219. +{
  220. + u8 irq = d->hwirq >> 8;
  221. + u8 data;
  222. +
  223. + qpnpint_irq_unmask(d);
  224. +
  225. + data = 1 << irq;
  226. + qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
  227. +}
  228. +
  229. +static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
  230. +{
  231. + struct spmi_pmic_arb_qpnpint_type type;
  232. + u8 irq = d->hwirq >> 8;
  233. +
  234. + qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
  235. +
  236. + if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
  237. + type.type |= 1 << irq;
  238. + if (flow_type & IRQF_TRIGGER_RISING)
  239. + type.polarity_high |= 1 << irq;
  240. + if (flow_type & IRQF_TRIGGER_FALLING)
  241. + type.polarity_low |= 1 << irq;
  242. + } else {
  243. + if ((flow_type & (IRQF_TRIGGER_HIGH)) &&
  244. + (flow_type & (IRQF_TRIGGER_LOW)))
  245. + return -EINVAL;
  246. +
  247. + type.type &= ~(1 << irq); /* level trig */
  248. + if (flow_type & IRQF_TRIGGER_HIGH)
  249. + type.polarity_high |= 1 << irq;
  250. + else
  251. + type.polarity_low |= 1 << irq;
  252. + }
  253. +
  254. + qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
  255. + return 0;
  256. +}
  257. +
  258. +static struct irq_chip pmic_arb_irqchip = {
  259. + .name = "pmic_arb",
  260. + .irq_enable = qpnpint_irq_enable,
  261. + .irq_ack = qpnpint_irq_ack,
  262. + .irq_mask = qpnpint_irq_mask,
  263. + .irq_unmask = qpnpint_irq_unmask,
  264. + .irq_set_type = qpnpint_irq_set_type,
  265. + .flags = IRQCHIP_MASK_ON_SUSPEND
  266. + | IRQCHIP_SKIP_SET_WAKE,
  267. +};
  268. +
  269. +struct spmi_pmic_arb_irq_spec {
  270. + unsigned slave:4;
  271. + unsigned per:8;
  272. + unsigned irq:3;
  273. +};
  274. +
  275. +static int search_mapping_table(struct spmi_pmic_arb_dev *pa,
  276. + struct spmi_pmic_arb_irq_spec *spec,
  277. + u8 *apid)
  278. +{
  279. + u16 ppid = spec->slave << 8 | spec->per;
  280. + u32 *mapping_table = pa->mapping_table;
  281. + int index = 0, i;
  282. + u32 data;
  283. +
  284. + for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
  285. + data = mapping_table[index];
  286. +
  287. + if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) {
  288. + if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) {
  289. + index = SPMI_MAPPING_BIT_IS_1_RESULT(data);
  290. + } else {
  291. + *apid = SPMI_MAPPING_BIT_IS_1_RESULT(data);
  292. + return 0;
  293. + }
  294. + } else {
  295. + if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) {
  296. + index = SPMI_MAPPING_BIT_IS_0_RESULT(data);
  297. + } else {
  298. + *apid = SPMI_MAPPING_BIT_IS_0_RESULT(data);
  299. + return 0;
  300. + }
  301. + }
  302. + }
  303. +
  304. + return -ENODEV;
  305. +}
  306. +
  307. +static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
  308. + struct device_node *controller,
  309. + const u32 *intspec,
  310. + unsigned int intsize,
  311. + unsigned long *out_hwirq,
  312. + unsigned int *out_type)
  313. +{
  314. + struct spmi_pmic_arb_dev *pa = d->host_data;
  315. + struct spmi_pmic_arb_irq_spec spec;
  316. + int err;
  317. + u8 apid;
  318. +
  319. + dev_dbg(&pa->spmic->dev,
  320. + "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
  321. + intspec[0], intspec[1], intspec[2]);
  322. +
  323. + if (d->of_node != controller)
  324. + return -EINVAL;
  325. + if (intsize != 4)
  326. + return -EINVAL;
  327. + if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
  328. + return -EINVAL;
  329. +
  330. + spec.slave = intspec[0];
  331. + spec.per = intspec[1];
  332. + spec.irq = intspec[2];
  333. +
  334. + err = search_mapping_table(pa, &spec, &apid);
  335. + if (err)
  336. + return err;
  337. +
  338. + pa->apid_to_ppid[apid] = spec.slave << 8 | spec.per;
  339. +
  340. + /* Keep track of {max,min}_apid for bounding search during interrupt */
  341. + if (apid > pa->max_apid)
  342. + pa->max_apid = apid;
  343. + if (apid < pa->min_apid)
  344. + pa->min_apid = apid;
  345. +
  346. + *out_hwirq = spec.slave << 24
  347. + | spec.per << 16
  348. + | spec.irq << 8
  349. + | apid;
  350. + *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;
  351. +
  352. + dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
  353. +
  354. + return 0;
  355. +}
  356. +
  357. +static int qpnpint_irq_domain_map(struct irq_domain *d,
  358. + unsigned int virq,
  359. + irq_hw_number_t hwirq)
  360. +{
  361. + struct spmi_pmic_arb_dev *pa = d->host_data;
  362. +
  363. + dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq);
  364. +
  365. + irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq);
  366. + irq_set_chip_data(virq, d->host_data);
  367. + irq_set_noprobe(virq);
  368. + return 0;
  369. +}
  370. +
  371. +static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
  372. + .map = qpnpint_irq_domain_map,
  373. + .xlate = qpnpint_irq_domain_dt_translate,
  374. +};
  375. +
  376. static int spmi_pmic_arb_probe(struct platform_device *pdev)
  377. {
  378. struct spmi_pmic_arb_dev *pa;
  379. struct spmi_controller *ctrl;
  380. struct resource *res;
  381. - u32 channel;
  382. + u32 channel, ee;
  383. int err, i;
  384. ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa));
  385. @@ -319,6 +642,7 @@ static int spmi_pmic_arb_probe(struct pl
  386. return -ENOMEM;
  387. pa = spmi_controller_get_drvdata(ctrl);
  388. + pa->spmic = ctrl;
  389. res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
  390. pa->base = devm_ioremap_resource(&ctrl->dev, res);
  391. @@ -341,6 +665,12 @@ static int spmi_pmic_arb_probe(struct pl
  392. goto err_put_ctrl;
  393. }
  394. + pa->irq = platform_get_irq_byname(pdev, "periph_irq");
  395. + if (pa->irq < 0) {
  396. + err = pa->irq;
  397. + goto err_put_ctrl;
  398. + }
  399. +
  400. err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
  401. if (err) {
  402. dev_err(&pdev->dev, "channel unspecified.\n");
  403. @@ -355,6 +685,29 @@ static int spmi_pmic_arb_probe(struct pl
  404. pa->channel = channel;
  405. + err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee);
  406. + if (err) {
  407. + dev_err(&pdev->dev, "EE unspecified.\n");
  408. + goto err_put_ctrl;
  409. + }
  410. +
  411. + if (ee > 5) {
  412. + dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee);
  413. + err = -EINVAL;
  414. + goto err_put_ctrl;
  415. + }
  416. +
  417. + pa->ee = ee;
  418. +
  419. + for (i = 0; i < ARRAY_SIZE(pa->mapping_table); ++i)
  420. + pa->mapping_table[i] = readl_relaxed(
  421. + pa->cnfg + SPMI_MAPPING_TABLE_REG(i));
  422. +
  423. + /* Initialize max_apid/min_apid to the opposite bounds, during
  424. + * the irq domain translation, we are sure to update these */
  425. + pa->max_apid = 0;
  426. + pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1;
  427. +
  428. platform_set_drvdata(pdev, ctrl);
  429. raw_spin_lock_init(&pa->lock);
  430. @@ -362,15 +715,31 @@ static int spmi_pmic_arb_probe(struct pl
  431. ctrl->read_cmd = pmic_arb_read_cmd;
  432. ctrl->write_cmd = pmic_arb_write_cmd;
  433. + dev_dbg(&pdev->dev, "adding irq domain\n");
  434. + pa->domain = irq_domain_add_tree(pdev->dev.of_node,
  435. + &pmic_arb_irq_domain_ops, pa);
  436. + if (!pa->domain) {
  437. + dev_err(&pdev->dev, "unable to create irq_domain\n");
  438. + err = -ENOMEM;
  439. + goto err_put_ctrl;
  440. + }
  441. +
  442. + irq_set_handler_data(pa->irq, pa);
  443. + irq_set_chained_handler(pa->irq, pmic_arb_chained_irq);
  444. +
  445. err = spmi_controller_add(ctrl);
  446. if (err)
  447. - goto err_put_ctrl;
  448. + goto err_domain_remove;
  449. dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n",
  450. pmic_arb_base_read(pa, PMIC_ARB_VERSION));
  451. return 0;
  452. +err_domain_remove:
  453. + irq_set_chained_handler(pa->irq, NULL);
  454. + irq_set_handler_data(pa->irq, NULL);
  455. + irq_domain_remove(pa->domain);
  456. err_put_ctrl:
  457. spmi_controller_put(ctrl);
  458. return err;
  459. @@ -379,7 +748,11 @@ err_put_ctrl:
  460. static int spmi_pmic_arb_remove(struct platform_device *pdev)
  461. {
  462. struct spmi_controller *ctrl = platform_get_drvdata(pdev);
  463. + struct spmi_pmic_arb_dev *pa = spmi_controller_get_drvdata(ctrl);
  464. spmi_controller_remove(ctrl);
  465. + irq_set_chained_handler(pa->irq, NULL);
  466. + irq_set_handler_data(pa->irq, NULL);
  467. + irq_domain_remove(pa->domain);
  468. spmi_controller_put(ctrl);
  469. return 0;
  470. }