Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip/riscv-intc: Introduce Andes hart-level interrupt controller

Add support for the Andes hart-level interrupt controller. This
controller provides interrupt mask/unmask functions to access the
custom register (SLIE) where the non-standard S-mode local interrupt
enable bits are located. The base of custom interrupt number is set
to 256.

To share the riscv_intc_domain_map() with the generic RISC-V INTC and
ACPI, add a chip parameter to riscv_intc_init_common(), so it can be
passed to the irq_domain_set_info() as a private data.

Andes hart-level interrupt controller requires the "andestech,cpu-intc"
compatible string to be present in interrupt-controller of cpu node to
enable the use of custom local interrupt source.
e.g.,

cpu0: cpu@0 {
compatible = "andestech,ax45mp", "riscv";
...
cpu0-intc: interrupt-controller {
#interrupt-cells = <0x01>;
compatible = "andestech,cpu-intc", "riscv,cpu-intc";
interrupt-controller;
};
};

Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Randolph <randolph@andestech.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20240222083946.3977135-4-peterlin@andestech.com

authored by

Yu Chien Peter Lin and committed by
Thomas Gleixner
f4cc33e7 96303bcb

+69 -7
+51 -7
drivers/irqchip/irq-riscv-intc.c
··· 17 17 #include <linux/module.h> 18 18 #include <linux/of.h> 19 19 #include <linux/smp.h> 20 + #include <linux/soc/andes/irq.h> 20 21 21 22 static struct irq_domain *intc_domain; 22 23 static unsigned int riscv_intc_nr_irqs __ro_after_init = BITS_PER_LONG; ··· 49 48 csr_set(CSR_IE, BIT(d->hwirq)); 50 49 } 51 50 51 + static void andes_intc_irq_mask(struct irq_data *d) 52 + { 53 + /* 54 + * Andes specific S-mode local interrupt causes (hwirq) 55 + * are defined as (256 + n) and controlled by n-th bit 56 + * of SLIE. 57 + */ 58 + unsigned int mask = BIT(d->hwirq % BITS_PER_LONG); 59 + 60 + if (d->hwirq < ANDES_SLI_CAUSE_BASE) 61 + csr_clear(CSR_IE, mask); 62 + else 63 + csr_clear(ANDES_CSR_SLIE, mask); 64 + } 65 + 66 + static void andes_intc_irq_unmask(struct irq_data *d) 67 + { 68 + unsigned int mask = BIT(d->hwirq % BITS_PER_LONG); 69 + 70 + if (d->hwirq < ANDES_SLI_CAUSE_BASE) 71 + csr_set(CSR_IE, mask); 72 + else 73 + csr_set(ANDES_CSR_SLIE, mask); 74 + } 75 + 52 76 static void riscv_intc_irq_eoi(struct irq_data *d) 53 77 { 54 78 /* ··· 97 71 .irq_eoi = riscv_intc_irq_eoi, 98 72 }; 99 73 74 + static struct irq_chip andes_intc_chip = { 75 + .name = "RISC-V INTC", 76 + .irq_mask = andes_intc_irq_mask, 77 + .irq_unmask = andes_intc_irq_unmask, 78 + .irq_eoi = riscv_intc_irq_eoi, 79 + }; 80 + 100 81 static int riscv_intc_domain_map(struct irq_domain *d, unsigned int irq, 101 82 irq_hw_number_t hwirq) 102 83 { 84 + struct irq_chip *chip = d->host_data; 85 + 103 86 irq_set_percpu_devid(irq); 104 - irq_domain_set_info(d, irq, hwirq, &riscv_intc_chip, d->host_data, 105 - handle_percpu_devid_irq, NULL, NULL); 87 + irq_domain_set_info(d, irq, hwirq, chip, NULL, handle_percpu_devid_irq, 88 + NULL, NULL); 106 89 107 90 return 0; 108 91 } ··· 157 122 return intc_domain->fwnode; 158 123 } 159 124 160 - static int __init riscv_intc_init_common(struct fwnode_handle *fn) 125 + static int __init riscv_intc_init_common(struct fwnode_handle *fn, 126 + struct irq_chip *chip) 161 127 { 162 128 int rc; 163 129 164 - intc_domain = irq_domain_create_tree(fn, &riscv_intc_domain_ops, NULL); 130 + intc_domain = irq_domain_create_tree(fn, &riscv_intc_domain_ops, chip); 165 131 if (!intc_domain) { 166 132 pr_err("unable to add IRQ domain\n"); 167 133 return -ENXIO; ··· 188 152 static int __init riscv_intc_init(struct device_node *node, 189 153 struct device_node *parent) 190 154 { 191 - int rc; 155 + struct irq_chip *chip = &riscv_intc_chip; 192 156 unsigned long hartid; 157 + int rc; 193 158 194 159 rc = riscv_of_parent_hartid(node, &hartid); 195 160 if (rc < 0) { ··· 215 178 return 0; 216 179 } 217 180 218 - return riscv_intc_init_common(of_node_to_fwnode(node)); 181 + if (of_device_is_compatible(node, "andestech,cpu-intc")) { 182 + riscv_intc_custom_base = ANDES_SLI_CAUSE_BASE; 183 + riscv_intc_custom_nr_irqs = ANDES_RV_IRQ_LAST; 184 + chip = &andes_intc_chip; 185 + } 186 + 187 + return riscv_intc_init_common(of_node_to_fwnode(node), chip); 219 188 } 220 189 221 190 IRQCHIP_DECLARE(riscv, "riscv,cpu-intc", riscv_intc_init); 191 + IRQCHIP_DECLARE(andes, "andestech,cpu-intc", riscv_intc_init); 222 192 223 193 #ifdef CONFIG_ACPI 224 194 ··· 252 208 return -ENOMEM; 253 209 } 254 210 255 - return riscv_intc_init_common(fn); 211 + return riscv_intc_init_common(fn, &riscv_intc_chip); 256 212 } 257 213 258 214 IRQCHIP_ACPI_DECLARE(riscv_intc, ACPI_MADT_TYPE_RINTC, NULL,
+18
include/linux/soc/andes/irq.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2023 Andes Technology Corporation 4 + */ 5 + #ifndef __ANDES_IRQ_H 6 + #define __ANDES_IRQ_H 7 + 8 + /* Andes PMU irq number */ 9 + #define ANDES_RV_IRQ_PMOVI 18 10 + #define ANDES_RV_IRQ_LAST ANDES_RV_IRQ_PMOVI 11 + #define ANDES_SLI_CAUSE_BASE 256 12 + 13 + /* Andes PMU related registers */ 14 + #define ANDES_CSR_SLIE 0x9c4 15 + #define ANDES_CSR_SLIP 0x9c5 16 + #define ANDES_CSR_SCOUNTEROF 0x9d4 17 + 18 + #endif /* __ANDES_IRQ_H */