From 3c3ae716ed20ad98e2ab0a2d65a0057467cdcb4b Mon Sep 17 00:00:00 2001 From: Christian Bruel Date: Mon, 2 Oct 2023 14:00:53 +0200 Subject: [PATCH] Add support for stm32mp2 SoC family Define default serial with USART2 TIM2 32bit general timer configured with 5us tick. using arr overflow interrupt to support for 64bit timestamp and the channel1 ccr1 compare register for 32bit timeout. Signed-off-by: Christian Bruel --- .../stm32mp2/platsupport/plat/clock.h | 16 ++ .../stm32mp2/platsupport/plat/i2c.h | 13 ++ .../stm32mp2/platsupport/plat/serial.h | 32 +++ .../stm32mp2/platsupport/plat/timer.h | 70 +++++++ libplatsupport/src/arch/arm/irqchip/gic.c | 1 + libplatsupport/src/plat/stm32mp2/chardev.c | 47 +++++ libplatsupport/src/plat/stm32mp2/ltimer.c | 186 ++++++++++++++++++ libplatsupport/src/plat/stm32mp2/serial.c | 69 +++++++ libplatsupport/src/plat/stm32mp2/timer.c | 114 +++++++++++ 9 files changed, 548 insertions(+) create mode 100644 libplatsupport/plat_include/stm32mp2/platsupport/plat/clock.h create mode 100644 libplatsupport/plat_include/stm32mp2/platsupport/plat/i2c.h create mode 100644 libplatsupport/plat_include/stm32mp2/platsupport/plat/serial.h create mode 100644 libplatsupport/plat_include/stm32mp2/platsupport/plat/timer.h create mode 100644 libplatsupport/src/plat/stm32mp2/chardev.c create mode 100644 libplatsupport/src/plat/stm32mp2/ltimer.c create mode 100644 libplatsupport/src/plat/stm32mp2/serial.c create mode 100644 libplatsupport/src/plat/stm32mp2/timer.c 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..4fabf22bc --- /dev/null +++ b/libplatsupport/src/plat/stm32mp2/serial.c @@ -0,0 +1,69 @@ +/* + * 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; +}