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