-
Notifications
You must be signed in to change notification settings - Fork 630
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[nrf fromlist] drivers: clock_control: nrf2: add support for global h…
…fsll clock Add device driver support for global hsfll clock. Upstream PR #: 81735 Signed-off-by: Bjarki Arge Andreasen <[email protected]>
- Loading branch information
1 parent
63e3bef
commit 4313bb1
Showing
4 changed files
with
227 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
212 changes: 212 additions & 0 deletions
212
drivers/clock_control/clock_control_nrf2_global_hsfll.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
/* | ||
* Copyright (c) 2024 Nordic Semiconductor ASA | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#define DT_DRV_COMPAT nordic_nrf_global_hsfll | ||
|
||
#include "clock_control_nrf2_common.h" | ||
#include <zephyr/devicetree.h> | ||
#include <zephyr/drivers/clock_control/nrf_clock_control.h> | ||
#include <nrfs_gdfs.h> | ||
|
||
#include <zephyr/logging/log.h> | ||
LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); | ||
|
||
#define GLOBAL_HSFLL_CLOCK_FREQUENCIES \ | ||
DT_INST_PROP(0, supported_clock_frequencies) | ||
|
||
#define GLOBAL_HSFLL_CLOCK_FREQUENCIES_SIZE \ | ||
DT_INST_PROP_LEN(0, supported_clock_frequencies) | ||
|
||
#define GLOBAL_HSFLL_FREQ_REQ_TIMEOUT \ | ||
K_MSEC(CONFIG_CLOCK_CONTROL_NRF2_GLOBAL_HSFLL_TIMEOUT_MS) | ||
|
||
struct global_hsfll_dev_config { | ||
uint32_t clock_frequencies[GLOBAL_HSFLL_CLOCK_FREQUENCIES_SIZE]; | ||
}; | ||
|
||
struct global_hsfll_dev_data { | ||
STRUCT_CLOCK_CONFIG(global_hsfll, GLOBAL_HSFLL_CLOCK_FREQUENCIES_SIZE) clk_cfg; | ||
struct k_work evt_work; | ||
nrfs_gdfs_evt_type_t evt; | ||
struct k_work_delayable timeout_dwork; | ||
}; | ||
|
||
static uint32_t global_hsfll_get_max_clock_frequency(const struct device *dev) | ||
{ | ||
const struct global_hsfll_dev_config *dev_config = dev->config; | ||
|
||
return dev_config->clock_frequencies[ARRAY_SIZE(dev_config->clock_frequencies) - 1]; | ||
} | ||
|
||
static struct onoff_manager *global_hsfll_find_mgr(const struct device *dev, | ||
const struct nrf_clock_spec *spec) | ||
{ | ||
struct global_hsfll_dev_data *dev_data = dev->data; | ||
const struct global_hsfll_dev_config *dev_config = dev->config; | ||
uint32_t frequency; | ||
|
||
if (!spec) { | ||
return &dev_data->clk_cfg.onoff[0].mgr; | ||
} | ||
|
||
if (spec->accuracy || spec->precision) { | ||
LOG_ERR("invalid specification of accuracy or precision"); | ||
return NULL; | ||
} | ||
|
||
frequency = spec->frequency == NRF_CLOCK_CONTROL_FREQUENCY_MAX | ||
? global_hsfll_get_max_clock_frequency(dev) | ||
: spec->frequency; | ||
|
||
for (uint8_t i = 0; i < ARRAY_SIZE(dev_config->clock_frequencies); i++) { | ||
if (dev_config->clock_frequencies[i] < frequency) { | ||
continue; | ||
} | ||
|
||
return &dev_data->clk_cfg.onoff[i].mgr; | ||
} | ||
|
||
LOG_ERR("invalid frequency"); | ||
return NULL; | ||
} | ||
|
||
static int api_request_global_hsfll(const struct device *dev, | ||
const struct nrf_clock_spec *spec, | ||
struct onoff_client *cli) | ||
{ | ||
struct onoff_manager *mgr = global_hsfll_find_mgr(dev, spec); | ||
|
||
if (mgr) { | ||
return onoff_request(mgr, cli); | ||
} | ||
|
||
return -EINVAL; | ||
} | ||
|
||
static int api_release_global_hsfll(const struct device *dev, | ||
const struct nrf_clock_spec *spec) | ||
{ | ||
struct onoff_manager *mgr = global_hsfll_find_mgr(dev, spec); | ||
|
||
if (mgr) { | ||
return onoff_release(mgr); | ||
} | ||
|
||
return -EINVAL; | ||
} | ||
|
||
static int api_cancel_or_release_global_hsfll(const struct device *dev, | ||
const struct nrf_clock_spec *spec, | ||
struct onoff_client *cli) | ||
{ | ||
struct onoff_manager *mgr = global_hsfll_find_mgr(dev, spec); | ||
|
||
if (mgr) { | ||
return onoff_cancel_or_release(mgr, cli); | ||
} | ||
|
||
return -EINVAL; | ||
} | ||
|
||
static struct nrf_clock_control_driver_api driver_api = { | ||
.std_api = { | ||
.on = api_nosys_on_off, | ||
.off = api_nosys_on_off, | ||
}, | ||
.request = api_request_global_hsfll, | ||
.release = api_release_global_hsfll, | ||
.cancel_or_release = api_cancel_or_release_global_hsfll, | ||
}; | ||
|
||
static void global_hsfll_work_handler(struct k_work *work) | ||
{ | ||
struct global_hsfll_dev_data *dev_data = | ||
CONTAINER_OF(work, struct global_hsfll_dev_data, clk_cfg.work); | ||
uint8_t freq_idx; | ||
nrfs_err_t err; | ||
|
||
freq_idx = clock_config_update_begin(work); | ||
|
||
err = nrfs_gdfs_request_freq(freq_idx, dev_data); | ||
if (err != NRFS_SUCCESS) { | ||
clock_config_update_end(&dev_data->clk_cfg, -EIO); | ||
return; | ||
} | ||
|
||
k_work_schedule(&dev_data->timeout_dwork, GLOBAL_HSFLL_FREQ_REQ_TIMEOUT); | ||
} | ||
|
||
static void global_hsfll_evt_handler(struct k_work *work) | ||
{ | ||
struct global_hsfll_dev_data *dev_data = | ||
CONTAINER_OF(work, struct global_hsfll_dev_data, evt_work); | ||
int rc; | ||
|
||
k_work_cancel_delayable(&dev_data->timeout_dwork); | ||
rc = dev_data->evt == NRFS_GDFS_EVT_FREQ_CONFIRMED ? 0 : -EIO; | ||
clock_config_update_end(&dev_data->clk_cfg, rc); | ||
} | ||
|
||
static void global_hfsll_nrfs_gdfs_evt_handler(nrfs_gdfs_evt_t const *p_evt, void *context) | ||
{ | ||
struct global_hsfll_dev_data *dev_data = context; | ||
|
||
if (k_work_is_pending(&dev_data->evt_work)) { | ||
return; | ||
} | ||
|
||
dev_data->evt = p_evt->type; | ||
k_work_submit(&dev_data->evt_work); | ||
} | ||
|
||
static void global_hsfll_timeout_handler(struct k_work *work) | ||
{ | ||
struct k_work_delayable *dwork = k_work_delayable_from_work(work); | ||
struct global_hsfll_dev_data *dev_data = | ||
CONTAINER_OF(dwork, struct global_hsfll_dev_data, timeout_dwork); | ||
|
||
clock_config_update_end(&dev_data->clk_cfg, -ETIMEDOUT); | ||
} | ||
|
||
static int global_hfsll_init(const struct device *dev) | ||
{ | ||
struct global_hsfll_dev_data *dev_data = dev->data; | ||
int rc; | ||
nrfs_err_t err; | ||
|
||
rc = clock_config_init(&dev_data->clk_cfg, | ||
ARRAY_SIZE(dev_data->clk_cfg.onoff), | ||
global_hsfll_work_handler); | ||
if (rc < 0) { | ||
return rc; | ||
} | ||
|
||
k_work_init_delayable(&dev_data->timeout_dwork, global_hsfll_timeout_handler); | ||
k_work_init(&dev_data->evt_work, global_hsfll_evt_handler); | ||
|
||
err = nrfs_gdfs_init(global_hfsll_nrfs_gdfs_evt_handler); | ||
if (err != NRFS_SUCCESS) { | ||
return -EIO; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static struct global_hsfll_dev_data driver_data; | ||
|
||
static const struct global_hsfll_dev_config driver_config = { | ||
GLOBAL_HSFLL_CLOCK_FREQUENCIES | ||
}; | ||
|
||
DEVICE_DT_INST_DEFINE( | ||
0, | ||
global_hfsll_init, | ||
NULL, | ||
&driver_data, | ||
&driver_config, | ||
PRE_KERNEL_1, | ||
CONFIG_CLOCK_CONTROL_INIT_PRIORITY, | ||
&driver_api | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters