Skip to content
Open
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
7 changes: 7 additions & 0 deletions machine/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ pub trait Machinelike {

fn monotonic_now() -> u64;
fn monotonic_freq() -> u64;
fn rtc_raw() -> u64;
fn set_rtc_raw(time: u64) -> i32;
fn init_rtc() -> i32;
// index 0..32, 31 is used by the RTC
fn rtc_backup_register(index: u8) -> u32;
// index 0..32, 31 is used by the RTC
fn set_rtc_backup_register(index: u8, value: u32);
// Returns the frequency of the machine's systick timer in Hz.
fn systick_freq() -> u64;

Expand Down
25 changes: 24 additions & 1 deletion machine/cortex-m/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ pub struct ArmMachine;
impl hal_api::Machinelike for ArmMachine {
fn init() {
unsafe {
bindings::init_hal();
let ret = bindings::init_hal();
if ret != 0 {
panic!("init_hal failed: {}", ret);
}
bindings::init_debug_uart();
bindings::dwt_init();
}
Expand Down Expand Up @@ -84,6 +87,26 @@ impl hal_api::Machinelike for ArmMachine {
unsafe { bindings::monotonic_freq() }
}

fn rtc_raw() -> u64 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, you assume that every HAL exposes an rtc_raw function that returns a BCD-encoded time. I would not make this the public API. I think it makes more sense to expose POSIX time as universal format and make the conversion HAL/driver defined.

unsafe { bindings::rtc_raw() }
}

fn set_rtc_raw(time: u64) -> i32 {
unsafe { bindings::set_rtc_raw(time) }
}

fn init_rtc() -> i32 {
unsafe { bindings::init_rtc() }
}

fn rtc_backup_register(index: u8) -> u32 {
unsafe { bindings::rtc_backup_register(index) }
}

fn set_rtc_backup_register(index: u8, value: u32) {
unsafe { bindings::set_rtc_backup_register(index, value) }
}

fn systick_freq() -> u64 {
unsafe { bindings::systick_freq() }
}
Expand Down
19 changes: 19 additions & 0 deletions machine/cortex-m/src/stub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@ impl hal_api::Machinelike for StubMachine {
0
}

fn rtc_raw() -> u64 {
0
}

fn set_rtc_raw(_time: u64) -> i32 {
0
}

fn init_rtc() -> i32 {
0
}

fn rtc_backup_register(index: u8) -> u32 {
0
}

fn set_rtc_backup_register(index: u8, value: u32) {
}

fn systick_freq() -> u64 {
0
}
Expand Down
222 changes: 215 additions & 7 deletions machine/cortex-m/st/stm32l4/interface/clock.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,148 @@
#include "lib.h"
#include <stdatomic.h>
#include <assert.h>
#include <stm32l4xx_hal.h>
#include "stm32l4xx_hal_rcc.h"
#include "stm32l4xx_hal_rcc_ex.h"
#include <sys/_intsup.h>

static volatile uint64_t monotonic_hi = 0;
static volatile uint32_t tick = 0;

#define RTC_BKP_MAGIC 0x4F534952U

// use msb for the error type
// lower byte(s) contain hal status
enum ErrorTypes : uint64_t {
ERROR_CONTROL_VOLTAGE_SCALING = 0x01U << 56U,
ERROR_RCC_OSC_CONFIG = 0x02U << 56U,
ERROR_RCC_CLOCK_CONFIG = 0x03U << 56U,
ERROR_RTC_INIT_CLOCK_SOURCE = 0x04U << 56U,
ERROR_RTC_INIT = 0x05U << 56U,
ERROR_RTC_GET_TIME = 0x06U << 56U,
ERROR_RTC_GET_DATE = 0x07U << 56U,
ERROR_RTC_SET_TIME = 0x08U << 56U,
ERROR_RTC_SET_DATE = 0x09U << 56U
};

static RTC_HandleTypeDef rtc_handle;

/**
* Try to use LSE, fall back to LSI and enable CSS if both are available.
* @retval HAL_StatusTypeDef codes:
* bit 0-1: selecting LSE clock source
* bit 2-3: selecting LSI clock source
* bit 4-5: HAL_TIMEOUT from waiting for LSI ready
*/
static int init_rtc_clock_source(void)
{
HAL_PWR_EnableBkUpAccess();
int error = 0;

__HAL_RCC_LSI_ENABLE();

__HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_HIGH);
__HAL_RCC_LSE_CONFIG(RCC_LSE_ON);

RCC_PeriphCLKInitTypeDef periph = {0};
periph.PeriphClockSelection = RCC_PERIPHCLK_RTC;

periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
HAL_StatusTypeDef status = HAL_RCCEx_PeriphCLKConfig(&periph);

if (status != HAL_OK) {
error = status;
// fallback to LSI
periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
status = HAL_RCCEx_PeriphCLKConfig(&periph);
// if LSI selection also fails, return both errors
if (status != HAL_OK) {
error |= status << 2;
return error;
}
}

// ensure LSI is ready
uint32_t tickstart = HAL_GetTick();
while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == RESET) {
if ((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) {
error |= HAL_TIMEOUT << 4;
break;
}
}

__HAL_RCC_RTC_ENABLE();

// clock security system requires both LSE and LSI to be enabled.
if (!error) {
HAL_RCCEx_EnableLSECSS();
__HAL_RCC_ENABLE_IT(RCC_IT_LSECSS);
}
HAL_PWR_DisableBkUpAccess();

return error;
}

void handle_css_lse_interrupt()
{
HAL_PWR_EnableBkUpAccess();
__HAL_RCC_CLEAR_IT(RCC_IT_LSECSS);

// The software MUST then disable the LSECSSON bit
HAL_RCCEx_DisableLSECSS();
// stop the defective 32 kHz oscillator (disabling LSEON)
__HAL_RCC_LSE_CONFIG(RCC_LSE_OFF);

// and change the RTC clock source (no clock or LSI or HSE, with RTCSEL)
RCC_PeriphCLKInitTypeDef periph = {0};
periph.PeriphClockSelection = RCC_PERIPHCLK_RTC;
periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
HAL_StatusTypeDef status = HAL_RCCEx_PeriphCLKConfig(&periph);
if (status != HAL_OK) {
// internal clock failed, try again later?
__HAL_RCC_RTC_DISABLE();
}
HAL_PWR_DisableBkUpAccess();
}

uint64_t set_rtc_raw(uint64_t raw);
uint64_t init_rtc(void)
{
__HAL_RCC_PWR_CLK_ENABLE();

int ret = init_rtc_clock_source();
if (ret) {
return ERROR_RTC_INIT_CLOCK_SOURCE | ret;
}

rtc_handle.Instance = RTC;
rtc_handle.Init.HourFormat = RTC_HOURFORMAT_24;
rtc_handle.Init.AsynchPrediv = 0x7FU;
rtc_handle.Init.SynchPrediv = 0x00FFU;
Comment thread
gkuznik marked this conversation as resolved.
rtc_handle.Init.OutPut = RTC_OUTPUT_DISABLE;
rtc_handle.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
rtc_handle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;

ret = HAL_RTC_Init(&rtc_handle);
if (ret != HAL_OK) {
return ERROR_RTC_INIT | ret;
}

if (HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR31) != RTC_BKP_MAGIC) {
// Sat 01.01.2000
unsigned long long time = ((uint64_t)0) |
((uint64_t)0 << 8U) |
((uint64_t)0 << 16U) |
((uint64_t)RTC_WEEKDAY_SATURDAY << 32U) |
((uint64_t)RTC_MONTH_JANUARY << 40U) |
((uint64_t)1 << 48U) |
((uint64_t)0 << 56U);
return set_rtc_raw(time);
}
return 0;
}

