| /* |
| * Copyright (c) 2020 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <errno.h> |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| #include <zephyr/init.h> |
| #include <soc.h> |
| |
| #include "soc_flash_nrf.h" |
| |
| #include <zephyr/sys/__assert.h> |
| #include <zephyr/bluetooth/hci.h> |
| #include "controller/hal/ticker.h" |
| #include "controller/ticker/ticker.h" |
| #include "controller/include/ll.h" |
| |
| #define FLASH_RADIO_ABORT_DELAY_US 1500 |
| #define FLASH_RADIO_WORK_DELAY_US 200 |
| |
| /* delay needed for start execution-window */ |
| #define FLASH_SYNC_SWITCHING_TIME (FLASH_RADIO_ABORT_DELAY_US +\ |
| FLASH_RADIO_WORK_DELAY_US) |
| |
| struct ticker_sync_context { |
| uint32_t interval; /* timeslot interval. */ |
| uint32_t slot; /* timeslot length. */ |
| uint32_t ticks_begin; /* timeslot begin timestamp */ |
| int result; |
| }; |
| |
| static struct ticker_sync_context _ticker_sync_context; |
| |
| |
| /* semaphore for synchronization of flash operations */ |
| static struct k_sem sem_sync; |
| |
| static inline int _ticker_stop(uint8_t inst_idx, uint8_t u_id, uint8_t tic_id) |
| { |
| int ret = ticker_stop(inst_idx, u_id, tic_id, NULL, NULL); |
| |
| if (ret != TICKER_STATUS_SUCCESS && |
| ret != TICKER_STATUS_BUSY) { |
| __ASSERT(0, "Failed to stop ticker.\n"); |
| } |
| |
| return ret; |
| } |
| |
| static void time_slot_callback_work(uint32_t ticks_at_expire, |
| uint32_t ticks_drift, |
| uint32_t remainder, |
| uint16_t lazy, uint8_t force, |
| void *context) |
| { |
| struct flash_op_desc *op_desc; |
| uint8_t instance_index; |
| uint8_t ticker_id; |
| int rc; |
| |
| __ASSERT(ll_radio_state_is_idle(), |
| "Radio is on during flash operation.\n"); |
| |
| op_desc = context; |
| rc = op_desc->handler(op_desc->context); |
| if (rc != FLASH_OP_ONGOING) { |
| ll_timeslice_ticker_id_get(&instance_index, &ticker_id); |
| |
| /* Stop the time slot ticker */ |
| _ticker_stop(instance_index, 0, ticker_id); |
| |
| _ticker_sync_context.result = (rc == FLASH_OP_DONE) ? 0 : rc; |
| |
| /* notify thread that data is available */ |
| k_sem_give(&sem_sync); |
| } |
| } |
| |
| static void time_slot_delay(uint32_t ticks_at_expire, uint32_t ticks_delay, |
| ticker_timeout_func callback, void *context) |
| { |
| uint8_t instance_index; |
| uint8_t ticker_id; |
| int err; |
| |
| ll_timeslice_ticker_id_get(&instance_index, &ticker_id); |
| |
| /* start a secondary one-shot ticker after ticks_delay, |
| * this will let any radio role to gracefully abort and release the |
| * Radio h/w. |
| */ |
| err = ticker_start(instance_index, /* Radio instance ticker */ |
| 1, /* user id for link layer ULL_HIGH */ |
| /* (MAYFLY_CALL_ID_WORKER) */ |
| (ticker_id + 1), /* ticker_id */ |
| ticks_at_expire, /* current tick */ |
| ticks_delay, /* one-shot delayed timeout */ |
| 0, /* periodic timeout */ |
| 0, /* periodic remainder */ |
| 0, /* lazy, voluntary skips */ |
| 0, |
| callback, /* handler for executing radio abort or */ |
| /* flash work */ |
| context, /* the context for the flash operation */ |
| NULL, /* no op callback */ |
| NULL); |
| |
| if (err != TICKER_STATUS_SUCCESS && err != TICKER_STATUS_BUSY) { |
| _ticker_sync_context.result = 0; |
| |
| /* abort flash timeslots */ |
| _ticker_stop(instance_index, 0, ticker_id); |
| |
| /* notify thread that data is available */ |
| k_sem_give(&sem_sync); |
| } |
| } |
| |
| static void time_slot_callback_abort(uint32_t ticks_at_expire, |
| uint32_t ticks_drift, |
| uint32_t remainder, |
| uint16_t lazy, uint8_t force, |
| void *context) |
| { |
| ll_radio_state_abort(); |
| time_slot_delay(ticks_at_expire, |
| HAL_TICKER_US_TO_TICKS(FLASH_RADIO_WORK_DELAY_US), |
| time_slot_callback_work, |
| context); |
| } |
| |
| static void time_slot_callback_prepare(uint32_t ticks_at_expire, |
| uint32_t ticks_drift, |
| uint32_t remainder, |
| uint16_t lazy, uint8_t force, |
| void *context) |
| { |
| #if defined(CONFIG_BT_CTLR_LOW_LAT) |
| time_slot_callback_abort(ticks_at_expire, ticks_drift, remainder, lazy, |
| force, context); |
| #else /* !CONFIG_BT_CTLR_LOW_LAT */ |
| time_slot_delay(ticks_at_expire, |
| HAL_TICKER_US_TO_TICKS(FLASH_RADIO_ABORT_DELAY_US), |
| time_slot_callback_abort, |
| context); |
| #endif /* CONFIG_BT_CTLR_LOW_LAT */ |
| } |
| |
| |
| int nrf_flash_sync_init(void) |
| { |
| k_sem_init(&sem_sync, 0, 1); |
| return 0; |
| } |
| |
| void nrf_flash_sync_set_context(uint32_t duration) |
| { |
| |
| /* FLASH_SYNC_SWITCHING_TIME is delay which is always added by |
| * the slot calling mechanism |
| */ |
| _ticker_sync_context.interval = duration - FLASH_SYNC_SWITCHING_TIME; |
| _ticker_sync_context.slot = duration; |
| } |
| |
| int nrf_flash_sync_exe(struct flash_op_desc *op_desc) |
| { |
| uint8_t instance_index; |
| uint8_t ticker_id; |
| int result; |
| uint32_t err; |
| |
| ll_timeslice_ticker_id_get(&instance_index, &ticker_id); |
| |
| err = ticker_start(instance_index, |
| 3, /* user id for thread mode */ |
| /* (MAYFLY_CALL_ID_PROGRAM) */ |
| ticker_id, /* flash ticker id */ |
| ticker_ticks_now_get(), /* current tick */ |
| 0, /* first int. immediately */ |
| /* period */ |
| HAL_TICKER_US_TO_TICKS( |
| _ticker_sync_context.interval), |
| /* period remainder */ |
| HAL_TICKER_REMAINDER(_ticker_sync_context.interval), |
| 0, /* lazy, voluntary skips */ |
| HAL_TICKER_US_TO_TICKS(_ticker_sync_context.slot), |
| time_slot_callback_prepare, |
| op_desc, |
| NULL, /* no op callback */ |
| NULL); |
| |
| if (err != TICKER_STATUS_SUCCESS && err != TICKER_STATUS_BUSY) { |
| result = -ECANCELED; |
| } else if (k_sem_take(&sem_sync, K_MSEC(FLASH_TIMEOUT_MS)) != 0) { |
| /* Stop any scheduled jobs */ |
| _ticker_stop(instance_index, 3, ticker_id); |
| |
| /* wait for operation's complete overrun*/ |
| result = -ETIMEDOUT; |
| } else { |
| result = _ticker_sync_context.result; |
| } |
| |
| return result; |
| } |
| |
| bool nrf_flash_sync_is_required(void) |
| { |
| return ticker_is_initialized(0); |
| } |
| |
| void nrf_flash_sync_get_timestamp_begin(void) |
| { |
| _ticker_sync_context.ticks_begin = ticker_ticks_now_get(); |
| } |
| |
| bool nrf_flash_sync_check_time_limit(uint32_t iteration) |
| { |
| uint32_t ticks_diff; |
| |
| ticks_diff = ticker_ticks_diff_get(ticker_ticks_now_get(), |
| _ticker_sync_context.ticks_begin); |
| if (ticks_diff + ticks_diff/iteration > |
| HAL_TICKER_US_TO_TICKS(_ticker_sync_context.slot)) { |
| return true; |
| } |
| |
| return false; |
| } |