| .. | .. |
|---|
| 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 | |
|---|