-
Notifications
You must be signed in to change notification settings - Fork 96
libutils: Add support for stm32mp2 SoC family #205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| /* | ||
| * Copyright 2026, STMicroelectronics | ||
| * | ||
| * SPDX-License-Identifier: BSD-2-Clause | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <platsupport/io.h> | ||
|
|
||
| enum i2c_id { | ||
| NI2C | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| /* | ||
| * Copyright 2026, STMicroelectronics | ||
| * | ||
| * SPDX-License-Identifier: BSD-2-Clause | ||
| */ | ||
| #pragma once | ||
|
|
||
| #include <platsupport/ltimer.h> | ||
| #include <platsupport/timer.h> | ||
|
|
||
| /* 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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This structure you can move to timer.c instead, leaving a forward declaration behind. Same for most of the defines. But whatever you prefer. |
||
|
|
||
| #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); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| /* | ||
| * Copyright 2026, STMicroelectronics | ||
| * | ||
| * SPDX-License-Identifier: BSD-2-Clause | ||
| */ | ||
|
|
||
| #include "../../chardev.h" | ||
| #include "../../common.h" | ||
| #include <utils/util.h> | ||
|
|
||
| #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), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to see the other compatible UARTS in this list too, if possible. And if the address is calculable, do that instead of macro string concatenation. If the only difference is the memory address then there is no reason not to add them. No need to test the others in that case either. Even if it doesn't work because of e.g. missing clock or power domain enable, it's still better than nothing. |
||
| }; | ||
|
|
||
| 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; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,203 @@ | ||
| /* | ||
| * Copyright 2026, STMicroelectronics | ||
| * | ||
| * SPDX-License-Identifier: BSD-2-Clause | ||
| */ | ||
| #include <stdio.h> | ||
| #include <assert.h> | ||
|
|
||
| #include <utils/util.h> | ||
| #include <utils/time.h> | ||
|
|
||
| #include <platsupport/plat/timer.h> | ||
| #include <platsupport/io.h> | ||
|
|
||
| #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); | ||
| } | ||
|
Comment on lines
+120
to
+123
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indent off. |
||
| } | ||
|
|
||
| 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; | ||
| } | ||
|
Comment on lines
+157
to
+160
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this check really necessary? Seems a bit pointless to me, something is going to go horribly wrong sooner or later if it is NULL. |
||
|
|
||
| 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; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's good to give an overview here of which timers you're using and how and why (if it's more than one).