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