320-irqchip-add-support-for-bcm6345-style-periphery-irq-.patch 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. From 301744ecbeece89ab3a9d6beef7802fa22598f00 Mon Sep 17 00:00:00 2001
  2. From: Jonas Gorski <jogo@openwrt.org>
  3. Date: Sun, 30 Nov 2014 14:53:12 +0100
  4. Subject: [PATCH 1/5] irqchip: add support for bcm6345-style periphery irq
  5. controller
  6. Signed-off-by: Jonas Gorski <jogo@openwrt.org>
  7. ---
  8. .../brcm,bcm6345-periph-intc.txt | 50 +++
  9. drivers/irqchip/Kconfig | 4 +
  10. drivers/irqchip/Makefile | 1 +
  11. drivers/irqchip/irq-bcm6345-periph.c | 339 ++++++++++++++++++++
  12. include/linux/irqchip/irq-bcm6345-periph.h | 16 +
  13. 5 files changed, 410 insertions(+)
  14. create mode 100644 Documentation/devicetree/bindings/interrupt-controller/brcm,bcm6345-periph-intc.txt
  15. create mode 100644 drivers/irqchip/irq-bcm6345-periph.c
  16. create mode 100644 include/linux/irqchip/irq-bcm6345-periph.h
  17. --- /dev/null
  18. +++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm6345-periph-intc.txt
  19. @@ -0,0 +1,50 @@
  20. +Broadcom BCM6345 Level 1 periphery interrupt controller
  21. +
  22. +This block is a interrupt controller that is typically connected directly
  23. +to one of the HW INT lines on each CPU. Every BCM63XX xDSL chip since
  24. +BCM6345 has contained this hardware.
  25. +
  26. +Key elements of the hardware design include:
  27. +
  28. +- 32, 64, or 128 incoming level IRQ lines
  29. +
  30. +- All onchip peripherals are wired directly to an L2 input
  31. +
  32. +- A separate instance of the register set for each CPU, allowing individual
  33. + peripheral IRQs to be routed to any CPU
  34. +
  35. +- No atomic mask/unmask operations
  36. +
  37. +- No polarity/level/edge settings
  38. +
  39. +- No FIFO or priority encoder logic; software is expected to read all
  40. + 1-4 status words to determine which IRQs are pending
  41. +
  42. +Required properties:
  43. +
  44. +- compatible: Should be "brcm,bcm6345-periph-intc".
  45. +- reg: Specifies the base physical address and size of the registers.
  46. + Multiple register addresses may be specified, and must match the amount of
  47. + parent interrupts.
  48. +- interrupt-controller: Identifies the node as an interrupt controller.
  49. +- #interrupt-cells: Specifies the number of cells needed to encode an interrupt
  50. + source, should be 1.
  51. +- interrupt-parent: Specifies the phandle to the parent interrupt controller
  52. + this one is cascaded from.
  53. +- interrupts: Specifies the interrupt line(s) in the interrupt-parent controller
  54. + node, valid values depend on the type of parent interrupt controller.
  55. + Multiple lines are used to route interrupts to different cpus, with the first
  56. + assumed to be for the boot CPU.
  57. +
  58. +Example:
  59. +
  60. +periph_intc: interrupt-controller@f0406800 {
  61. + compatible = "brcm,bcm6345-periph-intc";
  62. + reg = <0x10000020 0x10>, <0x10000030 0x10>;
  63. +
  64. + interrupt-controller;
  65. + #interrupt-cells = <1>;
  66. +
  67. + interrupt-parent = <&cpu_intc>;
  68. + interrupts = <2>, <3>;
  69. +};
  70. --- a/drivers/irqchip/Kconfig
  71. +++ b/drivers/irqchip/Kconfig
  72. @@ -80,6 +80,10 @@ config BRCMSTB_L2_IRQ
  73. select GENERIC_IRQ_CHIP
  74. select IRQ_DOMAIN
  75. +config BCM6345_PERIPH_IRQ
  76. + bool
  77. + select IRQ_DOMAIN
  78. +
  79. config DW_APB_ICTL
  80. bool
  81. select GENERIC_IRQ_CHIP
  82. --- a/drivers/irqchip/Makefile
  83. +++ b/drivers/irqchip/Makefile
  84. @@ -9,6 +9,7 @@ obj-$(CONFIG_ARCH_MVEBU) += irq-armada-
  85. obj-$(CONFIG_IRQ_MXS) += irq-mxs.o
  86. obj-$(CONFIG_ARCH_TEGRA) += irq-tegra.o
  87. obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
  88. +obj-$(CONFIG_BCM6345_PERIPH_IRQ) += irq-bcm6345-periph.o
  89. obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o
  90. obj-$(CONFIG_METAG) += irq-metag-ext.o
  91. obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
  92. --- /dev/null
  93. +++ b/drivers/irqchip/irq-bcm6345-periph.c
  94. @@ -0,0 +1,339 @@
  95. +/*
  96. + * This file is subject to the terms and conditions of the GNU General Public
  97. + * License. See the file "COPYING" in the main directory of this archive
  98. + * for more details.
  99. + *
  100. + * Copyright (C) 2014 Jonas Gorski <jogo@openwrt.org>
  101. + */
  102. +
  103. +#include <linux/ioport.h>
  104. +#include <linux/irq.h>
  105. +#include <linux/irqchip.h>
  106. +#include <linux/irqchip/chained_irq.h>
  107. +#include <linux/irqchip/irq-bcm6345-periph.h>
  108. +#include <linux/kernel.h>
  109. +#include <linux/of.h>
  110. +#include <linux/of_irq.h>
  111. +#include <linux/of_address.h>
  112. +#include <linux/slab.h>
  113. +#include <linux/spinlock.h>
  114. +
  115. +#ifdef CONFIG_BCM63XX
  116. +#include <asm/mach-bcm63xx/bcm63xx_irq.h>
  117. +
  118. +#define VIRQ_BASE IRQ_INTERNAL_BASE
  119. +#else
  120. +#define VIRQ_BASE 0
  121. +#endif
  122. +
  123. +#define MAX_WORDS 4
  124. +#define MAX_PARENT_IRQS 2
  125. +#define IRQS_PER_WORD 32
  126. +
  127. +struct intc_block {
  128. + int parent_irq;
  129. + void __iomem *base;
  130. + void __iomem *en_reg[MAX_WORDS];
  131. + void __iomem *status_reg[MAX_WORDS];
  132. + u32 mask_cache[MAX_WORDS];
  133. +};
  134. +
  135. +struct intc_data {
  136. + struct irq_chip chip;
  137. + struct intc_block block[MAX_PARENT_IRQS];
  138. +
  139. + int num_words;
  140. +
  141. + struct irq_domain *domain;
  142. + raw_spinlock_t lock;
  143. +};
  144. +
  145. +static void bcm6345_periph_irq_handle(struct irq_desc *desc)
  146. +{
  147. + struct intc_data *data = irq_desc_get_handler_data(desc);
  148. + struct irq_chip *chip = irq_desc_get_chip(desc);
  149. + struct intc_block *block;
  150. + unsigned int irq = irq_desc_get_irq(desc);
  151. + unsigned int idx;
  152. +
  153. + chained_irq_enter(chip, desc);
  154. +
  155. + for (idx = 0; idx < MAX_PARENT_IRQS; idx++)
  156. + if (irq == data->block[idx].parent_irq)
  157. + block = &data->block[idx];
  158. +
  159. + for (idx = 0; idx < data->num_words; idx++) {
  160. + int base = idx * IRQS_PER_WORD;
  161. + unsigned long pending;
  162. + int hw_irq;
  163. +
  164. + raw_spin_lock(&data->lock);
  165. + pending = __raw_readl(block->en_reg[idx]) &
  166. + __raw_readl(block->status_reg[idx]);
  167. + raw_spin_unlock(&data->lock);
  168. +
  169. + for_each_set_bit(hw_irq, &pending, IRQS_PER_WORD) {
  170. + int virq;
  171. +
  172. + virq = irq_find_mapping(data->domain, base + hw_irq);
  173. + generic_handle_irq(virq);
  174. + }
  175. + }
  176. +
  177. + chained_irq_exit(chip, desc);
  178. +}
  179. +
  180. +static void __bcm6345_periph_enable(struct intc_block *block, int reg, int bit,
  181. + bool enable)
  182. +{
  183. + u32 val;
  184. +
  185. + val = __raw_readl(block->en_reg[reg]);
  186. + if (enable)
  187. + val |= BIT(bit);
  188. + else
  189. + val &= ~BIT(bit);
  190. + __raw_writel(val, block->en_reg[reg]);
  191. +}
  192. +
  193. +static void bcm6345_periph_irq_mask(struct irq_data *data)
  194. +{
  195. + unsigned int i, reg, bit;
  196. + struct intc_data *priv = data->domain->host_data;
  197. + irq_hw_number_t hwirq = irqd_to_hwirq(data);
  198. +
  199. + reg = hwirq / IRQS_PER_WORD;
  200. + bit = hwirq % IRQS_PER_WORD;
  201. +
  202. + raw_spin_lock(&priv->lock);
  203. + for (i = 0; i < MAX_PARENT_IRQS; i++) {
  204. + struct intc_block *block = &priv->block[i];
  205. +
  206. + if (!block->parent_irq)
  207. + break;
  208. +
  209. + __bcm6345_periph_enable(block, reg, bit, false);
  210. + }
  211. + raw_spin_unlock(&priv->lock);
  212. +}
  213. +
  214. +static void bcm6345_periph_irq_unmask(struct irq_data *data)
  215. +{
  216. + struct intc_data *priv = data->domain->host_data;
  217. + irq_hw_number_t hwirq = irqd_to_hwirq(data);
  218. + unsigned int i, reg, bit;
  219. +
  220. + reg = hwirq / IRQS_PER_WORD;
  221. + bit = hwirq % IRQS_PER_WORD;
  222. +
  223. + raw_spin_lock(&priv->lock);
  224. + for (i = 0; i < MAX_PARENT_IRQS; i++) {
  225. + struct intc_block *block = &priv->block[i];
  226. +
  227. + if (!block->parent_irq)
  228. + break;
  229. +
  230. + if (block->mask_cache[reg] & BIT(bit))
  231. + __bcm6345_periph_enable(block, reg, bit, true);
  232. + else
  233. + __bcm6345_periph_enable(block, reg, bit, false);
  234. + }
  235. + raw_spin_unlock(&priv->lock);
  236. +}
  237. +
  238. +#ifdef CONFIG_SMP
  239. +static int bcm6345_periph_set_affinity(struct irq_data *data,
  240. + const struct cpumask *mask, bool force)
  241. +{
  242. + irq_hw_number_t hwirq = irqd_to_hwirq(data);
  243. + struct intc_data *priv = data->domain->host_data;
  244. + unsigned int i, reg, bit;
  245. + unsigned long flags;
  246. + bool enabled;
  247. + int cpu;
  248. +
  249. + reg = hwirq / IRQS_PER_WORD;
  250. + bit = hwirq % IRQS_PER_WORD;
  251. +
  252. + /* we could route to more than one cpu, but performance
  253. + suffers, so fix it to one.
  254. + */
  255. + cpu = cpumask_any_and(mask, cpu_online_mask);
  256. + if (cpu >= nr_cpu_ids)
  257. + return -EINVAL;
  258. +
  259. + if (cpu >= MAX_PARENT_IRQS)
  260. + return -EINVAL;
  261. +
  262. + if (!priv->block[cpu].parent_irq)
  263. + return -EINVAL;
  264. +
  265. + raw_spin_lock_irqsave(&priv->lock, flags);
  266. + enabled = !irqd_irq_masked(data);
  267. + for (i = 0; i < MAX_PARENT_IRQS; i++) {
  268. + struct intc_block *block = &priv->block[i];
  269. +
  270. + if (!block->parent_irq)
  271. + break;
  272. +
  273. + if (i == cpu) {
  274. + block->mask_cache[reg] |= BIT(bit);
  275. + __bcm6345_periph_enable(block, reg, bit, enabled);
  276. + } else {
  277. + block->mask_cache[reg] &= ~BIT(bit);
  278. + __bcm6345_periph_enable(block, reg, bit, false);
  279. + }
  280. + }
  281. + raw_spin_unlock_irqrestore(&priv->lock, flags);
  282. +
  283. + return 0;
  284. +}
  285. +#endif
  286. +
  287. +static int bcm6345_periph_map(struct irq_domain *d, unsigned int irq,
  288. + irq_hw_number_t hw)
  289. +{
  290. + struct intc_data *priv = d->host_data;
  291. +
  292. + irq_set_chip_and_handler(irq, &priv->chip, handle_level_irq);
  293. +
  294. + return 0;
  295. +}
  296. +
  297. +static const struct irq_domain_ops bcm6345_periph_domain_ops = {
  298. + .xlate = irq_domain_xlate_onecell,
  299. + .map = bcm6345_periph_map,
  300. +};
  301. +
  302. +static int __init __bcm6345_periph_intc_init(struct device_node *node,
  303. + int num_blocks, int *irq,
  304. + void __iomem **base, int num_words)
  305. +{
  306. + struct intc_data *data;
  307. + unsigned int i, w, status_offset;
  308. +
  309. + data = kzalloc(sizeof(*data), GFP_KERNEL);
  310. + if (!data)
  311. + return -ENOMEM;
  312. +
  313. + raw_spin_lock_init(&data->lock);
  314. +
  315. + status_offset = num_words * sizeof(u32);
  316. +
  317. + for (i = 0; i < num_blocks; i++) {
  318. + struct intc_block *block = &data->block[i];
  319. +
  320. + block->parent_irq = irq[i];
  321. + block->base = base[i];
  322. +
  323. + for (w = 0; w < num_words; w++) {
  324. + int word_offset = sizeof(u32) * ((num_words - w) - 1);
  325. +
  326. + block->en_reg[w] = base[i] + word_offset;
  327. + block->status_reg[w] = base[i] + status_offset;
  328. + block->status_reg[w] += word_offset;
  329. +
  330. + /* route all interrupts to line 0 by default */
  331. + if (i == 0)
  332. + block->mask_cache[w] = 0xffffffff;
  333. + }
  334. +
  335. + irq_set_handler_data(block->parent_irq, data);
  336. + irq_set_chained_handler(block->parent_irq,
  337. + bcm6345_periph_irq_handle);
  338. + }
  339. +
  340. + data->num_words = num_words;
  341. +
  342. + data->chip.name = "bcm6345-periph-intc";
  343. + data->chip.irq_mask = bcm6345_periph_irq_mask;
  344. + data->chip.irq_unmask = bcm6345_periph_irq_unmask;
  345. +
  346. +#ifdef CONFIG_SMP
  347. + if (num_blocks > 1)
  348. + data->chip.irq_set_affinity = bcm6345_periph_set_affinity;
  349. +#endif
  350. +
  351. + data->domain = irq_domain_add_simple(node, IRQS_PER_WORD * num_words,
  352. + VIRQ_BASE,
  353. + &bcm6345_periph_domain_ops, data);
  354. + if (!data->domain) {
  355. + kfree(data);
  356. + return -EINVAL;
  357. + }
  358. +
  359. + return 0;
  360. +}
  361. +
  362. +void __init bcm6345_periph_intc_init(int num_blocks, int *irq,
  363. + void __iomem **base, int num_words)
  364. +{
  365. + __bcm6345_periph_intc_init(NULL, num_blocks, irq, base, num_words);
  366. +}
  367. +
  368. +#ifdef CONFIG_OF
  369. +static int __init bcm6345_periph_of_init(struct device_node *node,
  370. + struct device_node *parent)
  371. +{
  372. + struct resource res;
  373. + int num_irqs, ret = -EINVAL;
  374. + int irqs[MAX_PARENT_IRQS] = { 0 };
  375. + void __iomem *bases[MAX_PARENT_IRQS] = { NULL };
  376. + int words = 0;
  377. + int i;
  378. +
  379. + num_irqs = of_irq_count(node);
  380. +
  381. + if (num_irqs < 1 || num_irqs > MAX_PARENT_IRQS)
  382. + return -EINVAL;
  383. +
  384. + for (i = 0; i < num_irqs; i++) {
  385. + resource_size_t size;
  386. +
  387. + irqs[i] = irq_of_parse_and_map(node, i);
  388. + if (!irqs[i])
  389. + goto out_unmap;
  390. +
  391. + if (of_address_to_resource(node, i, &res))
  392. + goto out_unmap;
  393. +
  394. + size = resource_size(&res);
  395. + switch (size) {
  396. + case 8:
  397. + case 16:
  398. + case 32:
  399. + size = size / 8;
  400. + break;
  401. + default:
  402. + goto out_unmap;
  403. + }
  404. +
  405. + if (words && words != size) {
  406. + ret = -EINVAL;
  407. + goto out_unmap;
  408. + }
  409. + words = size;
  410. +
  411. + bases[i] = of_iomap(node, i);
  412. + if (!bases[i]) {
  413. + ret = -ENOMEM;
  414. + goto out_unmap;
  415. + }
  416. + }
  417. +
  418. + ret = __bcm6345_periph_intc_init(node, num_irqs, irqs, bases, words);
  419. + if (!ret)
  420. + return 0;
  421. +
  422. +out_unmap:
  423. + for (i = 0; i < num_irqs; i++) {
  424. + iounmap(bases[i]);
  425. + irq_dispose_mapping(irqs[i]);
  426. + }
  427. +
  428. + return ret;
  429. +}
  430. +
  431. +IRQCHIP_DECLARE(bcm6345_periph_intc, "brcm,bcm6345-l1-intc",
  432. + bcm6345_periph_of_init);
  433. +#endif
  434. --- /dev/null
  435. +++ b/include/linux/irqchip/irq-bcm6345-periph.h
  436. @@ -0,0 +1,16 @@
  437. +/*
  438. + * This file is subject to the terms and conditions of the GNU General Public
  439. + * License. See the file "COPYING" in the main directory of this archive
  440. + * for more details.
  441. + *
  442. + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
  443. + * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
  444. + */
  445. +
  446. +#ifndef __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_PERIPH_H
  447. +#define __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_PERIPH_H
  448. +
  449. +void bcm6345_periph_intc_init(int num_blocks, int *irq, void __iomem **base,
  450. + int num_words);
  451. +
  452. +#endif /* __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_PERIPH_H */