forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclock_control_nrf2_fll16m.c
275 lines (215 loc) · 6.71 KB
/
clock_control_nrf2_fll16m.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nordic_nrf_fll16m
#include "clock_control_nrf2_common.h"
#include <zephyr/devicetree.h>
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
#include <soc_lrcconf.h>
#include <hal/nrf_bicr.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
"multiple instances not supported");
#define FLAG_HFXO_STARTED BIT(FLAGS_COMMON_BITS)
#define FLL16M_MODE_OPEN_LOOP 0
#define FLL16M_MODE_CLOSED_LOOP 1
#define FLL16M_MODE_BYPASS 2
#define FLL16M_MODE_DEFAULT FLL16M_MODE_OPEN_LOOP
#define FLL16M_HFXO_NODE DT_INST_PHANDLE_BY_NAME(0, clocks, hfxo)
#define FLL16M_HFXO_ACCURACY DT_PROP(FLL16M_HFXO_NODE, accuracy_ppm)
#define FLL16M_OPEN_LOOP_ACCURACY DT_INST_PROP(0, open_loop_accuracy_ppm)
#define FLL16M_CLOSED_LOOP_BASE_ACCURACY DT_INST_PROP(0, closed_loop_base_accuracy_ppm)
#define FLL16M_MAX_ACCURACY FLL16M_HFXO_ACCURACY
#define BICR (NRF_BICR_Type *)DT_REG_ADDR(DT_NODELABEL(bicr))
/* Clock options sorted from lowest to highest accuracy */
static struct clock_options {
uint16_t accuracy;
uint8_t mode;
} clock_options[] = {
{
.accuracy = FLL16M_OPEN_LOOP_ACCURACY,
.mode = FLL16M_MODE_OPEN_LOOP,
},
{
.mode = FLL16M_MODE_CLOSED_LOOP,
},
{
/* Bypass mode uses HFXO */
.accuracy = FLL16M_HFXO_ACCURACY,
.mode = FLL16M_MODE_BYPASS,
},
};
struct fll16m_dev_data {
STRUCT_CLOCK_CONFIG(fll16m, ARRAY_SIZE(clock_options)) clk_cfg;
struct onoff_client hfxo_cli;
sys_snode_t fll16m_node;
};
struct fll16m_dev_config {
uint32_t fixed_frequency;
};
static void activate_fll16m_mode(struct fll16m_dev_data *dev_data, uint8_t mode)
{
/* TODO: change to nrf_lrcconf_* function when such is available. */
if (mode != FLL16M_MODE_DEFAULT) {
soc_lrcconf_poweron_request(&dev_data->fll16m_node, NRF_LRCCONF_POWER_MAIN);
}
NRF_LRCCONF010->CLKCTRL[0].SRC = mode;
if (mode == FLL16M_MODE_DEFAULT) {
soc_lrcconf_poweron_release(&dev_data->fll16m_node, NRF_LRCCONF_POWER_MAIN);
}
nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_CLKSTART_0);
clock_config_update_end(&dev_data->clk_cfg, 0);
}
static void hfxo_cb(struct onoff_manager *mgr,
struct onoff_client *cli,
uint32_t state,
int res)
{
ARG_UNUSED(mgr);
ARG_UNUSED(state);
struct fll16m_dev_data *dev_data =
CONTAINER_OF(cli, struct fll16m_dev_data, hfxo_cli);
if (res < 0) {
clock_config_update_end(&dev_data->clk_cfg, res);
} else {
(void)atomic_or(&dev_data->clk_cfg.flags, FLAG_HFXO_STARTED);
activate_fll16m_mode(dev_data, FLL16M_MODE_BYPASS);
}
}
static void fll16m_work_handler(struct k_work *work)
{
const struct device *hfxo = DEVICE_DT_GET(FLL16M_HFXO_NODE);
struct fll16m_dev_data *dev_data =
CONTAINER_OF(work, struct fll16m_dev_data, clk_cfg.work);
uint8_t to_activate_idx;
to_activate_idx = clock_config_update_begin(work);
if (clock_options[to_activate_idx].mode == FLL16M_MODE_BYPASS) {
int rc;
/* Bypass mode requires HFXO to be running first. */
sys_notify_init_callback(&dev_data->hfxo_cli.notify, hfxo_cb);
rc = nrf_clock_control_request(hfxo, NULL, &dev_data->hfxo_cli);
if (rc < 0) {
clock_config_update_end(&dev_data->clk_cfg, rc);
}
} else {
atomic_val_t prev_flags;
prev_flags = atomic_and(&dev_data->clk_cfg.flags,
~FLAG_HFXO_STARTED);
if (prev_flags & FLAG_HFXO_STARTED) {
(void)nrf_clock_control_release(hfxo, NULL);
}
activate_fll16m_mode(dev_data,
clock_options[to_activate_idx].mode);
}
}
static struct onoff_manager *fll16m_find_mgr(const struct device *dev,
const struct nrf_clock_spec *spec)
{
struct fll16m_dev_data *dev_data = dev->data;
const struct fll16m_dev_config *dev_config = dev->config;
uint16_t accuracy;
if (!spec) {
return &dev_data->clk_cfg.onoff[0].mgr;
}
if (spec->frequency > dev_config->fixed_frequency) {
LOG_ERR("invalid frequency");
return NULL;
}
if (spec->precision) {
LOG_ERR("invalid precision");
return NULL;
}
accuracy = spec->accuracy == NRF_CLOCK_CONTROL_ACCURACY_MAX
? FLL16M_MAX_ACCURACY
: spec->accuracy;
for (int i = 0; i < ARRAY_SIZE(clock_options); ++i) {
if (accuracy &&
accuracy < clock_options[i].accuracy) {
continue;
}
return &dev_data->clk_cfg.onoff[i].mgr;
}
LOG_ERR("invalid accuracy");
return NULL;
}
static int api_request_fll16m(const struct device *dev,
const struct nrf_clock_spec *spec,
struct onoff_client *cli)
{
struct onoff_manager *mgr = fll16m_find_mgr(dev, spec);
if (mgr) {
return onoff_request(mgr, cli);
}
return -EINVAL;
}
static int api_release_fll16m(const struct device *dev,
const struct nrf_clock_spec *spec)
{
struct onoff_manager *mgr = fll16m_find_mgr(dev, spec);
if (mgr) {
return onoff_release(mgr);
}
return -EINVAL;
}
static int api_cancel_or_release_fll16m(const struct device *dev,
const struct nrf_clock_spec *spec,
struct onoff_client *cli)
{
struct onoff_manager *mgr = fll16m_find_mgr(dev, spec);
if (mgr) {
return onoff_cancel_or_release(mgr, cli);
}
return -EINVAL;
}
static int api_get_rate_fll16m(const struct device *dev,
clock_control_subsys_t sys,
uint32_t *rate)
{
ARG_UNUSED(sys);
const struct fll16m_dev_config *dev_config = dev->config;
*rate = dev_config->fixed_frequency;
return 0;
}
static int fll16m_init(const struct device *dev)
{
struct fll16m_dev_data *dev_data = dev->data;
nrf_bicr_lfosc_mode_t lfosc_mode;
clock_options[1].accuracy = FLL16M_CLOSED_LOOP_BASE_ACCURACY;
/* Closed-loop mode uses LFXO as source if present, HFXO otherwise */
lfosc_mode = nrf_bicr_lfosc_mode_get(BICR);
if (lfosc_mode != NRF_BICR_LFOSC_MODE_UNCONFIGURED &&
lfosc_mode != NRF_BICR_LFOSC_MODE_DISABLED) {
int ret;
uint16_t accuracy;
ret = lfosc_get_accuracy(&accuracy);
if (ret < 0) {
return ret;
}
clock_options[1].accuracy += accuracy;
} else {
clock_options[1].accuracy += FLL16M_HFXO_ACCURACY;
}
return clock_config_init(&dev_data->clk_cfg,
ARRAY_SIZE(dev_data->clk_cfg.onoff),
fll16m_work_handler);
}
static DEVICE_API(nrf_clock_control, fll16m_drv_api) = {
.std_api = {
.on = api_nosys_on_off,
.off = api_nosys_on_off,
.get_rate = api_get_rate_fll16m,
},
.request = api_request_fll16m,
.release = api_release_fll16m,
.cancel_or_release = api_cancel_or_release_fll16m,
};
static struct fll16m_dev_data fll16m_data;
static const struct fll16m_dev_config fll16m_config = {
.fixed_frequency = DT_INST_PROP(0, clock_frequency),
};
DEVICE_DT_INST_DEFINE(0, fll16m_init, NULL,
&fll16m_data, &fll16m_config,
PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
&fll16m_drv_api);