| 1 | |
| 2 | |
| 3 | * Realtek Interrupt controller.
|
| 4 | *
|
| 5 | * The Realtek Interrupt controller is a big endian device found in the
|
| 6 | * Realtek MIPS SoCs.
|
| 7 | *
|
| 8 | * Copyright (C) 2020 Birger Koblitz <[email protected]>
|
| 9 | * Copyright (C) 2020 Bert Vermeulen <[email protected]>
|
| 10 | * Copyright (C) 2020 John Crispin <[email protected]>
|
| 11 |
|
| 12 |
|
| 13 | #include <linux/of_irq.h>
|
| 14 | #include <linux/irqchip.h>
|
| 15 | #include <linux/spinlock.h>
|
| 16 | #include <linux/of_address.h>
|
| 17 | #include <linux/irqchip/chained_irq.h>
|
| 18 |
|
| 19 |
|
| 20 | #define RTL_ICTL_GIMR 0x00
|
| 21 |
|
| 22 | #define RTL_ICTL_GISR 0x04
|
| 23 |
|
| 24 | #define RTL_ICTL_IRR0 0x08
|
| 25 | #define RTL_ICTL_IRR1 0x0c
|
| 26 | #define RTL_ICTL_IRR2 0x10
|
| 27 | #define RTL_ICTL_IRR3 0x14
|
| 28 |
|
| 29 | #define RTL_ICTL_NUM_INPUTS 32
|
| 30 |
|
| 31 | #define REG(cpu, x) (realtek_ictl_base[cpu] + x)
|
| 32 |
|
| 33 | struct realtek_ictl_output {
|
| 34 | struct fwnode_handle *fwnode;
|
| 35 | struct irq_domain *domain;
|
| 36 | unsigned int parent_irq;
|
| 37 | unsigned int parent_hwirq;
|
| 38 | unsigned int index;
|
| 39 | u32 mask;
|
| 40 | };
|
| 41 |
|
| 42 | static DEFINE_RAW_SPINLOCK(irq_lock);
|
| 43 | static void __iomem *realtek_ictl_base[NR_CPUS];
|
| 44 |
|
| 45 | |
| 46 | * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering,
|
| 47 | * placing IRQ 31 in the first four bits. A routing value of '0' means the
|
| 48 | * interrupt is left disconnected. Routing values {1..15} connect to output
|
| 49 | * lines {0..14}.
|
| 50 |
|
| 51 | #define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32))
|
| 52 | #define IRR_SHIFT(idx) ((idx * 4) % 32)
|
| 53 |
|
| 54 | static inline void enable_gimr(unsigned int cpu, unsigned int hw_irq)
|
| 55 | {
|
| 56 | u32 gimr;
|
| 57 |
|
| 58 | gimr = readl_be(REG(cpu, RTL_ICTL_GIMR));
|
| 59 | gimr |= BIT(hw_irq);
|
| 60 | writel_be(gimr, REG(cpu, RTL_ICTL_GIMR));
|
| 61 | }
|
| 62 |
|
| 63 | static inline void disable_gimr(unsigned int cpu, unsigned int hw_irq)
|
| 64 | {
|
| 65 | u32 gimr;
|
| 66 |
|
| 67 | gimr = readl_be(REG(cpu, RTL_ICTL_GIMR));
|
| 68 | gimr &= ~BIT(hw_irq);
|
| 69 | writel_be(gimr, REG(cpu, RTL_ICTL_GIMR));
|
| 70 | }
|
| 71 |
|
| 72 | static void write_irr(unsigned int cpu, int hw_irq, u32 value)
|
| 73 | {
|
| 74 | void __iomem *irr0 = REG(cpu, RTL_ICTL_IRR0);
|
| 75 | unsigned int offset = IRR_OFFSET(hw_irq);
|
| 76 | unsigned int shift = IRR_SHIFT(hw_irq);
|
| 77 | u32 irr;
|
| 78 |
|
| 79 | irr = readl_be(irr0 + offset) & ~(0xf << shift);
|
| 80 | irr |= (value & 0xf) << shift;
|
| 81 | writel_be(irr, irr0 + offset);
|
| 82 | }
|
| 83 |
|
| 84 | static void realtek_ictl_unmask_irq(struct irq_data *i)
|
| 85 | {
|
| 86 | unsigned int cpu;
|
| 87 |
|
| 88 | guard(raw_spinlock)(&irq_lock);
|
| 89 | for_each_cpu(cpu, irq_data_get_effective_affinity_mask(i))
|
| 90 | enable_gimr(cpu, i->hwirq);
|
| 91 | }
|
| 92 |
|
| 93 | static void realtek_ictl_mask_irq(struct irq_data *i)
|
| 94 | {
|
| 95 | unsigned int cpu;
|
| 96 |
|
| 97 | guard(raw_spinlock)(&irq_lock);
|
| 98 | for_each_cpu(cpu, irq_data_get_effective_affinity_mask(i))
|
| 99 | disable_gimr(cpu, i->hwirq);
|
| 100 | }
|
| 101 |
|
| 102 | static int realtek_ictl_irq_affinity(struct irq_data *i, const struct cpumask *dest, bool force)
|
| 103 | {
|
| 104 | if (!irqd_irq_masked(i))
|
| 105 | realtek_ictl_mask_irq(i);
|
| 106 |
|
| 107 | irq_data_update_effective_affinity(i, dest);
|
| 108 |
|
| 109 | if (!irqd_irq_masked(i))
|
| 110 | realtek_ictl_unmask_irq(i);
|
| 111 |
|
| 112 | return IRQ_SET_MASK_OK;
|
| 113 | }
|
| 114 |
|
| 115 | static struct irq_chip realtek_ictl_irq = {
|
| 116 | .name = "realtek-rtl-intc",
|
| 117 | .irq_mask = realtek_ictl_mask_irq,
|
| 118 | .irq_unmask = realtek_ictl_unmask_irq,
|
| 119 | .irq_set_affinity = realtek_ictl_irq_affinity,
|
| 120 | };
|
| 121 |
|
| 122 | static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw_irq)
|
| 123 | {
|
| 124 | struct realtek_ictl_output *output = d->host_data;
|
| 125 | unsigned int cpu;
|
| 126 |
|
| 127 | irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq);
|
| 128 |
|
| 129 | guard(raw_spinlock_irqsave)(&irq_lock);
|
| 130 | output->mask |= BIT(hw_irq);
|
| 131 | for_each_present_cpu(cpu)
|
| 132 | write_irr(cpu, hw_irq, output->parent_hwirq - 1);
|
| 133 |
|
| 134 | return 0;
|
| 135 | }
|
| 136 |
|
| 137 | static int intc_select(struct irq_domain *d, struct irq_fwspec *fwspec,
|
| 138 | enum irq_domain_bus_token bus_token)
|
| 139 | {
|
| 140 | struct realtek_ictl_output *output = d->host_data;
|
| 141 | unsigned int index = 0;
|
| 142 |
|
| 143 | if (fwspec->fwnode != output->fwnode)
|
| 144 | return false;
|
| 145 |
|
| 146 | if (fwspec->param_count == 2)
|
| 147 | index = fwspec->param[1];
|
| 148 |
|
| 149 | return index == output->index;
|
| 150 | }
|
| 151 |
|
| 152 | static const struct irq_domain_ops irq_domain_ops = {
|
| 153 | .map = intc_map,
|
| 154 | .select = intc_select,
|
| 155 | .xlate = irq_domain_xlate_onecell,
|
| 156 | };
|
| 157 |
|
| 158 | static void realtek_irq_dispatch(struct irq_desc *desc)
|
| 159 | {
|
| 160 | struct realtek_ictl_output *output = irq_desc_get_handler_data(desc);
|
| 161 | struct irq_chip *chip = irq_desc_get_chip(desc);
|
| 162 | unsigned int cpu = smp_processor_id();
|
| 163 | unsigned long pending;
|
| 164 | unsigned int hw_irq;
|
| 165 |
|
| 166 | chained_irq_enter(chip, desc);
|
| 167 | pending = readl_be(REG(cpu, RTL_ICTL_GIMR)) &
|
| 168 | readl_be(REG(cpu, RTL_ICTL_GISR)) & output->mask;
|
| 169 |
|
| 170 | if (unlikely(!pending)) {
|
| 171 | spurious_interrupt();
|
| 172 | goto out;
|
| 173 | }
|
| 174 |
|
| 175 | for_each_set_bit(hw_irq, &pending, RTL_ICTL_NUM_INPUTS)
|
| 176 | generic_handle_domain_irq(output->domain, hw_irq);
|
| 177 |
|
| 178 | out:
|
| 179 | chained_irq_exit(chip, desc);
|
| 180 | }
|
| 181 |
|
| 182 | static int __init realtek_setup_parents(struct device_node *node)
|
| 183 | {
|
| 184 | int p, cnt, err, parent_irq, num_parents = of_irq_count(node);
|
| 185 | struct realtek_ictl_output *output;
|
| 186 | struct irq_data *parent_data;
|
| 187 | struct of_phandle_args oirq;
|
| 188 | struct irq_domain *domain;
|
| 189 |
|
| 190 | cnt = max(1, num_parents);
|
| 191 | output = kzalloc_objs(*output, cnt);
|
| 192 | if (!output)
|
| 193 | return -ENOMEM;
|
| 194 |
|
| 195 | for (p = 0; p < cnt; p++) {
|
| 196 | if (WARN_ON(!num_parents)) {
|
| 197 | |
| 198 | * If DT contains no parent interrupts, assume MIPS IRQ 2 (HW0) is
|
| 199 | * connected to the first output. This is the case for all known hardware.
|
| 200 |
|
| 201 | oirq.np = of_find_compatible_node(NULL, NULL,
|
| 202 | "mti,cpu-interrupt-controller");
|
| 203 | if (!oirq.np) {
|
| 204 | err = -EINVAL;
|
| 205 | goto err_out;
|
| 206 | }
|
| 207 |
|
| 208 | oirq.args_count = 1;
|
| 209 | oirq.args[0] = 2;
|
| 210 | parent_irq = irq_create_of_mapping(&oirq);
|
| 211 | of_node_put(oirq.np);
|
| 212 | } else {
|
| 213 | parent_irq = of_irq_get(node, p);
|
| 214 | }
|
| 215 |
|
| 216 | if (parent_irq <= 0) {
|
| 217 | err = parent_irq ? parent_irq : -ENODEV;
|
| 218 | goto err_out;
|
| 219 | }
|
| 220 |
|
| 221 | parent_data = irq_get_irq_data(parent_irq);
|
| 222 | if (!parent_data) {
|
| 223 | err = -EINVAL;
|
| 224 | goto err_out;
|
| 225 | }
|
| 226 |
|
| 227 | domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS,
|
| 228 | &irq_domain_ops, &output[p]);
|
| 229 | if (!domain) {
|
| 230 | err = -ENOMEM;
|
| 231 | goto err_out;
|
| 232 | }
|
| 233 |
|
| 234 | output[p].domain = domain;
|
| 235 | output[p].fwnode = of_fwnode_handle(node);
|
| 236 | output[p].index = p;
|
| 237 | output[p].parent_irq = parent_irq;
|
| 238 | output[p].parent_hwirq = irqd_to_hwirq(parent_data);
|
| 239 | irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, &output[p]);
|
| 240 | }
|
| 241 |
|
| 242 | return 0;
|
| 243 |
|
| 244 | err_out:
|
| 245 | while (p--) {
|
| 246 | irq_set_chained_handler_and_data(output[p].parent_irq, NULL, NULL);
|
| 247 | irq_domain_remove(output[p].domain);
|
| 248 | }
|
| 249 |
|
| 250 | kfree(output);
|
| 251 |
|
| 252 | return err;
|
| 253 | }
|
| 254 |
|
| 255 | static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
|
| 256 | {
|
| 257 | unsigned int cpu;
|
| 258 |
|
| 259 | for_each_present_cpu(cpu) {
|
| 260 | realtek_ictl_base[cpu] = of_iomap(node, cpu);
|
| 261 | if (!realtek_ictl_base[cpu])
|
| 262 | return -ENXIO;
|
| 263 |
|
| 264 |
|
| 265 | for (unsigned int hw_irq = 0; hw_irq < RTL_ICTL_NUM_INPUTS; hw_irq++) {
|
| 266 | disable_gimr(cpu, hw_irq);
|
| 267 | write_irr(cpu, hw_irq, 0);
|
| 268 | }
|
| 269 | }
|
| 270 |
|
| 271 | return realtek_setup_parents(node);
|
| 272 | }
|
| 273 |
|
| 274 | IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init);
|