1 /*
2 * Copyright (c) 2023 Zephyr Project
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ti_cc13xx_cc26xx_timer_pwm
8
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/drivers/pinctrl.h>
11 #include <zephyr/drivers/pwm.h>
12
13 #include <driverlib/gpio.h>
14 #include <driverlib/prcm.h>
15 #include <driverlib/timer.h>
16 #include <inc/hw_memmap.h>
17 #include <inc/hw_types.h>
18 #include <ti/drivers/Power.h>
19 #include <ti/drivers/power/PowerCC26XX.h>
20
21 #include <zephyr/logging/log.h>
22 #define LOG_MODULE_NAME pwm_cc13xx_cc26xx_timer
23 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_PWM_LOG_LEVEL);
24
25 /* TODO: Clock frequency can be settable via KConfig, see TOP:PRCM:GPTCLKDIV */
26 #define CPU_FREQ ((uint32_t)DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency))
27
28 /* GPT peripherals in 16 bit mode have maximum 24 counter bits incl. the
29 * prescaler. Count is set to (2^24 - 2) to allow for a glitch free 100% duty
30 * cycle at max. period count.
31 */
32 #define PWM_COUNT_MAX 0xFFFFFE
33 #define PWM_INITIAL_PERIOD PWM_COUNT_MAX
34 #define PWM_INITIAL_DUTY 0U /* initially off */
35
36 struct pwm_cc13xx_cc26xx_data {
37 };
38
39 struct pwm_cc13xx_cc26xx_config {
40 const uint32_t gpt_base; /* GPT register base address */
41 const struct pinctrl_dev_config *pcfg;
42
43 LOG_INSTANCE_PTR_DECLARE(log);
44 };
45
write_value(const struct pwm_cc13xx_cc26xx_config * config,uint32_t value,uint32_t prescale_register,uint32_t value_register)46 static void write_value(const struct pwm_cc13xx_cc26xx_config *config, uint32_t value,
47 uint32_t prescale_register, uint32_t value_register)
48 {
49 /* Upper byte represents the prescaler value. */
50 uint8_t prescaleValue = 0xff & (value >> 16);
51
52 HWREG(config->gpt_base + prescale_register) = prescaleValue;
53
54 /* The remaining bytes represent the load / match value. */
55 HWREG(config->gpt_base + value_register) = value & 0xffff;
56 }
57
set_period_and_pulse(const struct pwm_cc13xx_cc26xx_config * config,uint32_t period,uint32_t pulse)58 static int set_period_and_pulse(const struct pwm_cc13xx_cc26xx_config *config, uint32_t period,
59 uint32_t pulse)
60 {
61 uint32_t match_value = pulse;
62
63 if (pulse == 0U) {
64 TimerDisable(config->gpt_base, TIMER_B);
65 #ifdef CONFIG_PM
66 Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY);
67 #endif
68 match_value = period + 1;
69 }
70
71 /* Fail if period is out of range */
72 if ((period > PWM_COUNT_MAX) || (period == 0)) {
73 LOG_ERR("Period (%d) is out of range.", period);
74 return -EINVAL;
75 }
76
77 /* Compare to new period and fail if invalid */
78 if (period < (match_value - 1) || (match_value < 0)) {
79 LOG_ERR("Period (%d) is shorter than pulse (%d).", period, pulse);
80 return -EINVAL;
81 }
82
83 /* Store new period and update timer */
84 write_value(config, period, GPT_O_TBPR, GPT_O_TBILR);
85 write_value(config, match_value, GPT_O_TBPMR, GPT_O_TBMATCHR);
86
87 if (pulse > 0U) {
88 #ifdef CONFIG_PM
89 Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY);
90 #endif
91 TimerEnable(config->gpt_base, TIMER_B);
92 }
93
94 LOG_DBG("Period and pulse successfully set.");
95 return 0;
96 }
97
set_cycles(const struct device * dev,uint32_t channel,uint32_t period,uint32_t pulse,pwm_flags_t flags)98 static int set_cycles(const struct device *dev, uint32_t channel, uint32_t period, uint32_t pulse,
99 pwm_flags_t flags)
100 {
101 const struct pwm_cc13xx_cc26xx_config *config = dev->config;
102
103 if (channel != 0) {
104 return -EIO;
105 }
106
107 if (flags & PWM_POLARITY_INVERTED) {
108 HWREG(config->gpt_base + GPT_O_CTL) |= GPT_CTL_TBPWML_INVERTED;
109 } else {
110 HWREG(config->gpt_base + GPT_O_CTL) |= GPT_CTL_TBPWML_NORMAL;
111 }
112
113 set_period_and_pulse(config, period, pulse);
114
115 return 0;
116 }
117
get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)118 static int get_cycles_per_sec(const struct device *dev, uint32_t channel, uint64_t *cycles)
119 {
120 if (channel > 0) {
121 return -EIO;
122 }
123
124 if (cycles) {
125 *cycles = CPU_FREQ;
126 }
127
128 return 0;
129 }
130
131 static DEVICE_API(pwm, pwm_driver_api) = {
132 .set_cycles = set_cycles,
133 .get_cycles_per_sec = get_cycles_per_sec,
134 };
135
136 #ifdef CONFIG_PM
get_timer_inst_number(const struct pwm_cc13xx_cc26xx_config * config)137 static int get_timer_inst_number(const struct pwm_cc13xx_cc26xx_config *config)
138 {
139 switch (config->gpt_base) {
140 case GPT0_BASE:
141 return 0;
142 case GPT1_BASE:
143 return 1;
144 case GPT2_BASE:
145 return 2;
146 case GPT3_BASE:
147 return 3;
148 default:
149 CODE_UNREACHABLE;
150 }
151 }
152 #else
get_timer_peripheral(const struct pwm_cc13xx_cc26xx_config * config)153 static int get_timer_peripheral(const struct pwm_cc13xx_cc26xx_config *config)
154 {
155 switch (config->gpt_base) {
156 case GPT0_BASE:
157 return PRCM_PERIPH_TIMER0;
158 case GPT1_BASE:
159 return PRCM_PERIPH_TIMER1;
160 case GPT2_BASE:
161 return PRCM_PERIPH_TIMER2;
162 case GPT3_BASE:
163 return PRCM_PERIPH_TIMER3;
164 default:
165 CODE_UNREACHABLE;
166 }
167 }
168 #endif /* CONFIG_PM */
169
init_pwm(const struct device * dev)170 static int init_pwm(const struct device *dev)
171 {
172 const struct pwm_cc13xx_cc26xx_config *config = dev->config;
173 pinctrl_soc_pin_t pin = config->pcfg->states[0].pins[0];
174 int ret;
175
176 #ifdef CONFIG_PM
177 /* Set dependency on gpio resource to turn on power domains */
178 Power_setDependency(get_timer_inst_number(config));
179 #else
180 /* Enable peripheral power domain. */
181 PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH);
182
183 /* Enable GPIO peripheral. */
184 PRCMPeripheralRunEnable(get_timer_peripheral(config));
185 PRCMPeripheralSleepEnable(get_timer_peripheral(config));
186 PRCMPeripheralDeepSleepEnable(get_timer_peripheral(config));
187
188 /* Load PRCM settings. */
189 PRCMLoadSet();
190 while (!PRCMLoadGet()) {
191 continue;
192 }
193 #endif /* CONFIG_PM */
194
195 ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
196 if (ret < 0) {
197 LOG_ERR("failed to setup PWM pinctrl");
198 return ret;
199 }
200
201 /* Configures the PWM idle output level.
202 *
203 * TODO: Make PWM idle high/low configurable via custom DT PWM flag.
204 */
205 GPIO_writeDio(pin.pin, 0);
206
207 GPIO_setOutputEnableDio(pin.pin, GPIO_OUTPUT_ENABLE);
208
209 /* Peripheral should not be accessed until power domain is on. */
210 while (PRCMPowerDomainsAllOn(PRCM_DOMAIN_PERIPH) != PRCM_DOMAIN_POWER_ON) {
211 continue;
212 }
213
214 TimerDisable(config->gpt_base, TIMER_B);
215
216 HWREG(config->gpt_base + GPT_O_CFG) = GPT_CFG_CFG_16BIT_TIMER;
217 /* Stall timer when debugging.
218 *
219 * TODO: Make debug stall configurable via custom DT prop.
220 */
221 HWREG(config->gpt_base + GPT_O_CTL) |= GPT_CTL_TBSTALL;
222
223 HWREG(config->gpt_base + GPT_O_TBMR) = GPT_TBMR_TBAMS_PWM | GPT_TBMR_TBMRSU_TOUPDATE |
224 GPT_TBMR_TBPWMIE_EN | GPT_TBMR_TBMR_PERIODIC;
225
226 set_period_and_pulse(config, PWM_INITIAL_PERIOD, PWM_INITIAL_DUTY);
227
228 return 0;
229 }
230
231 #define DT_TIMER(idx) DT_INST_PARENT(idx)
232 #define DT_TIMER_BASE_ADDR(idx) (DT_REG_ADDR(DT_TIMER(idx)))
233
234 #define PWM_DEVICE_INIT(idx) \
235 PINCTRL_DT_INST_DEFINE(idx); \
236 LOG_INSTANCE_REGISTER(LOG_MODULE_NAME, idx, CONFIG_PWM_LOG_LEVEL); \
237 static const struct pwm_cc13xx_cc26xx_config pwm_cc13xx_cc26xx_##idx##_config = { \
238 .gpt_base = DT_TIMER_BASE_ADDR(idx), \
239 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \
240 LOG_INSTANCE_PTR_INIT(log, LOG_MODULE_NAME, idx)}; \
241 \
242 static struct pwm_cc13xx_cc26xx_data pwm_cc13xx_cc26xx_##idx##_data; \
243 \
244 DEVICE_DT_INST_DEFINE(idx, init_pwm, NULL, &pwm_cc13xx_cc26xx_##idx##_data, \
245 &pwm_cc13xx_cc26xx_##idx##_config, POST_KERNEL, \
246 CONFIG_PWM_INIT_PRIORITY, &pwm_driver_api)
247
248 DT_INST_FOREACH_STATUS_OKAY(PWM_DEVICE_INIT);
249