1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT nordic_nrf_auxpll
7 
8 #include <errno.h>
9 #include <stdint.h>
10 
11 #include <zephyr/arch/cpu.h>
12 #include <zephyr/device.h>
13 #include <zephyr/devicetree.h>
14 #include <zephyr/drivers/clock_control.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/sys/util.h>
17 #include <zephyr/toolchain.h>
18 
19 #include <hal/nrf_auxpll.h>
20 
21 /* maximum lock time in ms, >10x time observed experimentally */
22 #define AUXPLL_LOCK_TIME_MAX_MS  20
23 /* lock wait step in ms*/
24 #define AUXPLL_LOCK_WAIT_STEP_MS 1
25 
26 struct clock_control_nrf_auxpll_config {
27 	NRF_AUXPLL_Type *auxpll;
28 	uint32_t ref_clk_hz;
29 	uint32_t ficr_ctune;
30 	nrf_auxpll_config_t cfg;
31 	uint16_t frequency;
32 	nrf_auxpll_ctrl_outsel_t out_div;
33 };
34 
clock_control_nrf_auxpll_on(const struct device * dev,clock_control_subsys_t sys)35 static int clock_control_nrf_auxpll_on(const struct device *dev, clock_control_subsys_t sys)
36 {
37 	const struct clock_control_nrf_auxpll_config *config = dev->config;
38 	bool locked;
39 	unsigned int wait = 0U;
40 
41 	ARG_UNUSED(sys);
42 
43 	nrf_auxpll_task_trigger(config->auxpll, NRF_AUXPLL_TASK_START);
44 
45 	do {
46 		locked = nrf_auxpll_mode_locked_check(config->auxpll);
47 		if (!locked) {
48 			k_msleep(AUXPLL_LOCK_WAIT_STEP_MS);
49 			wait += AUXPLL_LOCK_WAIT_STEP_MS;
50 		}
51 	} while (wait < AUXPLL_LOCK_TIME_MAX_MS && !locked);
52 
53 	return locked ? 0 : -ETIMEDOUT;
54 }
55 
clock_control_nrf_auxpll_off(const struct device * dev,clock_control_subsys_t sys)56 static int clock_control_nrf_auxpll_off(const struct device *dev, clock_control_subsys_t sys)
57 {
58 	const struct clock_control_nrf_auxpll_config *config = dev->config;
59 
60 	ARG_UNUSED(sys);
61 
62 	nrf_auxpll_task_trigger(config->auxpll, NRF_AUXPLL_TASK_STOP);
63 
64 	while (nrf_auxpll_running_check(config->auxpll)) {
65 	}
66 
67 	return 0;
68 }
69 
clock_control_nrf_auxpll_get_rate(const struct device * dev,clock_control_subsys_t sys,uint32_t * rate)70 static int clock_control_nrf_auxpll_get_rate(const struct device *dev, clock_control_subsys_t sys,
71 					     uint32_t *rate)
72 {
73 	const struct clock_control_nrf_auxpll_config *config = dev->config;
74 	uint8_t ratio;
75 
76 	ARG_UNUSED(sys);
77 
78 	ratio = nrf_auxpll_static_ratio_get(config->auxpll);
79 
80 	*rate = (ratio * config->ref_clk_hz +
81 		 (config->ref_clk_hz * (uint64_t)config->frequency) /
82 			 (AUXPLL_AUXPLLCTRL_FREQUENCY_FREQUENCY_MaximumDiv + 1U)) /
83 		config->out_div;
84 
85 	return 0;
86 }
87 
clock_control_nrf_auxpll_get_status(const struct device * dev,clock_control_subsys_t sys)88 static enum clock_control_status clock_control_nrf_auxpll_get_status(const struct device *dev,
89 								     clock_control_subsys_t sys)
90 {
91 	const struct clock_control_nrf_auxpll_config *config = dev->config;
92 
93 	ARG_UNUSED(sys);
94 
95 	if (nrf_auxpll_mode_locked_check(config->auxpll)) {
96 		return CLOCK_CONTROL_STATUS_ON;
97 	}
98 
99 	return CLOCK_CONTROL_STATUS_OFF;
100 }
101 
102 static DEVICE_API(clock_control, clock_control_nrf_auxpll_api) = {
103 	.on = clock_control_nrf_auxpll_on,
104 	.off = clock_control_nrf_auxpll_off,
105 	.get_rate = clock_control_nrf_auxpll_get_rate,
106 	.get_status = clock_control_nrf_auxpll_get_status,
107 };
108 
clock_control_nrf_auxpll_init(const struct device * dev)109 static int clock_control_nrf_auxpll_init(const struct device *dev)
110 {
111 	const struct clock_control_nrf_auxpll_config *config = dev->config;
112 
113 	nrf_auxpll_ctrl_frequency_set(config->auxpll, config->frequency);
114 
115 	nrf_auxpll_lock(config->auxpll);
116 	nrf_auxpll_trim_ctune_set(config->auxpll, sys_read8(config->ficr_ctune));
117 	nrf_auxpll_config_set(config->auxpll, &config->cfg);
118 	nrf_auxpll_ctrl_outsel_set(config->auxpll, config->out_div);
119 	nrf_auxpll_unlock(config->auxpll);
120 
121 	nrf_auxpll_ctrl_mode_set(config->auxpll, NRF_AUXPLL_CTRL_MODE_LOCKED);
122 
123 	return 0;
124 }
125 
126 #define CLOCK_CONTROL_NRF_AUXPLL_DEFINE(n)                                                         \
127 	static const struct clock_control_nrf_auxpll_config config##n = {                          \
128 		.auxpll = (NRF_AUXPLL_Type *)DT_INST_REG_ADDR(n),                                  \
129 		.ref_clk_hz = DT_PROP(DT_INST_CLOCKS_CTLR(n), clock_frequency),                    \
130 		.ficr_ctune = DT_REG_ADDR(DT_INST_PHANDLE(n, nordic_ficrs)) +                      \
131 			      DT_INST_PHA(n, nordic_ficrs, offset),                                \
132 		.cfg =                                                                             \
133 			{                                                                          \
134 				.outdrive = DT_INST_PROP(n, nordic_out_drive),                     \
135 				.current_tune = DT_INST_PROP(n, nordic_current_tune),              \
136 				.sdm_off = DT_INST_PROP(n, nordic_sdm_disable),                    \
137 				.dither_off = DT_INST_PROP(n, nordic_dither_disable),              \
138 				.range = DT_INST_ENUM_IDX(n, nordic_range),                        \
139 			},                                                                         \
140 		.frequency = DT_INST_PROP(n, nordic_frequency),                                    \
141 		.out_div = DT_INST_PROP(n, nordic_out_div),                                        \
142 	};                                                                                         \
143                                                                                                    \
144 	DEVICE_DT_INST_DEFINE(n, clock_control_nrf_auxpll_init, NULL, NULL, &config##n,            \
145 			      PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,                    \
146 			      &clock_control_nrf_auxpll_api);
147 
148 DT_INST_FOREACH_STATUS_OKAY(CLOCK_CONTROL_NRF_AUXPLL_DEFINE)
149