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