1 /*
2  * Copyright (c) 2023 Andriy Gelman <andriy.gelman@gmail.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT pwm_clock
8 
9 #include <stdint.h>
10 
11 #include <zephyr/device.h>
12 #include <zephyr/devicetree.h>
13 #include <zephyr/drivers/clock_control.h>
14 #include <zephyr/drivers/pwm.h>
15 #include <zephyr/dt-bindings/pwm/pwm.h>
16 #include <zephyr/kernel.h>
17 
18 #define LOG_LEVEL CONFIG_CLOCK_CONTROL_LOG_LEVEL
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(clock_control_pwm);
21 
22 BUILD_ASSERT(CONFIG_CLOCK_CONTROL_PWM_INIT_PRIORITY > CONFIG_PWM_INIT_PRIORITY,
23 	     "PWM must have a higher priority than PWM clock control");
24 
25 #define NUM_PWM_CLOCKS 1
26 
27 struct clock_control_pwm_config {
28 	const struct pwm_dt_spec pwm_dt;
29 	const uint16_t pwm_on_delay;
30 };
31 
32 struct clock_control_pwm_data {
33 	uint32_t clock_frequency;
34 	bool is_enabled;
35 };
36 
clock_control_pwm_on(const struct device * dev,clock_control_subsys_t sys)37 static int clock_control_pwm_on(const struct device *dev, clock_control_subsys_t sys)
38 {
39 	struct clock_control_pwm_data *data = dev->data;
40 	const struct clock_control_pwm_config *config = dev->config;
41 	const struct pwm_dt_spec *spec;
42 	int id = (int)sys;
43 	int ret;
44 
45 	if (id >= NUM_PWM_CLOCKS) {
46 		return -EINVAL;
47 	}
48 
49 	spec = &config->pwm_dt;
50 	if (data->clock_frequency == 0) {
51 		ret = pwm_set_dt(spec, spec->period, spec->period / 2);
52 	} else {
53 		uint64_t cycles_per_sec;
54 		uint32_t period_cycles;
55 
56 		ret = pwm_get_cycles_per_sec(spec->dev, spec->channel, &cycles_per_sec);
57 		if (ret) {
58 			return ret;
59 		}
60 
61 		if (cycles_per_sec % data->clock_frequency > 0) {
62 			LOG_WRN("Target clock frequency cannot be expressed in PWM clock ticks");
63 		}
64 
65 		period_cycles = cycles_per_sec / data->clock_frequency;
66 		ret = pwm_set_cycles(spec->dev, spec->channel, period_cycles, period_cycles / 2,
67 				     spec->flags);
68 	}
69 
70 	if (ret) {
71 		return ret;
72 	}
73 
74 	k_busy_wait(config->pwm_on_delay);
75 
76 	data->is_enabled = true;
77 
78 	return 0;
79 }
80 
clock_control_pwm_get_rate(const struct device * dev,clock_control_subsys_t sys,uint32_t * rate)81 static int clock_control_pwm_get_rate(const struct device *dev, clock_control_subsys_t sys,
82 				      uint32_t *rate)
83 {
84 	struct clock_control_pwm_data *data = dev->data;
85 	const struct clock_control_pwm_config *config = dev->config;
86 	int id = (int)sys;
87 
88 	if (id >= NUM_PWM_CLOCKS) {
89 		return -EINVAL;
90 	}
91 
92 	if (data->clock_frequency > 0) {
93 		*rate = data->clock_frequency;
94 	} else {
95 		*rate = NSEC_PER_SEC / config->pwm_dt.period;
96 	}
97 
98 	return 0;
99 }
100 
clock_control_pwm_set_rate(const struct device * dev,clock_control_subsys_rate_t sys,clock_control_subsys_rate_t rate)101 static int clock_control_pwm_set_rate(const struct device *dev, clock_control_subsys_rate_t sys,
102 				      clock_control_subsys_rate_t rate)
103 {
104 	struct clock_control_pwm_data *data = dev->data;
105 	uint32_t rate_hz = (uint32_t)rate;
106 	int id = (int)sys;
107 
108 	if (id >= NUM_PWM_CLOCKS) {
109 		return -EINVAL;
110 	}
111 
112 	if (data->clock_frequency == rate_hz && data->is_enabled) {
113 		return -EALREADY;
114 	}
115 
116 	data->clock_frequency = rate_hz;
117 
118 	return clock_control_pwm_on(dev, sys);
119 }
120 
clock_control_pwm_init(const struct device * dev)121 static int clock_control_pwm_init(const struct device *dev)
122 {
123 	const struct clock_control_pwm_config *config = dev->config;
124 
125 	if (!device_is_ready(config->pwm_dt.dev)) {
126 		return -ENODEV;
127 	}
128 
129 	return 0;
130 }
131 
132 static struct clock_control_driver_api clock_control_pwm_api = {
133 	.on = clock_control_pwm_on,
134 	.get_rate = clock_control_pwm_get_rate,
135 	.set_rate = clock_control_pwm_set_rate,
136 };
137 
138 #define PWM_CLOCK_INIT(i)                                                                          \
139                                                                                                    \
140 	BUILD_ASSERT(DT_INST_PROP_LEN(i, pwms) <= 1,                                               \
141 		     "One PWM per clock control node is supported");                               \
142                                                                                                    \
143 	BUILD_ASSERT(DT_INST_PROP(i, pwm_on_delay) <= UINT16_MAX,                                  \
144 		     "Maximum pwm-on-delay is 65535 usec");                                        \
145                                                                                                    \
146 	static const struct clock_control_pwm_config clock_control_pwm_config_##i = {              \
147 		.pwm_dt = PWM_DT_SPEC_INST_GET(i),                                                 \
148 		.pwm_on_delay = DT_INST_PROP(i, pwm_on_delay),                                     \
149 	};                                                                                         \
150                                                                                                    \
151 	static struct clock_control_pwm_data clock_control_pwm_data_##i = {                        \
152 		.clock_frequency = DT_INST_PROP_OR(i, clock_frequency, 0),                         \
153 	};                                                                                         \
154                                                                                                    \
155 	DEVICE_DT_INST_DEFINE(i, clock_control_pwm_init, NULL, &clock_control_pwm_data_##i,        \
156 			      &clock_control_pwm_config_##i, POST_KERNEL,                          \
157 			      CONFIG_CLOCK_CONTROL_PWM_INIT_PRIORITY, &clock_control_pwm_api);
158 
159 DT_INST_FOREACH_STATUS_OKAY(PWM_CLOCK_INIT)
160