Convert the driver to a dt-based device. Also update Kconfig so that it depends on the right compatible being enabled, initialize in POST_KERNEL (APPLICATION should not be used for devices) and remove one redundant include. Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
207 lines
5.1 KiB
C
207 lines
5.1 KiB
C
/*
|
|
* 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);
|