Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions libplatsupport/plat_include/stm32mp2/platsupport/plat/clock.h
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
};
13 changes: 13 additions & 0 deletions libplatsupport/plat_include/stm32mp2/platsupport/plat/i2c.h
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
};
19 changes: 19 additions & 0 deletions libplatsupport/plat_include/stm32mp2/platsupport/plat/serial.h
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
};
77 changes: 77 additions & 0 deletions libplatsupport/plat_include/stm32mp2/platsupport/plat/timer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2026, STMicroelectronics
*
* SPDX-License-Identifier: BSD-2-Clause
*/
Copy link
Contributor

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).

#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;
Copy link
Contributor

Choose a reason for hiding this comment

The 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);
1 change: 1 addition & 0 deletions libplatsupport/src/arch/arm/irqchip/gic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions libplatsupport/src/plat/stm32mp2/chardev.c
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),
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
}
203 changes: 203 additions & 0 deletions libplatsupport/src/plat/stm32mp2/ltimer.c
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
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
}
Loading