1 /*
2  * Copyright (c) 2021 Telink Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT telink_b91_pwm
8 
9 #include "pwm.h"
10 #include "clock.h"
11 #include <drivers/pwm.h>
12 #include <drivers/pinmux.h>
13 #include <dt-bindings/pinctrl/b91-pinctrl.h>
14 
15 
16 #define PWM_PCLK_SPEED      DT_INST_PROP(0, clock_frequency)
17 #define NUM_OF_CHANNELS     DT_INST_PROP(0, channels)
18 
19 static const uint32_t pwm_pins[] = B91_PINMUX_DT_INST_GET_ARRAY(0, 0);
20 
21 /* API implementation: init */
pwm_b91_init(const struct device * dev)22 static int pwm_b91_init(const struct device *dev)
23 {
24 	ARG_UNUSED(dev);
25 
26 	uint8_t clk_32k_en = 0;
27 	uint32_t pwm_clk_div = 0;
28 	const struct device *pinmux;
29 
30 	/* Calculate and check PWM clock divider */
31 	pwm_clk_div = sys_clk.pclk * 1000 * 1000 / PWM_PCLK_SPEED - 1;
32 	if (pwm_clk_div > 255) {
33 		return -EINVAL;
34 	}
35 
36 	/* Set PWM Peripheral clock */
37 	pwm_set_clk((unsigned char) (pwm_clk_div & 0xFF));
38 
39 	/* Set PWM 32k Channel clock if enabled */
40 	clk_32k_en |= DT_INST_PROP(0, clk32k_ch0_enable) ? PWM_CLOCK_32K_CHN_PWM0 : 0;
41 	clk_32k_en |= DT_INST_PROP(0, clk32k_ch1_enable) ? PWM_CLOCK_32K_CHN_PWM1 : 0;
42 	clk_32k_en |= DT_INST_PROP(0, clk32k_ch2_enable) ? PWM_CLOCK_32K_CHN_PWM2 : 0;
43 	clk_32k_en |= DT_INST_PROP(0, clk32k_ch3_enable) ? PWM_CLOCK_32K_CHN_PWM3 : 0;
44 	clk_32k_en |= DT_INST_PROP(0, clk32k_ch4_enable) ? PWM_CLOCK_32K_CHN_PWM4 : 0;
45 	clk_32k_en |= DT_INST_PROP(0, clk32k_ch5_enable) ? PWM_CLOCK_32K_CHN_PWM5 : 0;
46 	pwm_32k_chn_en(clk_32k_en);
47 
48 	/* Get PinMux driver */
49 	pinmux = DEVICE_DT_GET(DT_NODELABEL(pinmux));
50 	if (!device_is_ready(pinmux)) {
51 		return -ENODEV;
52 	}
53 
54 	/* Config PWM pins */
55 	for (int i = 0; i < ARRAY_SIZE(pwm_pins); i++) {
56 		pinmux_pin_set(pinmux, B91_PINMUX_GET_PIN(pwm_pins[i]),
57 			       B91_PINMUX_GET_FUNC(pwm_pins[i]));
58 	}
59 
60 	return 0;
61 }
62 
63 /* API implementation: pin_set */
pwm_b91_pin_set(const struct device * dev,uint32_t pwm,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)64 static int pwm_b91_pin_set(const struct device *dev, uint32_t pwm,
65 			   uint32_t period_cycles, uint32_t pulse_cycles,
66 			   pwm_flags_t flags)
67 {
68 	ARG_UNUSED(dev);
69 
70 	/* check pwm channel */
71 	if (pwm >= NUM_OF_CHANNELS) {
72 		return -EINVAL;
73 	}
74 
75 	/* check size of pulse and period (2 bytes) */
76 	if ((period_cycles > 0xFFFFu) ||
77 	    (pulse_cycles  > 0xFFFFu)) {
78 		return -EINVAL;
79 	}
80 
81 	/* set polarity */
82 	if (flags & PWM_POLARITY_INVERTED) {
83 		pwm_invert_en(pwm);
84 	} else {
85 		pwm_invert_dis(pwm);
86 	}
87 
88 	/* set pulse and period */
89 	pwm_set_tcmp(pwm, pulse_cycles);
90 	pwm_set_tmax(pwm, period_cycles);
91 
92 	/* start pwm */
93 	pwm_start(pwm);
94 
95 	return 0;
96 }
97 
98 /* API implementation: get_cycles_per_sec */
pwm_b91_get_cycles_per_sec(const struct device * dev,uint32_t pwm,uint64_t * cycles)99 static int pwm_b91_get_cycles_per_sec(const struct device *dev, uint32_t pwm,
100 				      uint64_t *cycles)
101 {
102 	ARG_UNUSED(dev);
103 
104 	/* check pwm channel */
105 	if (pwm >= NUM_OF_CHANNELS) {
106 		return -EINVAL;
107 	}
108 
109 	if (
110 		((pwm == 0u) && DT_INST_PROP(0, clk32k_ch0_enable)) ||
111 		((pwm == 1u) && DT_INST_PROP(0, clk32k_ch1_enable)) ||
112 		((pwm == 2u) && DT_INST_PROP(0, clk32k_ch2_enable)) ||
113 		((pwm == 3u) && DT_INST_PROP(0, clk32k_ch3_enable)) ||
114 		((pwm == 4u) && DT_INST_PROP(0, clk32k_ch4_enable)) ||
115 		((pwm == 5u) && DT_INST_PROP(0, clk32k_ch5_enable))
116 		) {
117 		*cycles = 32000u;
118 	} else {
119 		*cycles = sys_clk.pclk * 1000 * 1000 / (reg_pwm_clkdiv + 1);
120 	}
121 
122 	return 0;
123 }
124 
125 /* PWM driver APIs structure */
126 static const struct pwm_driver_api pwm_b91_driver_api = {
127 	.pin_set = pwm_b91_pin_set,
128 	.get_cycles_per_sec = pwm_b91_get_cycles_per_sec,
129 };
130 
131 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1,
132 	     "unsupported PWM instance");
133 
134 /* PWM driver registration */
135 #define PWM_B91_INIT(n)							       \
136 									       \
137 	DEVICE_DT_INST_DEFINE(n, pwm_b91_init,				       \
138 			      NULL, NULL, NULL,				       \
139 			      POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
140 			      &pwm_b91_driver_api);
141 
142 DT_INST_FOREACH_STATUS_OKAY(PWM_B91_INIT)
143