static void init_monotonic_timer(void)
{
const uint32_t target_hz = 1000000U;
Expand Down Expand Up @@ -55,16 +193,17 @@ void tim2_hndlr(void)
}
}

void init_clock_cfg(void)
uint64_t init_clock_cfg(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

/* 80 MHz on STM32L4+ => Range 1 normal mode, not boost */
__HAL_RCC_PWR_CLK_ENABLE();

if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) {
while (1) {}
int ret = HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
if (ret != HAL_OK) {
return ERROR_CONTROL_VOLTAGE_SCALING | ret;
}

/* HSI16 -> PLL -> 80 MHz SYSCLK */
Expand All @@ -80,8 +219,9 @@ void init_clock_cfg(void)
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // arbitrary unless you use PLLP
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // arbitrary unless you use PLLQ

if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
while (1) {}
ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
if (ret != HAL_OK) {
return ERROR_RCC_OSC_CONFIG | ret;
}

RCC_ClkInitStruct.ClockType =
Expand All @@ -95,12 +235,14 @@ void init_clock_cfg(void)
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
while (1) {}
ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
if (ret != HAL_OK) {
return ERROR_RCC_CLOCK_CONFIG | ret;
}

SystemCoreClockUpdate();
init_monotonic_timer();
return 0;
}

unsigned long long monotonic_now(void)
Expand Down Expand Up @@ -164,3 +306,69 @@ void do_tick(void)
{
tick++;
}


