1 /*
2 * Copyright (c) 2024 ENE Technology Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ene_kb1200_pwm
8
9 #include <zephyr/drivers/pwm.h>
10 #include <zephyr/drivers/pinctrl.h>
11 #include <reg/pwm.h>
12
13 /* Device config */
14 struct pwm_kb1200_config {
15 /* pwm controller base address */
16 struct pwm_regs *pwm;
17 const struct pinctrl_dev_config *pcfg;
18 };
19
20 /* Driver data */
21 struct pwm_kb1200_data {
22 /* PWM cycles per second */
23 uint32_t cycles_per_sec;
24 };
25
26 /* PWM api functions */
pwm_kb1200_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)27 static int pwm_kb1200_set_cycles(const struct device *dev, uint32_t channel, uint32_t period_cycles,
28 uint32_t pulse_cycles, pwm_flags_t flags)
29 {
30 /* Single channel for each pwm device */
31 ARG_UNUSED(channel);
32 const struct pwm_kb1200_config *config = dev->config;
33 int prescaler;
34 uint32_t high_len;
35 uint32_t cycle_len;
36
37 /*
38 * Calculate PWM prescaler that let period_cycles map to
39 * maximum pwm period cycles and won't exceed it.
40 * Then prescaler = ceil (period_cycles / pwm_max_period_cycles)
41 */
42 prescaler = DIV_ROUND_UP(period_cycles, PWM_MAX_CYCLES);
43 if (prescaler > PWM_MAX_PRESCALER) {
44 return -EINVAL;
45 }
46
47 /* If pulse_cycles is 0, switch PWM off and return. */
48 if (pulse_cycles == 0) {
49 config->pwm->PWMCFG &= ~PWM_ENABLE;
50 return 0;
51 }
52
53 high_len = (pulse_cycles / prescaler);
54 cycle_len = (period_cycles / prescaler);
55
56 /* Select PWM inverted polarity (ie. active-low pulse). */
57 if (flags & PWM_POLARITY_INVERTED) {
58 high_len = cycle_len - high_len;
59 }
60
61 /* Set PWM prescaler. */
62 config->pwm->PWMCFG = (config->pwm->PWMCFG & ~GENMASK(13, 8)) | ((prescaler - 1) << 8);
63
64 /*
65 * period_cycles: PWM Cycle Length
66 * pulse_cycles : PWM High Length
67 */
68 config->pwm->PWMHIGH = high_len;
69 config->pwm->PWMCYC = cycle_len;
70
71 /* Start pwm */
72 config->pwm->PWMCFG |= PWM_ENABLE;
73
74 return 0;
75 }
76
pwm_kb1200_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)77 static int pwm_kb1200_get_cycles_per_sec(const struct device *dev, uint32_t channel,
78 uint64_t *cycles)
79 {
80 /* Single channel for each pwm device */
81 ARG_UNUSED(channel);
82 ARG_UNUSED(dev);
83
84 if (cycles) {
85 /* User does not have to know about lowest clock,
86 * the driver will select the most relevant one.
87 */
88 *cycles = PWM_INPUT_FREQ_HI; /*32Mhz*/
89 }
90 return 0;
91 }
92
93 static DEVICE_API(pwm, pwm_kb1200_driver_api) = {
94 .set_cycles = pwm_kb1200_set_cycles,
95 .get_cycles_per_sec = pwm_kb1200_get_cycles_per_sec,
96 };
97
pwm_kb1200_init(const struct device * dev)98 static int pwm_kb1200_init(const struct device *dev)
99 {
100 int ret;
101 const struct pwm_kb1200_config *config = dev->config;
102
103 ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
104 if (ret != 0) {
105 return ret;
106 }
107 config->pwm->PWMCFG = PWM_SOURCE_CLK_32M | PWM_RULE1 | PWM_PUSHPULL;
108
109 return 0;
110 }
111
112 #define KB1200_PWM_INIT(inst) \
113 PINCTRL_DT_INST_DEFINE(inst); \
114 static const struct pwm_kb1200_config pwm_kb1200_cfg_##inst = { \
115 .pwm = (struct pwm_regs *)DT_INST_REG_ADDR(inst), \
116 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
117 }; \
118 static struct pwm_kb1200_data pwm_kb1200_data_##inst; \
119 DEVICE_DT_INST_DEFINE(inst, &pwm_kb1200_init, NULL, &pwm_kb1200_data_##inst, \
120 &pwm_kb1200_cfg_##inst, PRE_KERNEL_1, CONFIG_PWM_INIT_PRIORITY, \
121 &pwm_kb1200_driver_api);
122
123 DT_INST_FOREACH_STATUS_OKAY(KB1200_PWM_INIT)
124