1 /*
2 * Copyright (c) 2022 Dronetag
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT gpio_radio_coex
8
9 #include <errno.h>
10 #include <soc.h>
11
12 #include <zephyr/kernel.h>
13 #include <zephyr/device.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/drivers/gpio.h>
16 #include <zephyr/sys/__assert.h>
17 #include <zephyr/bluetooth/hci_types.h>
18
19 #include "controller/hal/ticker.h"
20 #include "controller/ticker/ticker.h"
21 #include "controller/include/ll.h"
22 #include "controller/ll_sw/nordic/hal/nrf5/debug.h"
23
24 LOG_MODULE_REGISTER(coex_ticker);
25
26 #define COEX_RADIO_WORK_DELAY_US 50
27 #define COEX_RADIO_WORK_RESCHEDULE_DELAY_US 200
28
29 struct coex_ticker_config {
30 struct gpio_dt_spec grant_spec;
31 size_t grant_delay_us;
32 };
33
34 struct coex_ticker_data {
35 const struct device *dev;
36 struct gpio_callback grant_irq_cb;
37 };
38
time_slot_delay(uint32_t ticks_at_expire,uint32_t ticks_delay,ticker_timeout_func callback,void * context)39 static int time_slot_delay(uint32_t ticks_at_expire, uint32_t ticks_delay,
40 ticker_timeout_func callback, void *context)
41 {
42 uint8_t instance_index;
43 uint8_t ticker_id;
44 int err;
45
46 ll_coex_ticker_id_get(&instance_index, &ticker_id);
47
48 /* start a secondary one-shot ticker after ticks_delay,
49 * this will let any radio role to gracefully abort and release the
50 * Radio h/w.
51 */
52 err = ticker_start(instance_index, /* Radio instance ticker */
53 1, /* user id for link layer ULL_HIGH */
54 /* (MAYFLY_CALL_ID_WORKER) */
55 ticker_id, /* ticker_id */
56 ticks_at_expire, /* current tick */
57 ticks_delay, /* one-shot delayed timeout */
58 0, /* periodic timeout */
59 0, /* periodic remainder */
60 0, /* lazy, voluntary skips */
61 0,
62 callback, /* handler for executing radio abort or */
63 /* coex work */
64 context, /* the context for the coex operation */
65 NULL, /* no op callback */
66 NULL);
67
68 return err;
69 }
70
71
time_slot_callback_work(uint32_t ticks_at_expire,uint32_t ticks_drift,uint32_t remainder,uint16_t lazy,uint8_t force,void * context)72 static void time_slot_callback_work(uint32_t ticks_at_expire,
73 uint32_t ticks_drift,
74 uint32_t remainder,
75 uint16_t lazy, uint8_t force,
76 void *context)
77 {
78 int ret;
79 uint8_t instance_index;
80 uint8_t ticker_id;
81 const struct device *dev = context;
82 const struct coex_ticker_config *config = dev->config;
83
84 __ASSERT(ll_radio_state_is_idle(),
85 "Radio is on during coex operation.\n");
86
87 /* Read grant pin */
88 if (gpio_pin_get_dt(&config->grant_spec) == 0) {
89 DEBUG_COEX_GRANT(1);
90
91 if (!ll_radio_state_is_idle()) {
92 ll_radio_state_abort();
93 }
94 /* Schedule another check for grant pin and abort any events scheduled */
95 time_slot_delay(ticker_ticks_now_get(),
96 HAL_TICKER_US_TO_TICKS(COEX_RADIO_WORK_RESCHEDULE_DELAY_US),
97 time_slot_callback_work,
98 context);
99 } else {
100 LOG_DBG("COEX Inhibit Off");
101 DEBUG_COEX_IRQ(0);
102 DEBUG_COEX_GRANT(0);
103
104 /* Enable coex pin interrupt */
105 gpio_pin_interrupt_configure_dt(&config->grant_spec, GPIO_INT_EDGE_TO_INACTIVE);
106
107 /* Stop the time slot ticker */
108 ll_coex_ticker_id_get(&instance_index, &ticker_id);
109
110 ret = ticker_stop(instance_index, 0, ticker_id, NULL, NULL);
111 if (ret != TICKER_STATUS_SUCCESS &&
112 ret != TICKER_STATUS_BUSY) {
113 __ASSERT(0, "Failed to stop ticker.\n");
114 }
115 }
116 }
117
time_slot_callback_abort(uint32_t ticks_at_expire,uint32_t ticks_drift,uint32_t remainder,uint16_t lazy,uint8_t force,void * context)118 static void time_slot_callback_abort(uint32_t ticks_at_expire,
119 uint32_t ticks_drift,
120 uint32_t remainder,
121 uint16_t lazy, uint8_t force,
122 void *context)
123 {
124 ll_radio_state_abort();
125 time_slot_delay(ticks_at_expire,
126 HAL_TICKER_US_TO_TICKS(COEX_RADIO_WORK_DELAY_US),
127 time_slot_callback_work,
128 context);
129 }
130
coex_ticker_grant_start(const struct device * dev)131 static int coex_ticker_grant_start(const struct device *dev)
132 {
133 const struct coex_ticker_config *cfg = dev->config;
134 uint32_t err;
135
136 err = time_slot_delay(
137 ticker_ticks_now_get(),
138 HAL_TICKER_US_TO_TICKS(cfg->grant_delay_us - COEX_RADIO_WORK_DELAY_US),
139 time_slot_callback_abort,
140 (void *)dev
141 );
142
143 if (err != TICKER_STATUS_SUCCESS && err != TICKER_STATUS_BUSY) {
144 return -EBUSY;
145 }
146
147 return 0;
148 }
149
150
coex_ticker_grant_irq_handler(const struct device * dev,struct gpio_callback * cb,uint32_t pins)151 static void coex_ticker_grant_irq_handler(const struct device *dev,
152 struct gpio_callback *cb, uint32_t pins)
153 {
154 ARG_UNUSED(dev);
155 ARG_UNUSED(pins);
156 struct coex_ticker_data *data = CONTAINER_OF(cb, struct coex_ticker_data, grant_irq_cb);
157 const struct coex_ticker_config *config = data->dev->config;
158
159 LOG_DBG("COEX Inhibit IRQ Detected");
160 DEBUG_COEX_IRQ(1);
161 DEBUG_COEX_GRANT(0);
162
163 gpio_pin_interrupt_configure_dt(&config->grant_spec, GPIO_INT_DISABLE);
164 coex_ticker_grant_start(data->dev);
165 }
166
167
coex_ticker_init(const struct device * dev)168 static int coex_ticker_init(const struct device *dev)
169 {
170 const struct coex_ticker_config *config = dev->config;
171 struct coex_ticker_data *data = dev->data;
172 int res;
173
174 data->dev = dev;
175
176 DEBUG_COEX_INIT();
177 res = gpio_pin_configure_dt(&config->grant_spec, GPIO_INPUT);
178 if (res) {
179 return res;
180 }
181
182 gpio_init_callback(&data->grant_irq_cb,
183 coex_ticker_grant_irq_handler,
184 BIT(config->grant_spec.pin));
185
186 res = gpio_add_callback(config->grant_spec.port, &data->grant_irq_cb);
187 if (res) {
188 return res;
189 }
190
191 res = gpio_pin_interrupt_configure_dt(&config->grant_spec, GPIO_INT_EDGE_TO_INACTIVE);
192 if (res) {
193 return res;
194 }
195
196 return 0;
197 }
198
199 static struct coex_ticker_config config = {
200 .grant_spec = GPIO_DT_SPEC_INST_GET(0, grant_gpios),
201 .grant_delay_us = DT_INST_PROP(0, grant_delay_us)
202 };
203 static struct coex_ticker_data data;
204
205 DEVICE_DT_INST_DEFINE(0, &coex_ticker_init, NULL, &data, &config,
206 POST_KERNEL, 90, NULL);
207