.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * linux/arch/arm/mach-mmp/irq.c |
---|
3 | 4 | * |
---|
.. | .. |
---|
6 | 7 | * |
---|
7 | 8 | * Author: Bin Yang <bin.yang@marvell.com> |
---|
8 | 9 | * Haojian Zhuang <haojian.zhuang@gmail.com> |
---|
9 | | - * |
---|
10 | | - * This program is free software; you can redistribute it and/or modify |
---|
11 | | - * it under the terms of the GNU General Public License version 2 as |
---|
12 | | - * published by the Free Software Foundation. |
---|
13 | 10 | */ |
---|
14 | 11 | |
---|
15 | 12 | #include <linux/module.h> |
---|
16 | 13 | #include <linux/init.h> |
---|
17 | 14 | #include <linux/irq.h> |
---|
18 | 15 | #include <linux/irqchip.h> |
---|
| 16 | +#include <linux/irqchip/chained_irq.h> |
---|
19 | 17 | #include <linux/irqdomain.h> |
---|
20 | 18 | #include <linux/io.h> |
---|
21 | 19 | #include <linux/ioport.h> |
---|
.. | .. |
---|
46 | 44 | unsigned int conf_enable; |
---|
47 | 45 | unsigned int conf_disable; |
---|
48 | 46 | unsigned int conf_mask; |
---|
| 47 | + unsigned int conf2_mask; |
---|
49 | 48 | unsigned int clr_mfp_irq_base; |
---|
50 | 49 | unsigned int clr_mfp_hwirq; |
---|
51 | 50 | struct irq_domain *domain; |
---|
.. | .. |
---|
55 | 54 | unsigned int conf_enable; |
---|
56 | 55 | unsigned int conf_disable; |
---|
57 | 56 | unsigned int conf_mask; |
---|
| 57 | + unsigned int conf2_mask; |
---|
58 | 58 | }; |
---|
59 | 59 | |
---|
60 | 60 | static void __iomem *mmp_icu_base; |
---|
| 61 | +static void __iomem *mmp_icu2_base; |
---|
61 | 62 | static struct icu_chip_data icu_data[MAX_ICU_NR]; |
---|
62 | 63 | static int max_icu_nr; |
---|
63 | 64 | |
---|
.. | .. |
---|
100 | 101 | r &= ~data->conf_mask; |
---|
101 | 102 | r |= data->conf_disable; |
---|
102 | 103 | writel_relaxed(r, mmp_icu_base + (hwirq << 2)); |
---|
| 104 | + |
---|
| 105 | + if (data->conf2_mask) { |
---|
| 106 | + /* |
---|
| 107 | + * ICU1 (above) only controls PJ4 MP1; if using SMP, |
---|
| 108 | + * we need to also mask the MP2 and MM cores via ICU2. |
---|
| 109 | + */ |
---|
| 110 | + r = readl_relaxed(mmp_icu2_base + (hwirq << 2)); |
---|
| 111 | + r &= ~data->conf2_mask; |
---|
| 112 | + writel_relaxed(r, mmp_icu2_base + (hwirq << 2)); |
---|
| 113 | + } |
---|
103 | 114 | } else { |
---|
104 | 115 | r = readl_relaxed(data->reg_mask) | (1 << hwirq); |
---|
105 | 116 | writel_relaxed(r, data->reg_mask); |
---|
.. | .. |
---|
135 | 146 | static void icu_mux_irq_demux(struct irq_desc *desc) |
---|
136 | 147 | { |
---|
137 | 148 | unsigned int irq = irq_desc_get_irq(desc); |
---|
| 149 | + struct irq_chip *chip = irq_desc_get_chip(desc); |
---|
138 | 150 | struct irq_domain *domain; |
---|
139 | 151 | struct icu_chip_data *data; |
---|
140 | 152 | int i; |
---|
141 | 153 | unsigned long mask, status, n; |
---|
| 154 | + |
---|
| 155 | + chained_irq_enter(chip, desc); |
---|
142 | 156 | |
---|
143 | 157 | for (i = 1; i < max_icu_nr; i++) { |
---|
144 | 158 | if (irq == icu_data[i].cascade_irq) { |
---|
.. | .. |
---|
149 | 163 | } |
---|
150 | 164 | if (i >= max_icu_nr) { |
---|
151 | 165 | pr_err("Spurious irq %d in MMP INTC\n", irq); |
---|
152 | | - return; |
---|
| 166 | + goto out; |
---|
153 | 167 | } |
---|
154 | 168 | |
---|
155 | 169 | mask = readl_relaxed(data->reg_mask); |
---|
.. | .. |
---|
161 | 175 | generic_handle_irq(icu_data[i].virq_base + n); |
---|
162 | 176 | } |
---|
163 | 177 | } |
---|
| 178 | + |
---|
| 179 | +out: |
---|
| 180 | + chained_irq_exit(chip, desc); |
---|
164 | 181 | } |
---|
165 | 182 | |
---|
166 | 183 | static int mmp_irq_domain_map(struct irq_domain *d, unsigned int irq, |
---|
.. | .. |
---|
179 | 196 | return 0; |
---|
180 | 197 | } |
---|
181 | 198 | |
---|
182 | | -const struct irq_domain_ops mmp_irq_domain_ops = { |
---|
| 199 | +static const struct irq_domain_ops mmp_irq_domain_ops = { |
---|
183 | 200 | .map = mmp_irq_domain_map, |
---|
184 | 201 | .xlate = mmp_irq_domain_xlate, |
---|
185 | 202 | }; |
---|
.. | .. |
---|
195 | 212 | .conf_disable = 0x0, |
---|
196 | 213 | .conf_mask = MMP2_ICU_INT_ROUTE_PJ4_IRQ | |
---|
197 | 214 | MMP2_ICU_INT_ROUTE_PJ4_FIQ, |
---|
| 215 | +}; |
---|
| 216 | + |
---|
| 217 | +static struct mmp_intc_conf mmp3_conf = { |
---|
| 218 | + .conf_enable = 0x20, |
---|
| 219 | + .conf_disable = 0x0, |
---|
| 220 | + .conf_mask = MMP2_ICU_INT_ROUTE_PJ4_IRQ | |
---|
| 221 | + MMP2_ICU_INT_ROUTE_PJ4_FIQ, |
---|
| 222 | + .conf2_mask = 0xf0, |
---|
198 | 223 | }; |
---|
199 | 224 | |
---|
200 | 225 | static void __exception_irq_entry mmp_handle_irq(struct pt_regs *regs) |
---|
.. | .. |
---|
398 | 423 | icu_data[0].conf_enable = mmp_conf.conf_enable; |
---|
399 | 424 | icu_data[0].conf_disable = mmp_conf.conf_disable; |
---|
400 | 425 | icu_data[0].conf_mask = mmp_conf.conf_mask; |
---|
401 | | - irq_set_default_host(icu_data[0].domain); |
---|
402 | 426 | set_handle_irq(mmp_handle_irq); |
---|
403 | 427 | max_icu_nr = 1; |
---|
404 | 428 | return 0; |
---|
.. | .. |
---|
417 | 441 | icu_data[0].conf_enable = mmp2_conf.conf_enable; |
---|
418 | 442 | icu_data[0].conf_disable = mmp2_conf.conf_disable; |
---|
419 | 443 | icu_data[0].conf_mask = mmp2_conf.conf_mask; |
---|
420 | | - irq_set_default_host(icu_data[0].domain); |
---|
421 | 444 | set_handle_irq(mmp2_handle_irq); |
---|
422 | 445 | max_icu_nr = 1; |
---|
423 | 446 | return 0; |
---|
424 | 447 | } |
---|
425 | 448 | IRQCHIP_DECLARE(mmp2_intc, "mrvl,mmp2-intc", mmp2_of_init); |
---|
426 | 449 | |
---|
| 450 | +static int __init mmp3_of_init(struct device_node *node, |
---|
| 451 | + struct device_node *parent) |
---|
| 452 | +{ |
---|
| 453 | + int ret; |
---|
| 454 | + |
---|
| 455 | + mmp_icu2_base = of_iomap(node, 1); |
---|
| 456 | + if (!mmp_icu2_base) { |
---|
| 457 | + pr_err("Failed to get interrupt controller register #2\n"); |
---|
| 458 | + return -ENODEV; |
---|
| 459 | + } |
---|
| 460 | + |
---|
| 461 | + ret = mmp_init_bases(node); |
---|
| 462 | + if (ret < 0) { |
---|
| 463 | + iounmap(mmp_icu2_base); |
---|
| 464 | + return ret; |
---|
| 465 | + } |
---|
| 466 | + |
---|
| 467 | + icu_data[0].conf_enable = mmp3_conf.conf_enable; |
---|
| 468 | + icu_data[0].conf_disable = mmp3_conf.conf_disable; |
---|
| 469 | + icu_data[0].conf_mask = mmp3_conf.conf_mask; |
---|
| 470 | + icu_data[0].conf2_mask = mmp3_conf.conf2_mask; |
---|
| 471 | + |
---|
| 472 | + if (!parent) { |
---|
| 473 | + /* This is the main interrupt controller. */ |
---|
| 474 | + set_handle_irq(mmp2_handle_irq); |
---|
| 475 | + } |
---|
| 476 | + |
---|
| 477 | + max_icu_nr = 1; |
---|
| 478 | + return 0; |
---|
| 479 | +} |
---|
| 480 | +IRQCHIP_DECLARE(mmp3_intc, "marvell,mmp3-intc", mmp3_of_init); |
---|
| 481 | + |
---|
427 | 482 | static int __init mmp2_mux_of_init(struct device_node *node, |
---|
428 | 483 | struct device_node *parent) |
---|
429 | 484 | { |
---|
430 | | - struct resource res; |
---|
431 | 485 | int i, ret, irq, j = 0; |
---|
432 | 486 | u32 nr_irqs, mfp_irq; |
---|
| 487 | + u32 reg[4]; |
---|
433 | 488 | |
---|
434 | 489 | if (!parent) |
---|
435 | 490 | return -ENODEV; |
---|
.. | .. |
---|
441 | 496 | pr_err("Not found mrvl,intc-nr-irqs property\n"); |
---|
442 | 497 | return -EINVAL; |
---|
443 | 498 | } |
---|
444 | | - ret = of_address_to_resource(node, 0, &res); |
---|
| 499 | + |
---|
| 500 | + /* |
---|
| 501 | + * For historical reasons, the "regs" property of the |
---|
| 502 | + * mrvl,mmp2-mux-intc is not a regular "regs" property containing |
---|
| 503 | + * addresses on the parent bus, but offsets from the intc's base. |
---|
| 504 | + * That is why we can't use of_address_to_resource() here. |
---|
| 505 | + */ |
---|
| 506 | + ret = of_property_read_variable_u32_array(node, "reg", reg, |
---|
| 507 | + ARRAY_SIZE(reg), |
---|
| 508 | + ARRAY_SIZE(reg)); |
---|
445 | 509 | if (ret < 0) { |
---|
446 | 510 | pr_err("Not found reg property\n"); |
---|
447 | 511 | return -EINVAL; |
---|
448 | 512 | } |
---|
449 | | - icu_data[i].reg_status = mmp_icu_base + res.start; |
---|
450 | | - ret = of_address_to_resource(node, 1, &res); |
---|
451 | | - if (ret < 0) { |
---|
452 | | - pr_err("Not found reg property\n"); |
---|
453 | | - return -EINVAL; |
---|
454 | | - } |
---|
455 | | - icu_data[i].reg_mask = mmp_icu_base + res.start; |
---|
| 513 | + icu_data[i].reg_status = mmp_icu_base + reg[0]; |
---|
| 514 | + icu_data[i].reg_mask = mmp_icu_base + reg[2]; |
---|
456 | 515 | icu_data[i].cascade_irq = irq_of_parse_and_map(node, 0); |
---|
457 | 516 | if (!icu_data[i].cascade_irq) |
---|
458 | 517 | return -EINVAL; |
---|