.. | .. |
---|
17 | 17 | #include <linux/irqchip/chained_irq.h> |
---|
18 | 18 | #include <linux/of_address.h> |
---|
19 | 19 | #include <linux/of_irq.h> |
---|
| 20 | +#include <linux/interrupt.h> |
---|
20 | 21 | |
---|
21 | 22 | #define APB_INT_ENABLE_L 0x00 |
---|
22 | 23 | #define APB_INT_ENABLE_H 0x04 |
---|
.. | .. |
---|
26 | 27 | #define APB_INT_FINALSTATUS_H 0x34 |
---|
27 | 28 | #define APB_INT_BASE_OFFSET 0x04 |
---|
28 | 29 | |
---|
29 | | -static void dw_apb_ictl_handler(struct irq_desc *desc) |
---|
| 30 | +/* irq domain of the primary interrupt controller. */ |
---|
| 31 | +static struct irq_domain *dw_apb_ictl_irq_domain; |
---|
| 32 | + |
---|
| 33 | +static void __irq_entry dw_apb_ictl_handle_irq(struct pt_regs *regs) |
---|
| 34 | +{ |
---|
| 35 | + struct irq_domain *d = dw_apb_ictl_irq_domain; |
---|
| 36 | + int n; |
---|
| 37 | + |
---|
| 38 | + for (n = 0; n < d->revmap_size; n += 32) { |
---|
| 39 | + struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, n); |
---|
| 40 | + u32 stat = readl_relaxed(gc->reg_base + APB_INT_FINALSTATUS_L); |
---|
| 41 | + |
---|
| 42 | + while (stat) { |
---|
| 43 | + u32 hwirq = ffs(stat) - 1; |
---|
| 44 | + |
---|
| 45 | + handle_domain_irq(d, hwirq, regs); |
---|
| 46 | + stat &= ~BIT(hwirq); |
---|
| 47 | + } |
---|
| 48 | + } |
---|
| 49 | +} |
---|
| 50 | + |
---|
| 51 | +static void dw_apb_ictl_handle_irq_cascaded(struct irq_desc *desc) |
---|
30 | 52 | { |
---|
31 | 53 | struct irq_domain *d = irq_desc_get_handler_data(desc); |
---|
32 | 54 | struct irq_chip *chip = irq_desc_get_chip(desc); |
---|
.. | .. |
---|
43 | 65 | u32 virq = irq_find_mapping(d, gc->irq_base + hwirq); |
---|
44 | 66 | |
---|
45 | 67 | generic_handle_irq(virq); |
---|
46 | | - stat &= ~(1 << hwirq); |
---|
| 68 | + stat &= ~BIT(hwirq); |
---|
47 | 69 | } |
---|
48 | 70 | } |
---|
49 | 71 | |
---|
50 | 72 | chained_irq_exit(chip, desc); |
---|
51 | 73 | } |
---|
| 74 | + |
---|
| 75 | +static int dw_apb_ictl_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, |
---|
| 76 | + unsigned int nr_irqs, void *arg) |
---|
| 77 | +{ |
---|
| 78 | + int i, ret; |
---|
| 79 | + irq_hw_number_t hwirq; |
---|
| 80 | + unsigned int type = IRQ_TYPE_NONE; |
---|
| 81 | + struct irq_fwspec *fwspec = arg; |
---|
| 82 | + |
---|
| 83 | + ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type); |
---|
| 84 | + if (ret) |
---|
| 85 | + return ret; |
---|
| 86 | + |
---|
| 87 | + for (i = 0; i < nr_irqs; i++) |
---|
| 88 | + irq_map_generic_chip(domain, virq + i, hwirq + i); |
---|
| 89 | + |
---|
| 90 | + return 0; |
---|
| 91 | +} |
---|
| 92 | + |
---|
| 93 | +static const struct irq_domain_ops dw_apb_ictl_irq_domain_ops = { |
---|
| 94 | + .translate = irq_domain_translate_onecell, |
---|
| 95 | + .alloc = dw_apb_ictl_irq_domain_alloc, |
---|
| 96 | + .free = irq_domain_free_irqs_top, |
---|
| 97 | +}; |
---|
52 | 98 | |
---|
53 | 99 | #ifdef CONFIG_PM |
---|
54 | 100 | static void dw_apb_ictl_resume(struct irq_data *d) |
---|
.. | .. |
---|
68 | 114 | static int __init dw_apb_ictl_init(struct device_node *np, |
---|
69 | 115 | struct device_node *parent) |
---|
70 | 116 | { |
---|
| 117 | + const struct irq_domain_ops *domain_ops; |
---|
71 | 118 | unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; |
---|
72 | 119 | struct resource r; |
---|
73 | 120 | struct irq_domain *domain; |
---|
74 | 121 | struct irq_chip_generic *gc; |
---|
75 | 122 | void __iomem *iobase; |
---|
76 | | - int ret, nrirqs, irq, i; |
---|
| 123 | + int ret, nrirqs, parent_irq, i; |
---|
77 | 124 | u32 reg; |
---|
78 | 125 | |
---|
79 | | - /* Map the parent interrupt for the chained handler */ |
---|
80 | | - irq = irq_of_parse_and_map(np, 0); |
---|
81 | | - if (irq <= 0) { |
---|
82 | | - pr_err("%pOF: unable to parse irq\n", np); |
---|
83 | | - return -EINVAL; |
---|
| 126 | + if (!parent) { |
---|
| 127 | + /* Used as the primary interrupt controller */ |
---|
| 128 | + parent_irq = 0; |
---|
| 129 | + domain_ops = &dw_apb_ictl_irq_domain_ops; |
---|
| 130 | + } else { |
---|
| 131 | + /* Map the parent interrupt for the chained handler */ |
---|
| 132 | + parent_irq = irq_of_parse_and_map(np, 0); |
---|
| 133 | + if (parent_irq <= 0) { |
---|
| 134 | + pr_err("%pOF: unable to parse irq\n", np); |
---|
| 135 | + return -EINVAL; |
---|
| 136 | + } |
---|
| 137 | + domain_ops = &irq_generic_chip_ops; |
---|
84 | 138 | } |
---|
85 | 139 | |
---|
86 | 140 | ret = of_address_to_resource(np, 0, &r); |
---|
.. | .. |
---|
105 | 159 | * DW IP can be configured to allow 2-64 irqs. We can determine |
---|
106 | 160 | * the number of irqs supported by writing into enable register |
---|
107 | 161 | * and look for bits not set, as corresponding flip-flops will |
---|
108 | | - * have been removed by sythesis tool. |
---|
| 162 | + * have been removed by synthesis tool. |
---|
109 | 163 | */ |
---|
110 | 164 | |
---|
111 | 165 | /* mask and enable all interrupts */ |
---|
.. | .. |
---|
120 | 174 | else |
---|
121 | 175 | nrirqs = fls(readl_relaxed(iobase + APB_INT_ENABLE_L)); |
---|
122 | 176 | |
---|
123 | | - domain = irq_domain_add_linear(np, nrirqs, |
---|
124 | | - &irq_generic_chip_ops, NULL); |
---|
| 177 | + domain = irq_domain_add_linear(np, nrirqs, domain_ops, NULL); |
---|
125 | 178 | if (!domain) { |
---|
126 | 179 | pr_err("%pOF: unable to add irq domain\n", np); |
---|
127 | 180 | ret = -ENOMEM; |
---|
.. | .. |
---|
146 | 199 | gc->chip_types[0].chip.irq_resume = dw_apb_ictl_resume; |
---|
147 | 200 | } |
---|
148 | 201 | |
---|
149 | | - irq_set_chained_handler_and_data(irq, dw_apb_ictl_handler, domain); |
---|
| 202 | + if (parent_irq) { |
---|
| 203 | + irq_set_chained_handler_and_data(parent_irq, |
---|
| 204 | + dw_apb_ictl_handle_irq_cascaded, domain); |
---|
| 205 | + } else { |
---|
| 206 | + dw_apb_ictl_irq_domain = domain; |
---|
| 207 | + set_handle_irq(dw_apb_ictl_handle_irq); |
---|
| 208 | + } |
---|
150 | 209 | |
---|
151 | 210 | return 0; |
---|
152 | 211 | |
---|