1 /*
2 * Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or
3 * an affiliate of Cypress Semiconductor Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include "airoc_wifi.h"
9 #include "airoc_whd_hal_common.h"
10
11 #include <bus_protocols/whd_bus_sdio_protocol.h>
12 #include <bus_protocols/whd_bus.h>
13 #include <bus_protocols/whd_sdio.h>
14 #include <zephyr/sd/sd.h>
15
16 LOG_MODULE_DECLARE(infineon_airoc_wifi, CONFIG_WIFI_LOG_LEVEL);
17
18 #ifdef __cplusplus
19 extern "C" {
20 #endif
21
22 struct whd_bus_priv {
23 whd_sdio_config_t sdio_config;
24 whd_bus_stats_t whd_bus_stats;
25 whd_sdio_t sdio_obj;
26 };
27
28 static whd_init_config_t init_config_default = {.thread_stack_size = CY_WIFI_THREAD_STACK_SIZE,
29 .thread_stack_start = NULL,
30 .thread_priority =
31 (uint32_t)CY_WIFI_THREAD_PRIORITY,
32 .country = CY_WIFI_COUNTRY};
33
34 /******************************************************
35 * Function
36 ******************************************************/
37
airoc_wifi_init_primary(const struct device * dev,whd_interface_t * interface,whd_netif_funcs_t * netif_funcs,whd_buffer_funcs_t * buffer_if)38 int airoc_wifi_init_primary(const struct device *dev, whd_interface_t *interface,
39 whd_netif_funcs_t *netif_funcs, whd_buffer_funcs_t *buffer_if)
40 {
41 int ret;
42 struct airoc_wifi_data *data = dev->data;
43 const struct airoc_wifi_config *config = dev->config;
44
45 whd_sdio_config_t whd_sdio_config = {
46 .sdio_1bit_mode = WHD_FALSE,
47 .high_speed_sdio_clock = WHD_FALSE,
48 };
49
50 #if DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios)
51 whd_oob_config_t oob_config = {
52 .host_oob_pin = (void *)&config->wifi_host_wake_gpio,
53 .dev_gpio_sel = DEFAULT_OOB_PIN,
54 .is_falling_edge =
55 (CY_WIFI_HOST_WAKE_IRQ_EVENT == GPIO_INT_TRIG_LOW) ? WHD_TRUE : WHD_FALSE,
56 .intr_priority = CY_WIFI_OOB_INTR_PRIORITY};
57 whd_sdio_config.oob_config = oob_config;
58 #endif
59
60 if (airoc_wifi_power_on(dev)) {
61 LOG_ERR("airoc_wifi_power_on retuens fail");
62 return -ENODEV;
63 }
64
65 if (!device_is_ready(config->bus_dev.bus_sdio)) {
66 LOG_ERR("SDHC device is not ready");
67 return -ENODEV;
68 }
69
70 ret = sd_init(config->bus_dev.bus_sdio, &data->card);
71 if (ret) {
72 return ret;
73 }
74
75 /* Init SDIO functions */
76 ret = sdio_init_func(&data->card, &data->sdio_func1, BACKPLANE_FUNCTION);
77 if (ret) {
78 LOG_ERR("sdio_enable_func BACKPLANE_FUNCTION, error: %x", ret);
79 return ret;
80 }
81 ret = sdio_init_func(&data->card, &data->sdio_func2, WLAN_FUNCTION);
82 if (ret) {
83 LOG_ERR("sdio_enable_func WLAN_FUNCTION, error: %x", ret);
84 return ret;
85 }
86 ret = sdio_set_block_size(&data->sdio_func1, SDIO_64B_BLOCK);
87 if (ret) {
88 LOG_ERR("Can't set block size for BACKPLANE_FUNCTION, error: %x", ret);
89 return ret;
90 }
91 ret = sdio_set_block_size(&data->sdio_func2, SDIO_64B_BLOCK);
92 if (ret) {
93 LOG_ERR("Can't set block size for WLAN_FUNCTION, error: %x", ret);
94 return ret;
95 }
96
97 /* Init wifi host driver (whd) */
98 cy_rslt_t whd_ret = whd_init(&data->whd_drv, &init_config_default, &resource_ops, buffer_if,
99 netif_funcs);
100 if (whd_ret == CY_RSLT_SUCCESS) {
101 whd_ret = whd_bus_sdio_attach(data->whd_drv, &whd_sdio_config,
102 (whd_sdio_t)&data->card);
103
104 if (whd_ret == CY_RSLT_SUCCESS) {
105 whd_ret = whd_wifi_on(data->whd_drv, interface);
106 }
107
108 if (whd_ret != CY_RSLT_SUCCESS) {
109 whd_deinit(*interface);
110 return -ENODEV;
111 }
112 }
113 return 0;
114 }
115
116 /*
117 * Implement SDIO CMD52/53 wrappers
118 */
119
airoc_wifi_get_sdio_func(struct sd_card * sd,whd_bus_function_t function)120 static struct sdio_func *airoc_wifi_get_sdio_func(struct sd_card *sd, whd_bus_function_t function)
121 {
122 struct airoc_wifi_data *data = CONTAINER_OF(sd, struct airoc_wifi_data, card);
123 struct sdio_func *func[] = {&sd->func0, &data->sdio_func1, &data->sdio_func2};
124
125 if (function > WLAN_FUNCTION) {
126 return NULL;
127 }
128
129 return func[function];
130 }
131
whd_bus_sdio_cmd52(whd_driver_t whd_driver,whd_bus_transfer_direction_t direction,whd_bus_function_t function,uint32_t address,uint8_t value,sdio_response_needed_t response_expected,uint8_t * response)132 whd_result_t whd_bus_sdio_cmd52(whd_driver_t whd_driver, whd_bus_transfer_direction_t direction,
133 whd_bus_function_t function, uint32_t address, uint8_t value,
134 sdio_response_needed_t response_expected, uint8_t *response)
135 {
136 int ret;
137 struct sd_card *sd = whd_driver->bus_priv->sdio_obj;
138 struct sdio_func *func = airoc_wifi_get_sdio_func(sd, function);
139
140 WHD_BUS_STATS_INCREMENT_VARIABLE(whd_driver->bus_priv, cmd52);
141
142 if (direction == BUS_WRITE) {
143 ret = sdio_rw_byte(func, address, value, response);
144 } else {
145 ret = sdio_read_byte(func, address, response);
146 }
147 WHD_BUS_STATS_CONDITIONAL_INCREMENT_VARIABLE(whd_driver->bus_priv, (ret != WHD_SUCCESS),
148 cmd52_fail);
149
150 /* Possibly device might not respond to this cmd. So, don't check return value here */
151 if ((ret != WHD_SUCCESS) && (address == SDIO_SLEEP_CSR)) {
152 return ret;
153 }
154
155 CHECK_RETURN(ret);
156 return WHD_SUCCESS;
157 }
158
whd_bus_sdio_cmd53(whd_driver_t whd_driver,whd_bus_transfer_direction_t direction,whd_bus_function_t function,sdio_transfer_mode_t mode,uint32_t address,uint16_t data_size,uint8_t * data,sdio_response_needed_t response_expected,uint32_t * response)159 whd_result_t whd_bus_sdio_cmd53(whd_driver_t whd_driver, whd_bus_transfer_direction_t direction,
160 whd_bus_function_t function, sdio_transfer_mode_t mode,
161 uint32_t address, uint16_t data_size, uint8_t *data,
162 sdio_response_needed_t response_expected, uint32_t *response)
163 {
164 whd_result_t ret;
165 struct sd_card *sd = whd_driver->bus_priv->sdio_obj;
166 struct sdio_func *func = airoc_wifi_get_sdio_func(sd, function);
167
168 if (direction == BUS_WRITE) {
169 WHD_BUS_STATS_INCREMENT_VARIABLE(whd_driver->bus_priv, cmd53_write);
170 ret = sdio_write_addr(func, address, data, data_size);
171 } else {
172 WHD_BUS_STATS_INCREMENT_VARIABLE(whd_driver->bus_priv, cmd53_read);
173 ret = sdio_read_addr(func, address, data, data_size);
174 }
175
176 WHD_BUS_STATS_CONDITIONAL_INCREMENT_VARIABLE(
177 whd_driver->bus_priv, ((ret != WHD_SUCCESS) && (direction == BUS_READ)),
178 cmd53_read_fail);
179 WHD_BUS_STATS_CONDITIONAL_INCREMENT_VARIABLE(
180 whd_driver->bus_priv, ((ret != WHD_SUCCESS) && (direction == BUS_WRITE)),
181 cmd53_write_fail);
182
183 CHECK_RETURN(ret);
184 return WHD_SUCCESS;
185 }
186
187 /*
188 * Implement SDIO Card interrupt
189 */
190
whd_bus_sdio_irq_handler(const struct device * dev,int reason,const void * user_data)191 void whd_bus_sdio_irq_handler(const struct device *dev, int reason, const void *user_data)
192 {
193 if (reason == SDHC_INT_SDIO) {
194 whd_driver_t whd_driver = (whd_driver_t)user_data;
195
196 WHD_BUS_STATS_INCREMENT_VARIABLE(whd_driver->bus_priv, sdio_intrs);
197
198 /* call thread notify to wake up WHD thread */
199 whd_thread_notify_irq(whd_driver);
200 }
201 }
202
whd_bus_sdio_irq_register(whd_driver_t whd_driver)203 whd_result_t whd_bus_sdio_irq_register(whd_driver_t whd_driver)
204 {
205 /* Nothing to do here, all handles by whd_bus_sdio_irq_enable function */
206 return WHD_SUCCESS;
207 }
208
whd_bus_sdio_irq_enable(whd_driver_t whd_driver,whd_bool_t enable)209 whd_result_t whd_bus_sdio_irq_enable(whd_driver_t whd_driver, whd_bool_t enable)
210 {
211 int ret;
212 struct sd_card *sd = whd_driver->bus_priv->sdio_obj;
213
214 /* Enable/disable SDIO Card interrupts */
215 if (enable) {
216 ret = sdhc_enable_interrupt(sd->sdhc, whd_bus_sdio_irq_handler, SDHC_INT_SDIO,
217 whd_driver);
218 } else {
219 ret = sdhc_disable_interrupt(sd->sdhc, SDHC_INT_SDIO);
220 }
221 return ret;
222 }
223
224 /*
225 * Implement OOB functionality
226 */
227
whd_bus_sdio_oob_irq_handler(const struct device * port,struct gpio_callback * cb,gpio_port_pins_t pins)228 void whd_bus_sdio_oob_irq_handler(const struct device *port, struct gpio_callback *cb,
229 gpio_port_pins_t pins)
230 {
231 #if DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios)
232 struct airoc_wifi_data *data = CONTAINER_OF(cb, struct airoc_wifi_data, host_oob_pin_cb);
233
234 /* Get OOB pin info */
235 const whd_oob_config_t *oob_config = &data->whd_drv->bus_priv->sdio_config.oob_config;
236 const struct gpio_dt_spec *host_oob_pin = oob_config->host_oob_pin;
237
238 /* Check OOB state is correct */
239 int expected_event = (oob_config->is_falling_edge == WHD_TRUE) ? 0 : 1;
240
241 if (!(pins & BIT(host_oob_pin->pin)) || (gpio_pin_get_dt(host_oob_pin) != expected_event)) {
242 WPRINT_WHD_ERROR(("Unexpected interrupt event %d\n", expected_event));
243 WHD_BUS_STATS_INCREMENT_VARIABLE(data->whd_drv->bus_priv, error_intrs);
244 return;
245 }
246
247 WHD_BUS_STATS_INCREMENT_VARIABLE(data->whd_drv->bus_priv, oob_intrs);
248
249 /* Call thread notify to wake up WHD thread */
250 whd_thread_notify_irq(data->whd_drv);
251
252 #endif /* DT_INST_NODE_HAS_PROP(0, wifi-host-wake-gpios) */
253 }
254
whd_bus_sdio_register_oob_intr(whd_driver_t whd_driver)255 whd_result_t whd_bus_sdio_register_oob_intr(whd_driver_t whd_driver)
256 {
257 #if DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios)
258 int ret;
259 const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0));
260 struct airoc_wifi_data *data = dev->data;
261
262 /* Get OOB pin info */
263 const whd_oob_config_t *oob_config = &whd_driver->bus_priv->sdio_config.oob_config;
264 const struct gpio_dt_spec *host_oob_pin = oob_config->host_oob_pin;
265
266 /* Check if OOB pin is ready */
267 if (!gpio_is_ready_dt(host_oob_pin)) {
268 WPRINT_WHD_ERROR(("%s: Failed at gpio_is_ready_dt for host_oob_pin\n", __func__));
269 return WHD_HAL_ERROR;
270 }
271
272 /* Configure OOB pin as output */
273 ret = gpio_pin_configure_dt(host_oob_pin, GPIO_INPUT);
274 if (ret != 0) {
275 WPRINT_WHD_ERROR((
276 " %s: Failed at gpio_pin_configure_dt for host_oob_pin, result code = %d\n",
277 __func__, ret));
278 return WHD_HAL_ERROR;
279 }
280
281 /* Initialize/add OOB pin callback */
282 gpio_init_callback(&data->host_oob_pin_cb, whd_bus_sdio_oob_irq_handler,
283 BIT(host_oob_pin->pin));
284
285 ret = gpio_add_callback_dt(host_oob_pin, &data->host_oob_pin_cb);
286 if (ret != 0) {
287 WPRINT_WHD_ERROR(
288 ("%s: Failed at gpio_add_callback_dt for host_oob_pin, result code = %d\n",
289 __func__, ret));
290 return WHD_HAL_ERROR;
291 }
292 #endif /* DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios) */
293
294 return WHD_SUCCESS;
295 }
296
whd_bus_sdio_unregister_oob_intr(whd_driver_t whd_driver)297 whd_result_t whd_bus_sdio_unregister_oob_intr(whd_driver_t whd_driver)
298 {
299 #if DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios)
300 int ret;
301 const whd_oob_config_t *oob_config = &whd_driver->bus_priv->sdio_config.oob_config;
302
303 /* Disable OOB pin interrupts */
304 ret = gpio_pin_interrupt_configure_dt(oob_config->host_oob_pin, GPIO_INT_DISABLE);
305 if (ret != 0) {
306 WPRINT_WHD_ERROR(("%s: Failed at gpio_pin_interrupt_configure_dt for host_oob_pin, "
307 "result code = %d\n",
308 __func__, ret));
309 return WHD_HAL_ERROR;
310 }
311 #endif /* DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios) */
312 return WHD_SUCCESS;
313 }
314
whd_bus_sdio_enable_oob_intr(whd_driver_t whd_driver,whd_bool_t enable)315 whd_result_t whd_bus_sdio_enable_oob_intr(whd_driver_t whd_driver, whd_bool_t enable)
316 {
317 #if DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios)
318 int ret;
319 const whd_oob_config_t *oob_config = &whd_driver->bus_priv->sdio_config.oob_config;
320 uint32_t trig_conf =
321 (oob_config->is_falling_edge == WHD_TRUE) ? GPIO_INT_TRIG_LOW : GPIO_INT_TRIG_HIGH;
322
323 /* Enable OOB pin interrupts */
324 ret = gpio_pin_interrupt_configure_dt(oob_config->host_oob_pin,
325 GPIO_INT_ENABLE | GPIO_INT_EDGE | trig_conf);
326 if (ret != 0) {
327 WPRINT_WHD_ERROR(("%s: Failed at gpio_pin_interrupt_configure_dt for host_oob_pin, "
328 "result code = %d\n",
329 __func__, ret));
330 return WHD_HAL_ERROR;
331 }
332 #endif /* DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios) */
333 return WHD_SUCCESS;
334 }
335
336 #ifdef __cplusplus
337 } /* extern "C" */
338 #endif
339