diff --git a/libplatsupport/plat_include/stm32mp2/platsupport/plat/clock.h b/libplatsupport/plat_include/stm32mp2/platsupport/plat/clock.h new file mode 100644 index 000000000..c96d5fa9a --- /dev/null +++ b/libplatsupport/plat_include/stm32mp2/platsupport/plat/clock.h @@ -0,0 +1,16 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma oncew + +enum clk_id { + CLK_MASTER, + NCLOCKS, +}; + +enum clock_gate { + NCLKGATES +}; diff --git a/libplatsupport/plat_include/stm32mp2/platsupport/plat/i2c.h b/libplatsupport/plat_include/stm32mp2/platsupport/plat/i2c.h new file mode 100644 index 000000000..f1c1e948f --- /dev/null +++ b/libplatsupport/plat_include/stm32mp2/platsupport/plat/i2c.h @@ -0,0 +1,13 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +enum i2c_id { + NI2C +}; diff --git a/libplatsupport/plat_include/stm32mp2/platsupport/plat/serial.h b/libplatsupport/plat_include/stm32mp2/platsupport/plat/serial.h new file mode 100644 index 000000000..88c15bbcd --- /dev/null +++ b/libplatsupport/plat_include/stm32mp2/platsupport/plat/serial.h @@ -0,0 +1,32 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#define USART1_PADDR 0x40330000 +#define USART2_PADDR 0x400e0000 +#define USART3_PADDR 0x400f0000 +#define USART6_PADDR 0x40220000 +#define UART4_PADDR 0x40100000 +#define UART5_PADDR 0x40110000 +#define UART7_PADDR 0x40370000 +#define UART8_PADDR 0x40380000 +#define UART9_PADDR 0x402c0000 + +/* official device names */ +enum chardev_id { + USART1, + USART2, + USART3, + USART6, + UART4, + UART5, + UART7, + UART8, + UART9, + /* defaults */ + PS_SERIAL_DEFAULT = USART2 +}; diff --git a/libplatsupport/plat_include/stm32mp2/platsupport/plat/timer.h b/libplatsupport/plat_include/stm32mp2/platsupport/plat/timer.h new file mode 100644 index 000000000..0a524ea20 --- /dev/null +++ b/libplatsupport/plat_include/stm32mp2/platsupport/plat/timer.h @@ -0,0 +1,70 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#pragma once + +#include +#include + +/* Input clock */ +#define TIM2_HZ 200000000UL +#define TICK_NS 5000UL + +/* RCC TIM2 basic timer */ +#define RCC_PADDR 0x44200000 +#define RCC_TIM2_OFF 0x704 +#define RCC_TIM2_PADDR (RCC_PADDR + RCC_TIM2_OFF) +#define RCC_TIM2_SIZE 4 +#define TIM2_IRQ 137 +#define TIM2_MAP 0x40000000 + +typedef struct stm32_regs { + uint32_t cr1; + uint32_t cr2; + uint32_t smcr; + uint32_t dier; + uint32_t sr; + uint32_t egr; + uint32_t ccmr1; + uint32_t ccmr2; + uint32_t ccer; + uint32_t cnt; + uint32_t psc; + uint32_t arr; + uint32_t rcr; /* n/a for TIM2 */ + uint32_t ccr1; + uint32_t ccr2; + uint32_t ccr3; + uint32_t ccr4; +} stm32_regs_t; + +#define STM32_TIM_CCER_CC1E BIT(0) + +#define STM32_TIM_CR1_CEN BIT(0) +#define STM32_TIM_CR1_UDIS BIT(1) +#define STM32_TIM_CR1_URS BIT(1) +#define STM32_TIM_CR1_OPM BIT(6) +#define STM32_TIM_CR1_ARPE BIT(7) + +#define STM32_TIM_EGR_UG BIT(0) + +#define STM32_TIM_DIER_UIE BIT(0) +#define STM32_TIM_DIER_CC1IE BIT(1) + +#define STM32_TIM_SR_UIF BIT(0) +#define STM32_TIM_SR_CC1IF BIT(1) + +typedef struct { + volatile stm32_regs_t *hw; + uint32_t periodic_t; + uint32_t cnt_h; /* keep overflow for get_time */ +} stm32_t; + +int stm32mp2_timer_init(stm32_t *stm32, ps_io_ops_t ops); +void stm32_timer_reset(stm32_t *stm32); +uint64_t stm32_get_time(stm32_t *stm32); +int stm32_set_timeout(stm32_t *dmt, uint64_t ns, bool periodic); +int stm32_start_timer(stm32_t *stm32); +int stm32_stop_timer(stm32_t *stm32); diff --git a/libplatsupport/src/arch/arm/irqchip/gic.c b/libplatsupport/src/arch/arm/irqchip/gic.c index 8c01b49bd..cf9f7c1ca 100644 --- a/libplatsupport/src/arch/arm/irqchip/gic.c +++ b/libplatsupport/src/arch/arm/irqchip/gic.c @@ -75,6 +75,7 @@ static int parse_arm_gic_interrupts(char *dtb_blob, int node_offset, int intr_co char *arm_gic_compatible_list[] = { "arm,gic-400", + "arm,cortex-a7-gic", "arm,cortex-a9-gic", "arm,cortex-a15-gic", NULL diff --git a/libplatsupport/src/plat/stm32mp2/chardev.c b/libplatsupport/src/plat/stm32mp2/chardev.c new file mode 100644 index 000000000..4f25735cc --- /dev/null +++ b/libplatsupport/src/plat/stm32mp2/chardev.c @@ -0,0 +1,47 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "../../chardev.h" +#include "../../common.h" +#include + +#define UART_DEFN(devid) { \ + .id = UART##devid, \ + .paddr = UART##devid##_PADDR, \ + .size = BIT(12), \ + .init_fn = &uart_init \ +} + +#define USART_DEFN(devid) { \ + .id = USART##devid, \ + .paddr = USART##devid##_PADDR, \ + .size = BIT(12), \ + .init_fn = &uart_init \ +} + +static const struct dev_defn dev_defn[] = { + USART_DEFN(1), + USART_DEFN(2), + USART_DEFN(3), + USART_DEFN(6), + UART_DEFN(4), + UART_DEFN(5), + UART_DEFN(7), + UART_DEFN(8), + UART_DEFN(9), +}; + +struct ps_chardevice * +ps_cdev_init(enum chardev_id id, const ps_io_ops_t *o, struct ps_chardevice *d) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(dev_defn); i++) { + if (dev_defn[i].id == id) { + return (dev_defn[i].init_fn(dev_defn + i, o, d)) ? NULL : d; + } + } + return NULL; +} diff --git a/libplatsupport/src/plat/stm32mp2/ltimer.c b/libplatsupport/src/plat/stm32mp2/ltimer.c new file mode 100644 index 000000000..0cc2bcfa6 --- /dev/null +++ b/libplatsupport/src/plat/stm32mp2/ltimer.c @@ -0,0 +1,186 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include + +#include +#include + +#include +#include + +#include "../../ltimer.h" + +typedef struct { + stm32_t stm32_timer; + irq_id_t irq_id; + timer_callback_data_t callback_data; + ltimer_callback_fn_t user_cb_fn; + void *user_cb_token; + ps_io_ops_t ops; +} stm32_ltimer_t; + +static pmem_region_t pmems = { + .type = PMEM_TYPE_DEVICE, + .base_addr = TIM2_MAP, + .length = PAGE_SIZE_4K +}; + +static ps_irq_t irqs = { + .type = PS_INTERRUPT, + .irq.number = TIM2_IRQ, +}; + +static int get_time(void *data, uint64_t *time) +{ + stm32_ltimer_t *stm32_ltimer = data; + + *time = stm32_get_time(&stm32_ltimer->stm32_timer); + + return 0; +} + +static int set_timeout(void *data, uint64_t ns, timeout_type_t type) +{ + uint64_t timeout; + stm32_ltimer_t *stm32_ltimer = data; + uint64_t time = stm32_get_time(&stm32_ltimer->stm32_timer); + + switch (type) { + case TIMEOUT_ABSOLUTE: + if ((ns - time) / TICK_NS > UINT32_MAX) { + ZF_LOGE("Timeout too far in the future"); + return ETIME; + } + timeout = ns; + break; + case TIMEOUT_RELATIVE: + case TIMEOUT_PERIODIC: + if (ns / TICK_NS > UINT32_MAX) { + ZF_LOGE("Timeout too far in the future"); + return ETIME; + } + timeout = ns + time; + break; + } + + return stm32_set_timeout(&stm32_ltimer->stm32_timer, timeout, type == TIMEOUT_PERIODIC); +} + +static int reset(void *data) +{ + stm32_ltimer_t *stm32_ltimer = data; + + stm32_stop_timer(&stm32_ltimer->stm32_timer); + stm32_start_timer(&stm32_ltimer->stm32_timer); + + return 0; +} + +static void destroy(void *data) +{ + stm32_ltimer_t *stm32_ltimer = data; + + stm32_stop_timer(&stm32_ltimer->stm32_timer); + + if (stm32_ltimer->stm32_timer.hw) { + ps_pmem_unmap(&stm32_ltimer->ops, pmems, (void *) stm32_ltimer->stm32_timer.hw); + } + + if (stm32_ltimer->irq_id != PS_INVALID_IRQ_ID) { + int error = ps_irq_unregister(&stm32_ltimer->ops.irq_ops, stm32_ltimer->irq_id); + ZF_LOGF_IF(error, "Failed to unregister IRQ"); + } + + ps_free(&stm32_ltimer->ops.malloc_ops, sizeof(stm32_ltimer_t), stm32_ltimer); +} + +static int handle_irq(void *data, ps_irq_t *irq) +{ + stm32_ltimer_t *stm32_ltimer = data; + + long irq_number = irq->irq.number; + stm32_t *stm32 = &stm32_ltimer->stm32_timer; + volatile stm32_regs_t *stm32_regs = stm32->hw; + uint32_t sr; + + assert(irq_number == irqs.irq.number); + + sr = stm32_regs->sr; + + if (sr & STM32_TIM_SR_UIF) { + stm32->cnt_h++; + sr &= ~STM32_TIM_SR_UIF; + + if (stm32_ltimer->user_cb_fn) { + stm32_ltimer->user_cb_fn(stm32_ltimer->user_cb_token, LTIMER_OVERFLOW_EVENT); + } + } + + if (sr & STM32_TIM_SR_CC1IF) { + if (stm32->periodic_t) { + stm32->hw->ccr1 += stm32->periodic_t; + } else { + stm32_regs->dier &= ~STM32_TIM_DIER_CC1IE; + } + sr &= ~STM32_TIM_SR_CC1IF; + + if (stm32_ltimer->user_cb_fn) { + stm32_ltimer->user_cb_fn(stm32_ltimer->user_cb_token, LTIMER_TIMEOUT_EVENT); + } + } + + stm32_regs->sr = sr; + + return 0; +} + +int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token) +{ + int rc; + void *stm32_map_base; + + rc = create_ltimer_simple(ltimer, ops, sizeof(stm32_ltimer_t), get_time, + set_timeout, reset, destroy); + if (rc) { + ZF_LOGE("ltimer creation failed"); + return rc; + } + + stm32_ltimer_t *stm32_ltimer = ltimer->data; + stm32_ltimer->ops = ops; + + stm32_t *stm32 = &stm32_ltimer->stm32_timer; + + stm32_ltimer->user_cb_fn = callback; + stm32_ltimer->user_cb_token = callback_token; + + stm32_map_base = ps_pmem_map(&ops, pmems, false, PS_MEM_NORMAL); + if (stm32_map_base == NULL) { + destroy(ltimer->data); + return EINVAL; + } + stm32_ltimer->stm32_timer.hw = stm32_map_base; + + stm32_ltimer->callback_data.ltimer = ltimer; + stm32_ltimer->callback_data.irq_handler = handle_irq; + stm32_ltimer->callback_data.irq = &irqs; + + stm32_ltimer->irq_id = ps_irq_register(&ops.irq_ops, irqs, handle_irq_wrapper, + &stm32_ltimer->callback_data); + if (stm32_ltimer->irq_id < 0) { + destroy(ltimer->data); + return EIO; + } + + rc = stm32mp2_timer_init(&stm32_ltimer->stm32_timer, ops); + if (rc) { + ZF_LOGE("Failed to init stm32 timeout timer"); + destroy(&stm32_ltimer); + return rc; + } + + return 0; +} diff --git a/libplatsupport/src/plat/stm32mp2/serial.c b/libplatsupport/src/plat/stm32mp2/serial.c new file mode 100644 index 000000000..9768f3be6 --- /dev/null +++ b/libplatsupport/src/plat/stm32mp2/serial.c @@ -0,0 +1,70 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include "../../chardev.h" + +#define REG_PTR(base, off) ((volatile uint32_t *)((base) + (off))) + +#define USART_ISR 0x1C +#define USART_RDR 0x24 +#define USART_TDR 0x28 +#define USART_ISR_RXNE BIT(5) +#define USART_ISR_TXE BIT(7) + +int uart_getchar(ps_chardevice_t *d) +{ + int ch = EOF; + + while ((*REG_PTR(d->vaddr, USART_ISR) & USART_ISR_RXNE) == 0); + + ch = *REG_PTR(d->vaddr, USART_RDR) & 0x7f; + + return ch; +} + +int uart_putchar(ps_chardevice_t* d, int c) +{ + while ((*REG_PTR(d->vaddr, USART_ISR) & USART_ISR_TXE) == 0); + + *REG_PTR(d->vaddr, USART_TDR) = c; + if (c == '\n' && (d->flags & SERIAL_AUTO_CR)) { + uart_putchar(d, '\r'); + } + + return c; +} + +static void +uart_handle_irq(ps_chardevice_t* dev) +{ + +} + +int uart_init(const struct dev_defn *defn, + const ps_io_ops_t *ops, + ps_chardevice_t *dev) +{ + memset(dev, 0, sizeof(*dev)); + char *vaddr = chardev_map(defn, ops); + if (vaddr == NULL) { + return -1; + } + + /* Set up all the device properties. */ + dev->id = defn->id; + dev->vaddr = (void*)vaddr; + dev->read = &uart_read; + dev->write = &uart_write; + dev->handle_irq = &uart_handle_irq; + dev->irqs = defn->irqs; + dev->ioops = *ops; + dev->flags = SERIAL_AUTO_CR; + + return 0; +} diff --git a/libplatsupport/src/plat/stm32mp2/timer.c b/libplatsupport/src/plat/stm32mp2/timer.c new file mode 100644 index 000000000..d0ef9f07f --- /dev/null +++ b/libplatsupport/src/plat/stm32mp2/timer.c @@ -0,0 +1,114 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/* Use 32 bit TIM2 general timer to handle 64 bit timestamp with ARR + overflow in up-counting mode. CNT is reset at earch overflow. + 32 bit timeout interrupt is supported with Channel 1 output compare. */ + +#include +#include +#include +#include +#include + +#include "../../services.h" +#include "../../ltimer.h" + +static void print_regs(stm32_t *stm32) +{ + printf("control register 1 >> 0x%08x\n", stm32->hw->cr1); + printf("control register 2 >> 0x%08x\n", stm32->hw->cr2); + printf("dier >> 0x%08x\n", stm32->hw->dier); + printf("status register >> 0x%08x\n", stm32->hw->sr); + printf("egr >> 0x%08x\n", stm32->hw->egr); + printf("ccr1 >> 0x%08x\n", stm32->hw->ccr1); + printf("counter >> 0x%08x\n", stm32->hw->cnt); + printf("prescaler >> 0x%08x\n", stm32->hw->psc); + printf("arr >> 0x%08x\n", stm32->hw->arr); +} + +int stm32_start_timer(stm32_t *stm32) +{ + stm32->hw->cnt = 0; + stm32->cnt_h = 0; + + stm32->hw->cr1 |= STM32_TIM_CR1_CEN; + + return 0; +} + +int stm32_stop_timer(stm32_t *stm32) +{ + stm32->hw->cr1 &= ~STM32_TIM_CR1_CEN; + stm32->hw->dier &= ~STM32_TIM_DIER_CC1IE; + + return 0; +} + +int stm32_set_timeout(stm32_t *stm32, uint64_t timeout, bool periodic) +{ + uint32_t target = timeout / TICK_NS; + + stm32->hw->ccr1 = target; + stm32->hw->sr &= ~STM32_TIM_SR_CC1IF; + stm32->hw->dier |= STM32_TIM_DIER_CC1IE; + stm32->periodic_t = periodic ? target : 0UL; + + return 0; +} + +uint64_t stm32_get_time(stm32_t *stm32) +{ + uint32_t cnt = stm32->hw->cnt; + uint32_t hi = stm32->cnt_h; + + /* Check for pending overflow. */ + if (stm32->hw->sr & STM32_TIM_SR_UIF) { + hi++; + /* In case it happened after the last read */ + cnt = stm32->hw->cnt; + } + + uint64_t cnt2 = ((uint64_t)hi << 32) | cnt; + + return (cnt2 * TICK_NS); +} + +void enable_rcc(void *rcc) +{ + volatile uint32_t *rcc_cfgr = rcc; + + /* reset and enable clock */ + *rcc_cfgr = BIT(0); /* assert */ + *rcc_cfgr = BIT(2) | BIT(1); /* de-assert and enable */ + + /* introduce read-back to wait the clock to propagate */ + while (!(*rcc_cfgr & (BIT(2) | BIT(1)))); +} + +int stm32mp2_timer_init(stm32_t *stm32, ps_io_ops_t ops) +{ + void *rcc = NULL; + ps_io_ops_t *io_ops = &ops; + + /* TIM2 RCC */ + MAP_IF_NULL(io_ops, RCC_TIM2, rcc); + if (rcc == NULL) { + return EINVAL; + } + + enable_rcc(rcc); + + stm32->hw->psc = (uint16_t)((TICK_NS * TIM2_HZ) / NS_IN_S) - 1; + stm32->hw->arr = UINT32_MAX; + stm32->hw->egr = STM32_TIM_EGR_UG; /* force update event */ + stm32->hw->dier = STM32_TIM_DIER_UIE; /* enable interrupt for overflow */ + stm32->hw->ccer = STM32_TIM_CCER_CC1E; /* enable channel1 output */ + + stm32_start_timer(stm32); + + return 0; +}