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