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