|
| 1 | +/* |
| 2 | + * Copyright (c) 2019 Max van Kessel |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <errno.h> |
| 8 | +#include <soc.h> |
| 9 | +#include <device.h> |
| 10 | +#include <clock_control/stm32_clock_control.h> |
| 11 | +#include <mfd/mfd_timer_stm32.h> |
| 12 | + |
| 13 | +#define DEV_CFG(dev) \ |
| 14 | + ((const struct mfd_timer_stm32_config * const) \ |
| 15 | + (dev)->config->config_info) |
| 16 | +#define DEV_DATA(dev) \ |
| 17 | + ((struct mfd_timer_stm32_data * const)(dev)->driver_data) |
| 18 | + |
| 19 | +struct mfd_timer_stm32_config { |
| 20 | + u32_t tim_base; /**< Timer base */ |
| 21 | + struct stm32_pclken pclken; /**< subsystem driving this peripheral */ |
| 22 | + |
| 23 | + u8_t align_mode; |
| 24 | + u8_t dir; |
| 25 | + u8_t msm; |
| 26 | + u8_t slave_mode; |
| 27 | + u8_t slave_trig; |
| 28 | + u8_t master_trig; |
| 29 | + u32_t prescaler; |
| 30 | +}; |
| 31 | + |
| 32 | +enum { |
| 33 | + ALIGN_EDGE = 0, |
| 34 | + ALIGN_CENTER_1, |
| 35 | + ALIGN_CENTER_2, |
| 36 | + ALIGN_CENTER_3 |
| 37 | +}; |
| 38 | + |
| 39 | +static inline void tim_stm32_get_clock(struct device *dev) |
| 40 | +{ |
| 41 | + struct mfd_timer_stm32_data *data = DEV_DATA(dev); |
| 42 | + struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME); |
| 43 | + |
| 44 | + __ASSERT_NO_MSG(clk); |
| 45 | + |
| 46 | + data->clock = clk; |
| 47 | +} |
| 48 | + |
| 49 | +static u32_t tim_stm32_get_rate(u32_t bus_clk, |
| 50 | + clock_control_subsys_t *sub_system) |
| 51 | +{ |
| 52 | + struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system); |
| 53 | + u32_t tim_clk, apb_psc; |
| 54 | + |
| 55 | + if (pclken->bus == STM32_CLOCK_BUS_APB1) { |
| 56 | + apb_psc = CONFIG_CLOCK_STM32_APB1_PRESCALER; |
| 57 | + } |
| 58 | +#if !defined(CONFIG_SOC_SERIES_STM32F0X) && !defined(CONFIG_SOC_SERIES_STM32G0X) |
| 59 | + else { |
| 60 | + apb_psc = CONFIG_CLOCK_STM32_APB2_PRESCALER; |
| 61 | + } |
| 62 | +#endif |
| 63 | + |
| 64 | + /* |
| 65 | + * If the APB prescaler equals 1, the timer clock frequencies |
| 66 | + * are set to the same frequency as that of the APB domain. |
| 67 | + * Otherwise, they are set to twice (×2) the frequency of the |
| 68 | + * APB domain. |
| 69 | + */ |
| 70 | + if (apb_psc == 1U) { |
| 71 | + tim_clk = bus_clk; |
| 72 | + } else { |
| 73 | + tim_clk = bus_clk * 2U; |
| 74 | + } |
| 75 | + |
| 76 | + return tim_clk; |
| 77 | +} |
| 78 | + |
| 79 | +static int init(struct device *dev) |
| 80 | +{ |
| 81 | + int err = -EIO; |
| 82 | + const struct mfd_timer_stm32_config *cfg = DEV_CFG(dev); |
| 83 | + struct mfd_timer_stm32_data *data = DEV_DATA(dev); |
| 84 | + |
| 85 | + tim_stm32_get_clock(dev); |
| 86 | + |
| 87 | + /* enable clock */ |
| 88 | + if (clock_control_on(data->clock, |
| 89 | + (clock_control_subsys_t *)&cfg->pclken) == 0) { |
| 90 | + err = 0; |
| 91 | + } |
| 92 | + |
| 93 | + if (err == 0) { |
| 94 | + TIM_TypeDef *tim = (TIM_TypeDef *)cfg->tim_base; |
| 95 | + u32_t mode = 0; |
| 96 | + |
| 97 | + data->tim = tim; |
| 98 | + |
| 99 | + /* TODO: create binding for me */ |
| 100 | + LL_TIM_SetClockSource(tim, LL_TIM_CLOCKSOURCE_INTERNAL); |
| 101 | + |
| 102 | + if (cfg->prescaler > 0) { |
| 103 | + LL_TIM_SetPrescaler(tim, cfg->prescaler - 1); |
| 104 | + } else { |
| 105 | + LL_TIM_SetPrescaler(tim, 0); |
| 106 | + } |
| 107 | + |
| 108 | + /* TODO find a more consistent solution for all soc's */ |
| 109 | + if (cfg->align_mode == ALIGN_EDGE) { |
| 110 | + mode = cfg->dir << TIM_CR1_DIR_Pos; |
| 111 | + } else { |
| 112 | + mode = cfg->align_mode << TIM_CR1_CMS_Pos; |
| 113 | + } |
| 114 | + |
| 115 | + LL_TIM_SetCounterMode(tim, mode); |
| 116 | + |
| 117 | + if (cfg->msm > 0) { |
| 118 | + /* Trigger input delayed to allow synchronization */ |
| 119 | + LL_TIM_EnableMasterSlaveMode(tim); |
| 120 | + } |
| 121 | + |
| 122 | + mode = cfg->slave_mode << TIM_SMCR_SMS_Pos; |
| 123 | + LL_TIM_SetSlaveMode(tim, mode); |
| 124 | + |
| 125 | + if (cfg->slave_mode != 0) { |
| 126 | + mode = cfg->slave_trig << TIM_SMCR_TS_Pos; |
| 127 | + LL_TIM_SetTriggerInput(tim, mode); |
| 128 | + } |
| 129 | + |
| 130 | + mode = cfg->master_trig << TIM_CR2_MMS_Pos; |
| 131 | + LL_TIM_SetTriggerOutput(tim, mode); |
| 132 | + |
| 133 | + /* TODO: create binding for me ?*/ |
| 134 | + LL_TIM_DisableARRPreload(tim); |
| 135 | + } |
| 136 | + |
| 137 | + return err; |
| 138 | +} |
| 139 | + |
| 140 | +static void enable(struct device *dev) |
| 141 | +{ |
| 142 | + const struct mfd_timer_stm32_config *cfg = DEV_CFG(dev); |
| 143 | + |
| 144 | + /* Timer is enabled by master timer if slave mode is set */ |
| 145 | + if ((cfg->slave_mode << TIM_SMCR_SMS_Pos) != LL_TIM_SLAVEMODE_TRIGGER) { |
| 146 | + struct mfd_timer_stm32_data *data = DEV_DATA(dev); |
| 147 | + |
| 148 | + LL_TIM_EnableCounter(data->tim); |
| 149 | + } |
| 150 | +} |
| 151 | + |
| 152 | +static void disable(struct device *dev) |
| 153 | +{ |
| 154 | + struct mfd_timer_stm32_data *data = DEV_DATA(dev); |
| 155 | + |
| 156 | + LL_TIM_DisableCounter(data->tim); |
| 157 | +} |
| 158 | + |
| 159 | +static int get_cycles_per_sec(struct device *dev, u64_t *cycles) |
| 160 | +{ |
| 161 | + int err = -EINVAL; |
| 162 | + const struct mfd_timer_stm32_config *cfg = DEV_CFG(dev); |
| 163 | + struct mfd_timer_stm32_data *data = DEV_DATA(dev); |
| 164 | + u32_t bus_clk, tim_clk; |
| 165 | + |
| 166 | + if (cycles != NULL) { |
| 167 | + /* Timer clock depends on APB prescaler */ |
| 168 | + err = clock_control_get_rate(data->clock, |
| 169 | + (clock_control_subsys_t *)&cfg->pclken, |
| 170 | + &bus_clk); |
| 171 | + |
| 172 | + if (err >= 0) { |
| 173 | + tim_clk = tim_stm32_get_rate(bus_clk, |
| 174 | + (clock_control_subsys_t *)&cfg->pclken); |
| 175 | + |
| 176 | + *cycles = (u64_t) (tim_clk / (cfg->prescaler)); |
| 177 | + |
| 178 | + err = 0; |
| 179 | + } |
| 180 | + } |
| 181 | + return err; |
| 182 | +} |
| 183 | + |
| 184 | +static const struct mfd_timer_stm32 api = { |
| 185 | + .enable = enable, |
| 186 | + .disable = disable, |
| 187 | + .get_cycles_per_sec = get_cycles_per_sec, |
| 188 | +}; |
| 189 | + |
| 190 | +#define TIMER_DEVICE_INIT(n) \ |
| 191 | +static struct mfd_timer_stm32_data mfd_timer_stm32_dev_data_ ## n; \ |
| 192 | +static const struct mfd_timer_stm32_config mfd_timer_stm32_dev_cfg_ ## n = { \ |
| 193 | + .tim_base = DT_INST_## n ##_ST_STM32_TIMERS_BASE_ADDRESS, \ |
| 194 | + .align_mode = DT_INST_## n ##_ST_STM32_TIMERS_ST_ALIGN_MODE_ENUM,\ |
| 195 | + .dir = DT_INST_## n ##_ST_STM32_TIMERS_ST_COUNTER_DIR_ENUM, \ |
| 196 | + .msm = DT_INST_## n ##_ST_STM32_TIMERS_ST_MASTER_SLAVE_MODE, \ |
| 197 | + .slave_mode = DT_INST_## n ##_ST_STM32_TIMERS_ST_SLAVE_MODE, \ |
| 198 | + .slave_trig = DT_INST_## n ##_ST_STM32_TIMERS_ST_SLAVE_TRIGGER_IN, \ |
| 199 | + .master_trig = DT_INST_## n ##_ST_STM32_TIMERS_ST_MASTER_TRIGGER_OUT,\ |
| 200 | + .prescaler = DT_INST_## n ##_ST_STM32_TIMERS_ST_PRESCALER, \ |
| 201 | + .pclken = { \ |
| 202 | + .bus = DT_INST_## n ##_ST_STM32_TIMERS_CLOCK_BUS, \ |
| 203 | + .enr = DT_INST_## n ##_ST_STM32_TIMERS_CLOCK_BITS }, \ |
| 204 | +}; \ |
| 205 | + \ |
| 206 | +DEVICE_AND_API_INIT(timer_stm32_ ## n, \ |
| 207 | + DT_INST_## n ##_ST_STM32_TIMERS_LABEL, \ |
| 208 | + &init, \ |
| 209 | + &mfd_timer_stm32_dev_data_ ## n, \ |
| 210 | + &mfd_timer_stm32_dev_cfg_ ## n, \ |
| 211 | + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ |
| 212 | + &api) |
| 213 | + |
| 214 | +#ifdef DT_INST_0_ST_STM32_TIMERS |
| 215 | +TIMER_DEVICE_INIT(0); |
| 216 | +#endif |
| 217 | + |
| 218 | +#ifdef DT_INST_1_ST_STM32_TIMERS |
| 219 | +TIMER_DEVICE_INIT(1); |
| 220 | +#endif |
| 221 | + |
| 222 | +#ifdef DT_INST_2_ST_STM32_TIMERS |
| 223 | +TIMER_DEVICE_INIT(2); |
| 224 | +#endif |
| 225 | + |
| 226 | +#ifdef DT_INST_3_ST_STM32_TIMERS |
| 227 | +TIMER_DEVICE_INIT(3); |
| 228 | +#endif |
| 229 | + |
| 230 | +#ifdef DT_INST_4_ST_STM32_TIMERS |
| 231 | +TIMER_DEVICE_INIT(4); |
| 232 | +#endif |
| 233 | + |
| 234 | +#ifdef DT_INST_6_ST_STM32_TIMERS |
| 235 | +TIMER_DEVICE_INIT(6); |
| 236 | +#endif |
| 237 | + |
| 238 | +#ifdef DT_INST_7_ST_STM32_TIMERS |
| 239 | +TIMER_DEVICE_INIT(7); |
| 240 | +#endif |
| 241 | + |
| 242 | +#ifdef DT_INST_8_ST_STM32_TIMERS |
| 243 | +TIMER_DEVICE_INIT(8); |
| 244 | +#endif |
| 245 | + |
| 246 | +#ifdef DT_INST_9_ST_STM32_TIMERS |
| 247 | +TIMER_DEVICE_INIT(9); |
| 248 | +#endif |
| 249 | + |
| 250 | +#ifdef DT_INST_10_ST_STM32_TIMERS |
| 251 | +TIMER_DEVICE_INIT(10); |
| 252 | +#endif |
| 253 | + |
| 254 | +#ifdef DT_INST_11_ST_STM32_TIMERS |
| 255 | +TIMER_DEVICE_INIT(11); |
| 256 | +#endif |
| 257 | + |
| 258 | +#ifdef DT_INST_12_ST_STM32_TIMERS |
| 259 | +TIMER_DEVICE_INIT(12); |
| 260 | +#endif |
| 261 | + |
| 262 | +#ifdef DT_INST_13_ST_STM32_TIMERS |
| 263 | +TIMER_DEVICE_INIT(13); |
| 264 | +#endif |
| 265 | + |
| 266 | +#ifdef DT_INST_14_ST_STM32_TIMERS |
| 267 | +TIMER_DEVICE_INIT(14); |
| 268 | +#endif |
| 269 | + |
| 270 | +#ifdef DT_INST_15_ST_STM32_TIMERS |
| 271 | +TIMER_DEVICE_INIT(15); |
| 272 | +#endif |
| 273 | + |
0 commit comments