diff --git a/boards/nordic/nrf54h20dk/bicr.json b/boards/nordic/nrf54h20dk/bicr.json new file mode 100644 index 00000000000..9937860052b --- /dev/null +++ b/boards/nordic/nrf54h20dk/bicr.json @@ -0,0 +1,32 @@ +{ + "power": { + "scheme": "VDDH_2V1_5V5" + }, + "ioPortPower": { + "p1Supply": "EXTERNAL_1V8", + "p2Supply": "EXTERNAL_1V8", + "p6Supply": "EXTERNAL_1V8", + "p7Supply": "EXTERNAL_1V8", + "p9Supply": "EXTERNAL_FULL" + }, + "ioPortImpedance": { + "p6ImpedanceOhms": 50, + "p7ImpedanceOhms": 50 + }, + "lfosc": { + "source": "LFXO", + "lfxo": { + "mode": "CRYSTAL", + "accuracyPPM": 20, + "startupTimeMs": 600, + "builtInLoadCapacitancePf": 15, + "builtInLoadCapacitors": true + } + }, + "hfxo": { + "mode": "CRYSTAL", + "startupTimeUs": 850, + "builtInLoadCapacitors": true, + "builtInLoadCapacitancePf": 14 + } +} diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_bicr.dtsi b/boards/nordic/nrf54h20dk/nrf54h20dk_bicr.dtsi deleted file mode 100644 index 34652755564..00000000000 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_bicr.dtsi +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2024 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - bicr: bicr@fff87b0 { - compatible = "nordic,nrf-bicr"; - reg = <0xfff87b0 0x48>; - - power-vddao5v0 = "external"; - power-vddao1v8 = "internal"; - power-vdd1v0 = "internal"; - power-vddrf1v0 = "shorted"; - power-vddao0v8 = "internal"; - power-vddvs0v8 = "internal"; - - inductor-present; - - ioport-power-rails = <&gpio1 2>, <&gpio2 2>, <&gpio6 2>, <&gpio7 2>, <&gpio9 4>; - ioport-drivectrls = <&gpio6 50>, <&gpio7 50>; - - lfosc-mode = "crystal"; - lfosc-loadcap = <15>; - - lfrc-autocalibration = <20 40 3>; - - hfxo-mode = "crystal"; - hfxo-loadcap = <56>; - }; -}; - -&gpio1 { - #ioport-power-rail-cells = <1>; -}; - -&gpio2 { - #ioport-power-rail-cells = <1>; -}; - -&gpio6 { - #ioport-power-rail-cells = <1>; - #ioport-drivectrl-cells = <1>; -}; - -&gpio7 { - #ioport-power-rail-cells = <1>; - #ioport-drivectrl-cells = <1>; -}; - -&gpio9 { - #ioport-power-rail-cells = <1>; -}; diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-common.dtsi b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-common.dtsi index 595307aec8c..0c5307c4c8c 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-common.dtsi +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-common.dtsi @@ -11,13 +11,8 @@ &hfxo { status = "okay"; accuracy-ppm = <30>; - startup-time-us = <850>; - mode = "crystal"; }; &lfxo { status = "okay"; - accuracy-ppm = <20>; - startup-time-us = <600000>; - mode = "crystal"; }; diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts index 9c3556b27c4..1868ba8c331 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts @@ -8,7 +8,6 @@ #include #include "nrf54h20dk_nrf54h20-common.dtsi" -#include "nrf54h20dk_bicr.dtsi" /delete-node/ &cpurad_cpusys_ipc; /delete-node/ &cpusec_cpurad_ipc; diff --git a/drivers/clock_control/clock_control_nrf2_common.c b/drivers/clock_control/clock_control_nrf2_common.c index ba576ccbb6c..da3b6b86863 100644 --- a/drivers/clock_control/clock_control_nrf2_common.c +++ b/drivers/clock_control/clock_control_nrf2_common.c @@ -5,6 +5,7 @@ #include "clock_control_nrf2_common.h" #include +#include #include LOG_MODULE_REGISTER(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); @@ -19,6 +20,8 @@ LOG_MODULE_REGISTER(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); (idx * sizeof(array[0])) - \ offsetof(type, array[0])) +#define BICR (NRF_BICR_Type *)DT_REG_ADDR(DT_NODELABEL(bicr)) + /* * Definition of `struct clock_config_generic`. * Used to access `clock_config_*` structures in a common way. @@ -83,6 +86,40 @@ static inline uint8_t get_index_of_highest_bit(uint32_t value) return value ? (uint8_t)(31 - __builtin_clz(value)) : 0; } +int lfosc_get_accuracy(uint16_t *accuracy) +{ + switch (nrf_bicr_lfosc_accuracy_get(BICR)) { + case NRF_BICR_LFOSC_ACCURACY_500PPM: + *accuracy = 500U; + break; + case NRF_BICR_LFOSC_ACCURACY_250PPM: + *accuracy = 250U; + break; + case NRF_BICR_LFOSC_ACCURACY_150PPM: + *accuracy = 150U; + break; + case NRF_BICR_LFOSC_ACCURACY_100PPM: + *accuracy = 100U; + break; + case NRF_BICR_LFOSC_ACCURACY_75PPM: + *accuracy = 75U; + break; + case NRF_BICR_LFOSC_ACCURACY_50PPM: + *accuracy = 50U; + break; + case NRF_BICR_LFOSC_ACCURACY_30PPM: + *accuracy = 30U; + break; + case NRF_BICR_LFOSC_ACCURACY_20PPM: + *accuracy = 20U; + break; + default: + return -EINVAL; + } + + return 0; +} + int clock_config_init(void *clk_cfg, uint8_t onoff_cnt, k_work_handler_t update_work_handler) { struct clock_config_generic *cfg = clk_cfg; diff --git a/drivers/clock_control/clock_control_nrf2_common.h b/drivers/clock_control/clock_control_nrf2_common.h index 858698c38ea..1f08e5b090f 100644 --- a/drivers/clock_control/clock_control_nrf2_common.h +++ b/drivers/clock_control/clock_control_nrf2_common.h @@ -36,6 +36,16 @@ struct clock_onoff { struct clock_onoff onoff[_onoff_cnt]; \ } +/** + * @brief Obtain LFOSC accuracy in ppm. + * + * @param[out] accuracy Accuracy in ppm. + * + * @retval 0 On success + * @retval -EINVAL If accuracy is not configured. + */ +int lfosc_get_accuracy(uint16_t *accuracy); + /** * @brief Initializes a clock configuration structure. * diff --git a/drivers/clock_control/clock_control_nrf2_fll16m.c b/drivers/clock_control/clock_control_nrf2_fll16m.c index 613e433e527..6aa38cbbff3 100644 --- a/drivers/clock_control/clock_control_nrf2_fll16m.c +++ b/drivers/clock_control/clock_control_nrf2_fll16m.c @@ -8,7 +8,9 @@ #include "clock_control_nrf2_common.h" #include #include + #include +#include #include LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); @@ -23,26 +25,17 @@ BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, #define FLL16M_MODE_BYPASS 2 #define FLL16M_MODE_DEFAULT FLL16M_MODE_OPEN_LOOP -#define FLL16M_LFXO_NODE DT_INST_PHANDLE_BY_NAME(0, clocks, lfxo) #define FLL16M_HFXO_NODE DT_INST_PHANDLE_BY_NAME(0, clocks, hfxo) -#define FLL16M_HAS_LFXO DT_NODE_HAS_STATUS_OKAY(FLL16M_LFXO_NODE) - -#define FLL16M_LFXO_ACCURACY DT_PROP(FLL16M_LFXO_NODE, accuracy_ppm) #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 -/* Closed-loop mode uses LFXO as source if present, HFXO otherwise */ -#if FLL16M_HAS_LFXO -#define FLL16M_CLOSED_LOOP_ACCURACY (FLL16M_CLOSED_LOOP_BASE_ACCURACY + FLL16M_LFXO_ACCURACY) -#else -#define FLL16M_CLOSED_LOOP_ACCURACY (FLL16M_CLOSED_LOOP_BASE_ACCURACY + FLL16M_HFXO_ACCURACY) -#endif +#define BICR (NRF_BICR_Type *)DT_REG_ADDR(DT_NODELABEL(bicr)) /* Clock options sorted from lowest to highest accuracy */ -static const struct clock_options { +static struct clock_options { uint16_t accuracy; uint8_t mode; } clock_options[] = { @@ -51,7 +44,6 @@ static const struct clock_options { .mode = FLL16M_MODE_OPEN_LOOP, }, { - .accuracy = FLL16M_CLOSED_LOOP_ACCURACY, .mode = FLL16M_MODE_CLOSED_LOOP, }, { @@ -233,6 +225,27 @@ static int api_get_rate_fll16m(const struct device *dev, 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), diff --git a/drivers/clock_control/clock_control_nrf2_hfxo.c b/drivers/clock_control/clock_control_nrf2_hfxo.c index 977c0aaa0d6..d73460e93d3 100644 --- a/drivers/clock_control/clock_control_nrf2_hfxo.c +++ b/drivers/clock_control/clock_control_nrf2_hfxo.c @@ -12,6 +12,7 @@ LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); #include +#include BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, "multiple instances not supported"); @@ -21,14 +22,44 @@ struct dev_data_hfxo { onoff_notify_fn notify; struct k_timer timer; sys_snode_t hfxo_node; +#if defined(CONFIG_ZERO_LATENCY_IRQS) + uint16_t request_count; +#endif /* CONFIG_ZERO_LATENCY_IRQS */ + k_timeout_t start_up_time; }; struct dev_config_hfxo { uint32_t fixed_frequency; uint16_t fixed_accuracy; - k_timeout_t start_up_time; }; +#define BICR (NRF_BICR_Type *)DT_REG_ADDR(DT_NODELABEL(bicr)) + +#if defined(CONFIG_ZERO_LATENCY_IRQS) +static uint32_t full_irq_lock(void) +{ + uint32_t mcu_critical_state; + + if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { + mcu_critical_state = __get_PRIMASK(); + __disable_irq(); + } else { + mcu_critical_state = irq_lock(); + } + + return mcu_critical_state; +} + +static void full_irq_unlock(uint32_t mcu_critical_state) +{ + if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { + __set_PRIMASK(mcu_critical_state); + } else { + irq_unlock(mcu_critical_state); + } +} +#endif /* CONFIG_ZERO_LATENCY_IRQS */ + static void hfxo_start_up_timer_handler(struct k_timer *timer) { struct dev_data_hfxo *dev_data = @@ -48,33 +79,102 @@ static void hfxo_start_up_timer_handler(struct k_timer *timer) } } +static void start_hfxo(struct dev_data_hfxo *dev_data) +{ + nrf_lrcconf_event_clear(NRF_LRCCONF010, NRF_LRCCONF_EVENT_HFXOSTARTED); + soc_lrcconf_poweron_request(&dev_data->hfxo_node, NRF_LRCCONF_POWER_MAIN); + nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_REQHFXO); +} + +static void request_hfxo(struct dev_data_hfxo *dev_data) +{ +#if defined(CONFIG_ZERO_LATENCY_IRQS) + unsigned int key; + + key = full_irq_lock(); + if (dev_data->request_count == 0) { + start_hfxo(dev_data); + } + + dev_data->request_count++; + full_irq_unlock(key); +#else + start_hfxo(dev_data); +#endif /* CONFIG_ZERO_LATENCY_IRQS */ +} + +#if IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) +void nrf_clock_control_hfxo_request(void) +{ + const struct device *dev = DEVICE_DT_INST_GET(0); + struct dev_data_hfxo *dev_data = dev->data; + + request_hfxo(dev_data); +} +#endif /* CONFIG_ZERO_LATENCY_IRQS */ + static void onoff_start_hfxo(struct onoff_manager *mgr, onoff_notify_fn notify) { struct dev_data_hfxo *dev_data = CONTAINER_OF(mgr, struct dev_data_hfxo, mgr); - const struct device *dev = DEVICE_DT_INST_GET(0); - const struct dev_config_hfxo *dev_config = dev->config; dev_data->notify = notify; - - nrf_lrcconf_event_clear(NRF_LRCCONF010, NRF_LRCCONF_EVENT_HFXOSTARTED); - soc_lrcconf_poweron_request(&dev_data->hfxo_node, NRF_LRCCONF_POWER_MAIN); - nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_REQHFXO); + request_hfxo(dev_data); /* Due to a hardware issue, the HFXOSTARTED event is currently * unreliable. Hence the timer is used to simply wait the expected * start-up time. To be removed once the hardware is fixed. */ - k_timer_start(&dev_data->timer, dev_config->start_up_time, K_NO_WAIT); + k_timer_start(&dev_data->timer, dev_data->start_up_time, K_NO_WAIT); +} + +static void stop_hfxo(struct dev_data_hfxo *dev_data) +{ + nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_STOPREQHFXO); + soc_lrcconf_poweron_release(&dev_data->hfxo_node, NRF_LRCCONF_POWER_MAIN); +} + +static void release_hfxo(struct dev_data_hfxo *dev_data) +{ +#if IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) + unsigned int key; + + key = full_irq_lock(); + if (dev_data->request_count < 1) { + full_irq_unlock(key); + /* Misuse of the API, release without request? */ + __ASSERT_NO_MSG(false); + /* In case asserts are disabled early return due to no requests pending */ + return; + } + + dev_data->request_count--; + if (dev_data->request_count < 1) { + stop_hfxo(dev_data); + } + + full_irq_unlock(key); +#else + stop_hfxo(dev_data); +#endif /* CONFIG_ZERO_LATENCY_IRQS */ } +#if IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) +void nrf_clock_control_hfxo_release(void) +{ + const struct device *dev = DEVICE_DT_INST_GET(0); + struct dev_data_hfxo *dev_data = dev->data; + + release_hfxo(dev_data); +} +#endif /* IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) */ + static void onoff_stop_hfxo(struct onoff_manager *mgr, onoff_notify_fn notify) { struct dev_data_hfxo *dev_data = CONTAINER_OF(mgr, struct dev_data_hfxo, mgr); - nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_STOPREQHFXO); - soc_lrcconf_poweron_release(&dev_data->hfxo_node, NRF_LRCCONF_POWER_MAIN); + release_hfxo(dev_data); notify(mgr, 0); } @@ -159,6 +259,7 @@ static int init_hfxo(const struct device *dev) .start = onoff_start_hfxo, .stop = onoff_stop_hfxo }; + uint32_t start_up_time; int rc; rc = onoff_manager_init(&dev_data->mgr, &transitions); @@ -166,6 +267,13 @@ static int init_hfxo(const struct device *dev) return rc; } + start_up_time = nrf_bicr_hfxo_startup_time_us_get(BICR); + if (start_up_time == NRF_BICR_HFXO_STARTUP_TIME_UNCONFIGURED) { + return -EINVAL; + } + + dev_data->start_up_time = K_USEC(start_up_time); + k_timer_init(&dev_data->timer, hfxo_start_up_timer_handler, NULL); return 0; @@ -187,7 +295,6 @@ static struct dev_data_hfxo data_hfxo; static const struct dev_config_hfxo config_hfxo = { .fixed_frequency = DT_INST_PROP(0, clock_frequency), .fixed_accuracy = DT_INST_PROP(0, accuracy_ppm), - .start_up_time = K_USEC(DT_INST_PROP(0, startup_time_us)), }; DEVICE_DT_INST_DEFINE(0, init_hfxo, NULL, diff --git a/drivers/clock_control/clock_control_nrf2_lfclk.c b/drivers/clock_control/clock_control_nrf2_lfclk.c index 36b8e247297..fc208b913c3 100644 --- a/drivers/clock_control/clock_control_nrf2_lfclk.c +++ b/drivers/clock_control/clock_control_nrf2_lfclk.c @@ -8,6 +8,7 @@ #include "clock_control_nrf2_common.h" #include #include +#include #include #include @@ -16,30 +17,25 @@ 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 LFCLK_LFXO_NODE DT_INST_PHANDLE_BY_NAME(0, clocks, lfxo) #define LFCLK_HFXO_NODE DT_INST_PHANDLE_BY_NAME(0, clocks, hfxo) -#define LFCLK_HAS_LFXO DT_NODE_HAS_STATUS_OKAY(LFCLK_LFXO_NODE) - #define LFCLK_LFLPRC_ACCURACY DT_INST_PROP(0, lflprc_accuracy_ppm) #define LFCLK_LFRC_ACCURACY DT_INST_PROP(0, lfrc_accuracy_ppm) -#define LFCLK_LFXO_ACCURACY DT_PROP(LFCLK_LFXO_NODE, accuracy_ppm) #define LFCLK_HFXO_ACCURACY DT_PROP(LFCLK_HFXO_NODE, accuracy_ppm) -#if LFCLK_HAS_LFXO -#define LFCLK_MAX_ACCURACY LFCLK_LFXO_ACCURACY -#else -#define LFCLK_MAX_ACCURACY LFCLK_HFXO_ACCURACY -#endif +#define LFCLK_MAX_OPTS 5 +#define LFCLK_DEF_OPTS 3 #define NRFS_CLOCK_TIMEOUT K_MSEC(CONFIG_CLOCK_CONTROL_NRF2_NRFS_CLOCK_TIMEOUT_MS) +#define BICR (NRF_BICR_Type *)DT_REG_ADDR(DT_NODELABEL(bicr)) + /* Clock options sorted from lowest to highest accuracy/precision */ -static const struct clock_options { +static struct clock_options { uint16_t accuracy : 15; uint16_t precision : 1; nrfs_clock_src_t src; -} clock_options[] = { +} clock_options[LFCLK_MAX_OPTS] = { { .accuracy = LFCLK_LFLPRC_ACCURACY, .precision = 0, @@ -56,43 +52,13 @@ static const struct clock_options { .precision = 1, .src = NRFS_CLOCK_SRC_LFCLK_SYNTH, }, -#if LFCLK_HAS_LFXO -#if DT_ENUM_HAS_VALUE(LFCLK_LFXO_NODE, mode, crystal) - { - .accuracy = LFCLK_LFXO_ACCURACY, - .src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE, - }, - { - .accuracy = LFCLK_LFXO_ACCURACY, - .precision = 1, - .src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE_HP, - }, -#elif DT_ENUM_HAS_VALUE(LFCLK_LFXO_NODE, mode, external_sine) - { - .accuracy = LFCLK_LFXO_ACCURACY, - .precision = 0, - .src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE, - }, - { - .accuracy = LFCLK_LFXO_ACCURACY, - .precision = 1, - .src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE_HP, - }, -#elif DT_ENUM_HAS_VALUE(LFCLK_LFXO_NODE, mode, external_square) - { - .accuracy = LFCLK_LFXO_ACCURACY, - .precision = 0, - .src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SQUARE, - }, -#else -#error "unsupported LFXO mode" -#endif -#endif }; struct lfclk_dev_data { STRUCT_CLOCK_CONFIG(lfclk, ARRAY_SIZE(clock_options)) clk_cfg; struct k_timer timer; + uint16_t max_accuracy; + uint8_t clock_options_cnt; }; struct lfclk_dev_config { @@ -156,10 +122,10 @@ static struct onoff_manager *lfclk_find_mgr(const struct device *dev, } accuracy = spec->accuracy == NRF_CLOCK_CONTROL_ACCURACY_MAX - ? LFCLK_MAX_ACCURACY + ? dev_data->max_accuracy : spec->accuracy; - for (int i = 0; i < ARRAY_SIZE(clock_options); ++i) { + for (int i = 0; i < dev_data->clock_options_cnt; ++i) { if ((accuracy && accuracy < clock_options[i].accuracy) || spec->precision > clock_options[i].precision) { @@ -227,6 +193,7 @@ static int api_get_rate_lfclk(const struct device *dev, static int lfclk_init(const struct device *dev) { struct lfclk_dev_data *dev_data = dev->data; + nrf_bicr_lfosc_mode_t lfosc_mode; nrfs_err_t res; res = nrfs_clock_init(clock_evt_handler); @@ -234,6 +201,58 @@ static int lfclk_init(const struct device *dev) return -EIO; } + dev_data->clock_options_cnt = LFCLK_DEF_OPTS; + + lfosc_mode = nrf_bicr_lfosc_mode_get(BICR); + + if (lfosc_mode == NRF_BICR_LFOSC_MODE_UNCONFIGURED || + lfosc_mode == NRF_BICR_LFOSC_MODE_DISABLED) { + dev_data->max_accuracy = LFCLK_HFXO_ACCURACY; + } else { + int ret; + + ret = lfosc_get_accuracy(&dev_data->max_accuracy); + if (ret < 0) { + LOG_ERR("LFOSC enabled with invalid accuracy"); + return ret; + } + + switch (lfosc_mode) { + case NRF_BICR_LFOSC_MODE_CRYSTAL: + clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy; + clock_options[LFCLK_MAX_OPTS - 2].precision = 0; + clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE; + + clock_options[LFCLK_MAX_OPTS - 1].accuracy = dev_data->max_accuracy; + clock_options[LFCLK_MAX_OPTS - 1].precision = 1; + clock_options[LFCLK_MAX_OPTS - 1].src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE_HP; + + dev_data->clock_options_cnt += 2; + break; + case NRF_BICR_LFOSC_MODE_EXTSINE: + clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy; + clock_options[LFCLK_MAX_OPTS - 2].precision = 0; + clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE; + + clock_options[LFCLK_MAX_OPTS - 1].accuracy = dev_data->max_accuracy; + clock_options[LFCLK_MAX_OPTS - 1].precision = 1; + clock_options[LFCLK_MAX_OPTS - 1].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE_HP; + + dev_data->clock_options_cnt += 2; + break; + case NRF_BICR_LFOSC_MODE_EXTSQUARE: + clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy; + clock_options[LFCLK_MAX_OPTS - 2].precision = 0; + clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SQUARE; + + dev_data->clock_options_cnt += 1; + break; + default: + LOG_ERR("Unexpected LFOSC mode"); + return -EINVAL; + } + } + k_timer_init(&dev_data->timer, lfclk_update_timeout_handler, NULL); return clock_config_init(&dev_data->clk_cfg, diff --git a/drivers/misc/coresight/Kconfig b/drivers/misc/coresight/Kconfig index a31d6e3d3f6..17741114e84 100644 --- a/drivers/misc/coresight/Kconfig +++ b/drivers/misc/coresight/Kconfig @@ -73,4 +73,40 @@ config NRF_ETR_SYNC_PERIOD sent on regular intervals. This frame is sent between Coresight formatter frames. Use 0 to disable. +config NRF_ETR_SHELL + bool "Use shell" + select UART_ASYNC_API + select UART_ASYNC_RX_HELPER + select SHELL_LOG_BACKEND_CUSTOM + depends on NRF_ETR_DECODE + default y if SHELL + help + Enable shell with Coresight STM logging support. + +if NRF_ETR_SHELL + +config NRF_ETR_SHELL_PROMPT + string "Displayed prompt name" + default "uart:~$ " + help + Displayed prompt name for UART shell with Coresight STM logging. + +config NRF_ETR_SHELL_ASYNC_RX_BUFFER_SIZE + int "Size of the RX buffer" + default 16 + help + Size of a single RX buffer. Together with buffer count it defines the + space that can hold RX data. It may be decreased if shell input is + slow and may need to be increased if long messages are pasted directly + to the shell prompt. + +config NRF_ETR_SHELL_ASYNC_RX_BUFFER_COUNT + int "Number of RX buffers" + default 4 + range 2 64 + help + Number of RX buffers. + +endif # NRF_ETR_SHELL + endif # NRF_ETR diff --git a/drivers/misc/coresight/nrf_etr.c b/drivers/misc/coresight/nrf_etr.c index dd1c13af4f7..a9179a95109 100644 --- a/drivers/misc/coresight/nrf_etr.c +++ b/drivers/misc/coresight/nrf_etr.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -16,7 +17,9 @@ #include #include #include +#include #include +#include #include #include LOG_MODULE_REGISTER(cs_etr_tbm); @@ -31,12 +34,6 @@ LOG_MODULE_REGISTER(cs_etr_tbm); #define MIN_DATA (2 * CORESIGHT_TRACE_FRAME_SIZE32) -#define MEMORY_SECTION(node) \ - COND_CODE_1(DT_NODE_HAS_PROP(node, memory_regions), \ - (__attribute__((__section__( \ - LINKER_DT_NODE_REGION_NAME(DT_PHANDLE(node, memory_regions)))))), \ - ()) - /* Since ETR debug is a part of logging infrastructure, logging cannot be used * for debugging. Printk is used (assuming CONFIG_LOG_PRINTK=n) */ @@ -86,8 +83,8 @@ static bool volatile use_async_uart; static struct k_sem uart_sem; static const struct device *uart_dev = DEVICE_DT_GET(UART_NODE); -static uint32_t frame_buf0[CORESIGHT_TRACE_FRAME_SIZE32] MEMORY_SECTION(UART_NODE); -static uint32_t frame_buf1[CORESIGHT_TRACE_FRAME_SIZE32] MEMORY_SECTION(UART_NODE); +static uint32_t frame_buf0[CORESIGHT_TRACE_FRAME_SIZE32] DMM_MEMORY_SECTION(UART_NODE); +static uint32_t frame_buf1[CORESIGHT_TRACE_FRAME_SIZE32] DMM_MEMORY_SECTION(UART_NODE); static uint32_t frame_buf_decode[CORESIGHT_TRACE_FRAME_SIZE32]; static uint32_t *frame_buf = IS_ENABLED(CONFIG_NRF_ETR_DECODE) ? frame_buf_decode : frame_buf0; @@ -136,6 +133,20 @@ static const char *const hw_evts[] = { "GD0 HS down", /* 31 Global domain high speed 0 down */ }; +#ifdef CONFIG_NRF_ETR_SHELL +#define RX_BUF_SIZE \ + (CONFIG_NRF_ETR_SHELL_ASYNC_RX_BUFFER_SIZE * CONFIG_NRF_ETR_SHELL_ASYNC_RX_BUFFER_COUNT) + +static void etr_timer_handler(struct k_timer *timer); +K_TIMER_DEFINE(etr_timer, etr_timer_handler, NULL); +static uint8_t rx_buf[RX_BUF_SIZE] DMM_MEMORY_SECTION(UART_NODE); +static struct uart_async_rx async_rx; +static atomic_t pending_rx_req; +static const struct shell etr_shell; +static shell_transport_handler_t shell_handler; +static void *shell_context; +#endif + static int log_output_func(uint8_t *buf, size_t size, void *ctx) { if (use_async_uart) { @@ -615,6 +626,7 @@ void nrf_etr_flush(void) irq_unlock(k); } +#ifndef CONFIG_NRF_ETR_SHELL static void etr_thread_func(void *dummy1, void *dummy2, void *dummy3) { uint64_t checkpoint = 0; @@ -647,6 +659,7 @@ static void etr_thread_func(void *dummy1, void *dummy2, void *dummy3) k_sleep(K_MSEC(CONFIG_NRF_ETR_BACKOFF)); } } +#endif static void uart_event_handler(const struct device *dev, struct uart_event *evt, void *user_data) { @@ -658,6 +671,33 @@ static void uart_event_handler(const struct device *dev, struct uart_event *evt, case UART_TX_DONE: k_sem_give(&uart_sem); break; +#ifdef CONFIG_NRF_ETR_SHELL + case UART_RX_RDY: + uart_async_rx_on_rdy(&async_rx, evt->data.rx.buf, evt->data.rx.len); + shell_handler(SHELL_TRANSPORT_EVT_RX_RDY, shell_context); + break; + case UART_RX_BUF_REQUEST: { + uint8_t *buf = uart_async_rx_buf_req(&async_rx); + size_t len = uart_async_rx_get_buf_len(&async_rx); + + if (buf) { + int err = uart_rx_buf_rsp(dev, buf, len); + + if (err < 0) { + uart_async_rx_on_buf_rel(&async_rx, buf); + } + } else { + atomic_inc(&pending_rx_req); + } + + break; + } + case UART_RX_BUF_RELEASED: + uart_async_rx_on_buf_rel(&async_rx, evt->data.rx_buf.buf); + break; + case UART_RX_DISABLED: + break; +#endif /* CONFIG_NRF_ETR_SHELL */ default: __ASSERT_NO_MSG(0); } @@ -671,7 +711,11 @@ static void tbm_event_handler(nrf_tbm_event_t event) tbm_full = true; } +#ifdef CONFIG_NRF_ETR_SHELL + k_poll_signal_raise(&etr_shell.ctx->signals[SHELL_SIGNAL_LOG_MSG], 0); +#else k_wakeup(&etr_thread); +#endif } int etr_process_init(void) @@ -691,12 +735,178 @@ int etr_process_init(void) nrfx_isr, nrfx_tbm_irq_handler, 0); irq_enable(DT_IRQN(DT_NODELABEL(tbm))); - k_thread_create(&etr_thread, etr_stack, K_KERNEL_STACK_SIZEOF(etr_stack), - etr_thread_func, NULL, NULL, NULL, K_LOWEST_APPLICATION_THREAD_PRIO, 0, - K_NO_WAIT); +#ifdef CONFIG_NRF_ETR_SHELL + uint32_t level = CONFIG_LOG_MAX_LEVEL; + static const struct shell_backend_config_flags cfg_flags = + SHELL_DEFAULT_BACKEND_CONFIG_FLAGS; + + shell_init(&etr_shell, NULL, cfg_flags, true, level); + k_timer_start(&etr_timer, K_MSEC(CONFIG_NRF_ETR_BACKOFF), K_NO_WAIT); + if (IS_ENABLED(CONFIG_NRF_ETR_DECODE) || IS_ENABLED(CONFIG_NRF_ETR_DEBUG)) { + err = decoder_init(); + if (err < 0) { + return err; + } + } +#else + k_thread_create(&etr_thread, etr_stack, K_KERNEL_STACK_SIZEOF(etr_stack), etr_thread_func, + NULL, NULL, NULL, K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT); k_thread_name_set(&etr_thread, "etr_process"); +#endif return 0; } SYS_INIT(etr_process_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); + +#ifdef CONFIG_NRF_ETR_SHELL + +static void etr_timer_handler(struct k_timer *timer) +{ + if (pending_data() >= MIN_DATA) { + k_poll_signal_raise(&etr_shell.ctx->signals[SHELL_SIGNAL_LOG_MSG], 0); + } else { + k_timer_start(timer, K_MSEC(CONFIG_NRF_ETR_BACKOFF), K_NO_WAIT); + } +} + +bool z_shell_log_backend_process(const struct shell_log_backend *backend) +{ + ARG_UNUSED(backend); + + process(); + k_timer_start(&etr_timer, K_MSEC(CONFIG_NRF_ETR_BACKOFF), K_NO_WAIT); + + return false; +} + +void z_shell_log_backend_disable(const struct shell_log_backend *backend) +{ + ARG_UNUSED(backend); +} + +void z_shell_log_backend_enable(const struct shell_log_backend *backend, void *ctx, + uint32_t init_log_level) +{ + ARG_UNUSED(backend); + ARG_UNUSED(ctx); + ARG_UNUSED(init_log_level); +} + +static int etr_shell_write(const struct shell_transport *transport, const void *data, size_t length, + size_t *cnt) +{ + size_t len = length; + uint8_t *buf = (uint8_t *)data; + size_t chunk_len; + + do { + chunk_len = MIN(len, sizeof(log_output_buf)); + len -= log_output_func(buf, chunk_len, NULL); + buf += chunk_len; + } while (len > 0); + + *cnt = length; + shell_handler(SHELL_TRANSPORT_EVT_TX_RDY, shell_context); + + return 0; +} + +static int rx_enable(uint8_t *buf, size_t len) +{ + return uart_rx_enable(uart_dev, buf, len, 10000); +} + +static int etr_shell_read(const struct shell_transport *transport, void *data, size_t length, + size_t *cnt) +{ + uint8_t *buf; + size_t blen; + bool buf_available; + + blen = uart_async_rx_data_claim(&async_rx, &buf, length); + memcpy(data, buf, blen); + buf_available = uart_async_rx_data_consume(&async_rx, blen); + + *cnt = blen; + if (pending_rx_req && buf_available) { + uint8_t *buf = uart_async_rx_buf_req(&async_rx); + size_t len = uart_async_rx_get_buf_len(&async_rx); + int err; + + __ASSERT_NO_MSG(buf != NULL); + atomic_dec(&pending_rx_req); + err = uart_rx_buf_rsp(uart_dev, buf, len); + /* If it is too late and RX is disabled then re-enable it. */ + if (err < 0) { + if (err == -EACCES) { + pending_rx_req = 0; + err = rx_enable(buf, len); + } else { + return err; + } + } + } + + return 0; +} + +static int etr_shell_enable(const struct shell_transport *transport, bool blocking_tx) +{ + return 0; +} + +static int etr_shell_uninit(const struct shell_transport *transport) +{ + return 0; +} + +static int etr_shell_init(const struct shell_transport *transport, const void *config, + shell_transport_handler_t evt_handler, void *context) +{ + int err; + uint8_t *buf; + static const struct uart_async_rx_config async_rx_config = { + .buffer = rx_buf, + .length = sizeof(rx_buf), + .buf_cnt = CONFIG_NRF_ETR_SHELL_ASYNC_RX_BUFFER_COUNT, + }; + + shell_context = context; + shell_handler = evt_handler; + err = uart_async_rx_init(&async_rx, &async_rx_config); + if (err) { + return err; + } + + buf = uart_async_rx_buf_req(&async_rx); + + return rx_enable(buf, uart_async_rx_get_buf_len(&async_rx)); +} + +#ifdef CONFIG_MCUMGR_TRANSPORT_SHELL +static void etr_shell_update(const struct shell_transport *transport) +{ +} +#endif + +const struct shell_transport_api shell_api = { + .init = etr_shell_init, + .uninit = etr_shell_uninit, + .enable = etr_shell_enable, + .write = etr_shell_write, + .read = etr_shell_read, +#ifdef CONFIG_MCUMGR_TRANSPORT_SHELL + .update = shell_update, +#endif /* CONFIG_MCUMGR_TRANSPORT_SHELL */ +}; + +static struct shell_transport transport = { + .api = &shell_api, + .ctx = NULL, +}; + +static uint8_t shell_out_buffer[CONFIG_SHELL_PRINTF_BUFF_SIZE]; +Z_SHELL_DEFINE(etr_shell, CONFIG_NRF_ETR_SHELL_PROMPT, &transport, shell_out_buffer, NULL, + SHELL_FLAG_OLF_CRLF); +#endif /* CONFIG_NRF_ETR_SHELL */ diff --git a/dts/bindings/clock/nordic,nrf54h-hfxo.yaml b/dts/bindings/clock/nordic,nrf54h-hfxo.yaml index 1dab8b99cf8..26280e77031 100644 --- a/dts/bindings/clock/nordic,nrf54h-hfxo.yaml +++ b/dts/bindings/clock/nordic,nrf54h-hfxo.yaml @@ -15,16 +15,3 @@ properties: type: int description: Clock accuracy in parts per million required: true - - startup-time-us: - type: int - description: Clock startup time in micro seconds - required: true - - mode: - type: string - description: HFXO operational mode - required: true - enum: - - "crystal" - - "external-square" diff --git a/dts/bindings/clock/nordic,nrf54h-lfxo.yaml b/dts/bindings/clock/nordic,nrf54h-lfxo.yaml index 3a85f27972d..705c2483ecd 100644 --- a/dts/bindings/clock/nordic,nrf54h-lfxo.yaml +++ b/dts/bindings/clock/nordic,nrf54h-lfxo.yaml @@ -10,22 +10,3 @@ include: fixed-clock.yaml properties: clock-frequency: const: 32768 - - accuracy-ppm: - type: int - description: Clock accuracy in parts per million - required: true - - startup-time-us: - type: int - description: Clock startup time in micro seconds - required: true - - mode: - type: string - description: LFXO operational mode - required: true - enum: - - "crystal" - - "external-sine" - - "external-square" diff --git a/dts/bindings/gpio/nordic,nrf-gpio.yaml b/dts/bindings/gpio/nordic,nrf-gpio.yaml index 0ba8a14e132..097a99d8fa9 100644 --- a/dts/bindings/gpio/nordic,nrf-gpio.yaml +++ b/dts/bindings/gpio/nordic,nrf-gpio.yaml @@ -5,7 +5,7 @@ description: NRF5 GPIO node compatible: "nordic,nrf-gpio" -include: [gpio-controller.yaml, base.yaml, "nordic,nrf-port-configs.yaml"] +include: [gpio-controller.yaml, base.yaml] properties: reg: diff --git a/dts/bindings/gpio/nordic,nrf-port-configs.yaml b/dts/bindings/gpio/nordic,nrf-port-configs.yaml deleted file mode 100644 index fd6754e53a0..00000000000 --- a/dts/bindings/gpio/nordic,nrf-port-configs.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2024 Nordic Semiconductor ASA -# SPDX-License-Identifier: LicenseRef-Nordic-4-Clause - -description: | - Nordic Port Configs - - Hardware configurations that apply to IO ports. - -properties: - ioport-power-rails: - type: phandle-array - description: | - Power supply configurations for GPIO ports. The configuration is board- - specific and stored in BICR. - The property is encoded as <&phandle rail>, where: - - - phandle is the phandle of the associated GPIO port - - rail is the 8-bit power rail configuration to apply to the port - - ioport-drivectrls: - type: phandle-array - description: | - Drive control settings for GPIO ports. The configuration is board-specific - and stored in BICR. - The property is encoded as <&phandle resistance>, where: - - - phandle is the phandle of the associated GPIO port - - resistance is the resistance in ohms to adjust in the port's drive - control, and can be one of the following: [33, 40, 50, 66, 100]. - -ioport-power-rail-cells: - - rail - -ioport-drivectrl-cells: - - resistance diff --git a/dts/bindings/misc/nordic,nrf-bicr.yaml b/dts/bindings/misc/nordic,nrf-bicr.yaml index 60894ee2e1d..f5c127e6cf1 100644 --- a/dts/bindings/misc/nordic,nrf-bicr.yaml +++ b/dts/bindings/misc/nordic,nrf-bicr.yaml @@ -1,118 +1,12 @@ # Copyright (c) 2024 Nordic Semiconductor ASA -# SPDX-License-Identifier: LicenseRef-Nordic-4-Clause +# SPDX-License-Identifier: Apache-2.0 -description: | - Nordic Board Information Configuration Registers (BICR) +description: Nordic BICR (Board Information Configuration Registers) compatible: "nordic,nrf-bicr" -include: [base.yaml, "nordic,nrf-port-configs.yaml"] +include: base.yaml properties: - power-vddao5v0: - type: string - description: VDD_AO_5V0 power rail configuration. - enum: - - "external" - - "shorted" - - power-vddao1v8: - type: string - description: VDD_AO_1V8 power rail configuration. - enum: - - "internal" - - "external" - - power-vdd1v0: - type: string - description: VDD_1V0 power rail configuration. - enum: - - "internal" - - "external" - - power-vddrf1v0: - type: string - description: VDD_RF_1V0 power rail configuration. - enum: - - "external" - - "shorted" - - power-vddao0v8: - type: string - description: VDD_AO_0V8 power rail configuration. - enum: - - "internal" - - "external" - - power-vddvs0v8: - type: string - description: VDD_VS_0V8 power rail configuration. - enum: - - "internal" - - "external" - - inductor-present: - type: boolean - description: DC/DC inductor present. - - lfosc-accuracy: - type: int - description: LFXO crystal or external signal accuracy in ppm. - enum: - - 500 - - 250 - - 150 - - 100 - - 75 - - 50 - - 30 - - 20 - - lfosc-mode: - type: string - description: | - LFXO operational mode. - enum: - - "crystal" - - "external-sine" - - "external-square" - - "disabled" - - lfosc-loadcap: - type: int - description: | - Built-in load capacitors selection in 1pF steps, up to 25pF max. If 0, - only external capacitors will be used. - - lfosc-startup: - type: int - description: LFXO startup time in milliseconds. - - lfrc-autocalibration: - type: array - description: | - A list of values pertaining to LFRC autocalibration settings. The prop - is encoded as , where: - - temp-interval is the temperature measurement interval in 0.25s steps - - temp-delta is the temperature delta that should trigger a calibration - in 0.25 degree steps - - interval-max-count are the max number of temp-interval periods in - between calibrations, independent of temperature changes. - - hfxo-mode: - type: string - description: | - HFXO operational mode. - enum: - - "crystal" - - "external-square" - - hfxo-loadcap: - type: int - description: | - Built-in load capacitors selection in 0.25 pF steps, up to 25.75 pF max. - If 0, only external capacitors will be used. - - hfxo-startup: - type: int - description: HFXO startup time in milliseconds. + reg: + required: true diff --git a/dts/common/nordic/nrf54h20.dtsi b/dts/common/nordic/nrf54h20.dtsi index a6b32ffdf89..942522245f6 100644 --- a/dts/common/nordic/nrf54h20.dtsi +++ b/dts/common/nordic/nrf54h20.dtsi @@ -222,7 +222,15 @@ cpuapp_uicr: uicr@fff8000 { compatible = "nordic,nrf-uicr-v2"; reg = <0xfff8000 DT_SIZE_K(2)>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xfff8000 DT_SIZE_K(2)>; domain = <2>; + + bicr: bicr@7b0 { + compatible = "nordic,nrf-bicr"; + reg = <0x7b0 48>; + }; }; cpurad_uicr: uicr@fffa000 { diff --git a/include/zephyr/drivers/clock_control/nrf_clock_control.h b/include/zephyr/drivers/clock_control/nrf_clock_control.h index 27ab518920f..8d9bcbe3441 100644 --- a/include/zephyr/drivers/clock_control/nrf_clock_control.h +++ b/include/zephyr/drivers/clock_control/nrf_clock_control.h @@ -317,6 +317,30 @@ int nrf_clock_control_cancel_or_release(const struct device *dev, return api->cancel_or_release(dev, spec, cli); } +/** @brief Request the HFXO from Zero Latency Interrupt context. + * + * Function is optimized for use in Zero Latency Interrupt context. + * It does not give notification when the HFXO is ready, so each + * user must put the request early enough to make sure the HFXO + * ramp-up has finished on time. + * + * This function uses reference counting so the caller must ensure + * that every nrf_clock_control_hfxo_request() call has a matching + * nrf_clock_control_hfxo_release() call. + */ +void nrf_clock_control_hfxo_request(void); + +/** @brief Release the HFXO from Zero Latency Interrupt context. + * + * Function is optimized for use in Zero Latency Interrupt context. + * + * Calls to this function must be coupled with prior calls + * to nrf_clock_control_hfxo_request(), because it uses basic + * reference counting to make sure the HFXO is released when + * there are no more pending requests. + */ +void nrf_clock_control_hfxo_release(void); + #endif /* defined(CONFIG_CLOCK_CONTROL_NRF2) */ /** @brief Get clock frequency that is used for the given node. diff --git a/include/zephyr/shell/shell.h b/include/zephyr/shell/shell.h index f8591107170..e2a40b85328 100644 --- a/include/zephyr/shell/shell.h +++ b/include/zephyr/shell/shell.h @@ -912,6 +912,41 @@ struct shell { extern void z_shell_print_stream(const void *user_ctx, const char *data, size_t data_len); + +/** @brief Internal macro for defining a shell instance. + * + * As it does not create the default shell logging backend it allows to use + * custom approach for integrating logging with shell. + * + * @param[in] _name Instance name. + * @param[in] _prompt Shell default prompt string. + * @param[in] _transport_iface Pointer to the transport interface. + * @param[in] _out_buf Output buffer. + * @param[in] _log_backend Pointer to the log backend instance. + * @param[in] _shell_flag Shell output newline sequence. + */ +#define Z_SHELL_DEFINE(_name, _prompt, _transport_iface, _out_buf, _log_backend, _shell_flag) \ + static const struct shell _name; \ + static struct shell_ctx UTIL_CAT(_name, _ctx); \ + Z_SHELL_HISTORY_DEFINE(_name##_history, CONFIG_SHELL_HISTORY_BUFFER); \ + Z_SHELL_FPRINTF_DEFINE(_name##_fprintf, &_name, _out_buf, CONFIG_SHELL_PRINTF_BUFF_SIZE, \ + true, z_shell_print_stream); \ + LOG_INSTANCE_REGISTER(shell, _name, CONFIG_SHELL_LOG_LEVEL); \ + Z_SHELL_STATS_DEFINE(_name); \ + static K_KERNEL_STACK_DEFINE(_name##_stack, CONFIG_SHELL_STACK_SIZE); \ + static struct k_thread _name##_thread; \ + static const STRUCT_SECTION_ITERABLE(shell, _name) = { \ + .default_prompt = _prompt, \ + .iface = _transport_iface, \ + .ctx = &UTIL_CAT(_name, _ctx), \ + .history = IS_ENABLED(CONFIG_SHELL_HISTORY) ? &_name##_history : NULL, \ + .shell_flag = _shell_flag, \ + .fprintf_ctx = &_name##_fprintf, \ + .stats = Z_SHELL_STATS_PTR(_name), \ + .log_backend = _log_backend, \ + LOG_INSTANCE_PTR_INIT(log, shell, _name).name = \ + STRINGIFY(_name), .thread = &_name##_thread, .stack = _name##_stack} + /** * @brief Macro for defining a shell instance. * @@ -925,37 +960,12 @@ extern void z_shell_print_stream(const void *user_ctx, const char *data, * message is dropped. * @param[in] _shell_flag Shell output newline sequence. */ -#define SHELL_DEFINE(_name, _prompt, _transport_iface, \ - _log_queue_size, _log_timeout, _shell_flag) \ - static const struct shell _name; \ - static struct shell_ctx UTIL_CAT(_name, _ctx); \ - static uint8_t _name##_out_buffer[CONFIG_SHELL_PRINTF_BUFF_SIZE]; \ - Z_SHELL_LOG_BACKEND_DEFINE(_name, _name##_out_buffer, \ - CONFIG_SHELL_PRINTF_BUFF_SIZE, \ - _log_queue_size, _log_timeout); \ - Z_SHELL_HISTORY_DEFINE(_name##_history, CONFIG_SHELL_HISTORY_BUFFER); \ - Z_SHELL_FPRINTF_DEFINE(_name##_fprintf, &_name, _name##_out_buffer, \ - CONFIG_SHELL_PRINTF_BUFF_SIZE, \ - true, z_shell_print_stream); \ - LOG_INSTANCE_REGISTER(shell, _name, CONFIG_SHELL_LOG_LEVEL); \ - Z_SHELL_STATS_DEFINE(_name); \ - static K_KERNEL_STACK_DEFINE(_name##_stack, CONFIG_SHELL_STACK_SIZE); \ - static struct k_thread _name##_thread; \ - static const STRUCT_SECTION_ITERABLE(shell, _name) = { \ - .default_prompt = _prompt, \ - .iface = _transport_iface, \ - .ctx = &UTIL_CAT(_name, _ctx), \ - .history = IS_ENABLED(CONFIG_SHELL_HISTORY) ? \ - &_name##_history : NULL, \ - .shell_flag = _shell_flag, \ - .fprintf_ctx = &_name##_fprintf, \ - .stats = Z_SHELL_STATS_PTR(_name), \ - .log_backend = Z_SHELL_LOG_BACKEND_PTR(_name), \ - LOG_INSTANCE_PTR_INIT(log, shell, _name) \ - .name = STRINGIFY(_name), \ - .thread = &_name##_thread, \ - .stack = _name##_stack \ - } +#define SHELL_DEFINE(_name, _prompt, _transport_iface, _log_queue_size, _log_timeout, _shell_flag) \ + static uint8_t _name##_out_buffer[CONFIG_SHELL_PRINTF_BUFF_SIZE]; \ + Z_SHELL_LOG_BACKEND_DEFINE(_name, _name##_out_buffer, CONFIG_SHELL_PRINTF_BUFF_SIZE, \ + _log_queue_size, _log_timeout); \ + Z_SHELL_DEFINE(_name, _prompt, _transport_iface, _name##_out_buffer, \ + Z_SHELL_LOG_BACKEND_PTR(_name), _shell_flag) /** * @brief Function for initializing a transport layer and internal shell state. diff --git a/modules/hal_nordic/CMakeLists.txt b/modules/hal_nordic/CMakeLists.txt index 0dd750d457e..0d3f2a2f5eb 100644 --- a/modules/hal_nordic/CMakeLists.txt +++ b/modules/hal_nordic/CMakeLists.txt @@ -11,9 +11,6 @@ add_subdirectory_ifdef(CONFIG_HAS_NRFS nrfs) if(CONFIG_NRF_REGTOOL_GENERATE_UICR) list(APPEND nrf_regtool_components GENERATE:UICR) endif() -if(CONFIG_NRF_REGTOOL_GENERATE_BICR) - list(APPEND nrf_regtool_components GENERATE:BICR) -endif() if(DEFINED nrf_regtool_components) find_package(nrf-regtool 8.0.0 REQUIRED COMPONENTS ${nrf_regtool_components} diff --git a/modules/hal_nordic/Kconfig.nrf_regtool b/modules/hal_nordic/Kconfig.nrf_regtool index b057133aaf5..396ec2050a0 100644 --- a/modules/hal_nordic/Kconfig.nrf_regtool +++ b/modules/hal_nordic/Kconfig.nrf_regtool @@ -11,13 +11,6 @@ config NRF_REGTOOL_GENERATE_UICR CPU domains that require UICR allocation aren't bootable without it being programmed alongside the firmware. -config NRF_REGTOOL_GENERATE_BICR - bool "Generate BICR" - help - Generate a BICR hex based on devicetree contents using nrf-regtool. - If not already present, the BICR must be included alongside - any firmware programmed to the board for the first time. - config NRF_REGTOOL_VERBOSITY int "Verbosity level of console output" range 0 3 diff --git a/modules/hal_nordic/nrf-regtool/nrf-regtoolConfig.cmake b/modules/hal_nordic/nrf-regtool/nrf-regtoolConfig.cmake index 8e93a35adc8..6bcaa453cce 100644 --- a/modules/hal_nordic/nrf-regtool/nrf-regtoolConfig.cmake +++ b/modules/hal_nordic/nrf-regtool/nrf-regtoolConfig.cmake @@ -58,8 +58,6 @@ foreach(component IN LISTS ${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS) ) set_property(TARGET runners_yaml_props_target PROPERTY hex_file ${merged_hex_file}) - elseif(component STREQUAL "GENERATE:BICR") - nrf_regtool_generate_peripheral(BICR ${PROJECT_BINARY_DIR}/bicr.hex) else() message(FATAL_ERROR "Unrecognized package component: \"${component}\"") endif() diff --git a/soc/nordic/nrf54h/CMakeLists.txt b/soc/nordic/nrf54h/CMakeLists.txt index 7edc4d43ea1..23c1cab1e77 100644 --- a/soc/nordic/nrf54h/CMakeLists.txt +++ b/soc/nordic/nrf54h/CMakeLists.txt @@ -16,4 +16,5 @@ zephyr_include_directories(.) # for the image correctly zephyr_linker_sources(SECTIONS SORT_KEY zzz_place_align_at_end align.ld) +add_subdirectory(bicr) add_subdirectory(gpd) diff --git a/soc/nordic/nrf54h/Kconfig b/soc/nordic/nrf54h/Kconfig index d54ef99da8e..45e66cd41a1 100644 --- a/soc/nordic/nrf54h/Kconfig +++ b/soc/nordic/nrf54h/Kconfig @@ -68,4 +68,5 @@ config SOC_NRF54H20_CPUPPR config SOC_NRF54H20_CPUFLPR depends on RISCV_CORE_NORDIC_VPR +rsource "bicr/Kconfig" rsource "gpd/Kconfig" diff --git a/soc/nordic/nrf54h/Kconfig.defconfig.nrf54h20_cpuapp b/soc/nordic/nrf54h/Kconfig.defconfig.nrf54h20_cpuapp index 61cc96da71f..595cc2d3886 100644 --- a/soc/nordic/nrf54h/Kconfig.defconfig.nrf54h20_cpuapp +++ b/soc/nordic/nrf54h/Kconfig.defconfig.nrf54h20_cpuapp @@ -11,7 +11,7 @@ config NUM_IRQS config NRF_REGTOOL_GENERATE_UICR default y -config NRF_REGTOOL_GENERATE_BICR - default y +config SHELL_BACKEND_SERIAL + default n if NRF_ETR_SHELL endif # SOC_NRF54H20_CPUAPP diff --git a/soc/nordic/nrf54h/bicr/CMakeLists.txt b/soc/nordic/nrf54h/bicr/CMakeLists.txt new file mode 100644 index 00000000000..a93e36abbd2 --- /dev/null +++ b/soc/nordic/nrf54h/bicr/CMakeLists.txt @@ -0,0 +1,21 @@ +if(CONFIG_SOC_NRF54H20_GENERATE_BICR) + set(bicr_json_file ${BOARD_DIR}/bicr.json) + set(bicr_hex_file ${PROJECT_BINARY_DIR}/bicr.hex) + set(svd_file ${ZEPHYR_HAL_NORDIC_MODULE_DIR}/nrfx/mdk/nrf54h20_application.svd) + + if(EXISTS ${bicr_json_file}) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${bicr_json_file}) + + execute_process( + COMMAND + ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_LIST_DIR}/bicrgen.py + --svd ${svd_file} + --input ${bicr_json_file} + --output ${bicr_hex_file} + WORKING_DIRECTORY ${BOARD_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + message(STATUS "Generated BICR hex file: ${bicr_hex_file}") + endif() +endif() diff --git a/soc/nordic/nrf54h/bicr/Kconfig b/soc/nordic/nrf54h/bicr/Kconfig new file mode 100644 index 00000000000..a1ba7d2400c --- /dev/null +++ b/soc/nordic/nrf54h/bicr/Kconfig @@ -0,0 +1,10 @@ +# Copyright (c) 2024 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +config SOC_NRF54H20_GENERATE_BICR + bool "Generate nRF54H20 BICR file" + depends on SOC_NRF54H20_CPUAPP + default y + help + This option generates a BICR file for the board being used. Board + directory must contain a "bicr.json" file for this option to work. diff --git a/soc/nordic/nrf54h/bicr/bicr-schema.json b/soc/nordic/nrf54h/bicr/bicr-schema.json new file mode 100644 index 00000000000..28eb208af3e --- /dev/null +++ b/soc/nordic/nrf54h/bicr/bicr-schema.json @@ -0,0 +1,422 @@ +{ + "title": "nRF54H20 BICR Configuration", + "type": "object", + "properties": { + "power": { + "type": "object", + "title": "Power supply configuration", + "properties": { + "scheme": { + "type": "string", + "title": "Power supply scheme", + "enumNames": [ + "Unconfigured (system will not boot)", + "VDDH supplied with 2.1-5.5 V and VDD regulated by the chip (inductor present)", + "Both VDD and VDDH supplied with 1.8 V (inductor present)" + ], + "enum": [ + "UNCONFIGURED", + "VDD_VDDH_1V8", + "VDDH_2V1_5V5" + ], + "default": "UNCONFIGURED" + } + }, + "required": [ + "scheme" + ] + }, + "ioPortPower": { + "type": "object", + "title": "IO port power configuration", + "properties": { + "p1Supply": { + "type": "string", + "title": "P1 power supply (VDDIO_P1)", + "enumNames": [ + "Not supplied (P1 not used)", + "VDDIO_P1 connected to an external 1.8 V supply or to VDD_EXT", + "VDDIO_P1 shorted to VDD" + ], + "enum": [ + "DISCONNECTED", + "EXTERNAL_1V8", + "SHORTED" + ], + "default": "DISCONNECTED" + }, + "p2Supply": { + "type": "string", + "title": "P2 power supply (VDDIO_P2)", + "enumNames": [ + "Not supplied (P2 not used)", + "VDDIO_P2 connected to an external 1.8 V supply or to VDD_EXT", + "VDDIO_P2 shorted to VDD" + ], + "enum": [ + "DISCONNECTED", + "EXTERNAL_1V8", + "SHORTED" + ], + "default": "DISCONNECTED" + }, + "p6Supply": { + "type": "string", + "title": "P6 power supply (VDDIO_P6)", + "enumNames": [ + "Not supplied (P6 not used)", + "VDDIO_P6 connected to an external 1.8 V supply or to VDD_EXT", + "VDDIO_P6 shorted to VDD" + ], + "enum": [ + "DISCONNECTED", + "EXTERNAL_1V8", + "SHORTED" + ], + "default": "DISCONNECTED" + }, + "p7Supply": { + "type": "string", + "title": "P7 power supply (VDDIO_P7)", + "enumNames": [ + "Not supplied (P7 not used)", + "VDDIO_P7 connected to an external 1.8 V supply or to VDD_EXT", + "VDDIO_P7 shorted to VDD" + ], + "enum": [ + "DISCONNECTED", + "EXTERNAL_1V8", + "SHORTED" + ], + "default": "DISCONNECTED" + }, + "p9Supply": { + "type": "string", + "title": "P9 power supply (VDDIO_P9)", + "enumNames": [ + "Not supplied (P9 not used)", + "VDDIO_P9 connected to an external 1.8 V supply or to VDD_EXT", + "VDDIO_P9 connected to an external supply (above 1.8 V, up to 3 V)", + "VDDIO_P9 shorted to VDD" + ], + "enum": [ + "DISCONNECTED", + "EXTERNAL_1V8", + "EXTERNAL_FULL", + "SHORTED" + ], + "default": "EXTERNAL_FULL" + } + }, + "required": [ + "p1Supply", + "p2Supply", + "p6Supply", + "p7Supply", + "p9Supply" + ] + }, + "ioPortImpedance": { + "type": "object", + "title": "IO port impedance configuration", + "properties": { + "p6ImpedanceOhms": { + "type": "number", + "title": "P6 impedance", + "enum": [ + 33, + 40, + 50, + 66, + 100 + ], + "default": 50 + }, + "p7ImpedanceOhms": { + "type": "number", + "title": "P7 impedance", + "enum": [ + 33, + 40, + 50, + 66, + 100 + ], + "default": 50 + } + }, + "required": [ + "p6ImpedanceOhms", + "p7ImpedanceOhms" + ] + }, + "lfosc": { + "type": "object", + "title": "Low Frequency Oscillator (LFOSC) configuration", + "properties": { + "source": { + "type": "string", + "title": "Source", + "enumNames": [ + "Low Frequency Crystal Oscillator (LFXO)", + "Low Frequency RC Oscillator (LFRC)" + ], + "enum": [ + "LFXO", + "LFRC" + ], + "default": "LFXO" + } + }, + "required": [ + "source" + ], + "dependencies": { + "source": { + "oneOf": [ + { + "properties": { + "source": { + "const": "LFXO" + }, + "lfxo": { + "type": "object", + "title": "Low Frequency Crystal Oscillator (LFXO) configuration", + "properties": { + "mode": { + "type": "string", + "title": "Mode", + "enumNames": [ + "Crystal", + "External sine signal", + "External square signal" + ], + "enum": [ + "CRYSTAL", + "ETX_SINE", + "EXT_SQUARE" + ], + "default": "CRYSTAL" + }, + "accuracyPPM": { + "type": "number", + "title": "Accuracy", + "enum": [ + 20, + 30, + 50, + 75, + 100, + 150, + 250, + 500 + ], + "default": 20 + }, + "startupTimeMs": { + "type": "number", + "title": "Startup time", + "minimum": 0, + "maximum": 4094, + "multipleOf": 1, + "default": 600 + } + }, + "required": [ + "mode", + "accuracyPPM", + "startupTimeMs" + ], + "dependencies": { + "mode": { + "oneOf": [ + { + "properties": { + "mode": { + "const": "CRYSTAL" + }, + "builtInLoadCapacitors": { + "type": "boolean", + "title": "Use built-in load capacitors", + "default": true + } + }, + "required": [ + "builtInLoadCapacitors" + ], + "dependencies": { + "builtInLoadCapacitors": { + "oneOf": [ + { + "properties": { + "builtInLoadCapacitors": { + "const": true + }, + "builtInLoadCapacitancePf": { + "type": "integer", + "title": "Built-in load capacitance", + "minimum": 1, + "maximum": 25, + "multipleOf": 1, + "default": 15 + } + }, + "required": [ + "builtInLoadCapacitancePf" + ] + } + ] + } + } + } + ] + } + } + } + } + }, + { + "properties": { + "source": { + "const": "LFRC" + }, + "lfrccal": { + "type": "object", + "title": "Low Frequency RC (LFRC) autocalibration configuration", + "properties": { + "calibrationEnabled": { + "type": "boolean", + "title": "Enable autocalibration", + "default": true + } + }, + "required": [ + "calibrationEnabled" + ], + "dependencies": { + "calibrationEnabled": { + "oneOf": [ + { + "properties": { + "calibrationEnabled": { + "const": true + }, + "tempMeasIntervalSeconds": { + "type": "number", + "title": "Temperature measurement interval", + "minimum": 0.25, + "maximum": 31.75, + "multipleOf": 0.25, + "default": 4 + }, + "tempDeltaCalibrationTriggerCelsius": { + "type": "number", + "title": "Temperature delta that should trigger calibration", + "minimum": 0.25, + "maximum": 31.75, + "multipleOf": 0.25, + "default": 0.5 + }, + "maxMeasIntervalBetweenCalibrations": { + "type": "number", + "title": "Maximum number of measurement intervals between calibrations", + "minimum": 0, + "maximum": 31, + "multipleOf": 1, + "default": 2 + } + }, + "required": [ + "tempMeasIntervalSeconds", + "tempDeltaCalibrationTriggerCelsius", + "maxMeasIntervalBetweenCalibrations" + ] + } + ] + } + } + } + } + } + ] + } + } + }, + "hfxo": { + "type": "object", + "title": "High Frequency Cristal Oscillator (HFXO) configuration", + "properties": { + "mode": { + "type": "string", + "title": "Mode", + "enumNames": [ + "Crystal", + "External square signal" + ], + "enum": [ + "CRYSTAL", + "EXT_SQUARE" + ], + "default": "CRYSTAL" + }, + "startupTimeUs": { + "type": "number", + "title": "Startup time", + "minimum": 0, + "maximum": 4294967294, + "multipleOf": 1, + "default": 850 + } + }, + "required": [ + "mode", + "startupTimeUs" + ], + "dependencies": { + "mode": { + "oneOf": [ + { + "properties": { + "mode": { + "const": "CRYSTAL" + }, + "builtInLoadCapacitors": { + "type": "boolean", + "title": "Use built-in load capacitors", + "default": true + } + }, + "required": [ + "builtInLoadCapacitors" + ], + "dependencies": { + "builtInLoadCapacitors": { + "oneOf": [ + { + "properties": { + "builtInLoadCapacitors": { + "const": true + }, + "builtInLoadCapacitancePf": { + "type": "number", + "title": "Built-in load capacitance", + "minimum": 0.25, + "maximum": 25.75, + "multipleOf": 0.25, + "default": 14 + } + }, + "required": [ + "builtInLoadCapacitancePf" + ] + } + ] + } + } + } + ] + } + } + } + } +} diff --git a/soc/nordic/nrf54h/bicr/bicrgen.py b/soc/nordic/nrf54h/bicr/bicrgen.py new file mode 100644 index 00000000000..b28fdc571a8 --- /dev/null +++ b/soc/nordic/nrf54h/bicr/bicrgen.py @@ -0,0 +1,703 @@ +""" +BICR Generation Tool +-------------------- + +This tool is used to generate a BICR (Board Information Configuration Register) +file from a JSON file that contains the BICR configuration. It can also be used +to do the reverse operation, i.e., to extract the BICR configuration from a BICR +hex file. + +:: + + JSON ┌────────────┐ JSON + │ │ │ ▲ + └─────►│ ├───────┘ + │ bicrgen.py │ + ┌─────►│ ├───────┐ + │ │ │ ▼ + HEX └────────────┘ HEX + +Usage:: + + python genbicr.py \ + --input \ + [--svd ] \ + [--output ] \ + [--list] + +Copyright (c) 2024 Nordic Semiconductor ASA +SPDX-License-Identifier: Apache-2.0 +""" + +import argparse +import json +import struct +import sys +import xml.etree.ElementTree as ET +from dataclasses import dataclass +from enum import Enum +from pathlib import Path +from pprint import pprint + +from intelhex import IntelHex + + +class Register: + def __init__(self, regs: ET.Element, name: str, data: bytes | bytearray | None = None) -> None: + cluster_name, reg_name = name.split(".") + + cluster = regs.find(f".//registers/cluster[name='{cluster_name}']") + self._offset = int(cluster.find("addressOffset").text, 0) + + self._reg = cluster.find(f".//register[name='{reg_name}']") + self._offset += int(self._reg.find("addressOffset").text, 0) + self._size = int(self._reg.find("size").text, 0) // 8 + + self._data = data + + @property + def offset(self) -> int: + return self._offset + + @property + def size(self) -> int: + return self._size + + def _msk_pos(self, name: str) -> tuple[int, int]: + field = self._reg.find(f".//fields/field[name='{name}']") + field_lsb = int(field.find("lsb").text, 0) + field_msb = int(field.find("msb").text, 0) + + mask = (0xFFFFFFFF - (1 << field_lsb) + 1) & (0xFFFFFFFF >> (31 - field_msb)) + + return mask, field_lsb + + def _enums(self, field: str) -> list[ET.Element]: + return self._reg.findall( + f".//fields/field[name='{field}']/enumeratedValues/enumeratedValue" + ) + + def __getitem__(self, field: str) -> int: + if not self._data: + raise TypeError("Empty register") + + msk, pos = self._msk_pos(field) + raw = struct.unpack("> pos + + def __setitem__(self, field: str, value: int) -> None: + if not isinstance(self._data, bytearray): + raise TypeError("Register is read-only") + + msk, pos = self._msk_pos(field) + raw = raw = struct.unpack(" str: + value = self[field] + for enum in self._enums(field): + if value == int(enum.find("value").text, 0): + return enum.find("name").text + + raise ValueError(f"Invalid enum value for {field}: {value}") + + def enum_set(self, field: str, value: str) -> None: + for enum in self._enums(field): + if value == enum.find("name").text: + self[field] = int(enum.find("value").text, 0) + return + + raise ValueError(f"Invalid enum value for {field}: {value}") + + +class PowerSupplyScheme(Enum): + UNCONFIGURED = "Unconfigured" + VDD_VDDH_1V8 = "VDD_VDDH_1V8" + VDDH_2V1_5V5 = "VDDH_2V1_5V5" + + +@dataclass +class PowerConfig: + scheme: PowerSupplyScheme + + @classmethod + def from_raw(cls: "PowerConfig", bicr_spec: ET.Element, data: bytes) -> "PowerConfig": + power_config = Register(bicr_spec, "POWER.CONFIG", data) + + if ( + power_config.enum_get("VDDAO5V0") == "Shorted" + and power_config.enum_get("VDDAO1V8") == "External" + ): + scheme = PowerSupplyScheme.VDD_VDDH_1V8 + elif ( + power_config.enum_get("VDDAO5V0") == "External" + and power_config.enum_get("VDDAO1V8") == "Internal" + ): + scheme = PowerSupplyScheme.VDDH_2V1_5V5 + else: + scheme = PowerSupplyScheme.UNCONFIGURED + + return cls(scheme=scheme) + + @classmethod + def from_json(cls: "PowerConfig", data: dict) -> "PowerConfig": + power = data["power"] + + return cls(scheme=PowerSupplyScheme[power["scheme"]]) + + def to_raw(self, bicr_spec: ET.Element, buf: bytearray): + power_config = Register(bicr_spec, "POWER.CONFIG", buf) + + if self.scheme == PowerSupplyScheme.VDD_VDDH_1V8: + power_config.enum_set("VDDAO5V0", "Shorted") + power_config.enum_set("VDDAO1V8", "External") + power_config.enum_set("VDD1V0", "Internal") + power_config.enum_set("VDDRF1V0", "Shorted") + power_config.enum_set("VDDAO0V8", "Internal") + power_config.enum_set("VDDVS0V8", "Internal") + power_config.enum_set("INDUCTOR", "Present") + elif self.scheme == PowerSupplyScheme.VDDH_2V1_5V5: + power_config.enum_set("VDDAO5V0", "External") + power_config.enum_set("VDDAO1V8", "Internal") + power_config.enum_set("VDD1V0", "Internal") + power_config.enum_set("VDDRF1V0", "Shorted") + power_config.enum_set("VDDAO0V8", "Internal") + power_config.enum_set("VDDVS0V8", "Internal") + power_config.enum_set("INDUCTOR", "Present") + else: + power_config.enum_set("VDDAO5V0", "Unconfigured") + power_config.enum_set("VDDAO1V8", "Unconfigured") + power_config.enum_set("VDD1V0", "Unconfigured") + power_config.enum_set("VDDRF1V0", "Unconfigured") + power_config.enum_set("VDDAO0V8", "Unconfigured") + power_config.enum_set("VDDVS0V8", "Unconfigured") + power_config.enum_set("INDUCTOR", "Unconfigured") + + def to_json(self, buf: dict): + buf["power"] = {"scheme": self.scheme.name} + + +class IoPortPower(Enum): + DISCONNECTED = "Disconnected" + SHORTED = "Shorted" + EXTERNAL_1V8 = "External1V8" + + +class IoPortPowerExtended(Enum): + DISCONNECTED = "Disconnected" + SHORTED = "Shorted" + EXTERNAL_1V8 = "External1V8" + EXTERNAL_FULL = "ExternalFull" + + +@dataclass +class IoPortPowerConfig: + p1_supply: IoPortPower + p2_supply: IoPortPower + p6_supply: IoPortPower + p7_supply: IoPortPower + p9_supply: IoPortPowerExtended + + @classmethod + def from_raw( + cls: "IoPortPowerConfig", bicr_spec: ET.Element, data: bytes + ) -> "IoPortPowerConfig": + ioport_power0 = Register(bicr_spec, "IOPORT.POWER0", data) + ioport_power1 = Register(bicr_spec, "IOPORT.POWER1", data) + + return cls( + p1_supply=IoPortPower(ioport_power0.enum_get("P1")), + p2_supply=IoPortPower(ioport_power0.enum_get("P2")), + p6_supply=IoPortPower(ioport_power0.enum_get("P6")), + p7_supply=IoPortPower(ioport_power0.enum_get("P7")), + p9_supply=IoPortPowerExtended(ioport_power1.enum_get("P9")), + ) + + @classmethod + def from_json(cls: "IoPortPowerConfig", data: dict) -> "IoPortPowerConfig": + ioport_power = data["ioPortPower"] + + return cls( + p1_supply=IoPortPower[ioport_power["p1Supply"]], + p2_supply=IoPortPower[ioport_power["p2Supply"]], + p6_supply=IoPortPower[ioport_power["p6Supply"]], + p7_supply=IoPortPower[ioport_power["p7Supply"]], + p9_supply=IoPortPowerExtended[ioport_power["p9Supply"]], + ) + + def to_raw(self, bicr_spec: ET.Element, buf: bytearray): + ioport_power0 = Register(bicr_spec, "IOPORT.POWER0", buf) + ioport_power1 = Register(bicr_spec, "IOPORT.POWER1", buf) + + ioport_power0.enum_set("P1", self.p1_supply.value) + ioport_power0.enum_set("P2", self.p2_supply.value) + ioport_power0.enum_set("P6", self.p6_supply.value) + ioport_power0.enum_set("P7", self.p7_supply.value) + ioport_power1.enum_set("P9", self.p9_supply.value) + + def to_json(self, buf: dict): + buf["ioPortPower"] = { + "p1Supply": self.p1_supply.name, + "p2Supply": self.p2_supply.name, + "p6Supply": self.p6_supply.name, + "p7Supply": self.p7_supply.name, + "p9Supply": self.p9_supply.name, + } + + +@dataclass +class IoPortImpedanceConfig: + p6_impedance_ohms: int + p7_impedance_ohms: int + + @classmethod + def from_raw( + cls: "IoPortImpedanceConfig", bicr_spec: ET.Element, data: bytes + ) -> "IoPortImpedanceConfig": + drivectl0 = Register(bicr_spec, "IOPORT.DRIVECTRL0", data) + + return cls( + p6_impedance_ohms=int(drivectl0.enum_get("P6")[4:]), + p7_impedance_ohms=int(drivectl0.enum_get("P7")[4:]), + ) + + @classmethod + def from_json(cls: "IoPortImpedanceConfig", data: dict) -> "IoPortImpedanceConfig": + ioport_impedance = data["ioPortImpedance"] + + return cls( + p6_impedance_ohms=ioport_impedance["p6ImpedanceOhms"], + p7_impedance_ohms=ioport_impedance["p7ImpedanceOhms"], + ) + + def to_raw(self, bicr_spec: ET.Element, buf: bytearray): + drivectl0 = Register(bicr_spec, "IOPORT.DRIVECTRL0", buf) + + drivectl0.enum_set("P6", f"Ohms{self.p6_impedance_ohms}") + drivectl0.enum_set("P7", f"Ohms{self.p7_impedance_ohms}") + + def to_json(self, buf: dict): + buf["ioPortImpedance"] = { + "p6ImpedanceOhms": self.p6_impedance_ohms, + "p7ImpedanceOhms": self.p7_impedance_ohms, + } + + +class LFXOMode(Enum): + CRYSTAL = "Crystal" + EXT_SINE = "ExtSine" + EXT_SQUARE = "ExtSquare" + + +@dataclass +class LFXOConfig: + accuracy_ppm: int + mode: LFXOMode + builtin_load_capacitors: bool + builtin_load_capacitance_pf: int | None + startup_time_ms: int + + @classmethod + def from_raw(cls: "LFXOConfig", bicr_spec: ET.Element, data: bytes) -> "LFXOConfig": + lfosc_lfxoconfig = Register(bicr_spec, "LFOSC.LFXOCONFIG", data) + + try: + loadcap = lfosc_lfxoconfig.enum_get("LOADCAP") + except ValueError: + builtin_load_capacitors = True + builtin_load_capacitance_pf = lfosc_lfxoconfig["LOADCAP"] + else: + if loadcap == "Unconfigured": + raise ValueError("Invalid LFXO load capacitors configuration") + + builtin_load_capacitors = False + builtin_load_capacitance_pf = None + + startup_time_ms = 0 + try: + lfosc_lfxoconfig.enum_get("TIME") + except ValueError: + startup_time_ms = lfosc_lfxoconfig["TIME"] + else: + raise ValueError("Invalid LFXO startup time (not configured)") + + return cls( + accuracy_ppm=int(lfosc_lfxoconfig.enum_get("ACCURACY")[:3]), + mode=LFXOMode(lfosc_lfxoconfig.enum_get("MODE")), + builtin_load_capacitors=builtin_load_capacitors, + builtin_load_capacitance_pf=builtin_load_capacitance_pf, + startup_time_ms=startup_time_ms, + ) + + @classmethod + def from_json(cls: "LFXOConfig", data: dict) -> "LFXOConfig": + lfxo = data["lfosc"]["lfxo"] + + builtin_load_capacitors = lfxo["builtInLoadCapacitors"] + if builtin_load_capacitors: + builtin_load_capacitance_pf = lfxo["builtInLoadCapacitancePf"] + else: + builtin_load_capacitance_pf = None + + return cls( + accuracy_ppm=lfxo["accuracyPPM"], + mode=LFXOMode[lfxo["mode"]], + builtin_load_capacitors=builtin_load_capacitors, + builtin_load_capacitance_pf=builtin_load_capacitance_pf, + startup_time_ms=lfxo["startupTimeMs"], + ) + + def to_raw(self, bicr_spec: ET.Element, buf: bytearray): + lfosc_lfxoconfig = Register(bicr_spec, "LFOSC.LFXOCONFIG", buf) + + lfosc_lfxoconfig.enum_set("ACCURACY", f"{self.accuracy_ppm}ppm") + lfosc_lfxoconfig.enum_set("MODE", self.mode.value) + lfosc_lfxoconfig["TIME"] = self.startup_time_ms + + if self.builtin_load_capacitors: + lfosc_lfxoconfig["LOADCAP"] = self.builtin_load_capacitance_pf + else: + lfosc_lfxoconfig.enum_set("LOADCAP", "External") + + def to_json(self, buf: dict): + lfosc = buf["lfosc"] + lfosc["lfxo"] = { + "accuracyPPM": self.accuracy_ppm, + "mode": self.mode.name, + "builtInLoadCapacitors": self.builtin_load_capacitors, + "startupTimeMs": self.startup_time_ms, + } + + if self.builtin_load_capacitors: + lfosc["lfxo"]["builtInLoadCapacitancePf"] = self.builtin_load_capacitance_pf + + +@dataclass +class LFRCCalibrationConfig: + calibration_enabled: bool + temp_meas_interval_seconds: float | None + temp_delta_calibration_trigger_celsius: float | None + max_meas_interval_between_calibrations: int | None + + @classmethod + def from_raw( + cls: "LFRCCalibrationConfig", bicr_spec: ET.Element, data: bytes + ) -> "LFRCCalibrationConfig": + lfosc_lfrcautocalconfig = Register(bicr_spec, "LFOSC.LFRCAUTOCALCONFIG", data) + + calibration_enabled = lfosc_lfrcautocalconfig.enum_get("ENABLE") == "Enabled" + if calibration_enabled: + return cls( + calibration_enabled=calibration_enabled, + temp_meas_interval_seconds=lfosc_lfrcautocalconfig["TEMPINTERVAL"], + temp_delta_calibration_trigger_celsius=lfosc_lfrcautocalconfig["TEMPDELTA"], + max_meas_interval_between_calibrations=lfosc_lfrcautocalconfig["INTERVALMAXNO"], + ) + else: + return cls( + calibration_enabled=calibration_enabled, + temp_meas_interval_seconds=None, + temp_delta_calibration_trigger_celsius=None, + max_meas_interval_between_calibrations=None, + ) + + @classmethod + def from_json(cls: "LFRCCalibrationConfig", data: dict) -> "LFRCCalibrationConfig": + lfrccal = data["lfosc"]["lfrccal"] + + calibration_enabled = lfrccal["calibrationEnabled"] + if calibration_enabled: + temp_meas_interval_seconds = lfrccal["tempMeasIntervalSeconds"] + temp_delta_calibration_trigger_celsius = lfrccal["tempDeltaCalibrationTriggerCelsius"] + max_meas_interval_between_calibrations = lfrccal["maxMeasIntervalBetweenCalibrations"] + else: + temp_meas_interval_seconds = None + temp_delta_calibration_trigger_celsius = None + max_meas_interval_between_calibrations = None + + return cls( + calibration_enabled=calibration_enabled, + temp_meas_interval_seconds=temp_meas_interval_seconds, + temp_delta_calibration_trigger_celsius=temp_delta_calibration_trigger_celsius, + max_meas_interval_between_calibrations=max_meas_interval_between_calibrations, + ) + + def to_raw(self, bicr_spec: ET.Element, buf: bytearray): + lfosc_lfrcautocalconfig = Register(bicr_spec, "LFOSC.LFRCAUTOCALCONFIG", buf) + + lfosc_lfrcautocalconfig.enum_set( + "ENABLE", "Enabled" if self.calibration_enabled else "Disabled" + ) + if self.calibration_enabled: + lfosc_lfrcautocalconfig["TEMPINTERVAL"] = self.temp_meas_interval_seconds + lfosc_lfrcautocalconfig["TEMPDELTA"] = self.temp_delta_calibration_trigger_celsius + lfosc_lfrcautocalconfig["INTERVALMAXNO"] = self.max_meas_interval_between_calibrations + + def to_json(self, buf: dict): + lfosc = buf["lfosc"] + lfosc["lfrccal"] = { + "calibrationEnabled": self.calibration_enabled, + } + + if self.calibration_enabled: + lfosc["lfrccal"]["tempMeasIntervalSeconds"] = self.temp_meas_interval_seconds + lfosc["lfrccal"]["tempDeltaCalibrationTriggerCelsius"] = ( + self.temp_delta_calibration_trigger_celsius + ) + lfosc["lfrccal"]["maxMeasIntervalBetweenCalibrations"] = ( + self.max_meas_interval_between_calibrations + ) + + +class LFOSCSource(Enum): + LFXO = "LFXO" + LFRC = "LFRC" + + +@dataclass +class LFOSCConfig: + source: LFOSCSource + lfxo: LFXOConfig | None + lfrccal: LFRCCalibrationConfig | None + + @classmethod + def from_raw(cls: "LFOSCConfig", bicr_spec: ET.Element, data: bytes) -> "LFOSCConfig": + lfosc_lfxoconfig = Register(bicr_spec, "LFOSC.LFXOCONFIG", data) + + mode = lfosc_lfxoconfig.enum_get("MODE") + if mode == "Disabled": + source = LFOSCSource.LFRC + lfxo = None + lfrccal = LFRCCalibrationConfig.from_raw(bicr_spec, data) + elif mode == "Unconfigured": + raise ValueError("Invalid LFOSC configuration") + else: + source = LFOSCSource.LFXO + lfxo = LFXOConfig.from_raw(bicr_spec, data) + lfrccal = None + + return cls( + source=source, + lfxo=lfxo, + lfrccal=lfrccal, + ) + + @classmethod + def from_json(cls: "LFOSCConfig", data: dict) -> "LFOSCConfig": + lfosc = data["lfosc"] + + source = LFOSCSource[lfosc["source"]] + if source == LFOSCSource.LFXO: + source = source + lfxo = LFXOConfig.from_json(data) + lfrccal = None + else: + source = source + lfxo = None + lfrccal = LFRCCalibrationConfig.from_json(data) + + return cls( + source=source, + lfxo=lfxo, + lfrccal=lfrccal, + ) + + def to_raw(self, bicr_spec: ET.Element, buf: bytearray): + lfosc_lfxoconfig = Register(bicr_spec, "LFOSC.LFXOCONFIG", buf) + + if self.source == LFOSCSource.LFRC: + lfosc_lfxoconfig.enum_set("MODE", "Disabled") + self.lfrccal.to_raw(bicr_spec, buf) + elif self.source == LFOSCSource.LFXO: + self.lfxo.to_raw(bicr_spec, buf) + + def to_json(self, buf: dict): + buf["lfosc"] = { + "source": self.source.name, + } + + if self.source == LFOSCSource.LFXO: + self.lfxo.to_json(buf) + else: + self.lfrccal.to_json(buf) + + +class HFXOMode(Enum): + CRYSTAL = "Crystal" + EXT_SQUARE = "ExtSquare" + + +@dataclass +class HFXOConfig: + mode: HFXOMode + builtin_load_capacitors: bool + builtin_load_capacitance_pf: float | None + startup_time_us: int + + @classmethod + def from_raw(cls: "HFXOConfig", bicr_spec: ET.Element, data: bytes) -> "HFXOConfig": + hfxo_config = Register(bicr_spec, "HFXO.CONFIG", data) + hfxo_startuptime = Register(bicr_spec, "HFXO.STARTUPTIME", data) + + mode = HFXOMode(hfxo_config.enum_get("MODE")) + + try: + loadcap = hfxo_config.enum_get("LOADCAP") + except ValueError: + builtin_load_capacitors = True + builtin_load_capacitance_pf = hfxo_config["LOADCAP"] * 0.25 + else: + if loadcap == "Unconfigured": + raise ValueError("Invalid HFXO load capacitors configuration") + + builtin_load_capacitors = False + builtin_load_capacitance_pf = None + + startup_time_us = 0 + try: + hfxo_startuptime.enum_get("TIME") + except ValueError: + startup_time_us = hfxo_startuptime["TIME"] + else: + raise ValueError("Invalid LFXO startup time (not configured)") + + return cls( + mode=mode, + builtin_load_capacitors=builtin_load_capacitors, + builtin_load_capacitance_pf=builtin_load_capacitance_pf, + startup_time_us=startup_time_us, + ) + + @classmethod + def from_json(cls: "HFXOConfig", data: dict) -> "HFXOConfig": + hfxo = data["hfxo"] + + builtin_load_capacitors = hfxo["builtInLoadCapacitors"] + if builtin_load_capacitors: + builtin_load_capacitance_pf = hfxo["builtInLoadCapacitancePf"] + else: + builtin_load_capacitance_pf = None + + return cls( + mode=HFXOMode[hfxo["mode"]], + builtin_load_capacitors=builtin_load_capacitors, + builtin_load_capacitance_pf=builtin_load_capacitance_pf, + startup_time_us=hfxo["startupTimeUs"], + ) + + def to_raw(self, bicr_spec: ET.Element, buf: bytearray): + hfxo_config = Register(bicr_spec, "HFXO.CONFIG", buf) + hfxo_startuptime = Register(bicr_spec, "HFXO.STARTUPTIME", buf) + + hfxo_config.enum_set("MODE", self.mode.value) + hfxo_startuptime["TIME"] = self.startup_time_us + + if self.builtin_load_capacitors: + hfxo_config["LOADCAP"] = int(self.builtin_load_capacitance_pf / 0.25) + else: + hfxo_config.enum_set("LOADCAP", "External") + + def to_json(self, buf: dict): + buf["hfxo"] = { + "mode": self.mode.name, + "builtInLoadCapacitors": self.builtin_load_capacitors, + "startupTimeUs": self.startup_time_us, + } + + if self.builtin_load_capacitors: + buf["hfxo"]["builtInLoadCapacitancePf"] = self.builtin_load_capacitance_pf + + +@dataclass +class BICR: + power: PowerConfig + ioport_power: IoPortPowerConfig + ioport_impedance: IoPortImpedanceConfig + lfosc: LFOSCConfig + hfxo: HFXOConfig + + @classmethod + def from_raw(cls: "BICR", bicr_spec: ET.Element, data: bytes) -> "BICR": + return cls( + power=PowerConfig.from_raw(bicr_spec, data), + ioport_power=IoPortPowerConfig.from_raw(bicr_spec, data), + ioport_impedance=IoPortImpedanceConfig.from_raw(bicr_spec, data), + lfosc=LFOSCConfig.from_raw(bicr_spec, data), + hfxo=HFXOConfig.from_raw(bicr_spec, data), + ) + + @classmethod + def from_json(cls: "BICR", data: dict) -> "BICR": + return cls( + power=PowerConfig.from_json(data), + ioport_power=IoPortPowerConfig.from_json(data), + ioport_impedance=IoPortImpedanceConfig.from_json(data), + lfosc=LFOSCConfig.from_json(data), + hfxo=HFXOConfig.from_json(data), + ) + + def to_raw(self, bicr_spec: ET.Element, buf: bytearray): + self.power.to_raw(bicr_spec, buf) + self.ioport_power.to_raw(bicr_spec, buf) + self.ioport_impedance.to_raw(bicr_spec, buf) + self.lfosc.to_raw(bicr_spec, buf) + self.hfxo.to_raw(bicr_spec, buf) + + def to_json(self, buf: dict): + self.power.to_json(buf) + self.ioport_power.to_json(buf) + self.ioport_impedance.to_json(buf) + self.lfosc.to_json(buf) + self.hfxo.to_json(buf) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(allow_abbrev=False) + parser.add_argument("-i", "--input", type=Path, required=True, help="Input file") + parser.add_argument("-s", "--svd", type=Path, help="SVD file") + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("-o", "--output", type=Path, help="Output file") + group.add_argument("-l", "--list", action="store_true", help="List BICR options") + args = parser.parse_args() + + if args.input.suffix == ".hex" or (args.output and args.output.suffix == ".hex"): + if not args.svd: + sys.exit("SVD file is required for hex files") + + bicr_spec = ET.parse(args.svd).getroot().find(".//peripheral[name='BICR_NS']") + + if args.input.suffix == ".hex": + ih = IntelHex() + ih.loadhex(args.input) + bicr = BICR.from_raw(bicr_spec, ih.tobinstr()) + elif args.input.suffix == ".json": + with open(args.input) as f: + data = json.load(f) + bicr = BICR.from_json(data) + else: + sys.exit("Unsupported input file format") + + if args.output: + if args.output.suffix == ".hex": + bicr_address = int(bicr_spec.find("baseAddress").text, 0) + last_reg = Register(bicr_spec, "TAMPC.ACTIVESHIELD") + bicr_size = last_reg.offset + last_reg.size + + buf = bytearray([0xFF] * bicr_size) + bicr.to_raw(bicr_spec, buf) + + ih = IntelHex() + ih.frombytes(buf, offset=bicr_address) + ih.tofile(args.output, format="hex") + elif args.output.suffix == ".json": + buf = dict() + bicr.to_json(buf) + + with open(args.output, "w") as f: + json.dump(buf, f, indent=4) + else: + sys.exit("Unsupported output file format") + elif args.list: + pprint(bicr) diff --git a/soc/nordic/validate_base_addresses.c b/soc/nordic/validate_base_addresses.c index f47c3dc456e..ef1dd194578 100644 --- a/soc/nordic/validate_base_addresses.c +++ b/soc/nordic/validate_base_addresses.c @@ -330,6 +330,7 @@ CHECK_DT_REG(uart136, NRF_UARTE136); CHECK_DT_REG(uart137, NRF_UARTE137); CHECK_DT_REG(uicr, NRF_UICR); CHECK_DT_REG(cpuapp_uicr, NRF_APPLICATION_UICR); +CHECK_DT_REG(bicr, NRF_APPLICATION_BICR); CHECK_DT_REG(cpurad_uicr, NRF_RADIOCORE_UICR); CHECK_DT_REG(usbd, NRF_USBD); CHECK_DT_REG(usbhs, NRF_USBHS); diff --git a/subsys/logging/Kconfig.misc b/subsys/logging/Kconfig.misc index 1813623b56f..a9c90526e14 100644 --- a/subsys/logging/Kconfig.misc +++ b/subsys/logging/Kconfig.misc @@ -6,7 +6,7 @@ menu "Misc" config LOG_CMDS bool "Shell commands" depends on SHELL - depends on !LOG_FRONTEND_ONLY && !LOG_MODE_MINIMAL + depends on !LOG_MODE_MINIMAL config LOG_TEST_CLEAR_MESSAGE_SPACE bool "Clear message after allocation" diff --git a/subsys/logging/log_cmds.c b/subsys/logging/log_cmds.c index 79e4457184f..fd7997a84bd 100644 --- a/subsys/logging/log_cmds.c +++ b/subsys/logging/log_cmds.c @@ -58,8 +58,8 @@ static const struct log_backend *backend_find(char const *name) static bool shell_state_precheck(const struct shell *sh) { - if (sh->log_backend->control_block->state - == SHELL_LOG_BACKEND_UNINIT) { + if (sh->log_backend && + (sh->log_backend->control_block->state == SHELL_LOG_BACKEND_UNINIT)) { shell_error(sh, "Shell log backend not initialized."); return false; } @@ -142,8 +142,7 @@ static int cmd_log_self_status(const struct shell *sh, return 0; } - log_status(sh, sh->log_backend->backend, argc, argv); - return 0; + return log_status(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv); } static int cmd_log_backend_status(const struct shell *sh, @@ -246,7 +245,7 @@ static int cmd_log_self_enable(const struct shell *sh, return 0; } - return log_enable(sh, sh->log_backend->backend, argc, argv); + return log_enable(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv); } static int cmd_log_backend_enable(const struct shell *sh, @@ -271,7 +270,7 @@ static int cmd_log_self_disable(const struct shell *sh, return 0; } - return log_disable(sh, sh->log_backend->backend, argc, argv); + return log_disable(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv); } static int cmd_log_backend_disable(const struct shell *sh, @@ -327,7 +326,7 @@ static int cmd_log_self_halt(const struct shell *sh, return 0; } - return log_halt(sh, sh->log_backend->backend, argc, argv); + return log_halt(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv); } static int cmd_log_backend_halt(const struct shell *sh, @@ -359,7 +358,7 @@ static int cmd_log_self_go(const struct shell *sh, return 0; } - return log_go(sh, sh->log_backend->backend, argc, argv); + return log_go(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv); } static int cmd_log_backend_go(const struct shell *sh, diff --git a/subsys/shell/CMakeLists.txt b/subsys/shell/CMakeLists.txt index ac8c3b2186f..276f177d7ba 100644 --- a/subsys/shell/CMakeLists.txt +++ b/subsys/shell/CMakeLists.txt @@ -11,10 +11,12 @@ zephyr_sources_ifdef( shell_ops.c ) -zephyr_sources_ifdef( - CONFIG_SHELL_LOG_BACKEND - shell_log_backend.c +if(NOT CONFIG_SHELL_LOG_BACKEND_CUSTOM) + zephyr_sources_ifdef( + CONFIG_SHELL_LOG_BACKEND + shell_log_backend.c ) +endif() zephyr_sources_ifdef( CONFIG_SHELL_HELP diff --git a/subsys/shell/Kconfig b/subsys/shell/Kconfig index 444e953dddd..696d070a7e4 100644 --- a/subsys/shell/Kconfig +++ b/subsys/shell/Kconfig @@ -277,6 +277,13 @@ config SHELL_LOG_BACKEND using the shell backend's LOG_LEVEL option (e.g. CONFIG_SHELL_TELNET_INIT_LOG_LEVEL_NONE=y). +config SHELL_LOG_BACKEND_CUSTOM + bool "Use custom backend implementation" + help + When enabled, standard implementation of shell log backend is not included. + It allows to provide custom implementation of multiplexing logging messages + with shell data. + config SHELL_LOG_FORMAT_TIMESTAMP bool "Format timestamp" default y