diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index a73de773d1df0..c03a71099ac44 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -4,6 +4,7 @@ add_definitions(-D__ZEPHYR_SUPERVISOR__) add_subdirectory(console) add_subdirectory(interrupt_controller) +add_subdirectory(mfd) add_subdirectory_if_kconfig(adc) add_subdirectory_if_kconfig(clock_control) diff --git a/drivers/Kconfig b/drivers/Kconfig index 4ca092c44fae6..20b08c2eaa2a0 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -95,4 +95,6 @@ source "drivers/eeprom/Kconfig" source "drivers/peci/Kconfig" +source "drivers/mfd/Kconfig" + endmenu diff --git a/drivers/mfd/CMakeLists.txt b/drivers/mfd/CMakeLists.txt new file mode 100644 index 0000000000000..b76d547d882c6 --- /dev/null +++ b/drivers/mfd/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_sources_ifdef(CONFIG_MFD_TIMER_STM32 mfd_timer_stm32.c) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig new file mode 100644 index 0000000000000..2ccf3520e9b84 --- /dev/null +++ b/drivers/mfd/Kconfig @@ -0,0 +1,16 @@ +# MFD configuration options + +# Copyright (c) 2019 Max van Kessel +# SPDX-License-Identifier: Apache-2.0 + +menu "Multifunction Device Drivers" + +config MFD_TIMER_STM32 + bool "STM32 MCU timer driver" + depends on SOC_FAMILY_STM32 + help + Enable This option enables the base timer driver for STM32 family of + processors. Say y if you wish to use timer interface on STM32 + MCU. + +endmenu \ No newline at end of file diff --git a/drivers/mfd/mfd_timer_stm32.c b/drivers/mfd/mfd_timer_stm32.c new file mode 100644 index 0000000000000..78ee372334b7f --- /dev/null +++ b/drivers/mfd/mfd_timer_stm32.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2019 Max van Kessel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#define DEV_CFG(dev) \ + ((const struct mfd_timer_stm32_config * const) \ + (dev)->config->config_info) +#define DEV_DATA(dev) \ + ((struct mfd_timer_stm32_data * const)(dev)->driver_data) + +struct mfd_timer_stm32_config { + u32_t tim_base; /**< Timer base */ + struct stm32_pclken pclken; /**< subsystem driving this peripheral */ + + u8_t align_mode; + u8_t dir; + u8_t msm; + u8_t slave_mode; + u8_t slave_trig; + u8_t master_trig; + u32_t prescaler; +}; + +enum { + ALIGN_EDGE = 0, + ALIGN_CENTER_1, + ALIGN_CENTER_2, + ALIGN_CENTER_3 +}; + +static inline void tim_stm32_get_clock(struct device *dev) +{ + struct mfd_timer_stm32_data *data = DEV_DATA(dev); + struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME); + + __ASSERT_NO_MSG(clk); + + data->clock = clk; +} + +static u32_t tim_stm32_get_rate(u32_t bus_clk, + clock_control_subsys_t *sub_system) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system); + u32_t tim_clk, apb_psc; + + if (pclken->bus == STM32_CLOCK_BUS_APB1) { + apb_psc = CONFIG_CLOCK_STM32_APB1_PRESCALER; + } +#if !defined(CONFIG_SOC_SERIES_STM32F0X) && !defined(CONFIG_SOC_SERIES_STM32G0X) + else { + apb_psc = CONFIG_CLOCK_STM32_APB2_PRESCALER; + } +#endif + + /* + * If the APB prescaler equals 1, the timer clock frequencies + * are set to the same frequency as that of the APB domain. + * Otherwise, they are set to twice (×2) the frequency of the + * APB domain. + */ + if (apb_psc == 1U) { + tim_clk = bus_clk; + } else { + tim_clk = bus_clk * 2U; + } + + return tim_clk; +} + +static int init(struct device *dev) +{ + int err = -EIO; + const struct mfd_timer_stm32_config *cfg = DEV_CFG(dev); + struct mfd_timer_stm32_data *data = DEV_DATA(dev); + + tim_stm32_get_clock(dev); + + /* enable clock */ + if (clock_control_on(data->clock, + (clock_control_subsys_t *)&cfg->pclken) == 0) { + err = 0; + } + + if (err == 0) { + TIM_TypeDef *tim = (TIM_TypeDef *)cfg->tim_base; + u32_t mode = 0; + + data->tim = tim; + + /* TODO: create binding for me */ + LL_TIM_SetClockSource(tim, LL_TIM_CLOCKSOURCE_INTERNAL); + + if (cfg->prescaler > 0) { + LL_TIM_SetPrescaler(tim, cfg->prescaler - 1); + } else { + LL_TIM_SetPrescaler(tim, 0); + } + + /* TODO find a more consistent solution for all soc's */ + if (cfg->align_mode == ALIGN_EDGE) { + mode = cfg->dir << TIM_CR1_DIR_Pos; + } else { + mode = cfg->align_mode << TIM_CR1_CMS_Pos; + } + + LL_TIM_SetCounterMode(tim, mode); + + if (cfg->msm > 0) { + /* Trigger input delayed to allow synchronization */ + LL_TIM_EnableMasterSlaveMode(tim); + } + + mode = cfg->slave_mode << TIM_SMCR_SMS_Pos; + LL_TIM_SetSlaveMode(tim, mode); + + if (cfg->slave_mode != 0) { + mode = cfg->slave_trig << TIM_SMCR_TS_Pos; + LL_TIM_SetTriggerInput(tim, mode); + } + + mode = cfg->master_trig << TIM_CR2_MMS_Pos; + LL_TIM_SetTriggerOutput(tim, mode); + + /* TODO: create binding for me ?*/ + LL_TIM_DisableARRPreload(tim); + } + + return err; +} + +static void enable(struct device *dev) +{ + const struct mfd_timer_stm32_config *cfg = DEV_CFG(dev); + + /* Timer is enabled by master timer if slave mode is set */ + if ((cfg->slave_mode << TIM_SMCR_SMS_Pos) != LL_TIM_SLAVEMODE_TRIGGER) { + struct mfd_timer_stm32_data *data = DEV_DATA(dev); + + LL_TIM_EnableCounter(data->tim); + } +} + +static void disable(struct device *dev) +{ + struct mfd_timer_stm32_data *data = DEV_DATA(dev); + + LL_TIM_DisableCounter(data->tim); +} + +static int get_cycles_per_sec(struct device *dev, u64_t *cycles) +{ + int err = -EINVAL; + const struct mfd_timer_stm32_config *cfg = DEV_CFG(dev); + struct mfd_timer_stm32_data *data = DEV_DATA(dev); + u32_t bus_clk, tim_clk; + + if (cycles != NULL) { + /* Timer clock depends on APB prescaler */ + err = clock_control_get_rate(data->clock, + (clock_control_subsys_t *)&cfg->pclken, + &bus_clk); + + if (err >= 0) { + tim_clk = tim_stm32_get_rate(bus_clk, + (clock_control_subsys_t *)&cfg->pclken); + + *cycles = (u64_t) (tim_clk / (cfg->prescaler)); + + err = 0; + } + } + return err; +} + +static const struct mfd_timer_stm32 api = { + .enable = enable, + .disable = disable, + .get_cycles_per_sec = get_cycles_per_sec, +}; + +#define TIMER_DEVICE_INIT(n) \ +static struct mfd_timer_stm32_data mfd_timer_stm32_dev_data_ ## n; \ +static const struct mfd_timer_stm32_config mfd_timer_stm32_dev_cfg_ ## n = { \ + .tim_base = DT_INST_## n ##_ST_STM32_TIMERS_BASE_ADDRESS, \ + .align_mode = DT_INST_## n ##_ST_STM32_TIMERS_ST_ALIGN_MODE_ENUM,\ + .dir = DT_INST_## n ##_ST_STM32_TIMERS_ST_COUNTER_DIR_ENUM, \ + .msm = DT_INST_## n ##_ST_STM32_TIMERS_ST_MASTER_SLAVE_MODE, \ + .slave_mode = DT_INST_## n ##_ST_STM32_TIMERS_ST_SLAVE_MODE, \ + .slave_trig = DT_INST_## n ##_ST_STM32_TIMERS_ST_SLAVE_TRIGGER_IN, \ + .master_trig = DT_INST_## n ##_ST_STM32_TIMERS_ST_MASTER_TRIGGER_OUT,\ + .prescaler = DT_INST_## n ##_ST_STM32_TIMERS_ST_PRESCALER, \ + .pclken = { \ + .bus = DT_INST_## n ##_ST_STM32_TIMERS_CLOCK_BUS, \ + .enr = DT_INST_## n ##_ST_STM32_TIMERS_CLOCK_BITS }, \ +}; \ + \ +DEVICE_AND_API_INIT(timer_stm32_ ## n, \ + DT_INST_## n ##_ST_STM32_TIMERS_LABEL, \ + &init, \ + &mfd_timer_stm32_dev_data_ ## n, \ + &mfd_timer_stm32_dev_cfg_ ## n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &api) + +#ifdef DT_INST_0_ST_STM32_TIMERS +TIMER_DEVICE_INIT(0); +#endif + +#ifdef DT_INST_1_ST_STM32_TIMERS +TIMER_DEVICE_INIT(1); +#endif + +#ifdef DT_INST_2_ST_STM32_TIMERS +TIMER_DEVICE_INIT(2); +#endif + +#ifdef DT_INST_3_ST_STM32_TIMERS +TIMER_DEVICE_INIT(3); +#endif + +#ifdef DT_INST_4_ST_STM32_TIMERS +TIMER_DEVICE_INIT(4); +#endif + +#ifdef DT_INST_5_ST_STM32_TIMERS +TIMER_DEVICE_INIT(5); +#endif + +#ifdef DT_INST_6_ST_STM32_TIMERS +TIMER_DEVICE_INIT(6); +#endif + +#ifdef DT_INST_7_ST_STM32_TIMERS +TIMER_DEVICE_INIT(7); +#endif + +#ifdef DT_INST_8_ST_STM32_TIMERS +TIMER_DEVICE_INIT(8); +#endif + +#ifdef DT_INST_9_ST_STM32_TIMERS +TIMER_DEVICE_INIT(9); +#endif + +#ifdef DT_INST_10_ST_STM32_TIMERS +TIMER_DEVICE_INIT(10); +#endif + +#ifdef DT_INST_11_ST_STM32_TIMERS +TIMER_DEVICE_INIT(11); +#endif + +#ifdef DT_INST_12_ST_STM32_TIMERS +TIMER_DEVICE_INIT(12); +#endif + +#ifdef DT_INST_13_ST_STM32_TIMERS +TIMER_DEVICE_INIT(13); +#endif + +#ifdef DT_INST_14_ST_STM32_TIMERS +TIMER_DEVICE_INIT(14); +#endif + +#ifdef DT_INST_15_ST_STM32_TIMERS +TIMER_DEVICE_INIT(15); +#endif + diff --git a/drivers/mfd/mfd_timer_stm32.h b/drivers/mfd/mfd_timer_stm32.h new file mode 100644 index 0000000000000..11c18e282d229 --- /dev/null +++ b/drivers/mfd/mfd_timer_stm32.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019 Max van Kessel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_MFD_MFD_TIMER_STM32_H_ +#define ZEPHYR_DRIVERS_MFD_MFD_TIMER_STM32_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MFD timer data structure + */ +struct mfd_timer_stm32_data { + TIM_TypeDef *tim; /**< Base address */ + struct device *clock; /**< Clock device */ +}; + +typedef void (*mfd_timer_stm32_enable_t)(struct device *dev); + +typedef void (*mfd_timer_stm32_disable_t)(struct device *dev); + +typedef int (*mfd_timer_stm32_get_cycles_per_sec_t)(struct device *dev, + u64_t *cycles); +struct mfd_timer_stm32 { + mfd_timer_stm32_enable_t enable; + mfd_timer_stm32_disable_t disable; + mfd_timer_stm32_get_cycles_per_sec_t get_cycles_per_sec; +}; + +/** + * @brief Enable stm32 timer device + * @param dev The device to enable + */ +static inline void mfd_timer_stm32_enable(struct device *dev) +{ + const struct mfd_timer_stm32 *api = (const struct mfd_timer_stm32 *) + dev->driver_api; + return api->enable(dev); +} + +/** + * @brief Disable stm32 timer device + * @param dev The device to disable + */ +static inline void mfd_timer_stm32_disable(struct device *dev) +{ + const struct mfd_timer_stm32 *api = (const struct mfd_timer_stm32 *) + dev->driver_api; + return api->disable(dev); +} + +/** + * @brief Get the clock rate (cycles per second) + * @param dev Pointer to timer device structure + * @param cycles Pointer to the memory to store clock rate + * (cycles per second) + * @retval 0 for success + * @retval negative errno code + */ +static inline int mfd_timer_stm32_get_cycles_per_sec(struct device *dev, + u64_t *cycles) +{ + const struct mfd_timer_stm32 *api = (const struct mfd_timer_stm32 *) + dev->driver_api; + return api->get_cycles_per_sec(dev, cycles); +} + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_DRIVERS_MFD_MFD_TIMER_STM32_H_ */ diff --git a/dts/bindings/timer/st,stm32-timers.yaml b/dts/bindings/timer/st,stm32-timers.yaml index a309198a26c38..23ec59a42f749 100644 --- a/dts/bindings/timer/st,stm32-timers.yaml +++ b/dts/bindings/timer/st,stm32-timers.yaml @@ -4,15 +4,64 @@ compatible: "st,stm32-timers" include: base.yaml +bus: stm32-timers + properties: - label: - required: true + "#address-cells": + required: false + const: 1 + "#size-cells": + required: false + const: 0 - reg: + label: required: true clocks: required: true - interrupts: - required: false + st,align-mode: + type: string + default: "edge" + description: Edge align or center align(1, 2, etc) + enum: + - "edge" + - "center1" + - "center2" + - "center3" + + st,counter-dir: + type: string + default: "up" + description: Counter direction. + enum: + - "up" + - "down" + + st,master-slave-mode: + type: boolean + description: The effect of an event on the trigger input (TRGI) is + delayed to allow a perfect synchronization between + the current timer and its slaves (through TRGO). (MSM) + + st,slave-mode: + type: int + default: 0 + description: When external signals are selected the active edge of + the trigger signal (TRGI) is linked to the polarity + selected on the external input. (SMS) + + st,slave-trigger-in: + type: int + default: 0 + description: Trigger input to be used to sync the counter (TS) + + st,master-trigger-out: + type: int + default: 0 + description: Information to be sent in master mode to slave timers for sync (MMS) + + st,prescaler: + type: int + default: 0 + description: Clock prescaler at the input of the timer diff --git a/dts/bindings/timer/stm32-timer-device.yaml b/dts/bindings/timer/stm32-timer-device.yaml new file mode 100644 index 0000000000000..5f9a09cbaacca --- /dev/null +++ b/dts/bindings/timer/stm32-timer-device.yaml @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Common fields for stm32-timers devices + +include: base.yaml + +on-bus: stm32-timers + +properties: + reg: + required: false + + label: + required: true diff --git a/soc/arm/st_stm32/stm32f4/soc.h b/soc/arm/st_stm32/stm32f4/soc.h index fdfdbec5fe1e7..5f1ea749c7b13 100644 --- a/soc/arm/st_stm32/stm32f4/soc.h +++ b/soc/arm/st_stm32/stm32f4/soc.h @@ -84,6 +84,10 @@ #include #endif +#ifdef CONFIG_MFD_TIMER_STM32 +#include +#endif + #endif /* !_ASMLANGUAGE */ #endif /* _STM32F4_SOC_H_ */