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

RISC-V: Add mechanism to provide custom IPI operations

We add mechanism to set custom IPI operations so that CLINT driver
from drivers directory can provide custom IPI operations.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Tested-by: Emil Renner Berhing <kernel@esmil.dk>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Palmer Dabbelt <palmerdabbelt@google.com>
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>

authored by

Anup Patel and committed by
Palmer Dabbelt
cc7f3f72 9123e3a7

+79 -48
-25
arch/riscv/include/asm/clint.h
··· 6 6 #include <linux/smp.h> 7 7 8 8 #ifdef CONFIG_RISCV_M_MODE 9 - extern u32 __iomem *clint_ipi_base; 10 - 11 9 void clint_init_boot_cpu(void); 12 - 13 - static inline void clint_send_ipi_single(unsigned long hartid) 14 - { 15 - writel(1, clint_ipi_base + hartid); 16 - } 17 - 18 - static inline void clint_send_ipi_mask(const struct cpumask *mask) 19 - { 20 - int cpu; 21 - 22 - for_each_cpu(cpu, mask) 23 - clint_send_ipi_single(cpuid_to_hartid_map(cpu)); 24 - } 25 - 26 - static inline void clint_clear_ipi(unsigned long hartid) 27 - { 28 - writel(0, clint_ipi_base + hartid); 29 - } 30 10 #else /* CONFIG_RISCV_M_MODE */ 31 11 #define clint_init_boot_cpu() do { } while (0) 32 - 33 - /* stubs to for code is only reachable under IS_ENABLED(CONFIG_RISCV_M_MODE): */ 34 - void clint_send_ipi_single(unsigned long hartid); 35 - void clint_send_ipi_mask(const struct cpumask *hartid_mask); 36 - void clint_clear_ipi(unsigned long hartid); 37 12 #endif /* CONFIG_RISCV_M_MODE */ 38 13 39 14 #endif /* _ASM_RISCV_CLINT_H */
+19
arch/riscv/include/asm/smp.h
··· 15 15 struct seq_file; 16 16 extern unsigned long boot_cpu_hartid; 17 17 18 + struct riscv_ipi_ops { 19 + void (*ipi_inject)(const struct cpumask *target); 20 + void (*ipi_clear)(void); 21 + }; 22 + 18 23 #ifdef CONFIG_SMP 19 24 /* 20 25 * Mapping between linux logical cpu index and hartid. ··· 44 39 45 40 int riscv_hartid_to_cpuid(int hartid); 46 41 void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out); 42 + 43 + /* Set custom IPI operations */ 44 + void riscv_set_ipi_ops(struct riscv_ipi_ops *ops); 45 + 46 + /* Clear IPI for current CPU */ 47 + void riscv_clear_ipi(void); 47 48 48 49 /* Secondary hart entry */ 49 50 asmlinkage void smp_callin(void); ··· 90 79 { 91 80 cpumask_clear(out); 92 81 cpumask_set_cpu(boot_cpu_hartid, out); 82 + } 83 + 84 + static inline void riscv_set_ipi_ops(struct riscv_ipi_ops *ops) 85 + { 86 + } 87 + 88 + static inline void riscv_clear_ipi(void) 89 + { 93 90 } 94 91 95 92 #endif /* CONFIG_SMP */
+21 -2
arch/riscv/kernel/clint.c
··· 5 5 6 6 #include <linux/io.h> 7 7 #include <linux/of_address.h> 8 + #include <linux/smp.h> 8 9 #include <linux/types.h> 9 10 #include <asm/clint.h> 10 11 #include <asm/csr.h> 11 12 #include <asm/timex.h> 12 - #include <asm/smp.h> 13 13 14 14 /* 15 15 * This is the layout used by the SiFive clint, which is also shared by the qemu ··· 20 20 #define CLINT_TIME_VAL_OFF 0xbff8 21 21 22 22 u32 __iomem *clint_ipi_base; 23 + 24 + static void clint_send_ipi(const struct cpumask *target) 25 + { 26 + unsigned int cpu; 27 + 28 + for_each_cpu(cpu, target) 29 + writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu)); 30 + } 31 + 32 + static void clint_clear_ipi(void) 33 + { 34 + writel(0, clint_ipi_base + cpuid_to_hartid_map(smp_processor_id())); 35 + } 36 + 37 + static struct riscv_ipi_ops clint_ipi_ops = { 38 + .ipi_inject = clint_send_ipi, 39 + .ipi_clear = clint_clear_ipi, 40 + }; 23 41 24 42 void clint_init_boot_cpu(void) 25 43 { ··· 58 40 riscv_time_cmp = base + CLINT_TIME_CMP_OFF; 59 41 riscv_time_val = base + CLINT_TIME_VAL_OFF; 60 42 61 - clint_clear_ipi(boot_cpu_hartid); 43 + clint_clear_ipi(); 44 + riscv_set_ipi_ops(&clint_ipi_ops); 62 45 }
+14
arch/riscv/kernel/sbi.c
··· 547 547 return __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_VERSION); 548 548 } 549 549 550 + static void sbi_send_cpumask_ipi(const struct cpumask *target) 551 + { 552 + struct cpumask hartid_mask; 553 + 554 + riscv_cpuid_to_hartid_mask(target, &hartid_mask); 555 + 556 + sbi_send_ipi(cpumask_bits(&hartid_mask)); 557 + } 558 + 559 + static struct riscv_ipi_ops sbi_ipi_ops = { 560 + .ipi_inject = sbi_send_cpumask_ipi 561 + }; 550 562 551 563 int __init sbi_init(void) 552 564 { ··· 598 586 __sbi_send_ipi = __sbi_send_ipi_v01; 599 587 __sbi_rfence = __sbi_rfence_v01; 600 588 } 589 + 590 + riscv_set_ipi_ops(&sbi_ipi_ops); 601 591 602 592 return 0; 603 593 }
+24 -19
arch/riscv/kernel/smp.c
··· 86 86 wait_for_interrupt(); 87 87 } 88 88 89 + static struct riscv_ipi_ops *ipi_ops; 90 + 91 + void riscv_set_ipi_ops(struct riscv_ipi_ops *ops) 92 + { 93 + ipi_ops = ops; 94 + } 95 + EXPORT_SYMBOL_GPL(riscv_set_ipi_ops); 96 + 97 + void riscv_clear_ipi(void) 98 + { 99 + if (ipi_ops && ipi_ops->ipi_clear) 100 + ipi_ops->ipi_clear(); 101 + 102 + csr_clear(CSR_IP, IE_SIE); 103 + } 104 + EXPORT_SYMBOL_GPL(riscv_clear_ipi); 105 + 89 106 static void send_ipi_mask(const struct cpumask *mask, enum ipi_message_type op) 90 107 { 91 - struct cpumask hartid_mask; 92 108 int cpu; 93 109 94 110 smp_mb__before_atomic(); ··· 112 96 set_bit(op, &ipi_data[cpu].bits); 113 97 smp_mb__after_atomic(); 114 98 115 - riscv_cpuid_to_hartid_mask(mask, &hartid_mask); 116 - if (IS_ENABLED(CONFIG_RISCV_SBI)) 117 - sbi_send_ipi(cpumask_bits(&hartid_mask)); 99 + if (ipi_ops && ipi_ops->ipi_inject) 100 + ipi_ops->ipi_inject(mask); 118 101 else 119 - clint_send_ipi_mask(mask); 102 + pr_warn("SMP: IPI inject method not available\n"); 120 103 } 121 104 122 105 static void send_ipi_single(int cpu, enum ipi_message_type op) 123 106 { 124 - int hartid = cpuid_to_hartid_map(cpu); 125 - 126 107 smp_mb__before_atomic(); 127 108 set_bit(op, &ipi_data[cpu].bits); 128 109 smp_mb__after_atomic(); 129 110 130 - if (IS_ENABLED(CONFIG_RISCV_SBI)) 131 - sbi_send_ipi(cpumask_bits(cpumask_of(hartid))); 111 + if (ipi_ops && ipi_ops->ipi_inject) 112 + ipi_ops->ipi_inject(cpumask_of(cpu)); 132 113 else 133 - clint_send_ipi_single(hartid); 134 - } 135 - 136 - static inline void clear_ipi(void) 137 - { 138 - if (IS_ENABLED(CONFIG_RISCV_SBI)) 139 - csr_clear(CSR_IP, IE_SIE); 140 - else 141 - clint_clear_ipi(cpuid_to_hartid_map(smp_processor_id())); 114 + pr_warn("SMP: IPI inject method not available\n"); 142 115 } 143 116 144 117 #ifdef CONFIG_IRQ_WORK ··· 145 140 146 141 irq_enter(); 147 142 148 - clear_ipi(); 143 + riscv_clear_ipi(); 149 144 150 145 while (true) { 151 146 unsigned long ops;
+1 -2
arch/riscv/kernel/smpboot.c
··· 147 147 struct mm_struct *mm = &init_mm; 148 148 unsigned int curr_cpuid = smp_processor_id(); 149 149 150 - if (!IS_ENABLED(CONFIG_RISCV_SBI)) 151 - clint_clear_ipi(cpuid_to_hartid_map(smp_processor_id())); 150 + riscv_clear_ipi(); 152 151 153 152 /* All kernel threads share the same mm context. */ 154 153 mmgrab(mm);