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

Configure Feed

Select the types of activity you want to include in your feed.

irqchip: Add RISC-V incoming MSI controller early driver

The RISC-V advanced interrupt architecture (AIA) specification
defines a new MSI controller called incoming message signalled
interrupt controller (IMSIC) which manages MSI on per-HART (or
per-CPU) basis. It also supports IPIs as software injected MSIs.
(For more details refer https://github.com/riscv/riscv-aia)

Add an early irqchip driver for RISC-V IMSIC which sets up the
IMSIC state and provide IPIs.

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-3-apatel@ventanamicro.com

authored by

Anup Patel and committed by
Thomas Gleixner
21a8f8a0 0151a8db

+1269
+7
drivers/irqchip/Kconfig
··· 540 540 depends on RISCV 541 541 select IRQ_DOMAIN_HIERARCHY 542 542 543 + config RISCV_IMSIC 544 + bool 545 + depends on RISCV 546 + select IRQ_DOMAIN_HIERARCHY 547 + select GENERIC_IRQ_MATRIX_ALLOCATOR 548 + select GENERIC_MSI_IRQ 549 + 543 550 config SIFIVE_PLIC 544 551 bool 545 552 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_IMSIC) += irq-riscv-imsic-state.o irq-riscv-imsic-early.o 98 99 obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o 99 100 obj-$(CONFIG_STARFIVE_JH8100_INTC) += irq-starfive-jh8100-intc.o 100 101 obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
+201
drivers/irqchip/irq-riscv-imsic-early.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 + #define pr_fmt(fmt) "riscv-imsic: " fmt 8 + #include <linux/cpu.h> 9 + #include <linux/interrupt.h> 10 + #include <linux/io.h> 11 + #include <linux/irq.h> 12 + #include <linux/irqchip.h> 13 + #include <linux/irqchip/chained_irq.h> 14 + #include <linux/module.h> 15 + #include <linux/spinlock.h> 16 + #include <linux/smp.h> 17 + 18 + #include "irq-riscv-imsic-state.h" 19 + 20 + static int imsic_parent_irq; 21 + 22 + #ifdef CONFIG_SMP 23 + static void imsic_ipi_send(unsigned int cpu) 24 + { 25 + struct imsic_local_config *local = per_cpu_ptr(imsic->global.local, cpu); 26 + 27 + writel_relaxed(IMSIC_IPI_ID, local->msi_va); 28 + } 29 + 30 + static void imsic_ipi_starting_cpu(void) 31 + { 32 + /* Enable IPIs for current CPU. */ 33 + __imsic_id_set_enable(IMSIC_IPI_ID); 34 + } 35 + 36 + static void imsic_ipi_dying_cpu(void) 37 + { 38 + /* Disable IPIs for current CPU. */ 39 + __imsic_id_clear_enable(IMSIC_IPI_ID); 40 + } 41 + 42 + static int __init imsic_ipi_domain_init(void) 43 + { 44 + int virq; 45 + 46 + /* Create IMSIC IPI multiplexing */ 47 + virq = ipi_mux_create(IMSIC_NR_IPI, imsic_ipi_send); 48 + if (virq <= 0) 49 + return virq < 0 ? virq : -ENOMEM; 50 + 51 + /* Set vIRQ range */ 52 + riscv_ipi_set_virq_range(virq, IMSIC_NR_IPI, true); 53 + 54 + /* Announce that IMSIC is providing IPIs */ 55 + pr_info("%pfwP: providing IPIs using interrupt %d\n", imsic->fwnode, IMSIC_IPI_ID); 56 + 57 + return 0; 58 + } 59 + #else 60 + static void imsic_ipi_starting_cpu(void) { } 61 + static void imsic_ipi_dying_cpu(void) { } 62 + static int __init imsic_ipi_domain_init(void) { return 0; } 63 + #endif 64 + 65 + /* 66 + * To handle an interrupt, we read the TOPEI CSR and write zero in one 67 + * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to 68 + * Linux interrupt number and let Linux IRQ subsystem handle it. 69 + */ 70 + static void imsic_handle_irq(struct irq_desc *desc) 71 + { 72 + struct irq_chip *chip = irq_desc_get_chip(desc); 73 + int err, cpu = smp_processor_id(); 74 + struct imsic_vector *vec; 75 + unsigned long local_id; 76 + 77 + chained_irq_enter(chip, desc); 78 + 79 + while ((local_id = csr_swap(CSR_TOPEI, 0))) { 80 + local_id >>= TOPEI_ID_SHIFT; 81 + 82 + if (local_id == IMSIC_IPI_ID) { 83 + if (IS_ENABLED(CONFIG_SMP)) 84 + ipi_mux_process(); 85 + continue; 86 + } 87 + 88 + if (unlikely(!imsic->base_domain)) 89 + continue; 90 + 91 + vec = imsic_vector_from_local_id(cpu, local_id); 92 + if (!vec) { 93 + pr_warn_ratelimited("vector not found for local ID 0x%lx\n", local_id); 94 + continue; 95 + } 96 + 97 + err = generic_handle_domain_irq(imsic->base_domain, vec->hwirq); 98 + if (unlikely(err)) 99 + pr_warn_ratelimited("hwirq 0x%x mapping not found\n", vec->hwirq); 100 + } 101 + 102 + chained_irq_exit(chip, desc); 103 + } 104 + 105 + static int imsic_starting_cpu(unsigned int cpu) 106 + { 107 + /* Mark per-CPU IMSIC state as online */ 108 + imsic_state_online(); 109 + 110 + /* Enable per-CPU parent interrupt */ 111 + enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq)); 112 + 113 + /* Setup IPIs */ 114 + imsic_ipi_starting_cpu(); 115 + 116 + /* 117 + * Interrupts identities might have been enabled/disabled while 118 + * this CPU was not running so sync-up local enable/disable state. 119 + */ 120 + imsic_local_sync_all(); 121 + 122 + /* Enable local interrupt delivery */ 123 + imsic_local_delivery(true); 124 + 125 + return 0; 126 + } 127 + 128 + static int imsic_dying_cpu(unsigned int cpu) 129 + { 130 + /* Cleanup IPIs */ 131 + imsic_ipi_dying_cpu(); 132 + 133 + /* Mark per-CPU IMSIC state as offline */ 134 + imsic_state_offline(); 135 + 136 + return 0; 137 + } 138 + 139 + static int __init imsic_early_probe(struct fwnode_handle *fwnode) 140 + { 141 + struct irq_domain *domain; 142 + int rc; 143 + 144 + /* Find parent domain and register chained handler */ 145 + domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); 146 + if (!domain) { 147 + pr_err("%pfwP: Failed to find INTC domain\n", fwnode); 148 + return -ENOENT; 149 + } 150 + imsic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); 151 + if (!imsic_parent_irq) { 152 + pr_err("%pfwP: Failed to create INTC mapping\n", fwnode); 153 + return -ENOENT; 154 + } 155 + 156 + /* Initialize IPI domain */ 157 + rc = imsic_ipi_domain_init(); 158 + if (rc) { 159 + pr_err("%pfwP: Failed to initialize IPI domain\n", fwnode); 160 + return rc; 161 + } 162 + 163 + /* Setup chained handler to the parent domain interrupt */ 164 + irq_set_chained_handler(imsic_parent_irq, imsic_handle_irq); 165 + 166 + /* 167 + * Setup cpuhp state (must be done after setting imsic_parent_irq) 168 + * 169 + * Don't disable per-CPU IMSIC file when CPU goes offline 170 + * because this affects IPI and the masking/unmasking of 171 + * virtual IPIs is done via generic IPI-Mux 172 + */ 173 + cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_IMSIC_STARTING, "irqchip/riscv/imsic:starting", 174 + imsic_starting_cpu, imsic_dying_cpu); 175 + 176 + return 0; 177 + } 178 + 179 + static int __init imsic_early_dt_init(struct device_node *node, struct device_node *parent) 180 + { 181 + struct fwnode_handle *fwnode = &node->fwnode; 182 + int rc; 183 + 184 + /* Setup IMSIC state */ 185 + rc = imsic_setup_state(fwnode); 186 + if (rc) { 187 + pr_err("%pfwP: failed to setup state (error %d)\n", fwnode, rc); 188 + return rc; 189 + } 190 + 191 + /* Do early setup of IPIs */ 192 + rc = imsic_early_probe(fwnode); 193 + if (rc) 194 + return rc; 195 + 196 + /* Ensure that OF platform device gets probed */ 197 + of_node_clear_flag(node, OF_POPULATED); 198 + return 0; 199 + } 200 + 201 + IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_early_dt_init);
+865
drivers/irqchip/irq-riscv-imsic-state.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 + #define pr_fmt(fmt) "riscv-imsic: " fmt 8 + #include <linux/cpu.h> 9 + #include <linux/bitmap.h> 10 + #include <linux/interrupt.h> 11 + #include <linux/irq.h> 12 + #include <linux/module.h> 13 + #include <linux/of.h> 14 + #include <linux/of_address.h> 15 + #include <linux/of_irq.h> 16 + #include <linux/seq_file.h> 17 + #include <linux/spinlock.h> 18 + #include <linux/smp.h> 19 + #include <asm/hwcap.h> 20 + 21 + #include "irq-riscv-imsic-state.h" 22 + 23 + #define IMSIC_DISABLE_EIDELIVERY 0 24 + #define IMSIC_ENABLE_EIDELIVERY 1 25 + #define IMSIC_DISABLE_EITHRESHOLD 1 26 + #define IMSIC_ENABLE_EITHRESHOLD 0 27 + 28 + static inline void imsic_csr_write(unsigned long reg, unsigned long val) 29 + { 30 + csr_write(CSR_ISELECT, reg); 31 + csr_write(CSR_IREG, val); 32 + } 33 + 34 + static inline unsigned long imsic_csr_read(unsigned long reg) 35 + { 36 + csr_write(CSR_ISELECT, reg); 37 + return csr_read(CSR_IREG); 38 + } 39 + 40 + static inline unsigned long imsic_csr_read_clear(unsigned long reg, unsigned long val) 41 + { 42 + csr_write(CSR_ISELECT, reg); 43 + return csr_read_clear(CSR_IREG, val); 44 + } 45 + 46 + static inline void imsic_csr_set(unsigned long reg, unsigned long val) 47 + { 48 + csr_write(CSR_ISELECT, reg); 49 + csr_set(CSR_IREG, val); 50 + } 51 + 52 + static inline void imsic_csr_clear(unsigned long reg, unsigned long val) 53 + { 54 + csr_write(CSR_ISELECT, reg); 55 + csr_clear(CSR_IREG, val); 56 + } 57 + 58 + struct imsic_priv *imsic; 59 + 60 + const struct imsic_global_config *imsic_get_global_config(void) 61 + { 62 + return imsic ? &imsic->global : NULL; 63 + } 64 + EXPORT_SYMBOL_GPL(imsic_get_global_config); 65 + 66 + static bool __imsic_eix_read_clear(unsigned long id, bool pend) 67 + { 68 + unsigned long isel, imask; 69 + 70 + isel = id / BITS_PER_LONG; 71 + isel *= BITS_PER_LONG / IMSIC_EIPx_BITS; 72 + isel += pend ? IMSIC_EIP0 : IMSIC_EIE0; 73 + imask = BIT(id & (__riscv_xlen - 1)); 74 + 75 + return !!(imsic_csr_read_clear(isel, imask) & imask); 76 + } 77 + 78 + static inline bool __imsic_id_read_clear_enabled(unsigned long id) 79 + { 80 + return __imsic_eix_read_clear(id, false); 81 + } 82 + 83 + static inline bool __imsic_id_read_clear_pending(unsigned long id) 84 + { 85 + return __imsic_eix_read_clear(id, true); 86 + } 87 + 88 + void __imsic_eix_update(unsigned long base_id, unsigned long num_id, bool pend, bool val) 89 + { 90 + unsigned long id = base_id, last_id = base_id + num_id; 91 + unsigned long i, isel, ireg; 92 + 93 + while (id < last_id) { 94 + isel = id / BITS_PER_LONG; 95 + isel *= BITS_PER_LONG / IMSIC_EIPx_BITS; 96 + isel += pend ? IMSIC_EIP0 : IMSIC_EIE0; 97 + 98 + /* 99 + * Prepare the ID mask to be programmed in the 100 + * IMSIC EIEx and EIPx registers. These registers 101 + * are XLEN-wide and we must not touch IDs which 102 + * are < base_id and >= (base_id + num_id). 103 + */ 104 + ireg = 0; 105 + for (i = id & (__riscv_xlen - 1); id < last_id && i < __riscv_xlen; i++) { 106 + ireg |= BIT(i); 107 + id++; 108 + } 109 + 110 + /* 111 + * The IMSIC EIEx and EIPx registers are indirectly 112 + * accessed via using ISELECT and IREG CSRs so we 113 + * need to access these CSRs without getting preempted. 114 + * 115 + * All existing users of this function call this 116 + * function with local IRQs disabled so we don't 117 + * need to do anything special here. 118 + */ 119 + if (val) 120 + imsic_csr_set(isel, ireg); 121 + else 122 + imsic_csr_clear(isel, ireg); 123 + } 124 + } 125 + 126 + static void __imsic_local_sync(struct imsic_local_priv *lpriv) 127 + { 128 + struct imsic_local_config *mlocal; 129 + struct imsic_vector *vec, *mvec; 130 + int i; 131 + 132 + lockdep_assert_held(&lpriv->lock); 133 + 134 + for_each_set_bit(i, lpriv->dirty_bitmap, imsic->global.nr_ids + 1) { 135 + if (!i || i == IMSIC_IPI_ID) 136 + goto skip; 137 + vec = &lpriv->vectors[i]; 138 + 139 + if (READ_ONCE(vec->enable)) 140 + __imsic_id_set_enable(i); 141 + else 142 + __imsic_id_clear_enable(i); 143 + 144 + /* 145 + * If the ID was being moved to a new ID on some other CPU 146 + * then we can get a MSI during the movement so check the 147 + * ID pending bit and re-trigger the new ID on other CPU 148 + * using MMIO write. 149 + */ 150 + mvec = READ_ONCE(vec->move); 151 + WRITE_ONCE(vec->move, NULL); 152 + if (mvec && mvec != vec) { 153 + if (__imsic_id_read_clear_pending(i)) { 154 + mlocal = per_cpu_ptr(imsic->global.local, mvec->cpu); 155 + writel_relaxed(mvec->local_id, mlocal->msi_va); 156 + } 157 + 158 + imsic_vector_free(&lpriv->vectors[i]); 159 + } 160 + 161 + skip: 162 + bitmap_clear(lpriv->dirty_bitmap, i, 1); 163 + } 164 + } 165 + 166 + void imsic_local_sync_all(void) 167 + { 168 + struct imsic_local_priv *lpriv = this_cpu_ptr(imsic->lpriv); 169 + unsigned long flags; 170 + 171 + raw_spin_lock_irqsave(&lpriv->lock, flags); 172 + bitmap_fill(lpriv->dirty_bitmap, imsic->global.nr_ids + 1); 173 + __imsic_local_sync(lpriv); 174 + raw_spin_unlock_irqrestore(&lpriv->lock, flags); 175 + } 176 + 177 + void imsic_local_delivery(bool enable) 178 + { 179 + if (enable) { 180 + imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD); 181 + imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY); 182 + return; 183 + } 184 + 185 + imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY); 186 + imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD); 187 + } 188 + 189 + #ifdef CONFIG_SMP 190 + static void imsic_local_timer_callback(struct timer_list *timer) 191 + { 192 + struct imsic_local_priv *lpriv = this_cpu_ptr(imsic->lpriv); 193 + unsigned long flags; 194 + 195 + raw_spin_lock_irqsave(&lpriv->lock, flags); 196 + __imsic_local_sync(lpriv); 197 + raw_spin_unlock_irqrestore(&lpriv->lock, flags); 198 + } 199 + 200 + static void __imsic_remote_sync(struct imsic_local_priv *lpriv, unsigned int cpu) 201 + { 202 + lockdep_assert_held(&lpriv->lock); 203 + 204 + /* 205 + * The spinlock acquire/release semantics ensure that changes 206 + * to vector enable, vector move and dirty bitmap are visible 207 + * to the target CPU. 208 + */ 209 + 210 + /* 211 + * We schedule a timer on the target CPU if the target CPU is not 212 + * same as the current CPU. An offline CPU will unconditionally 213 + * synchronize IDs through imsic_starting_cpu() when the 214 + * CPU is brought up. 215 + */ 216 + if (cpu_online(cpu)) { 217 + if (cpu == smp_processor_id()) { 218 + __imsic_local_sync(lpriv); 219 + return; 220 + } 221 + 222 + if (!timer_pending(&lpriv->timer)) { 223 + lpriv->timer.expires = jiffies + 1; 224 + add_timer_on(&lpriv->timer, cpu); 225 + } 226 + } 227 + } 228 + #else 229 + static void __imsic_remote_sync(struct imsic_local_priv *lpriv, unsigned int cpu) 230 + { 231 + lockdep_assert_held(&lpriv->lock); 232 + __imsic_local_sync(lpriv); 233 + } 234 + #endif 235 + 236 + void imsic_vector_mask(struct imsic_vector *vec) 237 + { 238 + struct imsic_local_priv *lpriv; 239 + 240 + lpriv = per_cpu_ptr(imsic->lpriv, vec->cpu); 241 + if (WARN_ON_ONCE(&lpriv->vectors[vec->local_id] != vec)) 242 + return; 243 + 244 + /* 245 + * This function is called through Linux irq subsystem with 246 + * irqs disabled so no need to save/restore irq flags. 247 + */ 248 + 249 + raw_spin_lock(&lpriv->lock); 250 + 251 + WRITE_ONCE(vec->enable, false); 252 + bitmap_set(lpriv->dirty_bitmap, vec->local_id, 1); 253 + __imsic_remote_sync(lpriv, vec->cpu); 254 + 255 + raw_spin_unlock(&lpriv->lock); 256 + } 257 + 258 + void imsic_vector_unmask(struct imsic_vector *vec) 259 + { 260 + struct imsic_local_priv *lpriv; 261 + 262 + lpriv = per_cpu_ptr(imsic->lpriv, vec->cpu); 263 + if (WARN_ON_ONCE(&lpriv->vectors[vec->local_id] != vec)) 264 + return; 265 + 266 + /* 267 + * This function is called through Linux irq subsystem with 268 + * irqs disabled so no need to save/restore irq flags. 269 + */ 270 + 271 + raw_spin_lock(&lpriv->lock); 272 + 273 + WRITE_ONCE(vec->enable, true); 274 + bitmap_set(lpriv->dirty_bitmap, vec->local_id, 1); 275 + __imsic_remote_sync(lpriv, vec->cpu); 276 + 277 + raw_spin_unlock(&lpriv->lock); 278 + } 279 + 280 + static bool imsic_vector_move_update(struct imsic_local_priv *lpriv, struct imsic_vector *vec, 281 + bool new_enable, struct imsic_vector *new_move) 282 + { 283 + unsigned long flags; 284 + bool enabled; 285 + 286 + raw_spin_lock_irqsave(&lpriv->lock, flags); 287 + 288 + /* Update enable and move details */ 289 + enabled = READ_ONCE(vec->enable); 290 + WRITE_ONCE(vec->enable, new_enable); 291 + WRITE_ONCE(vec->move, new_move); 292 + 293 + /* Mark the vector as dirty and synchronize */ 294 + bitmap_set(lpriv->dirty_bitmap, vec->local_id, 1); 295 + __imsic_remote_sync(lpriv, vec->cpu); 296 + 297 + raw_spin_unlock_irqrestore(&lpriv->lock, flags); 298 + 299 + return enabled; 300 + } 301 + 302 + void imsic_vector_move(struct imsic_vector *old_vec, struct imsic_vector *new_vec) 303 + { 304 + struct imsic_local_priv *old_lpriv, *new_lpriv; 305 + bool enabled; 306 + 307 + if (WARN_ON_ONCE(old_vec->cpu == new_vec->cpu)) 308 + return; 309 + 310 + old_lpriv = per_cpu_ptr(imsic->lpriv, old_vec->cpu); 311 + if (WARN_ON_ONCE(&old_lpriv->vectors[old_vec->local_id] != old_vec)) 312 + return; 313 + 314 + new_lpriv = per_cpu_ptr(imsic->lpriv, new_vec->cpu); 315 + if (WARN_ON_ONCE(&new_lpriv->vectors[new_vec->local_id] != new_vec)) 316 + return; 317 + 318 + /* 319 + * Move and re-trigger the new vector based on the pending 320 + * state of the old vector because we might get a device 321 + * interrupt on the old vector while device was being moved 322 + * to the new vector. 323 + */ 324 + enabled = imsic_vector_move_update(old_lpriv, old_vec, false, new_vec); 325 + imsic_vector_move_update(new_lpriv, new_vec, enabled, new_vec); 326 + } 327 + 328 + #ifdef CONFIG_GENERIC_IRQ_DEBUGFS 329 + void imsic_vector_debug_show(struct seq_file *m, struct imsic_vector *vec, int ind) 330 + { 331 + struct imsic_local_priv *lpriv; 332 + struct imsic_vector *mvec; 333 + bool is_enabled; 334 + 335 + lpriv = per_cpu_ptr(imsic->lpriv, vec->cpu); 336 + if (WARN_ON_ONCE(&lpriv->vectors[vec->local_id] != vec)) 337 + return; 338 + 339 + is_enabled = imsic_vector_isenabled(vec); 340 + mvec = imsic_vector_get_move(vec); 341 + 342 + seq_printf(m, "%*starget_cpu : %5u\n", ind, "", vec->cpu); 343 + seq_printf(m, "%*starget_local_id : %5u\n", ind, "", vec->local_id); 344 + seq_printf(m, "%*sis_reserved : %5u\n", ind, "", 345 + (vec->local_id <= IMSIC_IPI_ID) ? 1 : 0); 346 + seq_printf(m, "%*sis_enabled : %5u\n", ind, "", is_enabled ? 1 : 0); 347 + seq_printf(m, "%*sis_move_pending : %5u\n", ind, "", mvec ? 1 : 0); 348 + if (mvec) { 349 + seq_printf(m, "%*smove_cpu : %5u\n", ind, "", mvec->cpu); 350 + seq_printf(m, "%*smove_local_id : %5u\n", ind, "", mvec->local_id); 351 + } 352 + } 353 + 354 + void imsic_vector_debug_show_summary(struct seq_file *m, int ind) 355 + { 356 + irq_matrix_debug_show(m, imsic->matrix, ind); 357 + } 358 + #endif 359 + 360 + struct imsic_vector *imsic_vector_from_local_id(unsigned int cpu, unsigned int local_id) 361 + { 362 + struct imsic_local_priv *lpriv = per_cpu_ptr(imsic->lpriv, cpu); 363 + 364 + if (!lpriv || imsic->global.nr_ids < local_id) 365 + return NULL; 366 + 367 + return &lpriv->vectors[local_id]; 368 + } 369 + 370 + struct imsic_vector *imsic_vector_alloc(unsigned int hwirq, const struct cpumask *mask) 371 + { 372 + struct imsic_vector *vec = NULL; 373 + struct imsic_local_priv *lpriv; 374 + unsigned long flags; 375 + unsigned int cpu; 376 + int local_id; 377 + 378 + raw_spin_lock_irqsave(&imsic->matrix_lock, flags); 379 + local_id = irq_matrix_alloc(imsic->matrix, mask, false, &cpu); 380 + raw_spin_unlock_irqrestore(&imsic->matrix_lock, flags); 381 + if (local_id < 0) 382 + return NULL; 383 + 384 + lpriv = per_cpu_ptr(imsic->lpriv, cpu); 385 + vec = &lpriv->vectors[local_id]; 386 + vec->hwirq = hwirq; 387 + vec->enable = false; 388 + vec->move = NULL; 389 + 390 + return vec; 391 + } 392 + 393 + void imsic_vector_free(struct imsic_vector *vec) 394 + { 395 + unsigned long flags; 396 + 397 + raw_spin_lock_irqsave(&imsic->matrix_lock, flags); 398 + vec->hwirq = UINT_MAX; 399 + irq_matrix_free(imsic->matrix, vec->cpu, vec->local_id, false); 400 + raw_spin_unlock_irqrestore(&imsic->matrix_lock, flags); 401 + } 402 + 403 + static void __init imsic_local_cleanup(void) 404 + { 405 + struct imsic_local_priv *lpriv; 406 + int cpu; 407 + 408 + for_each_possible_cpu(cpu) { 409 + lpriv = per_cpu_ptr(imsic->lpriv, cpu); 410 + 411 + bitmap_free(lpriv->dirty_bitmap); 412 + kfree(lpriv->vectors); 413 + } 414 + 415 + free_percpu(imsic->lpriv); 416 + } 417 + 418 + static int __init imsic_local_init(void) 419 + { 420 + struct imsic_global_config *global = &imsic->global; 421 + struct imsic_local_priv *lpriv; 422 + struct imsic_vector *vec; 423 + int cpu, i; 424 + 425 + /* Allocate per-CPU private state */ 426 + imsic->lpriv = alloc_percpu(typeof(*imsic->lpriv)); 427 + if (!imsic->lpriv) 428 + return -ENOMEM; 429 + 430 + /* Setup per-CPU private state */ 431 + for_each_possible_cpu(cpu) { 432 + lpriv = per_cpu_ptr(imsic->lpriv, cpu); 433 + 434 + raw_spin_lock_init(&lpriv->lock); 435 + 436 + /* Allocate dirty bitmap */ 437 + lpriv->dirty_bitmap = bitmap_zalloc(global->nr_ids + 1, GFP_KERNEL); 438 + if (!lpriv->dirty_bitmap) 439 + goto fail_local_cleanup; 440 + 441 + #ifdef CONFIG_SMP 442 + /* Setup lazy timer for synchronization */ 443 + timer_setup(&lpriv->timer, imsic_local_timer_callback, TIMER_PINNED); 444 + #endif 445 + 446 + /* Allocate vector array */ 447 + lpriv->vectors = kcalloc(global->nr_ids + 1, sizeof(*lpriv->vectors), 448 + GFP_KERNEL); 449 + if (!lpriv->vectors) 450 + goto fail_local_cleanup; 451 + 452 + /* Setup vector array */ 453 + for (i = 0; i <= global->nr_ids; i++) { 454 + vec = &lpriv->vectors[i]; 455 + vec->cpu = cpu; 456 + vec->local_id = i; 457 + vec->hwirq = UINT_MAX; 458 + } 459 + } 460 + 461 + return 0; 462 + 463 + fail_local_cleanup: 464 + imsic_local_cleanup(); 465 + return -ENOMEM; 466 + } 467 + 468 + void imsic_state_online(void) 469 + { 470 + unsigned long flags; 471 + 472 + raw_spin_lock_irqsave(&imsic->matrix_lock, flags); 473 + irq_matrix_online(imsic->matrix); 474 + raw_spin_unlock_irqrestore(&imsic->matrix_lock, flags); 475 + } 476 + 477 + void imsic_state_offline(void) 478 + { 479 + unsigned long flags; 480 + 481 + raw_spin_lock_irqsave(&imsic->matrix_lock, flags); 482 + irq_matrix_offline(imsic->matrix); 483 + raw_spin_unlock_irqrestore(&imsic->matrix_lock, flags); 484 + 485 + #ifdef CONFIG_SMP 486 + struct imsic_local_priv *lpriv = this_cpu_ptr(imsic->lpriv); 487 + 488 + raw_spin_lock_irqsave(&lpriv->lock, flags); 489 + WARN_ON_ONCE(try_to_del_timer_sync(&lpriv->timer) < 0); 490 + raw_spin_unlock_irqrestore(&lpriv->lock, flags); 491 + #endif 492 + } 493 + 494 + static int __init imsic_matrix_init(void) 495 + { 496 + struct imsic_global_config *global = &imsic->global; 497 + 498 + raw_spin_lock_init(&imsic->matrix_lock); 499 + imsic->matrix = irq_alloc_matrix(global->nr_ids + 1, 500 + 0, global->nr_ids + 1); 501 + if (!imsic->matrix) 502 + return -ENOMEM; 503 + 504 + /* Reserve ID#0 because it is special and never implemented */ 505 + irq_matrix_assign_system(imsic->matrix, 0, false); 506 + 507 + /* Reserve IPI ID because it is special and used internally */ 508 + irq_matrix_assign_system(imsic->matrix, IMSIC_IPI_ID, false); 509 + 510 + return 0; 511 + } 512 + 513 + static int __init imsic_get_parent_hartid(struct fwnode_handle *fwnode, 514 + u32 index, unsigned long *hartid) 515 + { 516 + struct of_phandle_args parent; 517 + int rc; 518 + 519 + /* 520 + * Currently, only OF fwnode is supported so extend this 521 + * function for ACPI support. 522 + */ 523 + if (!is_of_node(fwnode)) 524 + return -EINVAL; 525 + 526 + rc = of_irq_parse_one(to_of_node(fwnode), index, &parent); 527 + if (rc) 528 + return rc; 529 + 530 + /* 531 + * Skip interrupts other than external interrupts for 532 + * current privilege level. 533 + */ 534 + if (parent.args[0] != RV_IRQ_EXT) 535 + return -EINVAL; 536 + 537 + return riscv_of_parent_hartid(parent.np, hartid); 538 + } 539 + 540 + static int __init imsic_get_mmio_resource(struct fwnode_handle *fwnode, 541 + u32 index, struct resource *res) 542 + { 543 + /* 544 + * Currently, only OF fwnode is supported so extend this 545 + * function for ACPI support. 546 + */ 547 + if (!is_of_node(fwnode)) 548 + return -EINVAL; 549 + 550 + return of_address_to_resource(to_of_node(fwnode), index, res); 551 + } 552 + 553 + static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode, 554 + struct imsic_global_config *global, 555 + u32 *nr_parent_irqs, 556 + u32 *nr_mmios) 557 + { 558 + unsigned long hartid; 559 + struct resource res; 560 + int rc; 561 + u32 i; 562 + 563 + /* 564 + * Currently, only OF fwnode is supported so extend this 565 + * function for ACPI support. 566 + */ 567 + if (!is_of_node(fwnode)) 568 + return -EINVAL; 569 + 570 + *nr_parent_irqs = 0; 571 + *nr_mmios = 0; 572 + 573 + /* Find number of parent interrupts */ 574 + while (!imsic_get_parent_hartid(fwnode, *nr_parent_irqs, &hartid)) 575 + (*nr_parent_irqs)++; 576 + if (!*nr_parent_irqs) { 577 + pr_err("%pfwP: no parent irqs available\n", fwnode); 578 + return -EINVAL; 579 + } 580 + 581 + /* Find number of guest index bits in MSI address */ 582 + rc = of_property_read_u32(to_of_node(fwnode), "riscv,guest-index-bits", 583 + &global->guest_index_bits); 584 + if (rc) 585 + global->guest_index_bits = 0; 586 + 587 + /* Find number of HART index bits */ 588 + rc = of_property_read_u32(to_of_node(fwnode), "riscv,hart-index-bits", 589 + &global->hart_index_bits); 590 + if (rc) { 591 + /* Assume default value */ 592 + global->hart_index_bits = __fls(*nr_parent_irqs); 593 + if (BIT(global->hart_index_bits) < *nr_parent_irqs) 594 + global->hart_index_bits++; 595 + } 596 + 597 + /* Find number of group index bits */ 598 + rc = of_property_read_u32(to_of_node(fwnode), "riscv,group-index-bits", 599 + &global->group_index_bits); 600 + if (rc) 601 + global->group_index_bits = 0; 602 + 603 + /* 604 + * Find first bit position of group index. 605 + * If not specified assumed the default APLIC-IMSIC configuration. 606 + */ 607 + rc = of_property_read_u32(to_of_node(fwnode), "riscv,group-index-shift", 608 + &global->group_index_shift); 609 + if (rc) 610 + global->group_index_shift = IMSIC_MMIO_PAGE_SHIFT * 2; 611 + 612 + /* Find number of interrupt identities */ 613 + rc = of_property_read_u32(to_of_node(fwnode), "riscv,num-ids", 614 + &global->nr_ids); 615 + if (rc) { 616 + pr_err("%pfwP: number of interrupt identities not found\n", fwnode); 617 + return rc; 618 + } 619 + 620 + /* Find number of guest interrupt identities */ 621 + rc = of_property_read_u32(to_of_node(fwnode), "riscv,num-guest-ids", 622 + &global->nr_guest_ids); 623 + if (rc) 624 + global->nr_guest_ids = global->nr_ids; 625 + 626 + /* Sanity check guest index bits */ 627 + i = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT; 628 + if (i < global->guest_index_bits) { 629 + pr_err("%pfwP: guest index bits too big\n", fwnode); 630 + return -EINVAL; 631 + } 632 + 633 + /* Sanity check HART index bits */ 634 + i = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT - global->guest_index_bits; 635 + if (i < global->hart_index_bits) { 636 + pr_err("%pfwP: HART index bits too big\n", fwnode); 637 + return -EINVAL; 638 + } 639 + 640 + /* Sanity check group index bits */ 641 + i = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT - 642 + global->guest_index_bits - global->hart_index_bits; 643 + if (i < global->group_index_bits) { 644 + pr_err("%pfwP: group index bits too big\n", fwnode); 645 + return -EINVAL; 646 + } 647 + 648 + /* Sanity check group index shift */ 649 + i = global->group_index_bits + global->group_index_shift - 1; 650 + if (i >= BITS_PER_LONG) { 651 + pr_err("%pfwP: group index shift too big\n", fwnode); 652 + return -EINVAL; 653 + } 654 + 655 + /* Sanity check number of interrupt identities */ 656 + if (global->nr_ids < IMSIC_MIN_ID || 657 + global->nr_ids >= IMSIC_MAX_ID || 658 + (global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID) { 659 + pr_err("%pfwP: invalid number of interrupt identities\n", fwnode); 660 + return -EINVAL; 661 + } 662 + 663 + /* Sanity check number of guest interrupt identities */ 664 + if (global->nr_guest_ids < IMSIC_MIN_ID || 665 + global->nr_guest_ids >= IMSIC_MAX_ID || 666 + (global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID) { 667 + pr_err("%pfwP: invalid number of guest interrupt identities\n", fwnode); 668 + return -EINVAL; 669 + } 670 + 671 + /* Compute base address */ 672 + rc = imsic_get_mmio_resource(fwnode, 0, &res); 673 + if (rc) { 674 + pr_err("%pfwP: first MMIO resource not found\n", fwnode); 675 + return -EINVAL; 676 + } 677 + global->base_addr = res.start; 678 + global->base_addr &= ~(BIT(global->guest_index_bits + 679 + global->hart_index_bits + 680 + IMSIC_MMIO_PAGE_SHIFT) - 1); 681 + global->base_addr &= ~((BIT(global->group_index_bits) - 1) << 682 + global->group_index_shift); 683 + 684 + /* Find number of MMIO register sets */ 685 + while (!imsic_get_mmio_resource(fwnode, *nr_mmios, &res)) 686 + (*nr_mmios)++; 687 + 688 + return 0; 689 + } 690 + 691 + int __init imsic_setup_state(struct fwnode_handle *fwnode) 692 + { 693 + u32 i, j, index, nr_parent_irqs, nr_mmios, nr_handlers = 0; 694 + struct imsic_global_config *global; 695 + struct imsic_local_config *local; 696 + void __iomem **mmios_va = NULL; 697 + struct resource *mmios = NULL; 698 + unsigned long reloff, hartid; 699 + phys_addr_t base_addr; 700 + int rc, cpu; 701 + 702 + /* 703 + * Only one IMSIC instance allowed in a platform for clean 704 + * implementation of SMP IRQ affinity and per-CPU IPIs. 705 + * 706 + * This means on a multi-socket (or multi-die) platform we 707 + * will have multiple MMIO regions for one IMSIC instance. 708 + */ 709 + if (imsic) { 710 + pr_err("%pfwP: already initialized hence ignoring\n", fwnode); 711 + return -EALREADY; 712 + } 713 + 714 + if (!riscv_isa_extension_available(NULL, SxAIA)) { 715 + pr_err("%pfwP: AIA support not available\n", fwnode); 716 + return -ENODEV; 717 + } 718 + 719 + imsic = kzalloc(sizeof(*imsic), GFP_KERNEL); 720 + if (!imsic) 721 + return -ENOMEM; 722 + imsic->fwnode = fwnode; 723 + global = &imsic->global; 724 + 725 + global->local = alloc_percpu(typeof(*global->local)); 726 + if (!global->local) { 727 + rc = -ENOMEM; 728 + goto out_free_priv; 729 + } 730 + 731 + /* Parse IMSIC fwnode */ 732 + rc = imsic_parse_fwnode(fwnode, global, &nr_parent_irqs, &nr_mmios); 733 + if (rc) 734 + goto out_free_local; 735 + 736 + /* Allocate MMIO resource array */ 737 + mmios = kcalloc(nr_mmios, sizeof(*mmios), GFP_KERNEL); 738 + if (!mmios) { 739 + rc = -ENOMEM; 740 + goto out_free_local; 741 + } 742 + 743 + /* Allocate MMIO virtual address array */ 744 + mmios_va = kcalloc(nr_mmios, sizeof(*mmios_va), GFP_KERNEL); 745 + if (!mmios_va) { 746 + rc = -ENOMEM; 747 + goto out_iounmap; 748 + } 749 + 750 + /* Parse and map MMIO register sets */ 751 + for (i = 0; i < nr_mmios; i++) { 752 + rc = imsic_get_mmio_resource(fwnode, i, &mmios[i]); 753 + if (rc) { 754 + pr_err("%pfwP: unable to parse MMIO regset %d\n", fwnode, i); 755 + goto out_iounmap; 756 + } 757 + 758 + base_addr = mmios[i].start; 759 + base_addr &= ~(BIT(global->guest_index_bits + 760 + global->hart_index_bits + 761 + IMSIC_MMIO_PAGE_SHIFT) - 1); 762 + base_addr &= ~((BIT(global->group_index_bits) - 1) << 763 + global->group_index_shift); 764 + if (base_addr != global->base_addr) { 765 + rc = -EINVAL; 766 + pr_err("%pfwP: address mismatch for regset %d\n", fwnode, i); 767 + goto out_iounmap; 768 + } 769 + 770 + mmios_va[i] = ioremap(mmios[i].start, resource_size(&mmios[i])); 771 + if (!mmios_va[i]) { 772 + rc = -EIO; 773 + pr_err("%pfwP: unable to map MMIO regset %d\n", fwnode, i); 774 + goto out_iounmap; 775 + } 776 + } 777 + 778 + /* Initialize local (or per-CPU )state */ 779 + rc = imsic_local_init(); 780 + if (rc) { 781 + pr_err("%pfwP: failed to initialize local state\n", 782 + fwnode); 783 + goto out_iounmap; 784 + } 785 + 786 + /* Configure handlers for target CPUs */ 787 + for (i = 0; i < nr_parent_irqs; i++) { 788 + rc = imsic_get_parent_hartid(fwnode, i, &hartid); 789 + if (rc) { 790 + pr_warn("%pfwP: hart ID for parent irq%d not found\n", fwnode, i); 791 + continue; 792 + } 793 + 794 + cpu = riscv_hartid_to_cpuid(hartid); 795 + if (cpu < 0) { 796 + pr_warn("%pfwP: invalid cpuid for parent irq%d\n", fwnode, i); 797 + continue; 798 + } 799 + 800 + /* Find MMIO location of MSI page */ 801 + index = nr_mmios; 802 + reloff = i * BIT(global->guest_index_bits) * 803 + IMSIC_MMIO_PAGE_SZ; 804 + for (j = 0; nr_mmios; j++) { 805 + if (reloff < resource_size(&mmios[j])) { 806 + index = j; 807 + break; 808 + } 809 + 810 + /* 811 + * MMIO region size may not be aligned to 812 + * BIT(global->guest_index_bits) * IMSIC_MMIO_PAGE_SZ 813 + * if holes are present. 814 + */ 815 + reloff -= ALIGN(resource_size(&mmios[j]), 816 + BIT(global->guest_index_bits) * IMSIC_MMIO_PAGE_SZ); 817 + } 818 + if (index >= nr_mmios) { 819 + pr_warn("%pfwP: MMIO not found for parent irq%d\n", fwnode, i); 820 + continue; 821 + } 822 + 823 + local = per_cpu_ptr(global->local, cpu); 824 + local->msi_pa = mmios[index].start + reloff; 825 + local->msi_va = mmios_va[index] + reloff; 826 + 827 + nr_handlers++; 828 + } 829 + 830 + /* If no CPU handlers found then can't take interrupts */ 831 + if (!nr_handlers) { 832 + pr_err("%pfwP: No CPU handlers found\n", fwnode); 833 + rc = -ENODEV; 834 + goto out_local_cleanup; 835 + } 836 + 837 + /* Initialize matrix allocator */ 838 + rc = imsic_matrix_init(); 839 + if (rc) { 840 + pr_err("%pfwP: failed to create matrix allocator\n", fwnode); 841 + goto out_local_cleanup; 842 + } 843 + 844 + /* We don't need MMIO arrays anymore so let's free-up */ 845 + kfree(mmios_va); 846 + kfree(mmios); 847 + 848 + return 0; 849 + 850 + out_local_cleanup: 851 + imsic_local_cleanup(); 852 + out_iounmap: 853 + for (i = 0; i < nr_mmios; i++) { 854 + if (mmios_va[i]) 855 + iounmap(mmios_va[i]); 856 + } 857 + kfree(mmios_va); 858 + kfree(mmios); 859 + out_free_local: 860 + free_percpu(imsic->global.local); 861 + out_free_priv: 862 + kfree(imsic); 863 + imsic = NULL; 864 + return rc; 865 + }
+107
drivers/irqchip/irq-riscv-imsic-state.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_IMSIC_STATE_H 8 + #define _IRQ_RISCV_IMSIC_STATE_H 9 + 10 + #include <linux/irqchip/riscv-imsic.h> 11 + #include <linux/irqdomain.h> 12 + #include <linux/fwnode.h> 13 + #include <linux/timer.h> 14 + 15 + #define IMSIC_IPI_ID 1 16 + #define IMSIC_NR_IPI 8 17 + 18 + struct imsic_vector { 19 + /* Fixed details of the vector */ 20 + unsigned int cpu; 21 + unsigned int local_id; 22 + /* Details saved by driver in the vector */ 23 + unsigned int hwirq; 24 + /* Details accessed using local lock held */ 25 + bool enable; 26 + struct imsic_vector *move; 27 + }; 28 + 29 + struct imsic_local_priv { 30 + /* Local lock to protect vector enable/move variables and dirty bitmap */ 31 + raw_spinlock_t lock; 32 + 33 + /* Local dirty bitmap for synchronization */ 34 + unsigned long *dirty_bitmap; 35 + 36 + #ifdef CONFIG_SMP 37 + /* Local timer for synchronization */ 38 + struct timer_list timer; 39 + #endif 40 + 41 + /* Local vector table */ 42 + struct imsic_vector *vectors; 43 + }; 44 + 45 + struct imsic_priv { 46 + /* Device details */ 47 + struct fwnode_handle *fwnode; 48 + 49 + /* Global configuration common for all HARTs */ 50 + struct imsic_global_config global; 51 + 52 + /* Per-CPU state */ 53 + struct imsic_local_priv __percpu *lpriv; 54 + 55 + /* State of IRQ matrix allocator */ 56 + raw_spinlock_t matrix_lock; 57 + struct irq_matrix *matrix; 58 + 59 + /* IRQ domains (created by platform driver) */ 60 + struct irq_domain *base_domain; 61 + }; 62 + 63 + extern struct imsic_priv *imsic; 64 + 65 + void __imsic_eix_update(unsigned long base_id, unsigned long num_id, bool pend, bool val); 66 + 67 + static inline void __imsic_id_set_enable(unsigned long id) 68 + { 69 + __imsic_eix_update(id, 1, false, true); 70 + } 71 + 72 + static inline void __imsic_id_clear_enable(unsigned long id) 73 + { 74 + __imsic_eix_update(id, 1, false, false); 75 + } 76 + 77 + void imsic_local_sync_all(void); 78 + void imsic_local_delivery(bool enable); 79 + 80 + void imsic_vector_mask(struct imsic_vector *vec); 81 + void imsic_vector_unmask(struct imsic_vector *vec); 82 + 83 + static inline bool imsic_vector_isenabled(struct imsic_vector *vec) 84 + { 85 + return READ_ONCE(vec->enable); 86 + } 87 + 88 + static inline struct imsic_vector *imsic_vector_get_move(struct imsic_vector *vec) 89 + { 90 + return READ_ONCE(vec->move); 91 + } 92 + 93 + void imsic_vector_move(struct imsic_vector *old_vec, struct imsic_vector *new_vec); 94 + 95 + struct imsic_vector *imsic_vector_from_local_id(unsigned int cpu, unsigned int local_id); 96 + 97 + struct imsic_vector *imsic_vector_alloc(unsigned int hwirq, const struct cpumask *mask); 98 + void imsic_vector_free(struct imsic_vector *vector); 99 + 100 + void imsic_vector_debug_show(struct seq_file *m, struct imsic_vector *vec, int ind); 101 + void imsic_vector_debug_show_summary(struct seq_file *m, int ind); 102 + 103 + void imsic_state_online(void); 104 + void imsic_state_offline(void); 105 + int imsic_setup_state(struct fwnode_handle *fwnode); 106 + 107 + #endif
+1
include/linux/cpuhotplug.h
··· 146 146 CPUHP_AP_IRQ_MIPS_GIC_STARTING, 147 147 CPUHP_AP_IRQ_LOONGARCH_STARTING, 148 148 CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING, 149 + CPUHP_AP_IRQ_RISCV_IMSIC_STARTING, 149 150 CPUHP_AP_ARM_MVEBU_COHERENCY, 150 151 CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING, 151 152 CPUHP_AP_PERF_X86_STARTING,
+87
include/linux/irqchip/riscv-imsic.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_IMSIC_H 7 + #define __LINUX_IRQCHIP_RISCV_IMSIC_H 8 + 9 + #include <linux/types.h> 10 + #include <linux/bitops.h> 11 + #include <asm/csr.h> 12 + 13 + #define IMSIC_MMIO_PAGE_SHIFT 12 14 + #define IMSIC_MMIO_PAGE_SZ BIT(IMSIC_MMIO_PAGE_SHIFT) 15 + #define IMSIC_MMIO_PAGE_LE 0x00 16 + #define IMSIC_MMIO_PAGE_BE 0x04 17 + 18 + #define IMSIC_MIN_ID 63 19 + #define IMSIC_MAX_ID 2048 20 + 21 + #define IMSIC_EIDELIVERY 0x70 22 + 23 + #define IMSIC_EITHRESHOLD 0x72 24 + 25 + #define IMSIC_EIP0 0x80 26 + #define IMSIC_EIP63 0xbf 27 + #define IMSIC_EIPx_BITS 32 28 + 29 + #define IMSIC_EIE0 0xc0 30 + #define IMSIC_EIE63 0xff 31 + #define IMSIC_EIEx_BITS 32 32 + 33 + #define IMSIC_FIRST IMSIC_EIDELIVERY 34 + #define IMSIC_LAST IMSIC_EIE63 35 + 36 + #define IMSIC_MMIO_SETIPNUM_LE 0x00 37 + #define IMSIC_MMIO_SETIPNUM_BE 0x04 38 + 39 + struct imsic_local_config { 40 + phys_addr_t msi_pa; 41 + void __iomem *msi_va; 42 + }; 43 + 44 + struct imsic_global_config { 45 + /* 46 + * MSI Target Address Scheme 47 + * 48 + * XLEN-1 12 0 49 + * | | | 50 + * ------------------------------------------------------------- 51 + * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index| 0 | 52 + * ------------------------------------------------------------- 53 + */ 54 + 55 + /* Bits representing Guest index, HART index, and Group index */ 56 + u32 guest_index_bits; 57 + u32 hart_index_bits; 58 + u32 group_index_bits; 59 + u32 group_index_shift; 60 + 61 + /* Global base address matching all target MSI addresses */ 62 + phys_addr_t base_addr; 63 + 64 + /* Number of interrupt identities */ 65 + u32 nr_ids; 66 + 67 + /* Number of guest interrupt identities */ 68 + u32 nr_guest_ids; 69 + 70 + /* Per-CPU IMSIC addresses */ 71 + struct imsic_local_config __percpu *local; 72 + }; 73 + 74 + #ifdef CONFIG_RISCV_IMSIC 75 + 76 + const struct imsic_global_config *imsic_get_global_config(void); 77 + 78 + #else 79 + 80 + static inline const struct imsic_global_config *imsic_get_global_config(void) 81 + { 82 + return NULL; 83 + } 84 + 85 + #endif 86 + 87 + #endif