/* * Copyright (c) 2022 Dronetag * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT gpio_radio_coex #include #include #include #include #include #include #include #include #include "controller/hal/ticker.h" #include "controller/ticker/ticker.h" #include "controller/include/ll.h" #include "controller/ll_sw/nordic/hal/nrf5/debug.h" LOG_MODULE_REGISTER(coex_ticker); #define COEX_RADIO_WORK_DELAY_US 50 #define COEX_RADIO_WORK_RESCHEDULE_DELAY_US 200 struct coex_ticker_config { struct gpio_dt_spec grant_spec; size_t grant_delay_us; }; struct coex_ticker_data { const struct device *dev; struct gpio_callback grant_irq_cb; }; static int 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_coex_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, /* 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 */ /* coex work */ context, /* the context for the coex operation */ NULL, /* no op callback */ NULL); return err; } 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) { int ret; uint8_t instance_index; uint8_t ticker_id; const struct device *dev = context; const struct coex_ticker_config *config = dev->config; __ASSERT(ll_radio_state_is_idle(), "Radio is on during coex operation.\n"); /* Read grant pin */ if (gpio_pin_get_dt(&config->grant_spec) == 0) { DEBUG_COEX_GRANT(1); if (!ll_radio_state_is_idle()) { ll_radio_state_abort(); } /* Schedule another check for grant pin and abort any events scheduled */ time_slot_delay(ticker_ticks_now_get(), HAL_TICKER_US_TO_TICKS(COEX_RADIO_WORK_RESCHEDULE_DELAY_US), time_slot_callback_work, context); } else { LOG_DBG("COEX Inhibit Off"); DEBUG_COEX_IRQ(0); DEBUG_COEX_GRANT(0); /* Enable coex pin interrupt */ gpio_pin_interrupt_configure_dt(&config->grant_spec, GPIO_INT_EDGE_TO_INACTIVE); /* Stop the time slot ticker */ ll_coex_ticker_id_get(&instance_index, &ticker_id); ret = ticker_stop(instance_index, 0, ticker_id, NULL, NULL); if (ret != TICKER_STATUS_SUCCESS && ret != TICKER_STATUS_BUSY) { __ASSERT(0, "Failed to stop ticker.\n"); } } } 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(COEX_RADIO_WORK_DELAY_US), time_slot_callback_work, context); } static int coex_ticker_grant_start(const struct device *dev) { const struct coex_ticker_config *cfg = dev->config; uint32_t err; err = time_slot_delay( ticker_ticks_now_get(), HAL_TICKER_US_TO_TICKS(cfg->grant_delay_us - COEX_RADIO_WORK_DELAY_US), time_slot_callback_abort, (void *)dev ); if (err != TICKER_STATUS_SUCCESS && err != TICKER_STATUS_BUSY) { return -EBUSY; } return 0; } static void coex_ticker_grant_irq_handler(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { ARG_UNUSED(dev); ARG_UNUSED(pins); struct coex_ticker_data *data = CONTAINER_OF(cb, struct coex_ticker_data, grant_irq_cb); const struct coex_ticker_config *config = data->dev->config; LOG_DBG("COEX Inhibit IRQ Detected"); DEBUG_COEX_IRQ(1); DEBUG_COEX_GRANT(0); gpio_pin_interrupt_configure_dt(&config->grant_spec, GPIO_INT_DISABLE); coex_ticker_grant_start(data->dev); } static int coex_ticker_init(const struct device *dev) { const struct coex_ticker_config *config = dev->config; struct coex_ticker_data *data = dev->data; int res; data->dev = dev; DEBUG_COEX_INIT(); res = gpio_pin_configure_dt(&config->grant_spec, GPIO_INPUT); if (res) { return res; } gpio_init_callback(&data->grant_irq_cb, coex_ticker_grant_irq_handler, BIT(config->grant_spec.pin)); res = gpio_add_callback(config->grant_spec.port, &data->grant_irq_cb); if (res) { return res; } res = gpio_pin_interrupt_configure_dt(&config->grant_spec, GPIO_INT_EDGE_TO_INACTIVE); if (res) { return res; } return 0; } static struct coex_ticker_config config = { .grant_spec = GPIO_DT_SPEC_INST_GET(0, grant_gpios), .grant_delay_us = DT_INST_PROP(0, grant_delay_us) }; static struct coex_ticker_data data; DEVICE_DT_INST_DEFINE(0, &coex_ticker_init, NULL, &data, &config, POST_KERNEL, 90, NULL);