112438b45SMarouene Boubakri // SPDX-License-Identifier: BSD-2-Clause
212438b45SMarouene Boubakri /*
312438b45SMarouene Boubakri * Copyright 2022-2023 NXP
412438b45SMarouene Boubakri */
512438b45SMarouene Boubakri
612438b45SMarouene Boubakri #include <assert.h>
712438b45SMarouene Boubakri #include <config.h>
812438b45SMarouene Boubakri #include <drivers/plic.h>
912438b45SMarouene Boubakri #include <io.h>
1012438b45SMarouene Boubakri #include <kernel/dt.h>
1112438b45SMarouene Boubakri #include <kernel/interrupt.h>
1212438b45SMarouene Boubakri #include <kernel/panic.h>
1312438b45SMarouene Boubakri #include <mm/core_memprot.h>
1412438b45SMarouene Boubakri #include <mm/core_mmu.h>
1512438b45SMarouene Boubakri #include <trace.h>
1612438b45SMarouene Boubakri
1712438b45SMarouene Boubakri #define PLIC_PRIORITY_OFFSET 0
1812438b45SMarouene Boubakri #define PLIC_PENDING_OFFSET 0x1000
1912438b45SMarouene Boubakri #define PLIC_ENABLE_OFFSET 0x2000
2012438b45SMarouene Boubakri #define PLIC_THRESHOLD_OFFSET 0x200000
2112438b45SMarouene Boubakri #define PLIC_CLAIM_OFFSET 0x200004
2212438b45SMarouene Boubakri
2312438b45SMarouene Boubakri #define PLIC_PRIORITY_SHIFT_PER_SOURCE U(2)
2412438b45SMarouene Boubakri #define PLIC_PENDING_SHIFT_PER_SOURCE U(0)
2512438b45SMarouene Boubakri
2612438b45SMarouene Boubakri #define PLIC_ENABLE_SHIFT_PER_TARGET U(7)
2712438b45SMarouene Boubakri #define PLIC_THRESHOLD_SHIFT_PER_TARGET U(12)
2812438b45SMarouene Boubakri #define PLIC_CLAIM_SHIFT_PER_TARGET U(12)
2912438b45SMarouene Boubakri
3012438b45SMarouene Boubakri #define PLIC_PRIORITY(base, source) \
3112438b45SMarouene Boubakri ((base) + PLIC_PRIORITY_OFFSET + \
3212438b45SMarouene Boubakri SHIFT_U32(source, PLIC_PRIORITY_SHIFT_PER_SOURCE) \
3312438b45SMarouene Boubakri )
3412438b45SMarouene Boubakri #define PLIC_PENDING(base, source) \
3512438b45SMarouene Boubakri ((base) + PLIC_PENDING_OFFSET + \
3612438b45SMarouene Boubakri (4 * ((source) / 32)) \
3712438b45SMarouene Boubakri )
3865a1d74fSAlvin Chang #define PLIC_ENABLE(base, source, context) \
3912438b45SMarouene Boubakri ((base) + PLIC_ENABLE_OFFSET + \
4065a1d74fSAlvin Chang SHIFT_U32(context, PLIC_ENABLE_SHIFT_PER_TARGET) +\
4112438b45SMarouene Boubakri (4 * ((source) / 32)) \
4212438b45SMarouene Boubakri )
4365a1d74fSAlvin Chang #define PLIC_THRESHOLD(base, context) \
4412438b45SMarouene Boubakri ((base) + PLIC_THRESHOLD_OFFSET + \
4565a1d74fSAlvin Chang SHIFT_U32(context, PLIC_THRESHOLD_SHIFT_PER_TARGET) \
4612438b45SMarouene Boubakri )
4765a1d74fSAlvin Chang #define PLIC_COMPLETE(base, context) \
4812438b45SMarouene Boubakri ((base) + PLIC_CLAIM_OFFSET + \
4965a1d74fSAlvin Chang SHIFT_U32(context, PLIC_CLAIM_SHIFT_PER_TARGET) \
5012438b45SMarouene Boubakri )
5165a1d74fSAlvin Chang #define PLIC_CLAIM(base, context) PLIC_COMPLETE(base, context)
5212438b45SMarouene Boubakri
5312438b45SMarouene Boubakri register_phys_mem_pgdir(MEM_AREA_IO_SEC, PLIC_BASE, PLIC_REG_SIZE);
5412438b45SMarouene Boubakri
55f33bc3efSAlvin Chang struct plic_data {
56f33bc3efSAlvin Chang vaddr_t plic_base;
57f33bc3efSAlvin Chang size_t max_it;
58f33bc3efSAlvin Chang struct itr_chip chip;
59f33bc3efSAlvin Chang };
60f33bc3efSAlvin Chang
61f33bc3efSAlvin Chang static struct plic_data plic_data __nex_bss;
62f33bc3efSAlvin Chang
6365a1d74fSAlvin Chang /*
6465a1d74fSAlvin Chang * We assume that each hart has M-mode and S-mode, so the contexts look like:
6565a1d74fSAlvin Chang * PLIC context 0 is hart 0 M-mode
6665a1d74fSAlvin Chang * PLIC context 1 is hart 0 S-mode
6765a1d74fSAlvin Chang * PLIC context 2 is hart 1 M-mode
6865a1d74fSAlvin Chang * PLIC context 3 is hart 1 S-mode
6965a1d74fSAlvin Chang * ...
7065a1d74fSAlvin Chang */
plic_get_context(void)7165a1d74fSAlvin Chang static uint32_t plic_get_context(void)
7265a1d74fSAlvin Chang {
7365a1d74fSAlvin Chang size_t hartid = get_core_pos();
7465a1d74fSAlvin Chang bool smode = IS_ENABLED(CFG_RISCV_S_MODE) ? true : false;
7565a1d74fSAlvin Chang
7665a1d74fSAlvin Chang return hartid * 2 + smode;
7765a1d74fSAlvin Chang }
7865a1d74fSAlvin Chang
7912438b45SMarouene Boubakri static bool __maybe_unused
plic_is_pending(struct plic_data * pd,uint32_t source)8012438b45SMarouene Boubakri plic_is_pending(struct plic_data *pd, uint32_t source)
8112438b45SMarouene Boubakri {
8212438b45SMarouene Boubakri return io_read32(PLIC_PENDING(pd->plic_base, source)) &
8312438b45SMarouene Boubakri BIT(source % 32);
8412438b45SMarouene Boubakri }
8512438b45SMarouene Boubakri
plic_set_pending(struct plic_data * pd,uint32_t source)8612438b45SMarouene Boubakri static void plic_set_pending(struct plic_data *pd, uint32_t source)
8712438b45SMarouene Boubakri {
8812438b45SMarouene Boubakri io_setbits32(PLIC_PENDING(pd->plic_base, source), BIT(source % 32));
8912438b45SMarouene Boubakri }
9012438b45SMarouene Boubakri
plic_enable_interrupt(struct plic_data * pd,uint32_t source)9112438b45SMarouene Boubakri static void plic_enable_interrupt(struct plic_data *pd, uint32_t source)
9212438b45SMarouene Boubakri {
9365a1d74fSAlvin Chang uint32_t context = plic_get_context();
9465a1d74fSAlvin Chang
9565a1d74fSAlvin Chang io_setbits32(PLIC_ENABLE(pd->plic_base, source, context),
9612438b45SMarouene Boubakri BIT(source & 0x1f));
9712438b45SMarouene Boubakri }
9812438b45SMarouene Boubakri
9912438b45SMarouene Boubakri static uint32_t __maybe_unused
plic_get_interrupt_enable(struct plic_data * pd,uint32_t source)10012438b45SMarouene Boubakri plic_get_interrupt_enable(struct plic_data *pd, uint32_t source)
10112438b45SMarouene Boubakri {
10265a1d74fSAlvin Chang uint32_t context = plic_get_context();
10365a1d74fSAlvin Chang
10465a1d74fSAlvin Chang return io_read32(PLIC_ENABLE(pd->plic_base, source, context)) &
10512438b45SMarouene Boubakri BIT(source & 0x1f);
10612438b45SMarouene Boubakri }
10712438b45SMarouene Boubakri
plic_disable_interrupt(struct plic_data * pd,uint32_t source)10812438b45SMarouene Boubakri static void plic_disable_interrupt(struct plic_data *pd, uint32_t source)
10912438b45SMarouene Boubakri {
11065a1d74fSAlvin Chang uint32_t context = plic_get_context();
11165a1d74fSAlvin Chang
11265a1d74fSAlvin Chang io_clrbits32(PLIC_ENABLE(pd->plic_base, source, context),
11312438b45SMarouene Boubakri BIT(source & 0x1f));
11412438b45SMarouene Boubakri }
11512438b45SMarouene Boubakri
plic_get_threshold(struct plic_data * pd)11612438b45SMarouene Boubakri static uint32_t __maybe_unused plic_get_threshold(struct plic_data *pd)
11712438b45SMarouene Boubakri {
11865a1d74fSAlvin Chang uint32_t context = plic_get_context();
11965a1d74fSAlvin Chang
12065a1d74fSAlvin Chang return io_read32(PLIC_THRESHOLD(pd->plic_base, context));
12112438b45SMarouene Boubakri }
12212438b45SMarouene Boubakri
plic_set_threshold(struct plic_data * pd,uint32_t threshold)12312438b45SMarouene Boubakri static void plic_set_threshold(struct plic_data *pd, uint32_t threshold)
12412438b45SMarouene Boubakri {
12565a1d74fSAlvin Chang uint32_t context = plic_get_context();
12665a1d74fSAlvin Chang
12765a1d74fSAlvin Chang io_write32(PLIC_THRESHOLD(pd->plic_base, context), threshold);
12812438b45SMarouene Boubakri }
12912438b45SMarouene Boubakri
13012438b45SMarouene Boubakri static uint32_t __maybe_unused
plic_get_priority(struct plic_data * pd,uint32_t source)13112438b45SMarouene Boubakri plic_get_priority(struct plic_data *pd, uint32_t source)
13212438b45SMarouene Boubakri {
13312438b45SMarouene Boubakri return io_read32(PLIC_PRIORITY(pd->plic_base, source));
13412438b45SMarouene Boubakri }
13512438b45SMarouene Boubakri
plic_set_priority(struct plic_data * pd,uint32_t source,uint32_t priority)13612438b45SMarouene Boubakri static void plic_set_priority(struct plic_data *pd, uint32_t source,
13712438b45SMarouene Boubakri uint32_t priority)
13812438b45SMarouene Boubakri {
13912438b45SMarouene Boubakri io_write32(PLIC_PRIORITY(pd->plic_base, source), priority);
14012438b45SMarouene Boubakri }
14112438b45SMarouene Boubakri
plic_claim_interrupt(struct plic_data * pd)14212438b45SMarouene Boubakri static uint32_t plic_claim_interrupt(struct plic_data *pd)
14312438b45SMarouene Boubakri {
14465a1d74fSAlvin Chang uint32_t context = plic_get_context();
14565a1d74fSAlvin Chang
14665a1d74fSAlvin Chang return io_read32(PLIC_CLAIM(pd->plic_base, context));
14712438b45SMarouene Boubakri }
14812438b45SMarouene Boubakri
plic_complete_interrupt(struct plic_data * pd,uint32_t source)14912438b45SMarouene Boubakri static void plic_complete_interrupt(struct plic_data *pd, uint32_t source)
15012438b45SMarouene Boubakri {
15165a1d74fSAlvin Chang uint32_t context = plic_get_context();
15265a1d74fSAlvin Chang
15365a1d74fSAlvin Chang io_write32(PLIC_CLAIM(pd->plic_base, context), source);
15412438b45SMarouene Boubakri }
15512438b45SMarouene Boubakri
plic_op_configure(struct itr_chip * chip,size_t it,uint32_t type __unused,uint32_t prio)156*2a50ce7dSEtienne Carriere static void plic_op_configure(struct itr_chip *chip, size_t it,
157*2a50ce7dSEtienne Carriere uint32_t type __unused, uint32_t prio)
15812438b45SMarouene Boubakri {
15912438b45SMarouene Boubakri struct plic_data *pd = container_of(chip, struct plic_data, chip);
16012438b45SMarouene Boubakri
16112438b45SMarouene Boubakri if (it > pd->max_it)
16212438b45SMarouene Boubakri panic();
16312438b45SMarouene Boubakri
16412438b45SMarouene Boubakri plic_disable_interrupt(pd, it);
16512438b45SMarouene Boubakri plic_set_priority(pd, it, prio);
16612438b45SMarouene Boubakri }
16712438b45SMarouene Boubakri
plic_op_enable(struct itr_chip * chip,size_t it)16812438b45SMarouene Boubakri static void plic_op_enable(struct itr_chip *chip, size_t it)
16912438b45SMarouene Boubakri {
17012438b45SMarouene Boubakri struct plic_data *pd = container_of(chip, struct plic_data, chip);
17112438b45SMarouene Boubakri
17212438b45SMarouene Boubakri if (it > pd->max_it)
17312438b45SMarouene Boubakri panic();
17412438b45SMarouene Boubakri
17512438b45SMarouene Boubakri plic_enable_interrupt(pd, it);
17612438b45SMarouene Boubakri }
17712438b45SMarouene Boubakri
plic_op_disable(struct itr_chip * chip,size_t it)17812438b45SMarouene Boubakri static void plic_op_disable(struct itr_chip *chip, size_t it)
17912438b45SMarouene Boubakri {
18012438b45SMarouene Boubakri struct plic_data *pd = container_of(chip, struct plic_data, chip);
18112438b45SMarouene Boubakri
18212438b45SMarouene Boubakri if (it > pd->max_it)
18312438b45SMarouene Boubakri panic();
18412438b45SMarouene Boubakri
18512438b45SMarouene Boubakri plic_disable_interrupt(pd, it);
18612438b45SMarouene Boubakri }
18712438b45SMarouene Boubakri
plic_op_raise_pi(struct itr_chip * chip,size_t it)18812438b45SMarouene Boubakri static void plic_op_raise_pi(struct itr_chip *chip, size_t it)
18912438b45SMarouene Boubakri {
19012438b45SMarouene Boubakri struct plic_data *pd = container_of(chip, struct plic_data, chip);
19112438b45SMarouene Boubakri
19212438b45SMarouene Boubakri if (it > pd->max_it)
19312438b45SMarouene Boubakri panic();
19412438b45SMarouene Boubakri
19512438b45SMarouene Boubakri plic_set_pending(pd, it);
19612438b45SMarouene Boubakri }
19712438b45SMarouene Boubakri
plic_op_raise_sgi(struct itr_chip * chip __unused,size_t it __unused,uint32_t cpu_mask __unused)19812438b45SMarouene Boubakri static void plic_op_raise_sgi(struct itr_chip *chip __unused,
1994a38b437SAlvin Chang size_t it __unused, uint32_t cpu_mask __unused)
20012438b45SMarouene Boubakri {
20112438b45SMarouene Boubakri }
20212438b45SMarouene Boubakri
plic_op_set_affinity(struct itr_chip * chip __unused,size_t it __unused,uint8_t cpu_mask __unused)20312438b45SMarouene Boubakri static void plic_op_set_affinity(struct itr_chip *chip __unused,
20412438b45SMarouene Boubakri size_t it __unused, uint8_t cpu_mask __unused)
20512438b45SMarouene Boubakri {
20612438b45SMarouene Boubakri }
20712438b45SMarouene Boubakri
plic_dt_get_irq(const uint32_t * properties __unused,int count __unused,uint32_t * type __unused,uint32_t * prio __unused)20812438b45SMarouene Boubakri static int plic_dt_get_irq(const uint32_t *properties __unused,
20912438b45SMarouene Boubakri int count __unused, uint32_t *type __unused,
21012438b45SMarouene Boubakri uint32_t *prio __unused)
21112438b45SMarouene Boubakri {
21212438b45SMarouene Boubakri return DT_INFO_INVALID_INTERRUPT;
21312438b45SMarouene Boubakri }
21412438b45SMarouene Boubakri
probe_max_it(vaddr_t plic_base __unused)21512438b45SMarouene Boubakri static size_t probe_max_it(vaddr_t plic_base __unused)
21612438b45SMarouene Boubakri {
21712438b45SMarouene Boubakri return PLIC_NUM_SOURCES;
21812438b45SMarouene Boubakri }
21912438b45SMarouene Boubakri
22012438b45SMarouene Boubakri static const struct itr_ops plic_ops = {
221*2a50ce7dSEtienne Carriere .configure = plic_op_configure,
222a1ee298aSAlvin Chang .mask = plic_op_disable,
223a1ee298aSAlvin Chang .unmask = plic_op_enable,
22412438b45SMarouene Boubakri .enable = plic_op_enable,
22512438b45SMarouene Boubakri .disable = plic_op_disable,
22612438b45SMarouene Boubakri .raise_pi = plic_op_raise_pi,
22712438b45SMarouene Boubakri .raise_sgi = plic_op_raise_sgi,
22812438b45SMarouene Boubakri .set_affinity = plic_op_set_affinity,
22912438b45SMarouene Boubakri };
23012438b45SMarouene Boubakri
plic_init_base_addr(struct plic_data * pd,paddr_t plic_base_pa)231f33bc3efSAlvin Chang static void plic_init_base_addr(struct plic_data *pd, paddr_t plic_base_pa)
23212438b45SMarouene Boubakri {
23312438b45SMarouene Boubakri vaddr_t plic_base = 0;
23412438b45SMarouene Boubakri
23512438b45SMarouene Boubakri assert(cpu_mmu_enabled());
23612438b45SMarouene Boubakri
23712438b45SMarouene Boubakri plic_base = core_mmu_get_va(plic_base_pa, MEM_AREA_IO_SEC,
23812438b45SMarouene Boubakri PLIC_REG_SIZE);
23912438b45SMarouene Boubakri if (!plic_base)
24012438b45SMarouene Boubakri panic();
24112438b45SMarouene Boubakri
24212438b45SMarouene Boubakri pd->plic_base = plic_base;
24312438b45SMarouene Boubakri pd->max_it = probe_max_it(plic_base);
24412438b45SMarouene Boubakri pd->chip.ops = &plic_ops;
24512438b45SMarouene Boubakri
24612438b45SMarouene Boubakri if (IS_ENABLED(CFG_DT))
24712438b45SMarouene Boubakri pd->chip.dt_get_irq = plic_dt_get_irq;
24812438b45SMarouene Boubakri }
24912438b45SMarouene Boubakri
plic_hart_init(void)250f33bc3efSAlvin Chang void plic_hart_init(void)
25112438b45SMarouene Boubakri {
25212438b45SMarouene Boubakri /* TODO: To be called by secondary harts */
25312438b45SMarouene Boubakri }
25412438b45SMarouene Boubakri
plic_init(paddr_t plic_base_pa)255f33bc3efSAlvin Chang void plic_init(paddr_t plic_base_pa)
25612438b45SMarouene Boubakri {
257f33bc3efSAlvin Chang struct plic_data *pd = &plic_data;
25812438b45SMarouene Boubakri size_t n = 0;
25912438b45SMarouene Boubakri
26012438b45SMarouene Boubakri plic_init_base_addr(pd, plic_base_pa);
26112438b45SMarouene Boubakri
26212438b45SMarouene Boubakri for (n = 0; n <= pd->max_it; n++) {
26312438b45SMarouene Boubakri plic_disable_interrupt(pd, n);
26412438b45SMarouene Boubakri plic_set_priority(pd, n, 1);
26512438b45SMarouene Boubakri }
26612438b45SMarouene Boubakri
26712438b45SMarouene Boubakri plic_set_threshold(pd, 0);
268f33bc3efSAlvin Chang
269f33bc3efSAlvin Chang interrupt_main_init(&plic_data.chip);
27012438b45SMarouene Boubakri }
27112438b45SMarouene Boubakri
plic_it_handle(void)272f33bc3efSAlvin Chang void plic_it_handle(void)
27312438b45SMarouene Boubakri {
274f33bc3efSAlvin Chang struct plic_data *pd = &plic_data;
27512438b45SMarouene Boubakri uint32_t id = plic_claim_interrupt(pd);
27612438b45SMarouene Boubakri
277c41ef459SAlvin Chang if (id > 0 && id <= pd->max_it)
27899e2612cSEtienne Carriere interrupt_call_handlers(&pd->chip, id);
27912438b45SMarouene Boubakri else
28012438b45SMarouene Boubakri DMSG("ignoring interrupt %" PRIu32, id);
28112438b45SMarouene Boubakri
28212438b45SMarouene Boubakri plic_complete_interrupt(pd, id);
28312438b45SMarouene Boubakri }
284