Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2019 Fraunhofer AISEC, * Lukas Auer <lukas.auer@aisec.fraunhofer.de> */ #include <common.h> #include <cpu_func.h> #include <dm.h> #include <asm/barrier.h> #include <asm/smp.h> DECLARE_GLOBAL_DATA_PTR; /** * riscv_send_ipi() - Send inter-processor interrupt (IPI) * * Platform code must provide this function. * * @hart: Hart ID of receiving hart * @return 0 if OK, -ve on error */ extern int riscv_send_ipi(int hart); /** * riscv_clear_ipi() - Clear inter-processor interrupt (IPI) * * Platform code must provide this function. * * @hart: Hart ID of hart to be cleared * @return 0 if OK, -ve on error */ extern int riscv_clear_ipi(int hart); /** * riscv_get_ipi() - Get status of inter-processor interrupt (IPI) * * Platform code must provide this function. * * @hart: Hart ID of hart to be checked * @pending: Pointer to variable with result of the check, * 1 if IPI is pending, 0 otherwise * @return 0 if OK, -ve on error */ extern int riscv_get_ipi(int hart, int *pending); static int send_ipi_many(struct ipi_data *ipi, int wait) { ofnode node, cpus; u32 reg; int ret, pending; cpus = ofnode_path("/cpus"); if (!ofnode_valid(cpus)) { pr_err("Can't find cpus node!\n"); return -EINVAL; } ofnode_for_each_subnode(node, cpus) { /* skip if hart is marked as not available in the device tree */ if (!ofnode_is_available(node)) continue; /* read hart ID of CPU */ ret = ofnode_read_u32(node, "reg", ®); if (ret) continue; /* skip if it is the hart we are running on */ if (reg == gd->arch.boot_hart) continue; if (reg >= CONFIG_NR_CPUS) { pr_err("Hart ID %d is out of range, increase CONFIG_NR_CPUS\n", reg); continue; } #ifndef CONFIG_XIP /* skip if hart is not available */ if (!(gd->arch.available_harts & (1 << reg))) continue; #endif gd->arch.ipi[reg].addr = ipi->addr; gd->arch.ipi[reg].arg0 = ipi->arg0; gd->arch.ipi[reg].arg1 = ipi->arg1; ret = riscv_send_ipi(reg); if (ret) { pr_err("Cannot send IPI to hart %d\n", reg); return ret; } if (wait) { pending = 1; while (pending) { ret = riscv_get_ipi(reg, &pending); if (ret) return ret; } } } return 0; } void handle_ipi(ulong hart) { int ret; void (*smp_function)(ulong hart, ulong arg0, ulong arg1); if (hart >= CONFIG_NR_CPUS) return; __smp_mb(); smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr; invalidate_icache_all(); /* * Clear the IPI to acknowledge the request before jumping to the * requested function. */ ret = riscv_clear_ipi(hart); if (ret) { pr_err("Cannot clear IPI of hart %ld\n", hart); return; } smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1); } int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait) { int ret = 0; struct ipi_data ipi; ipi.addr = addr; ipi.arg0 = arg0; ipi.arg1 = arg1; ret = send_ipi_many(&ipi, wait); return ret; } |