xref: /optee_os/core/drivers/plic.c (revision a1ee298a3ea405d82d1de19cc7d34f57c86281df)
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  */
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
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 
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 
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
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 
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 
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 
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
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 
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 
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 
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 
15612438b45SMarouene Boubakri static void plic_op_add(struct itr_chip *chip, size_t it,
15712438b45SMarouene Boubakri 			uint32_t type __unused,
15812438b45SMarouene Boubakri 			uint32_t prio)
15912438b45SMarouene Boubakri {
16012438b45SMarouene Boubakri 	struct plic_data *pd = container_of(chip, struct plic_data, chip);
16112438b45SMarouene Boubakri 
16212438b45SMarouene Boubakri 	if (it > pd->max_it)
16312438b45SMarouene Boubakri 		panic();
16412438b45SMarouene Boubakri 
16512438b45SMarouene Boubakri 	plic_disable_interrupt(pd, it);
16612438b45SMarouene Boubakri 	plic_set_priority(pd, it, prio);
16712438b45SMarouene Boubakri }
16812438b45SMarouene Boubakri 
16912438b45SMarouene Boubakri static void plic_op_enable(struct itr_chip *chip, size_t it)
17012438b45SMarouene Boubakri {
17112438b45SMarouene Boubakri 	struct plic_data *pd = container_of(chip, struct plic_data, chip);
17212438b45SMarouene Boubakri 
17312438b45SMarouene Boubakri 	if (it > pd->max_it)
17412438b45SMarouene Boubakri 		panic();
17512438b45SMarouene Boubakri 
17612438b45SMarouene Boubakri 	plic_enable_interrupt(pd, it);
17712438b45SMarouene Boubakri }
17812438b45SMarouene Boubakri 
17912438b45SMarouene Boubakri static void plic_op_disable(struct itr_chip *chip, size_t it)
18012438b45SMarouene Boubakri {
18112438b45SMarouene Boubakri 	struct plic_data *pd = container_of(chip, struct plic_data, chip);
18212438b45SMarouene Boubakri 
18312438b45SMarouene Boubakri 	if (it > pd->max_it)
18412438b45SMarouene Boubakri 		panic();
18512438b45SMarouene Boubakri 
18612438b45SMarouene Boubakri 	plic_disable_interrupt(pd, it);
18712438b45SMarouene Boubakri }
18812438b45SMarouene Boubakri 
18912438b45SMarouene Boubakri static void plic_op_raise_pi(struct itr_chip *chip, size_t it)
19012438b45SMarouene Boubakri {
19112438b45SMarouene Boubakri 	struct plic_data *pd = container_of(chip, struct plic_data, chip);
19212438b45SMarouene Boubakri 
19312438b45SMarouene Boubakri 	if (it > pd->max_it)
19412438b45SMarouene Boubakri 		panic();
19512438b45SMarouene Boubakri 
19612438b45SMarouene Boubakri 	plic_set_pending(pd, it);
19712438b45SMarouene Boubakri }
19812438b45SMarouene Boubakri 
19912438b45SMarouene Boubakri static void plic_op_raise_sgi(struct itr_chip *chip __unused,
20012438b45SMarouene Boubakri 			      size_t it __unused, uint8_t cpu_mask __unused)
20112438b45SMarouene Boubakri {
20212438b45SMarouene Boubakri }
20312438b45SMarouene Boubakri 
20412438b45SMarouene Boubakri static void plic_op_set_affinity(struct itr_chip *chip __unused,
20512438b45SMarouene Boubakri 				 size_t it __unused, uint8_t cpu_mask __unused)
20612438b45SMarouene Boubakri {
20712438b45SMarouene Boubakri }
20812438b45SMarouene Boubakri 
20912438b45SMarouene Boubakri static int plic_dt_get_irq(const uint32_t *properties __unused,
21012438b45SMarouene Boubakri 			   int count __unused, uint32_t *type __unused,
21112438b45SMarouene Boubakri 			   uint32_t *prio __unused)
21212438b45SMarouene Boubakri {
21312438b45SMarouene Boubakri 	return DT_INFO_INVALID_INTERRUPT;
21412438b45SMarouene Boubakri }
21512438b45SMarouene Boubakri 
21612438b45SMarouene Boubakri static size_t probe_max_it(vaddr_t plic_base __unused)
21712438b45SMarouene Boubakri {
21812438b45SMarouene Boubakri 	return PLIC_NUM_SOURCES;
21912438b45SMarouene Boubakri }
22012438b45SMarouene Boubakri 
22112438b45SMarouene Boubakri static const struct itr_ops plic_ops = {
22212438b45SMarouene Boubakri 	.add = plic_op_add,
223*a1ee298aSAlvin Chang 	.mask = plic_op_disable,
224*a1ee298aSAlvin Chang 	.unmask = plic_op_enable,
22512438b45SMarouene Boubakri 	.enable = plic_op_enable,
22612438b45SMarouene Boubakri 	.disable = plic_op_disable,
22712438b45SMarouene Boubakri 	.raise_pi = plic_op_raise_pi,
22812438b45SMarouene Boubakri 	.raise_sgi = plic_op_raise_sgi,
22912438b45SMarouene Boubakri 	.set_affinity = plic_op_set_affinity,
23012438b45SMarouene Boubakri };
23112438b45SMarouene Boubakri 
232f33bc3efSAlvin Chang static void plic_init_base_addr(struct plic_data *pd, paddr_t plic_base_pa)
23312438b45SMarouene Boubakri {
23412438b45SMarouene Boubakri 	vaddr_t plic_base = 0;
23512438b45SMarouene Boubakri 
23612438b45SMarouene Boubakri 	assert(cpu_mmu_enabled());
23712438b45SMarouene Boubakri 
23812438b45SMarouene Boubakri 	plic_base = core_mmu_get_va(plic_base_pa, MEM_AREA_IO_SEC,
23912438b45SMarouene Boubakri 				    PLIC_REG_SIZE);
24012438b45SMarouene Boubakri 	if (!plic_base)
24112438b45SMarouene Boubakri 		panic();
24212438b45SMarouene Boubakri 
24312438b45SMarouene Boubakri 	pd->plic_base = plic_base;
24412438b45SMarouene Boubakri 	pd->max_it = probe_max_it(plic_base);
24512438b45SMarouene Boubakri 	pd->chip.ops = &plic_ops;
24612438b45SMarouene Boubakri 
24712438b45SMarouene Boubakri 	if (IS_ENABLED(CFG_DT))
24812438b45SMarouene Boubakri 		pd->chip.dt_get_irq = plic_dt_get_irq;
24912438b45SMarouene Boubakri }
25012438b45SMarouene Boubakri 
251f33bc3efSAlvin Chang void plic_hart_init(void)
25212438b45SMarouene Boubakri {
25312438b45SMarouene Boubakri 	/* TODO: To be called by secondary harts */
25412438b45SMarouene Boubakri }
25512438b45SMarouene Boubakri 
256f33bc3efSAlvin Chang void plic_init(paddr_t plic_base_pa)
25712438b45SMarouene Boubakri {
258f33bc3efSAlvin Chang 	struct plic_data *pd = &plic_data;
25912438b45SMarouene Boubakri 	size_t n = 0;
26012438b45SMarouene Boubakri 
26112438b45SMarouene Boubakri 	plic_init_base_addr(pd, plic_base_pa);
26212438b45SMarouene Boubakri 
26312438b45SMarouene Boubakri 	for (n = 0; n <= pd->max_it; n++) {
26412438b45SMarouene Boubakri 		plic_disable_interrupt(pd, n);
26512438b45SMarouene Boubakri 		plic_set_priority(pd, n, 1);
26612438b45SMarouene Boubakri 	}
26712438b45SMarouene Boubakri 
26812438b45SMarouene Boubakri 	plic_set_threshold(pd, 0);
269f33bc3efSAlvin Chang 
270f33bc3efSAlvin Chang 	interrupt_main_init(&plic_data.chip);
27112438b45SMarouene Boubakri }
27212438b45SMarouene Boubakri 
273f33bc3efSAlvin Chang void plic_it_handle(void)
27412438b45SMarouene Boubakri {
275f33bc3efSAlvin Chang 	struct plic_data *pd = &plic_data;
27612438b45SMarouene Boubakri 	uint32_t id = plic_claim_interrupt(pd);
27712438b45SMarouene Boubakri 
27812438b45SMarouene Boubakri 	if (id <= pd->max_it)
27912438b45SMarouene Boubakri 		itr_handle(id);
28012438b45SMarouene Boubakri 	else
28112438b45SMarouene Boubakri 		DMSG("ignoring interrupt %" PRIu32, id);
28212438b45SMarouene Boubakri 
28312438b45SMarouene Boubakri 	plic_complete_interrupt(pd, id);
28412438b45SMarouene Boubakri }
285