|
| 1 | +/* |
| 2 | + * Copyright (c) 2025 Nordic Semiconductor ASA |
| 3 | + * SPDX-License-Identifier: Apache-2.0 |
| 4 | + */ |
| 5 | + |
| 6 | +#define DT_DRV_COMPAT nordic_nrfs_audiopll |
| 7 | + |
| 8 | +#include "clock_control_nrf2_common.h" |
| 9 | +#include <zephyr/devicetree.h> |
| 10 | +#include <zephyr/dt-bindings/clock/nrfs-audiopll.h> |
| 11 | +#include <zephyr/drivers/clock_control/nrf_clock_control.h> |
| 12 | +#include <nrfs_audiopll.h> |
| 13 | +#include <nrfs_backend_ipc_service.h> |
| 14 | + |
| 15 | +#include <zephyr/logging/log.h> |
| 16 | +LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); |
| 17 | + |
| 18 | +#define SHIM_DEFAULT_PRESCALER AUDIOPLL_DIV_12 |
| 19 | + |
| 20 | +BUILD_ASSERT( |
| 21 | + DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, |
| 22 | + "multiple instances not supported" |
| 23 | +); |
| 24 | + |
| 25 | +BUILD_ASSERT(DT_INST_PROP(0, frequency) >= NRFS_AUDIOPLL_FREQ_MIN); |
| 26 | +BUILD_ASSERT(DT_INST_PROP(0, frequency) <= NRFS_AUDIOPLL_FREQ_MAX); |
| 27 | + |
| 28 | +struct shim_data { |
| 29 | + struct onoff_manager mgr; |
| 30 | + onoff_notify_fn mgr_notify; |
| 31 | + const struct device *dev; |
| 32 | + struct k_sem evt_sem; |
| 33 | + nrfs_audiopll_evt_type_t evt; |
| 34 | +}; |
| 35 | + |
| 36 | +static int shim_nrfs_request_enable(const struct device *dev) |
| 37 | +{ |
| 38 | + struct shim_data *dev_data = dev->data; |
| 39 | + nrfs_err_t err; |
| 40 | + |
| 41 | + LOG_DBG("send enable request"); |
| 42 | + |
| 43 | + dev_data->evt = NRFS_AUDIOPLL_EVT_ENABLED; |
| 44 | + err = nrfs_audiopll_enable_request(dev_data); |
| 45 | + if (err != NRFS_SUCCESS) { |
| 46 | + return -EIO; |
| 47 | + } |
| 48 | + |
| 49 | + return 0; |
| 50 | +} |
| 51 | + |
| 52 | +static int shim_nrfs_request_disable(const struct device *dev) |
| 53 | +{ |
| 54 | + struct shim_data *dev_data = dev->data; |
| 55 | + nrfs_err_t err; |
| 56 | + |
| 57 | + LOG_DBG("send disable request"); |
| 58 | + |
| 59 | + dev_data->evt = NRFS_AUDIOPLL_EVT_DISABLED; |
| 60 | + err = nrfs_audiopll_disable_request(dev_data); |
| 61 | + if (err != NRFS_SUCCESS) { |
| 62 | + return -EIO; |
| 63 | + } |
| 64 | + |
| 65 | + return 0; |
| 66 | +} |
| 67 | + |
| 68 | +static void onoff_start_option(struct onoff_manager *mgr, onoff_notify_fn notify) |
| 69 | +{ |
| 70 | + struct shim_data *dev_data = CONTAINER_OF(mgr, struct shim_data, mgr); |
| 71 | + const struct device *dev = dev_data->dev; |
| 72 | + int ret; |
| 73 | + |
| 74 | + dev_data->mgr_notify = notify; |
| 75 | + |
| 76 | + ret = shim_nrfs_request_enable(dev); |
| 77 | + if (ret) { |
| 78 | + dev_data->mgr_notify = NULL; |
| 79 | + notify(mgr, -EIO); |
| 80 | + } |
| 81 | +} |
| 82 | + |
| 83 | +static void onoff_stop_option(struct onoff_manager *mgr, onoff_notify_fn notify) |
| 84 | +{ |
| 85 | + struct shim_data *dev_data = CONTAINER_OF(mgr, struct shim_data, mgr); |
| 86 | + const struct device *dev = dev_data->dev; |
| 87 | + int ret; |
| 88 | + |
| 89 | + dev_data->mgr_notify = notify; |
| 90 | + |
| 91 | + ret = shim_nrfs_request_disable(dev); |
| 92 | + if (ret) { |
| 93 | + dev_data->mgr_notify = NULL; |
| 94 | + notify(mgr, -EIO); |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +static const struct onoff_transitions shim_mgr_transitions = { |
| 99 | + .start = onoff_start_option, |
| 100 | + .stop = onoff_stop_option |
| 101 | +}; |
| 102 | + |
| 103 | +/* |
| 104 | + * Formula: |
| 105 | + * |
| 106 | + * frequency = ((4 + (freq_fraction * 2^-16)) * 32000000) / 12 |
| 107 | + * |
| 108 | + * Simplified linear approximation: |
| 109 | + * |
| 110 | + * frequency = 10666666 + (((13333292 - 10666666) / 65535) * freq_fraction) |
| 111 | + * frequency = 10666666 + ((2666626 / 65535) * freq_fraction) |
| 112 | + * frequency = ((10666666 * 65535) + (2666626 * freq_fraction)) / 65535 |
| 113 | + * frequency = (699039956310 + (2666626 * freq_fraction)) / 65535 |
| 114 | + * |
| 115 | + * Isolate freq_fraction: |
| 116 | + * |
| 117 | + * frequency = (699039956310 + (2666626 * freq_fraction)) / 65535 |
| 118 | + * frequency * 65535 = 699039956310 + (2666626 * freq_fraction) |
| 119 | + * (frequency * 65535) - 699039956310 = 2666626 * freq_fraction |
| 120 | + * freq_fraction = ((frequency * 65535) - 699039956310) / 2666626 |
| 121 | + */ |
| 122 | +static uint16_t shim_frequency_to_freq_fraction(uint32_t frequency) |
| 123 | +{ |
| 124 | + uint64_t freq_fraction; |
| 125 | + |
| 126 | + freq_fraction = frequency; |
| 127 | + freq_fraction *= 65535; |
| 128 | + freq_fraction -= 699039956310; |
| 129 | + freq_fraction = DIV_ROUND_CLOSEST(freq_fraction, 2666626); |
| 130 | + |
| 131 | + return (uint16_t)freq_fraction; |
| 132 | +} |
| 133 | + |
| 134 | +static int shim_nrfs_request_freq_sync(const struct device *dev, uint16_t freq_fraction) |
| 135 | +{ |
| 136 | + struct shim_data *dev_data = dev->data; |
| 137 | + nrfs_err_t err; |
| 138 | + |
| 139 | + LOG_DBG("send freq request"); |
| 140 | + |
| 141 | + err = nrfs_audiopll_request_freq(freq_fraction, dev_data); |
| 142 | + if (err != NRFS_SUCCESS) { |
| 143 | + return -EIO; |
| 144 | + } |
| 145 | + |
| 146 | + k_sem_take(&dev_data->evt_sem, K_FOREVER); |
| 147 | + return dev_data->evt == NRFS_AUDIOPLL_EVT_FREQ_CONFIRMED ? 0 : -EIO; |
| 148 | +} |
| 149 | + |
| 150 | +static int shim_nrfs_request_prescaler_sync(const struct device *dev, |
| 151 | + enum audiopll_prescaler_div div) |
| 152 | +{ |
| 153 | + struct shim_data *dev_data = dev->data; |
| 154 | + nrfs_err_t err; |
| 155 | + |
| 156 | + LOG_DBG("send prescaler request"); |
| 157 | + |
| 158 | + err = nrfs_audiopll_request_prescaler(div, dev_data); |
| 159 | + if (err != NRFS_SUCCESS) { |
| 160 | + return -EIO; |
| 161 | + } |
| 162 | + |
| 163 | + k_sem_take(&dev_data->evt_sem, K_FOREVER); |
| 164 | + return dev_data->evt == NRFS_AUDIOPLL_EVT_PRESCALER_CONFIRMED ? 0 : -EIO; |
| 165 | +} |
| 166 | + |
| 167 | +static void shim_nrfs_audiopll_init_evt_handler(nrfs_audiopll_evt_t const *evt, void *context) |
| 168 | +{ |
| 169 | + struct shim_data *dev_data = context; |
| 170 | + |
| 171 | + LOG_DBG("init resp evt %u", (uint32_t)evt->type); |
| 172 | + |
| 173 | + dev_data->evt = evt->type; |
| 174 | + k_sem_give(&dev_data->evt_sem); |
| 175 | +} |
| 176 | + |
| 177 | +static void shim_nrfs_audiopll_evt_handler(nrfs_audiopll_evt_t const *evt, void *context) |
| 178 | +{ |
| 179 | + struct shim_data *dev_data = context; |
| 180 | + int ret; |
| 181 | + |
| 182 | + LOG_DBG("resp evt %u", (uint32_t)evt->type); |
| 183 | + |
| 184 | + if (dev_data->mgr_notify == NULL) { |
| 185 | + return; |
| 186 | + } |
| 187 | + |
| 188 | + ret = evt->type == dev_data->evt ? 0 : -EIO; |
| 189 | + dev_data->mgr_notify(&dev_data->mgr, ret); |
| 190 | +} |
| 191 | + |
| 192 | +static int api_request_audiopll(const struct device *dev, |
| 193 | + const struct nrf_clock_spec *spec, |
| 194 | + struct onoff_client *cli) |
| 195 | +{ |
| 196 | + struct shim_data *dev_data = dev->data; |
| 197 | + struct onoff_manager *mgr = &dev_data->mgr; |
| 198 | + |
| 199 | + ARG_UNUSED(spec); |
| 200 | + |
| 201 | + return onoff_request(mgr, cli); |
| 202 | +} |
| 203 | + |
| 204 | +static int api_release_audiopll(const struct device *dev, |
| 205 | + const struct nrf_clock_spec *spec) |
| 206 | +{ |
| 207 | + struct shim_data *dev_data = dev->data; |
| 208 | + struct onoff_manager *mgr = &dev_data->mgr; |
| 209 | + |
| 210 | + ARG_UNUSED(spec); |
| 211 | + |
| 212 | + return onoff_release(mgr); |
| 213 | +} |
| 214 | + |
| 215 | +static int api_cancel_or_release_audiopll(const struct device *dev, |
| 216 | + const struct nrf_clock_spec *spec, |
| 217 | + struct onoff_client *cli) |
| 218 | +{ |
| 219 | + struct shim_data *dev_data = dev->data; |
| 220 | + struct onoff_manager *mgr = &dev_data->mgr; |
| 221 | + |
| 222 | + ARG_UNUSED(spec); |
| 223 | + |
| 224 | + return onoff_cancel_or_release(mgr, cli); |
| 225 | +} |
| 226 | + |
| 227 | +static DEVICE_API(nrf_clock_control, shim_driver_api) = { |
| 228 | + .std_api = { |
| 229 | + .on = api_nosys_on_off, |
| 230 | + .off = api_nosys_on_off, |
| 231 | + }, |
| 232 | + .request = api_request_audiopll, |
| 233 | + .release = api_release_audiopll, |
| 234 | + .cancel_or_release = api_cancel_or_release_audiopll, |
| 235 | +}; |
| 236 | + |
| 237 | +static int shim_init(const struct device *dev) |
| 238 | +{ |
| 239 | + struct shim_data *dev_data = dev->data; |
| 240 | + nrfs_err_t err; |
| 241 | + int ret; |
| 242 | + uint16_t freq_fraction; |
| 243 | + |
| 244 | + LOG_DBG("waiting for nrfs backend connected"); |
| 245 | + err = nrfs_backend_wait_for_connection(K_FOREVER); |
| 246 | + if (err != NRFS_SUCCESS) { |
| 247 | + LOG_ERR("nrfs backend not connected"); |
| 248 | + return -ENODEV; |
| 249 | + } |
| 250 | + |
| 251 | + k_sem_init(&dev_data->evt_sem, 0, 1); |
| 252 | + |
| 253 | + err = nrfs_audiopll_init(shim_nrfs_audiopll_init_evt_handler); |
| 254 | + if (err != NRFS_SUCCESS) { |
| 255 | + LOG_ERR("failed to init audiopll service"); |
| 256 | + return -ENODEV; |
| 257 | + } |
| 258 | + |
| 259 | + ret = shim_nrfs_request_prescaler_sync(dev, SHIM_DEFAULT_PRESCALER); |
| 260 | + if (ret) { |
| 261 | + LOG_ERR("failed to set prescaler divider"); |
| 262 | + return ret; |
| 263 | + } |
| 264 | + |
| 265 | + freq_fraction = shim_frequency_to_freq_fraction(DT_INST_PROP(0, frequency)); |
| 266 | + |
| 267 | + LOG_DBG("requesting freq_fraction %u for frequency %uHz", |
| 268 | + freq_fraction, |
| 269 | + DT_INST_PROP(0, frequency)); |
| 270 | + |
| 271 | + ret = shim_nrfs_request_freq_sync(dev, freq_fraction); |
| 272 | + if (ret) { |
| 273 | + LOG_ERR("failed to set freq_fraction"); |
| 274 | + return ret; |
| 275 | + } |
| 276 | + |
| 277 | + nrfs_audiopll_uninit(); |
| 278 | + |
| 279 | + ret = onoff_manager_init(&dev_data->mgr, &shim_mgr_transitions); |
| 280 | + if (ret < 0) { |
| 281 | + LOG_ERR("failed to init onoff manager"); |
| 282 | + return ret; |
| 283 | + } |
| 284 | + |
| 285 | + err = nrfs_audiopll_init(shim_nrfs_audiopll_evt_handler); |
| 286 | + if (err != NRFS_SUCCESS) { |
| 287 | + LOG_ERR("failed to init audiopll service"); |
| 288 | + return -ENODEV; |
| 289 | + } |
| 290 | + |
| 291 | + return 0; |
| 292 | +} |
| 293 | + |
| 294 | +static struct shim_data shim_data = { |
| 295 | + .dev = DEVICE_DT_INST_GET(0), |
| 296 | +}; |
| 297 | + |
| 298 | +DEVICE_DT_INST_DEFINE( |
| 299 | + 0, |
| 300 | + shim_init, |
| 301 | + NULL, |
| 302 | + &shim_data, |
| 303 | + NULL, |
| 304 | + POST_KERNEL, |
| 305 | + UTIL_INC(CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO), |
| 306 | + &shim_driver_api, |
| 307 | +); |
0 commit comments