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