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

irqchip: Add RISC-V advanced PLIC driver for direct-mode

The RISC-V advanced interrupt architecture (AIA) specification defines
advanced platform-level interrupt controller (APLIC) which has two modes
of operation: 1) Direct mode and 2) MSI mode.
(For more details, refer https://github.com/riscv/riscv-aia)

In APLIC direct-mode, wired interrupts are forwared to CPUs (or HARTs)
as a local external interrupt.

Add a platform irqchip driver for the RISC-V APLIC direct-mode to
support RISC-V platforms having only wired interrupts.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Björn Töpel <bjorn@rivosinc.com>
Reviewed-by: Björn Töpel <bjorn@rivosinc.com>
Link: https://lore.kernel.org/r/20240307140307.646078-7-apatel@ventanamicro.com

authored by

Anup Patel and committed by
Thomas Gleixner
2333df5a 3b806a5a

+732
+5
drivers/irqchip/Kconfig
··· 540 540 depends on RISCV 541 541 select IRQ_DOMAIN_HIERARCHY 542 542 543 + config RISCV_APLIC 544 + bool 545 + depends on RISCV 546 + select IRQ_DOMAIN_HIERARCHY 547 + 543 548 config RISCV_IMSIC 544 549 bool 545 550 depends on RISCV
+1
drivers/irqchip/Makefile
··· 95 95 obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o 96 96 obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o 97 97 obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o 98 + obj-$(CONFIG_RISCV_APLIC) += irq-riscv-aplic-main.o irq-riscv-aplic-direct.o 98 99 obj-$(CONFIG_RISCV_IMSIC) += irq-riscv-imsic-state.o irq-riscv-imsic-early.o irq-riscv-imsic-platform.o 99 100 obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o 100 101 obj-$(CONFIG_STARFIVE_JH8100_INTC) += irq-starfive-jh8100-intc.o
+326
drivers/irqchip/irq-riscv-aplic-direct.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2021 Western Digital Corporation or its affiliates. 4 + * Copyright (C) 2022 Ventana Micro Systems Inc. 5 + */ 6 + 7 + #include <linux/bitfield.h> 8 + #include <linux/bitops.h> 9 + #include <linux/cpu.h> 10 + #include <linux/interrupt.h> 11 + #include <linux/irqchip.h> 12 + #include <linux/irqchip/chained_irq.h> 13 + #include <linux/irqchip/riscv-aplic.h> 14 + #include <linux/module.h> 15 + #include <linux/of_address.h> 16 + #include <linux/printk.h> 17 + #include <linux/smp.h> 18 + 19 + #include "irq-riscv-aplic-main.h" 20 + 21 + #define APLIC_DISABLE_IDELIVERY 0 22 + #define APLIC_ENABLE_IDELIVERY 1 23 + #define APLIC_DISABLE_ITHRESHOLD 1 24 + #define APLIC_ENABLE_ITHRESHOLD 0 25 + 26 + struct aplic_direct { 27 + struct aplic_priv priv; 28 + struct irq_domain *irqdomain; 29 + struct cpumask lmask; 30 + }; 31 + 32 + struct aplic_idc { 33 + unsigned int hart_index; 34 + void __iomem *regs; 35 + struct aplic_direct *direct; 36 + }; 37 + 38 + static unsigned int aplic_direct_parent_irq; 39 + static DEFINE_PER_CPU(struct aplic_idc, aplic_idcs); 40 + 41 + static void aplic_direct_irq_eoi(struct irq_data *d) 42 + { 43 + /* 44 + * The fasteoi_handler requires irq_eoi() callback hence 45 + * provide a dummy handler. 46 + */ 47 + } 48 + 49 + #ifdef CONFIG_SMP 50 + static int aplic_direct_set_affinity(struct irq_data *d, const struct cpumask *mask_val, 51 + bool force) 52 + { 53 + struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 54 + struct aplic_direct *direct = container_of(priv, struct aplic_direct, priv); 55 + struct aplic_idc *idc; 56 + unsigned int cpu, val; 57 + struct cpumask amask; 58 + void __iomem *target; 59 + 60 + cpumask_and(&amask, &direct->lmask, mask_val); 61 + 62 + if (force) 63 + cpu = cpumask_first(&amask); 64 + else 65 + cpu = cpumask_any_and(&amask, cpu_online_mask); 66 + 67 + if (cpu >= nr_cpu_ids) 68 + return -EINVAL; 69 + 70 + idc = per_cpu_ptr(&aplic_idcs, cpu); 71 + target = priv->regs + APLIC_TARGET_BASE + (d->hwirq - 1) * sizeof(u32); 72 + val = FIELD_PREP(APLIC_TARGET_HART_IDX, idc->hart_index); 73 + val |= FIELD_PREP(APLIC_TARGET_IPRIO, APLIC_DEFAULT_PRIORITY); 74 + writel(val, target); 75 + 76 + irq_data_update_effective_affinity(d, cpumask_of(cpu)); 77 + 78 + return IRQ_SET_MASK_OK_DONE; 79 + } 80 + #endif 81 + 82 + static struct irq_chip aplic_direct_chip = { 83 + .name = "APLIC-DIRECT", 84 + .irq_mask = aplic_irq_mask, 85 + .irq_unmask = aplic_irq_unmask, 86 + .irq_set_type = aplic_irq_set_type, 87 + .irq_eoi = aplic_direct_irq_eoi, 88 + #ifdef CONFIG_SMP 89 + .irq_set_affinity = aplic_direct_set_affinity, 90 + #endif 91 + .flags = IRQCHIP_SET_TYPE_MASKED | 92 + IRQCHIP_SKIP_SET_WAKE | 93 + IRQCHIP_MASK_ON_SUSPEND, 94 + }; 95 + 96 + static int aplic_direct_irqdomain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, 97 + unsigned long *hwirq, unsigned int *type) 98 + { 99 + struct aplic_priv *priv = d->host_data; 100 + 101 + return aplic_irqdomain_translate(fwspec, priv->gsi_base, hwirq, type); 102 + } 103 + 104 + static int aplic_direct_irqdomain_alloc(struct irq_domain *domain, unsigned int virq, 105 + unsigned int nr_irqs, void *arg) 106 + { 107 + struct aplic_priv *priv = domain->host_data; 108 + struct aplic_direct *direct = container_of(priv, struct aplic_direct, priv); 109 + struct irq_fwspec *fwspec = arg; 110 + irq_hw_number_t hwirq; 111 + unsigned int type; 112 + int i, ret; 113 + 114 + ret = aplic_irqdomain_translate(fwspec, priv->gsi_base, &hwirq, &type); 115 + if (ret) 116 + return ret; 117 + 118 + for (i = 0; i < nr_irqs; i++) { 119 + irq_domain_set_info(domain, virq + i, hwirq + i, &aplic_direct_chip, 120 + priv, handle_fasteoi_irq, NULL, NULL); 121 + irq_set_affinity(virq + i, &direct->lmask); 122 + } 123 + 124 + return 0; 125 + } 126 + 127 + static const struct irq_domain_ops aplic_direct_irqdomain_ops = { 128 + .translate = aplic_direct_irqdomain_translate, 129 + .alloc = aplic_direct_irqdomain_alloc, 130 + .free = irq_domain_free_irqs_top, 131 + }; 132 + 133 + /* 134 + * To handle an APLIC direct interrupts, we just read the CLAIMI register 135 + * which will return highest priority pending interrupt and clear the 136 + * pending bit of the interrupt. This process is repeated until CLAIMI 137 + * register return zero value. 138 + */ 139 + static void aplic_direct_handle_irq(struct irq_desc *desc) 140 + { 141 + struct aplic_idc *idc = this_cpu_ptr(&aplic_idcs); 142 + struct irq_domain *irqdomain = idc->direct->irqdomain; 143 + struct irq_chip *chip = irq_desc_get_chip(desc); 144 + irq_hw_number_t hw_irq; 145 + int irq; 146 + 147 + chained_irq_enter(chip, desc); 148 + 149 + while ((hw_irq = readl(idc->regs + APLIC_IDC_CLAIMI))) { 150 + hw_irq = hw_irq >> APLIC_IDC_TOPI_ID_SHIFT; 151 + irq = irq_find_mapping(irqdomain, hw_irq); 152 + 153 + if (unlikely(irq <= 0)) { 154 + dev_warn_ratelimited(idc->direct->priv.dev, 155 + "hw_irq %lu mapping not found\n", hw_irq); 156 + } else { 157 + generic_handle_irq(irq); 158 + } 159 + } 160 + 161 + chained_irq_exit(chip, desc); 162 + } 163 + 164 + static void aplic_idc_set_delivery(struct aplic_idc *idc, bool en) 165 + { 166 + u32 de = (en) ? APLIC_ENABLE_IDELIVERY : APLIC_DISABLE_IDELIVERY; 167 + u32 th = (en) ? APLIC_ENABLE_ITHRESHOLD : APLIC_DISABLE_ITHRESHOLD; 168 + 169 + /* Priority must be less than threshold for interrupt triggering */ 170 + writel(th, idc->regs + APLIC_IDC_ITHRESHOLD); 171 + 172 + /* Delivery must be set to 1 for interrupt triggering */ 173 + writel(de, idc->regs + APLIC_IDC_IDELIVERY); 174 + } 175 + 176 + static int aplic_direct_dying_cpu(unsigned int cpu) 177 + { 178 + if (aplic_direct_parent_irq) 179 + disable_percpu_irq(aplic_direct_parent_irq); 180 + 181 + return 0; 182 + } 183 + 184 + static int aplic_direct_starting_cpu(unsigned int cpu) 185 + { 186 + if (aplic_direct_parent_irq) { 187 + enable_percpu_irq(aplic_direct_parent_irq, 188 + irq_get_trigger_type(aplic_direct_parent_irq)); 189 + } 190 + 191 + return 0; 192 + } 193 + 194 + static int aplic_direct_parse_parent_hwirq(struct device *dev, u32 index, 195 + u32 *parent_hwirq, unsigned long *parent_hartid) 196 + { 197 + struct of_phandle_args parent; 198 + int rc; 199 + 200 + /* 201 + * Currently, only OF fwnode is supported so extend this 202 + * function for ACPI support. 203 + */ 204 + if (!is_of_node(dev->fwnode)) 205 + return -EINVAL; 206 + 207 + rc = of_irq_parse_one(to_of_node(dev->fwnode), index, &parent); 208 + if (rc) 209 + return rc; 210 + 211 + rc = riscv_of_parent_hartid(parent.np, parent_hartid); 212 + if (rc) 213 + return rc; 214 + 215 + *parent_hwirq = parent.args[0]; 216 + return 0; 217 + } 218 + 219 + int aplic_direct_setup(struct device *dev, void __iomem *regs) 220 + { 221 + int i, j, rc, cpu, current_cpu, setup_count = 0; 222 + struct aplic_direct *direct; 223 + struct irq_domain *domain; 224 + struct aplic_priv *priv; 225 + struct aplic_idc *idc; 226 + unsigned long hartid; 227 + u32 v, hwirq; 228 + 229 + direct = devm_kzalloc(dev, sizeof(*direct), GFP_KERNEL); 230 + if (!direct) 231 + return -ENOMEM; 232 + priv = &direct->priv; 233 + 234 + rc = aplic_setup_priv(priv, dev, regs); 235 + if (rc) { 236 + dev_err(dev, "failed to create APLIC context\n"); 237 + return rc; 238 + } 239 + 240 + /* Setup per-CPU IDC and target CPU mask */ 241 + current_cpu = get_cpu(); 242 + for (i = 0; i < priv->nr_idcs; i++) { 243 + rc = aplic_direct_parse_parent_hwirq(dev, i, &hwirq, &hartid); 244 + if (rc) { 245 + dev_warn(dev, "parent irq for IDC%d not found\n", i); 246 + continue; 247 + } 248 + 249 + /* 250 + * Skip interrupts other than external interrupts for 251 + * current privilege level. 252 + */ 253 + if (hwirq != RV_IRQ_EXT) 254 + continue; 255 + 256 + cpu = riscv_hartid_to_cpuid(hartid); 257 + if (cpu < 0) { 258 + dev_warn(dev, "invalid cpuid for IDC%d\n", i); 259 + continue; 260 + } 261 + 262 + cpumask_set_cpu(cpu, &direct->lmask); 263 + 264 + idc = per_cpu_ptr(&aplic_idcs, cpu); 265 + idc->hart_index = i; 266 + idc->regs = priv->regs + APLIC_IDC_BASE + i * APLIC_IDC_SIZE; 267 + idc->direct = direct; 268 + 269 + aplic_idc_set_delivery(idc, true); 270 + 271 + /* 272 + * Boot cpu might not have APLIC hart_index = 0 so check 273 + * and update target registers of all interrupts. 274 + */ 275 + if (cpu == current_cpu && idc->hart_index) { 276 + v = FIELD_PREP(APLIC_TARGET_HART_IDX, idc->hart_index); 277 + v |= FIELD_PREP(APLIC_TARGET_IPRIO, APLIC_DEFAULT_PRIORITY); 278 + for (j = 1; j <= priv->nr_irqs; j++) 279 + writel(v, priv->regs + APLIC_TARGET_BASE + (j - 1) * sizeof(u32)); 280 + } 281 + 282 + setup_count++; 283 + } 284 + put_cpu(); 285 + 286 + /* Find parent domain and register chained handler */ 287 + domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), 288 + DOMAIN_BUS_ANY); 289 + if (!aplic_direct_parent_irq && domain) { 290 + aplic_direct_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); 291 + if (aplic_direct_parent_irq) { 292 + irq_set_chained_handler(aplic_direct_parent_irq, 293 + aplic_direct_handle_irq); 294 + 295 + /* 296 + * Setup CPUHP notifier to enable parent 297 + * interrupt on all CPUs 298 + */ 299 + cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, 300 + "irqchip/riscv/aplic:starting", 301 + aplic_direct_starting_cpu, 302 + aplic_direct_dying_cpu); 303 + } 304 + } 305 + 306 + /* Fail if we were not able to setup IDC for any CPU */ 307 + if (!setup_count) 308 + return -ENODEV; 309 + 310 + /* Setup global config and interrupt delivery */ 311 + aplic_init_hw_global(priv, false); 312 + 313 + /* Create irq domain instance for the APLIC */ 314 + direct->irqdomain = irq_domain_create_linear(dev->fwnode, priv->nr_irqs + 1, 315 + &aplic_direct_irqdomain_ops, priv); 316 + if (!direct->irqdomain) { 317 + dev_err(dev, "failed to create direct irq domain\n"); 318 + return -ENOMEM; 319 + } 320 + 321 + /* Advertise the interrupt controller */ 322 + dev_info(dev, "%d interrupts directly connected to %d CPUs\n", 323 + priv->nr_irqs, priv->nr_idcs); 324 + 325 + return 0; 326 + }
+211
drivers/irqchip/irq-riscv-aplic-main.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2021 Western Digital Corporation or its affiliates. 4 + * Copyright (C) 2022 Ventana Micro Systems Inc. 5 + */ 6 + 7 + #include <linux/bitfield.h> 8 + #include <linux/irqchip/riscv-aplic.h> 9 + #include <linux/module.h> 10 + #include <linux/of.h> 11 + #include <linux/of_irq.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/printk.h> 14 + 15 + #include "irq-riscv-aplic-main.h" 16 + 17 + void aplic_irq_unmask(struct irq_data *d) 18 + { 19 + struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 20 + 21 + writel(d->hwirq, priv->regs + APLIC_SETIENUM); 22 + } 23 + 24 + void aplic_irq_mask(struct irq_data *d) 25 + { 26 + struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 27 + 28 + writel(d->hwirq, priv->regs + APLIC_CLRIENUM); 29 + } 30 + 31 + int aplic_irq_set_type(struct irq_data *d, unsigned int type) 32 + { 33 + struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 34 + void __iomem *sourcecfg; 35 + u32 val = 0; 36 + 37 + switch (type) { 38 + case IRQ_TYPE_NONE: 39 + val = APLIC_SOURCECFG_SM_INACTIVE; 40 + break; 41 + case IRQ_TYPE_LEVEL_LOW: 42 + val = APLIC_SOURCECFG_SM_LEVEL_LOW; 43 + break; 44 + case IRQ_TYPE_LEVEL_HIGH: 45 + val = APLIC_SOURCECFG_SM_LEVEL_HIGH; 46 + break; 47 + case IRQ_TYPE_EDGE_FALLING: 48 + val = APLIC_SOURCECFG_SM_EDGE_FALL; 49 + break; 50 + case IRQ_TYPE_EDGE_RISING: 51 + val = APLIC_SOURCECFG_SM_EDGE_RISE; 52 + break; 53 + default: 54 + return -EINVAL; 55 + } 56 + 57 + sourcecfg = priv->regs + APLIC_SOURCECFG_BASE; 58 + sourcecfg += (d->hwirq - 1) * sizeof(u32); 59 + writel(val, sourcecfg); 60 + 61 + return 0; 62 + } 63 + 64 + int aplic_irqdomain_translate(struct irq_fwspec *fwspec, u32 gsi_base, 65 + unsigned long *hwirq, unsigned int *type) 66 + { 67 + if (WARN_ON(fwspec->param_count < 2)) 68 + return -EINVAL; 69 + if (WARN_ON(!fwspec->param[0])) 70 + return -EINVAL; 71 + 72 + /* For DT, gsi_base is always zero. */ 73 + *hwirq = fwspec->param[0] - gsi_base; 74 + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 75 + 76 + WARN_ON(*type == IRQ_TYPE_NONE); 77 + 78 + return 0; 79 + } 80 + 81 + void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) 82 + { 83 + u32 val; 84 + #ifdef CONFIG_RISCV_M_MODE 85 + u32 valh; 86 + 87 + if (msi_mode) { 88 + val = lower_32_bits(priv->msicfg.base_ppn); 89 + valh = FIELD_PREP(APLIC_xMSICFGADDRH_BAPPN, upper_32_bits(priv->msicfg.base_ppn)); 90 + valh |= FIELD_PREP(APLIC_xMSICFGADDRH_LHXW, priv->msicfg.lhxw); 91 + valh |= FIELD_PREP(APLIC_xMSICFGADDRH_HHXW, priv->msicfg.hhxw); 92 + valh |= FIELD_PREP(APLIC_xMSICFGADDRH_LHXS, priv->msicfg.lhxs); 93 + valh |= FIELD_PREP(APLIC_xMSICFGADDRH_HHXS, priv->msicfg.hhxs); 94 + writel(val, priv->regs + APLIC_xMSICFGADDR); 95 + writel(valh, priv->regs + APLIC_xMSICFGADDRH); 96 + } 97 + #endif 98 + 99 + /* Setup APLIC domaincfg register */ 100 + val = readl(priv->regs + APLIC_DOMAINCFG); 101 + val |= APLIC_DOMAINCFG_IE; 102 + if (msi_mode) 103 + val |= APLIC_DOMAINCFG_DM; 104 + writel(val, priv->regs + APLIC_DOMAINCFG); 105 + if (readl(priv->regs + APLIC_DOMAINCFG) != val) 106 + dev_warn(priv->dev, "unable to write 0x%x in domaincfg\n", val); 107 + } 108 + 109 + static void aplic_init_hw_irqs(struct aplic_priv *priv) 110 + { 111 + int i; 112 + 113 + /* Disable all interrupts */ 114 + for (i = 0; i <= priv->nr_irqs; i += 32) 115 + writel(-1U, priv->regs + APLIC_CLRIE_BASE + (i / 32) * sizeof(u32)); 116 + 117 + /* Set interrupt type and default priority for all interrupts */ 118 + for (i = 1; i <= priv->nr_irqs; i++) { 119 + writel(0, priv->regs + APLIC_SOURCECFG_BASE + (i - 1) * sizeof(u32)); 120 + writel(APLIC_DEFAULT_PRIORITY, 121 + priv->regs + APLIC_TARGET_BASE + (i - 1) * sizeof(u32)); 122 + } 123 + 124 + /* Clear APLIC domaincfg */ 125 + writel(0, priv->regs + APLIC_DOMAINCFG); 126 + } 127 + 128 + int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *regs) 129 + { 130 + struct of_phandle_args parent; 131 + int rc; 132 + 133 + /* 134 + * Currently, only OF fwnode is supported so extend this 135 + * function for ACPI support. 136 + */ 137 + if (!is_of_node(dev->fwnode)) 138 + return -EINVAL; 139 + 140 + /* Save device pointer and register base */ 141 + priv->dev = dev; 142 + priv->regs = regs; 143 + 144 + /* Find out number of interrupt sources */ 145 + rc = of_property_read_u32(to_of_node(dev->fwnode), "riscv,num-sources", 146 + &priv->nr_irqs); 147 + if (rc) { 148 + dev_err(dev, "failed to get number of interrupt sources\n"); 149 + return rc; 150 + } 151 + 152 + /* 153 + * Find out number of IDCs based on parent interrupts 154 + * 155 + * If "msi-parent" property is present then we ignore the 156 + * APLIC IDCs which forces the APLIC driver to use MSI mode. 157 + */ 158 + if (!of_property_present(to_of_node(dev->fwnode), "msi-parent")) { 159 + while (!of_irq_parse_one(to_of_node(dev->fwnode), priv->nr_idcs, &parent)) 160 + priv->nr_idcs++; 161 + } 162 + 163 + /* Setup initial state APLIC interrupts */ 164 + aplic_init_hw_irqs(priv); 165 + 166 + return 0; 167 + } 168 + 169 + static int aplic_probe(struct platform_device *pdev) 170 + { 171 + struct device *dev = &pdev->dev; 172 + bool msi_mode = false; 173 + void __iomem *regs; 174 + int rc; 175 + 176 + /* Map the MMIO registers */ 177 + regs = devm_platform_ioremap_resource(pdev, 0); 178 + if (!regs) { 179 + dev_err(dev, "failed map MMIO registers\n"); 180 + return -ENOMEM; 181 + } 182 + 183 + /* 184 + * If msi-parent property is present then setup APLIC MSI 185 + * mode otherwise setup APLIC direct mode. 186 + */ 187 + if (is_of_node(dev->fwnode)) 188 + msi_mode = of_property_present(to_of_node(dev->fwnode), "msi-parent"); 189 + if (msi_mode) 190 + rc = -ENODEV; 191 + else 192 + rc = aplic_direct_setup(dev, regs); 193 + if (rc) 194 + dev_err(dev, "failed to setup APLIC in %s mode\n", msi_mode ? "MSI" : "direct"); 195 + 196 + return rc; 197 + } 198 + 199 + static const struct of_device_id aplic_match[] = { 200 + { .compatible = "riscv,aplic" }, 201 + {} 202 + }; 203 + 204 + static struct platform_driver aplic_driver = { 205 + .driver = { 206 + .name = "riscv-aplic", 207 + .of_match_table = aplic_match, 208 + }, 209 + .probe = aplic_probe, 210 + }; 211 + builtin_platform_driver(aplic_driver);
+44
drivers/irqchip/irq-riscv-aplic-main.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021 Western Digital Corporation or its affiliates. 4 + * Copyright (C) 2022 Ventana Micro Systems Inc. 5 + */ 6 + 7 + #ifndef _IRQ_RISCV_APLIC_MAIN_H 8 + #define _IRQ_RISCV_APLIC_MAIN_H 9 + 10 + #include <linux/device.h> 11 + #include <linux/io.h> 12 + #include <linux/irq.h> 13 + #include <linux/irqdomain.h> 14 + #include <linux/fwnode.h> 15 + 16 + #define APLIC_DEFAULT_PRIORITY 1 17 + 18 + struct aplic_msicfg { 19 + phys_addr_t base_ppn; 20 + u32 hhxs; 21 + u32 hhxw; 22 + u32 lhxs; 23 + u32 lhxw; 24 + }; 25 + 26 + struct aplic_priv { 27 + struct device *dev; 28 + u32 gsi_base; 29 + u32 nr_irqs; 30 + u32 nr_idcs; 31 + void __iomem *regs; 32 + struct aplic_msicfg msicfg; 33 + }; 34 + 35 + void aplic_irq_unmask(struct irq_data *d); 36 + void aplic_irq_mask(struct irq_data *d); 37 + int aplic_irq_set_type(struct irq_data *d, unsigned int type); 38 + int aplic_irqdomain_translate(struct irq_fwspec *fwspec, u32 gsi_base, 39 + unsigned long *hwirq, unsigned int *type); 40 + void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode); 41 + int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *regs); 42 + int aplic_direct_setup(struct device *dev, void __iomem *regs); 43 + 44 + #endif
+145
include/linux/irqchip/riscv-aplic.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021 Western Digital Corporation or its affiliates. 4 + * Copyright (C) 2022 Ventana Micro Systems Inc. 5 + */ 6 + #ifndef __LINUX_IRQCHIP_RISCV_APLIC_H 7 + #define __LINUX_IRQCHIP_RISCV_APLIC_H 8 + 9 + #include <linux/bitops.h> 10 + 11 + #define APLIC_MAX_IDC BIT(14) 12 + #define APLIC_MAX_SOURCE 1024 13 + 14 + #define APLIC_DOMAINCFG 0x0000 15 + #define APLIC_DOMAINCFG_RDONLY 0x80000000 16 + #define APLIC_DOMAINCFG_IE BIT(8) 17 + #define APLIC_DOMAINCFG_DM BIT(2) 18 + #define APLIC_DOMAINCFG_BE BIT(0) 19 + 20 + #define APLIC_SOURCECFG_BASE 0x0004 21 + #define APLIC_SOURCECFG_D BIT(10) 22 + #define APLIC_SOURCECFG_CHILDIDX_MASK 0x000003ff 23 + #define APLIC_SOURCECFG_SM_MASK 0x00000007 24 + #define APLIC_SOURCECFG_SM_INACTIVE 0x0 25 + #define APLIC_SOURCECFG_SM_DETACH 0x1 26 + #define APLIC_SOURCECFG_SM_EDGE_RISE 0x4 27 + #define APLIC_SOURCECFG_SM_EDGE_FALL 0x5 28 + #define APLIC_SOURCECFG_SM_LEVEL_HIGH 0x6 29 + #define APLIC_SOURCECFG_SM_LEVEL_LOW 0x7 30 + 31 + #define APLIC_MMSICFGADDR 0x1bc0 32 + #define APLIC_MMSICFGADDRH 0x1bc4 33 + #define APLIC_SMSICFGADDR 0x1bc8 34 + #define APLIC_SMSICFGADDRH 0x1bcc 35 + 36 + #ifdef CONFIG_RISCV_M_MODE 37 + #define APLIC_xMSICFGADDR APLIC_MMSICFGADDR 38 + #define APLIC_xMSICFGADDRH APLIC_MMSICFGADDRH 39 + #else 40 + #define APLIC_xMSICFGADDR APLIC_SMSICFGADDR 41 + #define APLIC_xMSICFGADDRH APLIC_SMSICFGADDRH 42 + #endif 43 + 44 + #define APLIC_xMSICFGADDRH_L BIT(31) 45 + #define APLIC_xMSICFGADDRH_HHXS_MASK 0x1f 46 + #define APLIC_xMSICFGADDRH_HHXS_SHIFT 24 47 + #define APLIC_xMSICFGADDRH_HHXS (APLIC_xMSICFGADDRH_HHXS_MASK << \ 48 + APLIC_xMSICFGADDRH_HHXS_SHIFT) 49 + #define APLIC_xMSICFGADDRH_LHXS_MASK 0x7 50 + #define APLIC_xMSICFGADDRH_LHXS_SHIFT 20 51 + #define APLIC_xMSICFGADDRH_LHXS (APLIC_xMSICFGADDRH_LHXS_MASK << \ 52 + APLIC_xMSICFGADDRH_LHXS_SHIFT) 53 + #define APLIC_xMSICFGADDRH_HHXW_MASK 0x7 54 + #define APLIC_xMSICFGADDRH_HHXW_SHIFT 16 55 + #define APLIC_xMSICFGADDRH_HHXW (APLIC_xMSICFGADDRH_HHXW_MASK << \ 56 + APLIC_xMSICFGADDRH_HHXW_SHIFT) 57 + #define APLIC_xMSICFGADDRH_LHXW_MASK 0xf 58 + #define APLIC_xMSICFGADDRH_LHXW_SHIFT 12 59 + #define APLIC_xMSICFGADDRH_LHXW (APLIC_xMSICFGADDRH_LHXW_MASK << \ 60 + APLIC_xMSICFGADDRH_LHXW_SHIFT) 61 + #define APLIC_xMSICFGADDRH_BAPPN_MASK 0xfff 62 + #define APLIC_xMSICFGADDRH_BAPPN_SHIFT 0 63 + #define APLIC_xMSICFGADDRH_BAPPN (APLIC_xMSICFGADDRH_BAPPN_MASK << \ 64 + APLIC_xMSICFGADDRH_BAPPN_SHIFT) 65 + 66 + #define APLIC_xMSICFGADDR_PPN_SHIFT 12 67 + 68 + #define APLIC_xMSICFGADDR_PPN_HART(__lhxs) \ 69 + (BIT(__lhxs) - 1) 70 + 71 + #define APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) \ 72 + (BIT(__lhxw) - 1) 73 + #define APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs) \ 74 + ((__lhxs)) 75 + #define APLIC_xMSICFGADDR_PPN_LHX(__lhxw, __lhxs) \ 76 + (APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) << \ 77 + APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs)) 78 + 79 + #define APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) \ 80 + (BIT(__hhxw) - 1) 81 + #define APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs) \ 82 + ((__hhxs) + APLIC_xMSICFGADDR_PPN_SHIFT) 83 + #define APLIC_xMSICFGADDR_PPN_HHX(__hhxw, __hhxs) \ 84 + (APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << \ 85 + APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs)) 86 + 87 + #define APLIC_IRQBITS_PER_REG 32 88 + 89 + #define APLIC_SETIP_BASE 0x1c00 90 + #define APLIC_SETIPNUM 0x1cdc 91 + 92 + #define APLIC_CLRIP_BASE 0x1d00 93 + #define APLIC_CLRIPNUM 0x1ddc 94 + 95 + #define APLIC_SETIE_BASE 0x1e00 96 + #define APLIC_SETIENUM 0x1edc 97 + 98 + #define APLIC_CLRIE_BASE 0x1f00 99 + #define APLIC_CLRIENUM 0x1fdc 100 + 101 + #define APLIC_SETIPNUM_LE 0x2000 102 + #define APLIC_SETIPNUM_BE 0x2004 103 + 104 + #define APLIC_GENMSI 0x3000 105 + 106 + #define APLIC_TARGET_BASE 0x3004 107 + #define APLIC_TARGET_HART_IDX_SHIFT 18 108 + #define APLIC_TARGET_HART_IDX_MASK 0x3fff 109 + #define APLIC_TARGET_HART_IDX (APLIC_TARGET_HART_IDX_MASK << \ 110 + APLIC_TARGET_HART_IDX_SHIFT) 111 + #define APLIC_TARGET_GUEST_IDX_SHIFT 12 112 + #define APLIC_TARGET_GUEST_IDX_MASK 0x3f 113 + #define APLIC_TARGET_GUEST_IDX (APLIC_TARGET_GUEST_IDX_MASK << \ 114 + APLIC_TARGET_GUEST_IDX_SHIFT) 115 + #define APLIC_TARGET_IPRIO_SHIFT 0 116 + #define APLIC_TARGET_IPRIO_MASK 0xff 117 + #define APLIC_TARGET_IPRIO (APLIC_TARGET_IPRIO_MASK << \ 118 + APLIC_TARGET_IPRIO_SHIFT) 119 + #define APLIC_TARGET_EIID_SHIFT 0 120 + #define APLIC_TARGET_EIID_MASK 0x7ff 121 + #define APLIC_TARGET_EIID (APLIC_TARGET_EIID_MASK << \ 122 + APLIC_TARGET_EIID_SHIFT) 123 + 124 + #define APLIC_IDC_BASE 0x4000 125 + #define APLIC_IDC_SIZE 32 126 + 127 + #define APLIC_IDC_IDELIVERY 0x00 128 + 129 + #define APLIC_IDC_IFORCE 0x04 130 + 131 + #define APLIC_IDC_ITHRESHOLD 0x08 132 + 133 + #define APLIC_IDC_TOPI 0x18 134 + #define APLIC_IDC_TOPI_ID_SHIFT 16 135 + #define APLIC_IDC_TOPI_ID_MASK 0x3ff 136 + #define APLIC_IDC_TOPI_ID (APLIC_IDC_TOPI_ID_MASK << \ 137 + APLIC_IDC_TOPI_ID_SHIFT) 138 + #define APLIC_IDC_TOPI_PRIO_SHIFT 0 139 + #define APLIC_IDC_TOPI_PRIO_MASK 0xff 140 + #define APLIC_IDC_TOPI_PRIO (APLIC_IDC_TOPI_PRIO_MASK << \ 141 + APLIC_IDC_TOPI_PRIO_SHIFT) 142 + 143 + #define APLIC_IDC_CLAIMI 0x1c 144 + 145 + #endif