diff options
-rw-r--r-- | include/sbi/sbi_ipi.h | 34 | ||||
-rw-r--r-- | include/sbi/sbi_tlb.h | 6 | ||||
-rw-r--r-- | lib/sbi/sbi_ipi.c | 113 | ||||
-rw-r--r-- | lib/sbi/sbi_tlb.c | 43 |
4 files changed, 148 insertions, 48 deletions
diff --git a/include/sbi/sbi_ipi.h b/include/sbi/sbi_ipi.h index 4a1bed0..d680148 100644 --- a/include/sbi/sbi_ipi.h +++ b/include/sbi/sbi_ipi.h @@ -14,17 +14,45 @@ /* clang-format off */ -#define SBI_IPI_EVENT_SOFT 0x1 -#define SBI_IPI_EVENT_FENCE 0x2 -#define SBI_IPI_EVENT_HALT 0x10 +#define SBI_IPI_EVENT_MAX __riscv_xlen /* clang-format on */ struct sbi_scratch; +/** IPI event operations or callbacks */ +struct sbi_ipi_event_ops { + /** Name of the IPI event operations */ + char name[32]; + + /** Update callback to save/enqueue data for remote HART + * Note: This is an optional callback and it is called just before + * triggering IPI to remote HART. + */ + int (* update)(struct sbi_scratch *scratch, + struct sbi_scratch *remote_scratch, + u32 remote_hartid, void *data); + + /** Sync callback to wait for remote HART + * Note: This is an optional callback and it is called just after + * triggering IPI to remote HART. + */ + void (* sync)(struct sbi_scratch *scratch); + + /** Process callback to handle IPI event + * Note: This is a mandatory callback and it is called on the + * remote HART after IPI is triggered. + */ + void (* process)(struct sbi_scratch *scratch); +}; + int sbi_ipi_send_many(struct sbi_scratch *scratch, ulong hmask, ulong hbase, u32 event, void *data); +int sbi_ipi_event_create(const struct sbi_ipi_event_ops *ops); + +void sbi_ipi_event_destroy(u32 event); + int sbi_ipi_send_smode(struct sbi_scratch *scratch, ulong hmask, ulong hbase); void sbi_ipi_clear_smode(struct sbi_scratch *scratch); diff --git a/include/sbi/sbi_tlb.h b/include/sbi/sbi_tlb.h index 38a9418..c8d24ef 100644 --- a/include/sbi/sbi_tlb.h +++ b/include/sbi/sbi_tlb.h @@ -43,12 +43,6 @@ struct sbi_tlb_info { #define SBI_TLB_INFO_SIZE sizeof(struct sbi_tlb_info) -int sbi_tlb_fifo_update(struct sbi_scratch *scratch, u32 hartid, void *data); - -void sbi_tlb_fifo_process(struct sbi_scratch *scratch); - -void sbi_tlb_fifo_sync(struct sbi_scratch *scratch); - int sbi_tlb_fifo_request(struct sbi_scratch *scratch, ulong hmask, ulong hbase, struct sbi_tlb_info *tinfo); diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c index 68c3584..42868c5 100644 --- a/lib/sbi/sbi_ipi.c +++ b/lib/sbi/sbi_ipi.c @@ -26,34 +26,43 @@ struct sbi_ipi_data { static unsigned long ipi_data_off; -static int sbi_ipi_send(struct sbi_scratch *scratch, u32 hartid, u32 event, - void *data) +static const struct sbi_ipi_event_ops *ipi_ops_array[SBI_IPI_EVENT_MAX]; + +static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid, + u32 event, void *data) { int ret; struct sbi_scratch *remote_scratch = NULL; const struct sbi_platform *plat = sbi_platform_ptr(scratch); struct sbi_ipi_data *ipi_data; + const struct sbi_ipi_event_ops *ipi_ops; - if (sbi_platform_hart_disabled(plat, hartid)) - return -1; + if ((SBI_IPI_EVENT_MAX <= event) || + !ipi_ops_array[event] || + sbi_platform_hart_disabled(plat, remote_hartid)) + return SBI_EINVAL; + ipi_ops = ipi_ops_array[event]; /* * Set IPI type on remote hart's scratch area and * trigger the interrupt */ - remote_scratch = sbi_hart_id_to_scratch(scratch, hartid); + remote_scratch = sbi_hart_id_to_scratch(scratch, remote_hartid); ipi_data = sbi_scratch_offset_ptr(remote_scratch, ipi_data_off); - if (event == SBI_IPI_EVENT_FENCE) { - ret = sbi_tlb_fifo_update(remote_scratch, hartid, data); + + if (ipi_ops->update) { + ret = ipi_ops->update(scratch, remote_scratch, + remote_hartid, data); if (ret < 0) return ret; } + atomic_raw_set_bit(event, &ipi_data->ipi_type); smp_wmb(); - sbi_platform_ipi_send(plat, hartid); + sbi_platform_ipi_send(plat, remote_hartid); - if (event == SBI_IPI_EVENT_FENCE) - sbi_tlb_fifo_sync(scratch); + if (ipi_ops->sync) + ipi_ops->sync(scratch); return 0; } @@ -101,10 +110,47 @@ int sbi_ipi_send_many(struct sbi_scratch *scratch, ulong hmask, ulong hbase, return 0; } +int sbi_ipi_event_create(const struct sbi_ipi_event_ops *ops) +{ + int i, ret = SBI_ENOSPC; + + if (!ops || !ops->process) + return SBI_EINVAL; + + for (i = 0; i < SBI_IPI_EVENT_MAX; i++) { + if (!ipi_ops_array[i]) { + ret = i; + ipi_ops_array[i] = ops; + break; + } + } + + return ret; +} + +void sbi_ipi_event_destroy(u32 event) +{ + if (SBI_IPI_EVENT_MAX <= event) + return; + + ipi_ops_array[event] = NULL; +} + +static void sbi_ipi_process_smode(struct sbi_scratch *scratch) +{ + csr_set(CSR_MIP, MIP_SSIP); +} + +static struct sbi_ipi_event_ops ipi_smode_ops = { + .name = "IPI_SMODE", + .process = sbi_ipi_process_smode, +}; + +static u32 ipi_smode_event = SBI_IPI_EVENT_MAX; + int sbi_ipi_send_smode(struct sbi_scratch *scratch, ulong hmask, ulong hbase) { - return sbi_ipi_send_many(scratch, hmask, hbase, - SBI_IPI_EVENT_SOFT, NULL); + return sbi_ipi_send_many(scratch, hmask, hbase, ipi_smode_event, NULL); } void sbi_ipi_clear_smode(struct sbi_scratch *scratch) @@ -112,16 +158,28 @@ void sbi_ipi_clear_smode(struct sbi_scratch *scratch) csr_clear(CSR_MIP, MIP_SSIP); } +static void sbi_ipi_process_halt(struct sbi_scratch *scratch) +{ + sbi_exit(scratch); +} + +static struct sbi_ipi_event_ops ipi_halt_ops = { + .name = "IPI_HALT", + .process = sbi_ipi_process_halt, +}; + +static u32 ipi_halt_event = SBI_IPI_EVENT_MAX; + int sbi_ipi_send_halt(struct sbi_scratch *scratch, ulong hmask, ulong hbase) { - return sbi_ipi_send_many(scratch, hmask, hbase, - SBI_IPI_EVENT_HALT, NULL); + return sbi_ipi_send_many(scratch, hmask, hbase, ipi_halt_event, NULL); } void sbi_ipi_process(struct sbi_scratch *scratch) { unsigned long ipi_type; unsigned int ipi_event; + const struct sbi_ipi_event_ops *ipi_ops; const struct sbi_platform *plat = sbi_platform_ptr(scratch); struct sbi_ipi_data *ipi_data = sbi_scratch_offset_ptr(scratch, ipi_data_off); @@ -135,19 +193,9 @@ void sbi_ipi_process(struct sbi_scratch *scratch) if (!(ipi_type & 1UL)) goto skip; - switch (ipi_event) { - case SBI_IPI_EVENT_SOFT: - csr_set(CSR_MIP, MIP_SSIP); - break; - case SBI_IPI_EVENT_FENCE: - sbi_tlb_fifo_process(scratch); - break; - case SBI_IPI_EVENT_HALT: - sbi_exit(scratch); - break; - default: - break; - }; + ipi_ops = ipi_ops_array[ipi_event]; + if (ipi_ops && ipi_ops->process) + ipi_ops->process(scratch); skip: ipi_type = ipi_type >> 1; @@ -165,9 +213,20 @@ int sbi_ipi_init(struct sbi_scratch *scratch, bool cold_boot) "IPI_DATA"); if (!ipi_data_off) return SBI_ENOMEM; + ret = sbi_ipi_event_create(&ipi_smode_ops); + if (ret < 0) + return ret; + ipi_smode_event = ret; + ret = sbi_ipi_event_create(&ipi_halt_ops); + if (ret < 0) + return ret; + ipi_halt_event = ret; } else { if (!ipi_data_off) return SBI_ENOMEM; + if (SBI_IPI_EVENT_MAX <= ipi_smode_event || + SBI_IPI_EVENT_MAX <= ipi_halt_event) + return SBI_ENOSPC; } ipi_data = sbi_scratch_offset_ptr(scratch, ipi_data_off); diff --git a/lib/sbi/sbi_tlb.c b/lib/sbi/sbi_tlb.c index c07f4a4..0d9ba19 100644 --- a/lib/sbi/sbi_tlb.c +++ b/lib/sbi/sbi_tlb.c @@ -222,7 +222,7 @@ static void sbi_tlb_fifo_process_count(struct sbi_scratch *scratch, int count) } } -void sbi_tlb_fifo_process(struct sbi_scratch *scratch) +static void sbi_tlb_fifo_process(struct sbi_scratch *scratch) { struct sbi_tlb_info tinfo; struct sbi_fifo *tlb_fifo = @@ -232,7 +232,7 @@ void sbi_tlb_fifo_process(struct sbi_scratch *scratch) sbi_tlb_entry_process(scratch, &tinfo); } -void sbi_tlb_fifo_sync(struct sbi_scratch *scratch) +static void sbi_tlb_fifo_sync(struct sbi_scratch *scratch) { unsigned long *tlb_sync = sbi_scratch_offset_ptr(scratch, tlb_sync_off); @@ -315,11 +315,12 @@ static int sbi_tlb_fifo_update_cb(void *in, void *data) return ret; } -int sbi_tlb_fifo_update(struct sbi_scratch *rscratch, u32 hartid, void *data) +static int sbi_tlb_fifo_update(struct sbi_scratch *scratch, + struct sbi_scratch *remote_scratch, + u32 remote_hartid, void *data) { int ret; struct sbi_fifo *tlb_fifo_r; - struct sbi_scratch *lscratch; struct sbi_tlb_info *tinfo = data; u32 curr_hartid = sbi_current_hartid(); @@ -337,13 +338,12 @@ int sbi_tlb_fifo_update(struct sbi_scratch *rscratch, u32 hartid, void *data) * If the request is to queue a tlb flush entry for itself * then just do a local flush and return; */ - if (hartid == curr_hartid) { + if (remote_hartid == curr_hartid) { sbi_tlb_local_flush(tinfo); return -1; } - lscratch = sbi_hart_id_to_scratch(rscratch, curr_hartid); - tlb_fifo_r = sbi_scratch_offset_ptr(rscratch, tlb_fifo_off); + tlb_fifo_r = sbi_scratch_offset_ptr(remote_scratch, tlb_fifo_off); ret = sbi_fifo_inplace_update(tlb_fifo_r, data, sbi_tlb_fifo_update_cb); if (ret != SBI_FIFO_UNCHANGED) { @@ -359,23 +359,32 @@ int sbi_tlb_fifo_update(struct sbi_scratch *rscratch, u32 hartid, void *data) * TODO: Introduce a wait/wakeup event mechanism to handle * this properly. */ - sbi_tlb_fifo_process_count(lscratch, 1); - sbi_dprintf(rscratch, "hart%d: hart%d tlb fifo full\n", - curr_hartid, hartid); + sbi_tlb_fifo_process_count(scratch, 1); + sbi_dprintf(remote_scratch, "hart%d: hart%d tlb fifo full\n", + curr_hartid, remote_hartid); } return 0; } +static struct sbi_ipi_event_ops tlb_ops = { + .name = "IPI_TLB", + .update = sbi_tlb_fifo_update, + .sync = sbi_tlb_fifo_sync, + .process = sbi_tlb_fifo_process, +}; + +static u32 tlb_event = SBI_IPI_EVENT_MAX; + int sbi_tlb_fifo_request(struct sbi_scratch *scratch, ulong hmask, ulong hbase, struct sbi_tlb_info *tinfo) { - return sbi_ipi_send_many(scratch, hmask, hbase, - SBI_IPI_EVENT_FENCE, tinfo); + return sbi_ipi_send_many(scratch, hmask, hbase, tlb_event, tinfo); } int sbi_tlb_fifo_init(struct sbi_scratch *scratch, bool cold_boot) { + int ret; void *tlb_mem; unsigned long *tlb_sync; struct sbi_fifo *tlb_q; @@ -400,12 +409,22 @@ int sbi_tlb_fifo_init(struct sbi_scratch *scratch, bool cold_boot) sbi_scratch_free_offset(tlb_sync_off); return SBI_ENOMEM; } + ret = sbi_ipi_event_create(&tlb_ops); + if (ret < 0) { + sbi_scratch_free_offset(tlb_fifo_mem_off); + sbi_scratch_free_offset(tlb_fifo_off); + sbi_scratch_free_offset(tlb_sync_off); + return ret; + } + tlb_event = ret; tlb_range_flush_limit = sbi_platform_tlbr_flush_limit(plat); } else { if (!tlb_sync_off || !tlb_fifo_off || !tlb_fifo_mem_off) return SBI_ENOMEM; + if (SBI_IPI_EVENT_MAX <= tlb_event) + return SBI_ENOSPC; } tlb_sync = sbi_scratch_offset_ptr(scratch, tlb_sync_off); |