| 
 | 1 | +/*  | 
 | 2 | + * Copyright (c) 2016-2025 Nordic Semiconductor ASA  | 
 | 3 | + * Copyright (c) 2016 Vinayak Kariappa Chettimada  | 
 | 4 | + *  | 
 | 5 | + * SPDX-License-Identifier: Apache-2.0  | 
 | 6 | + */  | 
 | 7 | + | 
 | 8 | +#include <soc.h>  | 
 | 9 | +#include <zephyr/sys/onoff.h>  | 
 | 10 | +#include <zephyr/drivers/clock_control.h>  | 
 | 11 | +#include <zephyr/drivers/clock_control/nrf_clock_control.h>  | 
 | 12 | +#include "clock_control_nrf_common.h"  | 
 | 13 | +#include <nrfx_clock_xo24m.h>  | 
 | 14 | +#include <zephyr/logging/log.h>  | 
 | 15 | +#include <zephyr/shell/shell.h>  | 
 | 16 | +#include <zephyr/irq.h>  | 
 | 17 | + | 
 | 18 | +LOG_MODULE_REGISTER(clock_control_xo24m, CONFIG_CLOCK_CONTROL_LOG_LEVEL);  | 
 | 19 | + | 
 | 20 | +#define DT_DRV_COMPAT nordic_nrf_clock_xo24m  | 
 | 21 | + | 
 | 22 | +#define CLOCK_DEVICE_XO24M DEVICE_DT_GET(DT_NODELABEL(xo24m))  | 
 | 23 | + | 
 | 24 | +#define CTX_ONOFF		BIT(6)  | 
 | 25 | +#define CTX_API			BIT(7)  | 
 | 26 | +#define CTX_MASK (CTX_ONOFF | CTX_API)  | 
 | 27 | + | 
 | 28 | +#define STATUS_MASK		0x7  | 
 | 29 | +#define GET_STATUS(flags)	(flags & STATUS_MASK)  | 
 | 30 | +#define GET_CTX(flags)		(flags & CTX_MASK)  | 
 | 31 | + | 
 | 32 | +/* Helper logging macros which prepends subsys name to the log. */  | 
 | 33 | +#ifdef CONFIG_LOG  | 
 | 34 | +#define CLOCK_LOG(lvl, dev, ...) \  | 
 | 35 | +	LOG_##lvl("%s: " GET_ARG_N(1, __VA_ARGS__), \  | 
 | 36 | +		"xo24m" \  | 
 | 37 | +		COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__),\  | 
 | 38 | +				(), (, GET_ARGS_LESS_N(1, __VA_ARGS__))))  | 
 | 39 | +#else  | 
 | 40 | +#define CLOCK_LOG(...)  | 
 | 41 | +#endif  | 
 | 42 | + | 
 | 43 | +#define ERR(dev, ...) CLOCK_LOG(ERR, dev, __VA_ARGS__)  | 
 | 44 | +#define WRN(dev, ...) CLOCK_LOG(WRN, dev, __VA_ARGS__)  | 
 | 45 | +#define INF(dev, ...) CLOCK_LOG(INF, dev, __VA_ARGS__)  | 
 | 46 | +#define DBG(dev, ...) CLOCK_LOG(DBG, dev, __VA_ARGS__)  | 
 | 47 | + | 
 | 48 | +typedef void (*clk_ctrl_func_t)(void);  | 
 | 49 | + | 
 | 50 | +typedef struct {  | 
 | 51 | +	struct onoff_manager mgr;  | 
 | 52 | +	clock_control_cb_t cb;  | 
 | 53 | +	void *user_data;  | 
 | 54 | +	uint32_t flags;  | 
 | 55 | +} xo24m_data_t;  | 
 | 56 | + | 
 | 57 | +typedef struct {  | 
 | 58 | +	clk_ctrl_func_t start;		/* Clock start function */  | 
 | 59 | +	clk_ctrl_func_t stop;		/* Clock stop function */  | 
 | 60 | +} xo24m_config_t;  | 
 | 61 | + | 
 | 62 | +static int set_off_state(uint32_t *flags, uint32_t ctx)  | 
 | 63 | +{  | 
 | 64 | +	int err = 0;  | 
 | 65 | +	unsigned int key = irq_lock();  | 
 | 66 | +	uint32_t current_ctx = GET_CTX(*flags);  | 
 | 67 | + | 
 | 68 | +	if ((current_ctx != 0) && (current_ctx != ctx)) {  | 
 | 69 | +		err = -EPERM;  | 
 | 70 | +	} else {  | 
 | 71 | +		*flags = CLOCK_CONTROL_STATUS_OFF;  | 
 | 72 | +	}  | 
 | 73 | + | 
 | 74 | +	irq_unlock(key);  | 
 | 75 | + | 
 | 76 | +	return err;  | 
 | 77 | +}  | 
 | 78 | + | 
 | 79 | +static int set_starting_state(uint32_t *flags, uint32_t ctx)  | 
 | 80 | +{  | 
 | 81 | +	int err = 0;  | 
 | 82 | +	unsigned int key = irq_lock();  | 
 | 83 | +	uint32_t current_ctx = GET_CTX(*flags);  | 
 | 84 | + | 
 | 85 | +	if ((*flags & (STATUS_MASK)) == CLOCK_CONTROL_STATUS_OFF) {  | 
 | 86 | +		*flags = CLOCK_CONTROL_STATUS_STARTING | ctx;  | 
 | 87 | +	} else if (current_ctx != ctx) {  | 
 | 88 | +		err = -EPERM;  | 
 | 89 | +	} else {  | 
 | 90 | +		err = -EALREADY;  | 
 | 91 | +	}  | 
 | 92 | + | 
 | 93 | +	irq_unlock(key);  | 
 | 94 | + | 
 | 95 | +	return err;  | 
 | 96 | +}  | 
 | 97 | + | 
 | 98 | +static void set_on_state(uint32_t *flags)  | 
 | 99 | +{  | 
 | 100 | +	unsigned int key = irq_lock();  | 
 | 101 | + | 
 | 102 | +	*flags = CLOCK_CONTROL_STATUS_ON | GET_CTX(*flags);  | 
 | 103 | +	irq_unlock(key);  | 
 | 104 | +}  | 
 | 105 | + | 
 | 106 | +static void clkstarted_handle(const struct device *dev)  | 
 | 107 | +{  | 
 | 108 | +	clock_control_cb_t callback = ((xo24m_data_t *)dev->data)->cb;  | 
 | 109 | +	void *user_data = ((xo24m_data_t *)dev->data)->user_data;  | 
 | 110 | + | 
 | 111 | +	((xo24m_data_t *)dev->data)->cb = NULL;  | 
 | 112 | +	set_on_state(&((xo24m_data_t *)dev->data)->flags);  | 
 | 113 | +	DBG(dev, "Clock started");  | 
 | 114 | + | 
 | 115 | +	if (callback) {  | 
 | 116 | +		callback(dev, NULL, user_data);  | 
 | 117 | +	}  | 
 | 118 | +}  | 
 | 119 | + | 
 | 120 | +static void xo24m_start(void)  | 
 | 121 | +{  | 
 | 122 | +	nrfx_clock_xo24m_start();  | 
 | 123 | +}  | 
 | 124 | + | 
 | 125 | +static void xo24m_stop(void)  | 
 | 126 | +{  | 
 | 127 | +	nrfx_clock_xo24m_stop();  | 
 | 128 | +}  | 
 | 129 | + | 
 | 130 | +static int stop(const struct device *dev, uint32_t ctx)  | 
 | 131 | +{  | 
 | 132 | +	int err;  | 
 | 133 | + | 
 | 134 | +	err = set_off_state(&((xo24m_data_t *)dev->data)->flags, ctx);  | 
 | 135 | +	if (err < 0) {  | 
 | 136 | +		return err;  | 
 | 137 | +	}  | 
 | 138 | + | 
 | 139 | +	((xo24m_config_t *)dev->config)->stop();  | 
 | 140 | + | 
 | 141 | +	return 0;  | 
 | 142 | +}  | 
 | 143 | + | 
 | 144 | +static int async_start(const struct device *dev, clock_control_cb_t cb, void *user_data,  | 
 | 145 | +		       uint32_t ctx)  | 
 | 146 | +{  | 
 | 147 | +	int err;  | 
 | 148 | + | 
 | 149 | +	err = set_starting_state(&((xo24m_data_t *)dev->data)->flags, ctx);  | 
 | 150 | +	if (err < 0) {  | 
 | 151 | +		return err;  | 
 | 152 | +	}  | 
 | 153 | + | 
 | 154 | +	((xo24m_data_t *)dev->data)->cb = cb;  | 
 | 155 | +	((xo24m_data_t *)dev->data)->user_data = user_data;  | 
 | 156 | + | 
 | 157 | +	((xo24m_config_t *)dev->config)->start();  | 
 | 158 | + | 
 | 159 | +	return 0;  | 
 | 160 | +}  | 
 | 161 | + | 
 | 162 | +static void blocking_start_callback(const struct device *dev,  | 
 | 163 | +				    clock_control_subsys_t subsys,  | 
 | 164 | +				    void *user_data)  | 
 | 165 | +{  | 
 | 166 | +	struct k_sem *sem = user_data;  | 
 | 167 | + | 
 | 168 | +	k_sem_give(sem);  | 
 | 169 | +}  | 
 | 170 | + | 
 | 171 | +static clock_control_subsys_t get_subsys(struct onoff_manager *mgr) //TODO remove  | 
 | 172 | +{  | 
 | 173 | +	xo24m_data_t *data = CLOCK_DEVICE_XO24M->data;  | 
 | 174 | +	size_t offset = (size_t)(mgr - data->mgr);  | 
 | 175 | + | 
 | 176 | +	return (clock_control_subsys_t)offset;  | 
 | 177 | +}  | 
 | 178 | + | 
 | 179 | +static void onoff_stop(struct onoff_manager *mgr,  | 
 | 180 | +			onoff_notify_fn notify)  | 
 | 181 | +{  | 
 | 182 | +	int res;  | 
 | 183 | + | 
 | 184 | +	res = stop(CLOCK_DEVICE_XO24M, CTX_ONOFF);  | 
 | 185 | +	notify(mgr, res);  | 
 | 186 | +}  | 
 | 187 | + | 
 | 188 | +static void onoff_started_callback(const struct device *dev,  | 
 | 189 | +				   clock_control_subsys_t sys,  | 
 | 190 | +				   void *user_data)  | 
 | 191 | +{  | 
 | 192 | +	ARG_UNUSED(sys);  | 
 | 193 | + | 
 | 194 | +	onoff_notify_fn notify = user_data;  | 
 | 195 | + | 
 | 196 | +	notify(&((xo24m_data_t *)dev->data)->mgr, 0);  | 
 | 197 | +}  | 
 | 198 | + | 
 | 199 | +static void onoff_start(struct onoff_manager *mgr,  | 
 | 200 | +			onoff_notify_fn notify)  | 
 | 201 | +{  | 
 | 202 | +	int err;  | 
 | 203 | + | 
 | 204 | +	err = async_start(CLOCK_DEVICE_XO24M, onoff_started_callback, notify, CTX_ONOFF);  | 
 | 205 | +	if (err < 0) {  | 
 | 206 | +		notify(mgr, err);  | 
 | 207 | +	}  | 
 | 208 | +}  | 
 | 209 | + | 
 | 210 | +static void clock_event_handler(void)  | 
 | 211 | +{  | 
 | 212 | +	const struct device *dev = CLOCK_DEVICE_XO24M;  | 
 | 213 | + | 
 | 214 | +	clkstarted_handle(dev);  | 
 | 215 | +}  | 
 | 216 | + | 
 | 217 | +static int api_start(const struct device *dev, clock_control_subsys_t subsys,  | 
 | 218 | +		     clock_control_cb_t cb, void *user_data)  | 
 | 219 | +{  | 
 | 220 | +	ARG_UNUSED(subsys);  | 
 | 221 | + | 
 | 222 | +	return async_start(dev, cb, user_data, CTX_API);  | 
 | 223 | +}  | 
 | 224 | + | 
 | 225 | +static int api_blocking_start(const struct device *dev,  | 
 | 226 | +			      clock_control_subsys_t subsys)  | 
 | 227 | +{  | 
 | 228 | +	struct k_sem sem = Z_SEM_INITIALIZER(sem, 0, 1);  | 
 | 229 | +	int err;  | 
 | 230 | + | 
 | 231 | +	if (!IS_ENABLED(CONFIG_MULTITHREADING)) {  | 
 | 232 | +		return -ENOTSUP;  | 
 | 233 | +	}  | 
 | 234 | + | 
 | 235 | +	err = api_start(dev, subsys, blocking_start_callback, &sem);  | 
 | 236 | +	if (err < 0) {  | 
 | 237 | +		return err;  | 
 | 238 | +	}  | 
 | 239 | + | 
 | 240 | +	return k_sem_take(&sem, K_MSEC(500));  | 
 | 241 | +}  | 
 | 242 | + | 
 | 243 | +static int api_stop(const struct device *dev, clock_control_subsys_t subsys)  | 
 | 244 | +{  | 
 | 245 | +	ARG_UNUSED(subsys);  | 
 | 246 | + | 
 | 247 | +	return stop(dev, CTX_API);  | 
 | 248 | +}  | 
 | 249 | + | 
 | 250 | +static enum clock_control_status api_get_status(const struct device *dev,  | 
 | 251 | +					    clock_control_subsys_t subsys)  | 
 | 252 | +{  | 
 | 253 | +	ARG_UNUSED(subsys);  | 
 | 254 | + | 
 | 255 | +	return GET_STATUS(((xo24m_data_t *)dev->data)->flags);  | 
 | 256 | +}  | 
 | 257 | + | 
 | 258 | +static int api_request(const struct device *dev,  | 
 | 259 | +		       const struct nrf_clock_spec *spec,  | 
 | 260 | +		       struct onoff_client *cli)  | 
 | 261 | +{  | 
 | 262 | +	xo24m_data_t *dev_data = dev->data;  | 
 | 263 | + | 
 | 264 | +	ARG_UNUSED(spec);  | 
 | 265 | + | 
 | 266 | +	return onoff_request(&dev_data->mgr, cli);  | 
 | 267 | +}  | 
 | 268 | + | 
 | 269 | +static int api_release(const struct device *dev,  | 
 | 270 | +		       const struct nrf_clock_spec *spec)  | 
 | 271 | +{  | 
 | 272 | +	xo24m_data_t *dev_data = dev->data;  | 
 | 273 | + | 
 | 274 | +	ARG_UNUSED(spec);  | 
 | 275 | + | 
 | 276 | +	return onoff_release(&dev_data->mgr);  | 
 | 277 | +}  | 
 | 278 | + | 
 | 279 | +static int api_cancel_or_release(const struct device *dev,  | 
 | 280 | +				 const struct nrf_clock_spec *spec,  | 
 | 281 | +				 struct onoff_client *cli)  | 
 | 282 | +{  | 
 | 283 | +	xo24m_data_t *dev_data = dev->data;  | 
 | 284 | + | 
 | 285 | +	ARG_UNUSED(spec);  | 
 | 286 | + | 
 | 287 | +	return onoff_cancel_or_release(&dev_data->mgr, cli);  | 
 | 288 | +}  | 
 | 289 | + | 
 | 290 | +static int clk_init(const struct device *dev)  | 
 | 291 | +{  | 
 | 292 | +	nrfx_err_t nrfx_err;  | 
 | 293 | +	int err;  | 
 | 294 | +	static const struct onoff_transitions transitions = {  | 
 | 295 | +		.start = onoff_start,  | 
 | 296 | +		.stop = onoff_stop  | 
 | 297 | +	};  | 
 | 298 | + | 
 | 299 | +	clock_control_nrf_common_connect_irq();  | 
 | 300 | + | 
 | 301 | +	nrfx_err = nrfx_clock_xo24m_init(clock_event_handler);  | 
 | 302 | +	if (nrfx_err != NRFX_SUCCESS) {  | 
 | 303 | +		return -EIO;  | 
 | 304 | +	}  | 
 | 305 | + | 
 | 306 | +	err = onoff_manager_init(&((xo24m_data_t *)dev->data)->mgr,  | 
 | 307 | +					&transitions);  | 
 | 308 | +	if (err < 0) {  | 
 | 309 | +		return err;  | 
 | 310 | +	}  | 
 | 311 | + | 
 | 312 | +	((xo24m_data_t *)dev->data)->flags = CLOCK_CONTROL_STATUS_OFF;  | 
 | 313 | + | 
 | 314 | +	return 0;  | 
 | 315 | +}  | 
 | 316 | + | 
 | 317 | +static DEVICE_API(nrf_clock_control, clock_control_api) = {  | 
 | 318 | +	.std_api = {  | 
 | 319 | +		.on = api_blocking_start,  | 
 | 320 | +		.off = api_stop,  | 
 | 321 | +		.async_on = api_start,  | 
 | 322 | +		.get_status = api_get_status,  | 
 | 323 | +	},  | 
 | 324 | +	.request = api_request,  | 
 | 325 | +	.release = api_release,  | 
 | 326 | +	.cancel_or_release = api_cancel_or_release,  | 
 | 327 | +};  | 
 | 328 | + | 
 | 329 | +static xo24m_data_t data;  | 
 | 330 | + | 
 | 331 | +static const xo24m_config_t config = {  | 
 | 332 | +	.start = xo24m_start,  | 
 | 333 | +	.stop = xo24m_stop,  | 
 | 334 | +};  | 
 | 335 | + | 
 | 336 | +DEVICE_DT_DEFINE(DT_NODELABEL(xo24m), clk_init, NULL,  | 
 | 337 | +		 &data, &config,  | 
 | 338 | +		 PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,  | 
 | 339 | +		 &clock_control_api);  | 
0 commit comments