1 /*
2 * Copyright (c) 2022 Microchip Technology Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT microchip_xec_bbled
8
9 /**
10 * @file
11 * @brief Microchip Breathing-Blinking LED controller
12 */
13
14 #include <soc.h>
15 #ifndef CONFIG_SOC_SERIES_MEC15XX
16 #include <zephyr/drivers/clock_control/mchp_xec_clock_control.h>
17 #include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h>
18 #endif
19 #include <zephyr/drivers/led.h>
20 #include <zephyr/drivers/pinctrl.h>
21 #include <zephyr/device.h>
22 #include <zephyr/kernel.h>
23
24 #include <zephyr/logging/log.h>
25 LOG_MODULE_REGISTER(led_xec, CONFIG_LED_LOG_LEVEL);
26
27 /* Same BBLED hardware block in MEC15xx and MEC172x families
28 * Config register
29 */
30 #define XEC_BBLED_CFG_MSK 0x1ffffu
31 #define XEC_BBLED_CFG_MODE_POS 0
32 #define XEC_BBLED_CFG_MODE_MSK 0x3u
33 #define XEC_BBLED_CFG_MODE_OFF 0
34 #define XEC_BBLED_CFG_MODE_BREATHING 0x1u
35 #define XEC_BBLED_CFG_MODE_PWM 0x2u
36 #define XEC_BBLED_CFG_MODE_ALWAYS_ON 0x3u
37 #define XEC_BBLED_CFG_CLK_SRC_48M_POS 2
38 #define XEC_BBLED_CFG_EN_UPDATE_POS 6
39 #define XEC_BBLED_CFG_RST_PWM_POS 7
40 #define XEC_BBLED_CFG_WDT_RLD_POS 8
41 #define XEC_BBLED_CFG_WDT_RLD_MSK0 0xffu
42 #define XEC_BBLED_CFG_WDT_RLD_MSK 0xff00u
43 #define XEC_BBLED_CFG_WDT_RLD_DFLT 0x1400u
44
45 /* Limits register */
46 #define XEC_BBLED_LIM_MSK 0xffffu
47 #define XEC_BBLED_LIM_MIN_POS 0
48 #define XEC_BBLED_LIM_MIN_MSK 0xffu
49 #define XEC_BBLED_LIM_MAX_POS 8
50 #define XEC_BBLED_LIM_MAX_MSK 0xff00u
51
52 /* Delay register */
53 #define XEC_BBLED_DLY_MSK 0xffffffu
54 #define XEC_BBLED_DLY_LO_POS 0
55 #define XEC_BBLED_DLY_LO_MSK 0xfffu
56 #define XEC_BBLED_DLY_HI_POS 12
57 #define XEC_BBLED_DLY_HI_MSK 0xfff000u
58
59 /* Update step size and update interval registers implement
60 * eight 4-bit fields numbered 0 to 7
61 */
62 #define XEC_BBLED_UPD_SSI_POS(n) ((uint32_t)(n) * 4u)
63 #define XEC_BBLED_UPD_SSI0_MSK(n) ((uint32_t)0xfu << XEC_BBLED_UPD_SSI_POS(n))
64
65 /* Output delay register: b[7:0] is delay in clock source units */
66 #define XEC_BBLED_OUT_DLY_MSK 0xffu
67
68 /* Delay.Lo register field */
69 #define XEC_BBLED_MAX_PRESCALER 4095u
70 /* Blink mode source frequency is 32768 Hz */
71 #define XEC_BBLED_BLINK_CLK_SRC_HZ 32768u
72 /* Fblink = 32768 / (256 * (prescaler+1))
73 * prescaler is 12 bit.
74 * Maximum Fblink = 128 Hz or 7.8125 ms
75 * Minimum Fblink = 32.25 mHz or 32000 ms
76 */
77 #define XEC_BBLED_BLINK_PERIOD_MAX_MS 32000u
78 #define XEC_BBLED_BLINK_PERIOD_MIN_MS 8u
79
80 struct xec_bbled_regs {
81 volatile uint32_t config;
82 volatile uint32_t limits;
83 volatile uint32_t delay;
84 volatile uint32_t update_step_size;
85 volatile uint32_t update_interval;
86 volatile uint32_t output_delay;
87 };
88
89 struct xec_bbled_config {
90 struct xec_bbled_regs * const regs;
91 const struct pinctrl_dev_config *pcfg;
92 uint8_t pcr_id;
93 uint8_t pcr_pos;
94 };
95
96 /* delay_on and delay_off are in milliseconds
97 * (prescale+1) = (32768 * Tblink_ms) / (256 * 1000)
98 * requires caller to limit delay_on and delay_off based
99 * on BBLED 32KHz minimum/maximum values.
100 */
calc_blink_32k_prescaler(uint32_t delay_on,uint32_t delay_off)101 static uint32_t calc_blink_32k_prescaler(uint32_t delay_on, uint32_t delay_off)
102 {
103 uint32_t temp = ((delay_on + delay_off) * XEC_BBLED_BLINK_CLK_SRC_HZ) / (256U * 1000U);
104 uint32_t prescaler = 0;
105
106 if (temp) {
107 temp--;
108 if (temp > XEC_BBLED_MAX_PRESCALER) {
109 prescaler = XEC_BBLED_MAX_PRESCALER;
110 } else {
111 prescaler = (uint32_t)temp;
112 }
113 }
114
115 return prescaler;
116 }
117
118 /* return duty cycle scaled to [0, 255]
119 * caller must insure delay_on and delay_off are in hardware range.
120 */
calc_blink_duty_cycle(uint32_t delay_on,uint32_t delay_off)121 static uint32_t calc_blink_duty_cycle(uint32_t delay_on, uint32_t delay_off)
122 {
123 return (256U * delay_on) / (delay_on + delay_off);
124 }
125
126 /* Enable HW blinking of the LED.
127 * delay_on = on time in milliseconds
128 * delay_off = off time in milliseconds
129 * BBLED blinking mode uses an 8-bit accumulator and an 8-bit duty cycle
130 * register. The duty cycle register is programmed once and the
131 * accumulator is used as an 8-bit up counter.
132 * The counter uses the 32768 Hz clock and is pre-scaled by the delay
133 * counter. Maximum blink rate is 128Hz to 32.25 mHz (7.8 ms to 32 seconds).
134 * 8-bit duty cycle values: 0x00 = full off, 0xff = full on.
135 * Fblink = 32768 / ((prescale + 1) * 256)
136 * HiWidth (seconds) = (1/Fblink) * (duty_cycle / 256)
137 * LoWidth (seconds) = (1/Fblink) * ((1 - duty_cycle) / 256)
138 * duty_cycle in [0, 1]. Register value for duty cycle is
139 * scaled to [0, 255].
140 * prescale is delay register low delay field, bits[11:0]
141 * duty_cycle is limits register minimum field, bits[7:0]
142 */
xec_bbled_blink(const struct device * dev,uint32_t led,uint32_t delay_on,uint32_t delay_off)143 static int xec_bbled_blink(const struct device *dev, uint32_t led,
144 uint32_t delay_on, uint32_t delay_off)
145 {
146 const struct xec_bbled_config * const config = dev->config;
147 struct xec_bbled_regs * const regs = config->regs;
148 uint32_t period, prescaler, dcs;
149
150 if (led) {
151 return -EINVAL;
152 }
153
154 /* insure period will not overflow uin32_t */
155 if ((delay_on > XEC_BBLED_BLINK_PERIOD_MAX_MS)
156 || (delay_off > XEC_BBLED_BLINK_PERIOD_MAX_MS)) {
157 return -EINVAL;
158 }
159
160 period = delay_on + delay_off;
161 if ((period < XEC_BBLED_BLINK_PERIOD_MIN_MS)
162 || (period > XEC_BBLED_BLINK_PERIOD_MAX_MS)) {
163 return -EINVAL;
164 }
165
166 prescaler = calc_blink_32k_prescaler(delay_on, delay_off);
167 dcs = calc_blink_duty_cycle(delay_on, delay_off);
168
169 regs->config = (regs->config & ~(XEC_BBLED_CFG_MODE_MSK))
170 | XEC_BBLED_CFG_MODE_OFF;
171 regs->delay = (regs->delay & ~(XEC_BBLED_DLY_LO_MSK))
172 | (prescaler & XEC_BBLED_DLY_LO_MSK);
173 regs->limits = (regs->limits & ~(XEC_BBLED_LIM_MIN_MSK))
174 | (dcs & XEC_BBLED_LIM_MIN_MSK);
175 regs->config = (regs->config & ~(XEC_BBLED_CFG_MODE_MSK))
176 | XEC_BBLED_CFG_MODE_PWM;
177 regs->config |= BIT(XEC_BBLED_CFG_EN_UPDATE_POS);
178
179 return 0;
180 }
181
xec_bbled_on(const struct device * dev,uint32_t led)182 static int xec_bbled_on(const struct device *dev, uint32_t led)
183 {
184 const struct xec_bbled_config * const config = dev->config;
185 struct xec_bbled_regs * const regs = config->regs;
186
187 if (led) {
188 return -EINVAL;
189 }
190
191 regs->config = (regs->config & ~(XEC_BBLED_CFG_MODE_MSK))
192 | XEC_BBLED_CFG_MODE_ALWAYS_ON;
193 return 0;
194 }
195
xec_bbled_off(const struct device * dev,uint32_t led)196 static int xec_bbled_off(const struct device *dev, uint32_t led)
197 {
198 const struct xec_bbled_config * const config = dev->config;
199 struct xec_bbled_regs * const regs = config->regs;
200
201 if (led) {
202 return -EINVAL;
203 }
204
205 regs->config = (regs->config & ~(XEC_BBLED_CFG_MODE_MSK))
206 | XEC_BBLED_CFG_MODE_OFF;
207 return 0;
208 }
209
210 #ifdef CONFIG_SOC_SERIES_MEC15XX
xec_bbled_slp_en_clr(const struct device * dev)211 static inline void xec_bbled_slp_en_clr(const struct device *dev)
212 {
213 const struct xec_bbled_config * const cfg = dev->config;
214 enum pcr_id pcr_val = PCR_MAX_ID;
215
216 switch (cfg->pcr_pos) {
217 case MCHP_PCR3_LED0_POS:
218 pcr_val = PCR_LED0;
219 break;
220 case MCHP_PCR3_LED1_POS:
221 pcr_val = PCR_LED1;
222 break;
223 case MCHP_PCR3_LED2_POS:
224 pcr_val = PCR_LED2;
225 break;
226 default:
227 return;
228 }
229
230 mchp_pcr_periph_slp_ctrl(pcr_val, 0);
231 }
232 #else
xec_bbled_slp_en_clr(const struct device * dev)233 static inline void xec_bbled_slp_en_clr(const struct device *dev)
234 {
235 const struct xec_bbled_config * const cfg = dev->config;
236
237 z_mchp_xec_pcr_periph_sleep(cfg->pcr_id, cfg->pcr_pos, 0);
238 }
239 #endif
240
xec_bbled_init(const struct device * dev)241 static int xec_bbled_init(const struct device *dev)
242 {
243 const struct xec_bbled_config * const config = dev->config;
244 struct xec_bbled_regs * const regs = config->regs;
245 int ret;
246
247 xec_bbled_slp_en_clr(dev);
248
249 /* soft reset, disable BBLED WDT, set clock source to default (32KHz domain) */
250 regs->config |= BIT(XEC_BBLED_CFG_RST_PWM_POS);
251 regs->config = XEC_BBLED_CFG_MODE_OFF;
252
253 ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
254 if (ret != 0) {
255 LOG_ERR("XEC BBLED pinctrl setup failed (%d)", ret);
256 }
257
258 return ret;
259 }
260
261 static DEVICE_API(led, xec_bbled_api) = {
262 .on = xec_bbled_on,
263 .off = xec_bbled_off,
264 .blink = xec_bbled_blink,
265 };
266
267 #define XEC_BBLED_PINCTRL_DEF(i) PINCTRL_DT_INST_DEFINE(i)
268
269 #define XEC_BBLED_CONFIG(i) \
270 static struct xec_bbled_config xec_bbled_config_##i = { \
271 .regs = (struct xec_bbled_regs * const)DT_INST_REG_ADDR(i), \
272 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(i), \
273 .pcr_id = (uint8_t)DT_INST_PROP_BY_IDX(i, pcrs, 0), \
274 .pcr_pos = (uint8_t)DT_INST_PROP_BY_IDX(i, pcrs, 1), \
275 }
276
277 #define XEC_BBLED_DEVICE(i) \
278 \
279 XEC_BBLED_PINCTRL_DEF(i); \
280 \
281 XEC_BBLED_CONFIG(i); \
282 \
283 DEVICE_DT_INST_DEFINE(i, &xec_bbled_init, NULL, \
284 NULL, &xec_bbled_config_##i, \
285 POST_KERNEL, CONFIG_LED_INIT_PRIORITY, \
286 &xec_bbled_api);
287
288 DT_INST_FOREACH_STATUS_OKAY(XEC_BBLED_DEVICE)
289