From 1bcc001c8b30c4abbc61761a9d3773b80e908c6c 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 USART1 32bit TIM2 timer configured with 5us tick, arr autoreload overflows timestamp and Channel1 ccr1 compare register for timeout. --- .../stm32mp2/platsupport/plat/clock.h | 16 ++ .../stm32mp2/platsupport/plat/i2c.h | 13 ++ .../stm32mp2/platsupport/plat/serial.h | 19 ++ .../stm32mp2/platsupport/plat/timer.h | 77 +++++++ libplatsupport/src/arch/arm/irqchip/gic.c | 1 + libplatsupport/src/plat/stm32mp2/chardev.c | 32 +++ libplatsupport/src/plat/stm32mp2/ltimer.c | 203 ++++++++++++++++++ libplatsupport/src/plat/stm32mp2/serial.c | 71 ++++++ libplatsupport/src/plat/stm32mp2/timer.c | 134 ++++++++++++ 9 files changed, 566 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..02c641d63 --- /dev/null +++ b/libplatsupport/plat_include/stm32mp2/platsupport/plat/serial.h @@ -0,0 +1,19 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#define USART1_PADDR 0x40330000 + +#define DEFAULT_SERIAL_PADDR USART1_PADDR +#define UART_REF_CLK 64000000 + +/* official device names */ +enum chardev_id { + USART1, + /* defaults */ + PS_SERIAL_DEFAULT = USART1 +}; 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..63ba56a90 --- /dev/null +++ b/libplatsupport/plat_include/stm32mp2/platsupport/plat/timer.h @@ -0,0 +1,77 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#pragma once + +#include +#include + +/* Input clock */ +#define TIM2_MHZ 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 + +#define RCC_ON(rcc) (*(volatile uint32_t *)(rcc) = 6) + +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; + uint64_t periodic_ns; + uint32_t cnt_h; /* keep overflow for get_time */ +} stm32_t; + +typedef struct { + /* set in init */ + const char *fdt_path; +} stm32_config_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..c23e71095 --- /dev/null +++ b/libplatsupport/src/plat/stm32mp2/chardev.c @@ -0,0 +1,32 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "../../chardev.h" +#include "../../common.h" +#include + +#define UART_DEFN(devid) { \ + .id = USART##devid, \ + .paddr = USART##devid##_PADDR, \ + .size = BIT(12), \ + .init_fn = &uart_init \ +} + +static const struct dev_defn dev_defn[] = { + UART_DEFN(1), +}; + +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..80a1d1ec2 --- /dev/null +++ b/libplatsupport/src/plat/stm32mp2/ltimer.c @@ -0,0 +1,203 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#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_TRIGGER, + .trigger.number = TIM2_IRQ, + .trigger.trigger = 1 + } +}; + +static int get_time(void *data, uint64_t *time) +{ + stm32_ltimer_t *stm32_ltimer = data; + assert(data != NULL); + assert(time != NULL); + + *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, time; + assert(data != NULL); + stm32_ltimer_t *stm32_ltimer = data; + + switch (type) { + case TIMEOUT_ABSOLUTE: + timeout = ns; + break; + case TIMEOUT_RELATIVE: + case TIMEOUT_PERIODIC: + time = stm32_get_time(&stm32_ltimer->stm32_timer); + 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) +{ + assert(data != NULL); + 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[0], (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) +{ + assert(data != NULL); + + 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; + + if (irq_number != irqs[0].irq.number) { + ZF_LOGE("Invalid IRQ number %ld received.", irq_number); + return EINVAL; + } + + if ((stm32_regs->sr & STM32_TIM_SR_UIF) && (stm32_regs->dier & STM32_TIM_DIER_UIE)) { + stm32_regs->sr &= ~STM32_TIM_SR_UIF; + stm32->cnt_h++; + + if (stm32_ltimer->user_cb_fn) { + stm32_ltimer->user_cb_fn(stm32_ltimer->user_cb_token, LTIMER_OVERFLOW_EVENT); + } + } + + if ((stm32_regs->sr & STM32_TIM_SR_CC1IF) && (stm32_regs->dier & STM32_TIM_DIER_CC1IE)) { + stm32_regs->sr &= ~STM32_TIM_SR_CC1IF; + + if (! stm32->periodic_ns) { + stm32_regs->dier &= ~STM32_TIM_DIER_CC1IE; + } + else { + uint64_t time = stm32_get_time(&stm32_ltimer->stm32_timer); + uint64_t target = stm32->periodic_ns + time; + target = target / TICK_NS; + if (((uint32_t)target > UINT32_MAX) || + (stm32->cnt_h && ((uint32_t)target < stm32->hw->cnt))) { + ZF_LOGW("%llu ticks overflow not supported \n", target); + return ETIME; + } + } + + if (stm32_ltimer->user_cb_fn) { + stm32_ltimer->user_cb_fn(stm32_ltimer->user_cb_token, LTIMER_TIMEOUT_EVENT); + } + + } + + 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; + + if (ltimer == NULL) { + ZF_LOGE("ltimer cannot be NULL"); + return EINVAL; + } + + 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[0], 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[0]; + + stm32_ltimer->irq_id = ps_irq_register(&ops.irq_ops, irqs[0], 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..8ce70f510 --- /dev/null +++ b/libplatsupport/src/plat/stm32mp2/serial.c @@ -0,0 +1,71 @@ +/* + * 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..d81f35246 --- /dev/null +++ b/libplatsupport/src/plat/stm32mp2/timer.c @@ -0,0 +1,134 @@ +/* + * Copyright 2026, STMicroelectronics + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#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) +{ + assert(stm32 != NULL); + assert(stm32->hw != NULL); + + stm32->hw->cnt = 0; + stm32->cnt_h = 0; + + stm32->hw->cr1 |= STM32_TIM_CR1_CEN; + + return 0; +} + +int stm32_stop_timer(stm32_t *stm32) +{ + assert(stm32 != NULL); + assert(stm32->hw != NULL); + + stm32->hw->cr1 &= ~STM32_TIM_CR1_CEN; + + return 0; +} + +int stm32_set_timeout(stm32_t *stm32, uint64_t ns, bool periodic) +{ + uint64_t target = ns / TICK_NS; + + assert(stm32 != NULL); + assert(stm32->hw != NULL); + + /* not supported yet. check from overflowed time domain */ + if (((uint32_t)target > UINT32_MAX) || + (stm32->cnt_h && ((uint32_t)target < stm32->hw->cnt))) { + ZF_LOGW("%llu ticks overflow not supported \n", target); + return ETIME; + } + + stm32->hw->ccr1 = (uint32_t)target; + stm32->hw->sr &= ~STM32_TIM_SR_CC1IF; + stm32->hw->dier |= STM32_TIM_DIER_CC1IE; + stm32->periodic_ns = periodic ? ns : 0L; + + return 0; +} + +uint64_t stm32_get_time(stm32_t *stm32) +{ + uint32_t cnt; + uint64_t cnt2; + uint32_t high1, high2; + + assert(stm32 != NULL); + assert(stm32->hw != NULL); + + if (!stm32->hw->cr1) { + ZF_LOGE("timestamp not started"); + return EIO; + } + + do { + high1 = stm32->cnt_h; + cnt = stm32->hw->cnt; + high2 = stm32->cnt_h; + } while (high1 != high2); + + cnt2 = ((uint64_t)high1 << 32) | cnt; + + return (cnt2 * TICK_NS); +} + +int stm32mp2_timer_init(stm32_t *stm32, ps_io_ops_t ops) +{ + void *rcc = NULL; + ps_io_ops_t* io_ops = &ops; + + if (stm32 == NULL) { + ZF_LOGE("stm32 cannot be null"); + return EINVAL; + } + + MAP_IF_NULL(io_ops, RCC_TIM2, rcc); + if (rcc == NULL) + return EINVAL; + + /* TIM2 RCC */ + RCC_ON(rcc); + + stm32->hw->cr1 &= ~STM32_TIM_CR1_CEN; + + stm32->hw->psc = (TICK_NS * TIM2_MHZ / NS_IN_S) - 1; + stm32->hw->arr = UINT32_MAX; + stm32->hw->sr = 0; + stm32->hw->dier |= STM32_TIM_DIER_UIE; /* interrupt for overflow */ + stm32->hw->ccmr1 = 0; + stm32->hw->ccer |= STM32_TIM_CCER_CC1E; + stm32->hw->dier &= ~STM32_TIM_DIER_CC1IE; + + stm32_start_timer(stm32); + + return 0; +} + +