1 /*
2 * Copyright (c) 2021 Sun Amar.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT silabs_gecko_pwm
8
9 #include <zephyr/drivers/pwm.h>
10 #include <zephyr/dt-bindings/pwm/pwm.h>
11 #include <em_cmu.h>
12 #include <em_timer.h>
13
14 /** PWM configuration. */
15 struct pwm_gecko_config {
16 TIMER_TypeDef *timer;
17 CMU_Clock_TypeDef clock;
18 uint16_t prescaler;
19 TIMER_Prescale_TypeDef prescale_enum;
20 uint8_t channel;
21 uint8_t location;
22 uint8_t port;
23 uint8_t pin;
24 };
25
pwm_gecko_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)26 static int pwm_gecko_set_cycles(const struct device *dev, uint32_t channel,
27 uint32_t period_cycles, uint32_t pulse_cycles,
28 pwm_flags_t flags)
29 {
30 TIMER_InitCC_TypeDef compare_config = TIMER_INITCC_DEFAULT;
31 const struct pwm_gecko_config *cfg = dev->config;
32
33 if (BUS_RegMaskedRead(&cfg->timer->CC[channel].CTRL,
34 _TIMER_CC_CTRL_MODE_MASK) != timerCCModePWM) {
35
36 #ifdef _TIMER_ROUTE_MASK
37 BUS_RegMaskedWrite(&cfg->timer->ROUTE,
38 _TIMER_ROUTE_LOCATION_MASK,
39 cfg->location << _TIMER_ROUTE_LOCATION_SHIFT);
40 BUS_RegMaskedSet(&cfg->timer->ROUTE, 1 << channel);
41 #elif defined(_TIMER_ROUTELOC0_MASK)
42 BUS_RegMaskedWrite(&cfg->timer->ROUTELOC0,
43 _TIMER_ROUTELOC0_CC0LOC_MASK <<
44 (channel * _TIMER_ROUTELOC0_CC1LOC_SHIFT),
45 cfg->location << (channel * _TIMER_ROUTELOC0_CC1LOC_SHIFT));
46 BUS_RegMaskedSet(&cfg->timer->ROUTEPEN, 1 << channel);
47 #else
48 #error Unsupported device
49 #endif
50
51 compare_config.mode = timerCCModePWM;
52 TIMER_InitCC(cfg->timer, channel, &compare_config);
53 }
54
55 cfg->timer->CC[channel].CTRL |= (flags & PWM_POLARITY_INVERTED) ?
56 TIMER_CC_CTRL_OUTINV : 0;
57
58 TIMER_TopSet(cfg->timer, period_cycles);
59
60 TIMER_CompareBufSet(cfg->timer, channel, pulse_cycles);
61
62 return 0;
63 }
64
pwm_gecko_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)65 static int pwm_gecko_get_cycles_per_sec(const struct device *dev,
66 uint32_t channel, uint64_t *cycles)
67 {
68 const struct pwm_gecko_config *cfg = dev->config;
69
70 *cycles = CMU_ClockFreqGet(cfg->clock) / cfg->prescaler;
71
72 return 0;
73 }
74
75 static const struct pwm_driver_api pwm_gecko_driver_api = {
76 .set_cycles = pwm_gecko_set_cycles,
77 .get_cycles_per_sec = pwm_gecko_get_cycles_per_sec,
78 };
79
pwm_gecko_init(const struct device * dev)80 static int pwm_gecko_init(const struct device *dev)
81 {
82 TIMER_Init_TypeDef timer = TIMER_INIT_DEFAULT;
83 const struct pwm_gecko_config *cfg = dev->config;
84
85 CMU_ClockEnable(cfg->clock, true);
86
87 CMU_ClockEnable(cmuClock_GPIO, true);
88 GPIO_PinModeSet(cfg->port, cfg->pin, gpioModePushPull, 0);
89
90 timer.prescale = cfg->prescale_enum;
91 TIMER_Init(cfg->timer, &timer);
92
93 return 0;
94 }
95
96 #define CLOCK_TIMER(id) _CONCAT(cmuClock_TIMER, id)
97 #define PRESCALING_FACTOR(factor) \
98 ((_CONCAT(timerPrescale, factor)))
99
100 #define PWM_GECKO_INIT(index) \
101 static const struct pwm_gecko_config pwm_gecko_config_##index = { \
102 .timer = (TIMER_TypeDef *)DT_REG_ADDR(DT_INST_PARENT(index)), \
103 .clock = CLOCK_TIMER(index), \
104 .prescaler = DT_INST_PROP(index, prescaler), \
105 .prescale_enum = (TIMER_Prescale_TypeDef) \
106 PRESCALING_FACTOR(DT_INST_PROP(index, prescaler)), \
107 .location = DT_INST_PROP_BY_IDX(index, pin_location, 0), \
108 .port = (GPIO_Port_TypeDef) \
109 DT_INST_PROP_BY_IDX(index, pin_location, 1), \
110 .pin = DT_INST_PROP_BY_IDX(index, pin_location, 2), \
111 }; \
112 \
113 DEVICE_DT_INST_DEFINE(index, &pwm_gecko_init, NULL, NULL, \
114 &pwm_gecko_config_##index, POST_KERNEL, \
115 CONFIG_PWM_INIT_PRIORITY, \
116 &pwm_gecko_driver_api);
117
118 DT_INST_FOREACH_STATUS_OKAY(PWM_GECKO_INIT)
119