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