uint32_t get_rtc_backup_register(uint32_t index)
{
return HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR0 + index);
}

void set_rtc_backup_register(uint32_t index, uint32_t value)
{
HAL_PWR_EnableBkUpAccess();
HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR0 + index, value);
HAL_PWR_DisableBkUpAccess();
}

uint64_t rtc_raw(void)
{
RTC_TimeTypeDef time = {0};
RTC_DateTypeDef date = {0};

int ret = HAL_RTC_GetTime(&rtc_handle, &time, RTC_FORMAT_BCD);
if (ret != HAL_OK) {
return ERROR_RTC_GET_TIME | ret;
}

ret = HAL_RTC_GetDate(&rtc_handle, &date, RTC_FORMAT_BCD);
if (ret != HAL_OK) {
return ERROR_RTC_GET_DATE | ret;
}

return ((uint64_t)time.Hours) |
((uint64_t)time.Minutes << 8U) |
((uint64_t)time.Seconds << 16U) |
((uint64_t)date.WeekDay << 24U) |
((uint64_t)date.Month << 32U) |
((uint64_t)date.Date << 40U) |
((uint64_t)date.Year << 48U);
}

uint64_t set_rtc_raw(uint64_t raw)
{
RTC_TimeTypeDef rtc_time = {0};
RTC_DateTypeDef rtc_date = {0};

rtc_time.Hours = (uint8_t)(raw & 0xFFU);
rtc_time.Minutes = (uint8_t)((raw >> 8U) & 0xFFU);
rtc_time.Seconds = (uint8_t)((raw >> 16U) & 0xFFU);
rtc_time.TimeFormat = RTC_HOURFORMAT_24;

rtc_date.WeekDay = (uint8_t)((raw >> 32U) & 0xFFU);
rtc_date.Month = (uint8_t)((raw >> 40U) & 0xFFU);
rtc_date.Date = (uint8_t)((raw >> 48U) & 0xFFU);
rtc_date.Year = (uint8_t)((raw >> 56U) & 0xFFU);

int ret = HAL_RTC_SetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BCD);
if (ret != HAL_OK) {
return ERROR_RTC_SET_TIME | ret;
}

ret = HAL_RTC_SetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BCD);
if (ret != HAL_OK) {
return ERROR_RTC_SET_DATE | ret;
}

HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR31, RTC_BKP_MAGIC);
return 0;
}
8 changes: 7 additions & 1 deletion machine/cortex-m/st/stm32l4/interface/export.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

// lib.c
unsigned long long systick_freq(void);
void init_hal(void);
int init_hal(void);
__attribute__((noreturn)) void system_reset(void);

// uart.c
Expand Down Expand Up @@ -229,3 +229,9 @@ unsigned long long monotonic_now(void);
unsigned long long monotonic_freq(void);
void delay_us(uint32_t delay_us);
void do_tick(void);
int init_rtc(void);
unsigned long long rtc_raw(void);
int set_rtc_raw(unsigned long long time);

unsigned long rtc_backup_register(unsigned char index);
void set_rtc_backup_register(unsigned char index, unsigned long value);
Loading