You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
5.1 KiB
206 lines
5.1 KiB
/* |
|
* Copyright (c) 2022 Dronetag |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT gpio_radio_coex |
|
|
|
#include <errno.h> |
|
#include <soc.h> |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/device.h> |
|
#include <zephyr/logging/log.h> |
|
#include <zephyr/drivers/gpio.h> |
|
#include <zephyr/sys/__assert.h> |
|
#include <zephyr/bluetooth/hci_types.h> |
|
|
|
#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);
|
|
|