/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Andes Technology Corporation * * Authors: * Zong Li * Nylon Chen * Leo Yu-Chi Liang * Yu Chien Peter Lin */ #include #include #include #include #include struct plicsw_data plicsw; static void plicsw_ipi_send(u32 hart_index) { ulong pending_reg; u32 interrupt_id, word_index, pending_bit; u32 target_hart = sbi_hartindex_to_hartid(hart_index); if (plicsw.hart_count <= target_hart) ebreak(); /* * We assign a single bit for each hart. * Bit 0 is hardwired to 0, thus unavailable. * Bit(X+1) indicates that IPI is sent to hartX. */ interrupt_id = target_hart + 1; word_index = interrupt_id / 32; pending_bit = interrupt_id % 32; pending_reg = plicsw.addr + PLICSW_PENDING_BASE + word_index * 4; /* Set target hart's mip.MSIP */ writel_relaxed(BIT(pending_bit), (void *)pending_reg); } static void plicsw_ipi_clear(void) { u32 target_hart = current_hartid(); ulong reg = plicsw.addr + PLICSW_CONTEXT_BASE + PLICSW_CONTEXT_CLAIM + PLICSW_CONTEXT_STRIDE * target_hart; if (plicsw.hart_count <= target_hart) ebreak(); /* Claim */ u32 source = readl((void *)reg); /* A successful claim will clear mip.MSIP */ /* Complete */ writel(source, (void *)reg); } static struct sbi_ipi_device plicsw_ipi = { .name = "andes_plicsw", .ipi_send = plicsw_ipi_send, .ipi_clear = plicsw_ipi_clear }; int plicsw_cold_ipi_init(struct plicsw_data *plicsw) { int rc; u32 interrupt_id, word_index, enable_bit; ulong enable_reg, priority_reg; /* Setup source priority */ for (int i = 0; i < plicsw->hart_count; i++) { priority_reg = plicsw->addr + PLICSW_PRIORITY_BASE + i * 4; writel(1, (void *)priority_reg); } /* * Setup enable for each hart, skip non-existent interrupt ID 0 * which is hardwired to 0. */ for (int i = 0; i < plicsw->hart_count; i++) { interrupt_id = i + 1; word_index = interrupt_id / 32; enable_bit = interrupt_id % 32; enable_reg = plicsw->addr + PLICSW_ENABLE_BASE + PLICSW_ENABLE_STRIDE * i + 4 * word_index; writel(BIT(enable_bit), (void *)enable_reg); } /* Add PLICSW region to the root domain */ rc = sbi_domain_root_add_memrange(plicsw->addr, plicsw->size, PLICSW_REGION_ALIGN, SBI_DOMAIN_MEMREGION_MMIO | SBI_DOMAIN_MEMREGION_M_READABLE | SBI_DOMAIN_MEMREGION_M_WRITABLE); if (rc) return rc; sbi_ipi_set_device(&plicsw_ipi); return 0; }