diff --git a/boards/raspberrypi/rpi_pico/doc/index.rst b/boards/raspberrypi/rpi_pico/doc/index.rst index b2af4693b01abe..631d12097a6dad 100644 --- a/boards/raspberrypi/rpi_pico/doc/index.rst +++ b/boards/raspberrypi/rpi_pico/doc/index.rst @@ -156,6 +156,24 @@ devices as well as both PIO devices). Programming and Debugging ************************* +System requirements +=================== + +Prerequisites for the Pico W +---------------------------- + +Building for the RaspberryPi Pico W requires the AIROC binary blobs +provided by `Infineon `_. Run the command +below to retrieve those files. + +.. code-block:: console + + west blobs fetch hal_infineon + +.. note:: + + It is recommended running the command above after :file:`west update`. + Flashing ======== diff --git a/boards/raspberrypi/rpi_pico/rpi_pico_rp2040_w.dts b/boards/raspberrypi/rpi_pico/rpi_pico_rp2040_w.dts index ae01f15d69784c..90a339f65b8711 100644 --- a/boards/raspberrypi/rpi_pico/rpi_pico_rp2040_w.dts +++ b/boards/raspberrypi/rpi_pico/rpi_pico_rp2040_w.dts @@ -1,5 +1,6 @@ /* * Copyright (c) 2023 Dave Rensberger - Beechwoods Software + * Copyright (c) 2024 Steve Boylan * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,3 +8,40 @@ /dts-v1/; #include "rpi_pico-common.dtsi" + +&pinctrl { + pio0_spi0_default: pio0_spi0_default { + /* gpio 25 is used for chip select, not assigned to the PIO */ + group1 { + pinmux = , ; + }; + }; +}; + +&pio0 { + status = "okay"; + + pio0_spi0: pio0_spi0 { + pinctrl-0 = <&pio0_spi0_default>; + pinctrl-names = "default"; + + compatible = "raspberrypi,pico-spi-pio"; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + clocks = < &clocks RPI_PICO_CLKID_CLK_SYS >; + cs-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>; + clk-gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>; + sio-gpios = <&gpio0 24 GPIO_ACTIVE_HIGH>; + airoc-wifi@0 { + status = "okay"; + reg = < 0 >; + compatible = "infineon,airoc-wifi"; + wifi-reg-on-gpios = < &gpio0 23 GPIO_ACTIVE_HIGH >; + bus-select-gpios = < &gpio0 24 GPIO_ACTIVE_HIGH >; + wifi-host-wake-gpios = < &gpio0 24 GPIO_ACTIVE_HIGH >; + spi-max-frequency = < 10000000 >; + spi-half-duplex; + }; + }; +}; diff --git a/boards/raspberrypi/rpi_pico/rpi_pico_rp2040_w_defconfig b/boards/raspberrypi/rpi_pico/rpi_pico_rp2040_w_defconfig index df003531af9c87..c862a254642b49 100644 --- a/boards/raspberrypi/rpi_pico/rpi_pico_rp2040_w_defconfig +++ b/boards/raspberrypi/rpi_pico/rpi_pico_rp2040_w_defconfig @@ -9,3 +9,23 @@ CONFIG_BUILD_OUTPUT_HEX=y CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_RESET=y CONFIG_CLOCK_CONTROL=y + +# Increase memory areas for WiFi stack +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_HEAP_MEM_POOL_SIZE=16384 +CONFIG_SHELL_STACK_SIZE=2560 + +# Default networking configuration +CONFIG_WIFI=y +CONFIG_NETWORKING=y +CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_IPV6=n +CONFIG_NET_IPV4=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_LOG=y +CONFIG_WIFI_AIROC=y +CONFIG_AIROC_WIFI_COUNTRY="US" +CONFIG_AIROC_WIFI_BUS_SDIO=n +CONFIG_AIROC_WIFI_BUS_SPI=y +CONFIG_AIROC_WIFI_BUS_SPI_DATA_IRQ_MUX=y +CONFIG_CYW43439=y diff --git a/drivers/wifi/infineon/CMakeLists.txt b/drivers/wifi/infineon/CMakeLists.txt index 20f69f181117fc..a1c8e501ed4730 100644 --- a/drivers/wifi/infineon/CMakeLists.txt +++ b/drivers/wifi/infineon/CMakeLists.txt @@ -5,7 +5,9 @@ zephyr_include_directories(./) zephyr_library_sources_ifdef(CONFIG_WIFI_AIROC airoc_wifi.c) -zephyr_library_sources_ifdef(CONFIG_WIFI_AIROC airoc_whd_hal.c) +zephyr_library_sources_ifdef(CONFIG_WIFI_AIROC airoc_whd_hal_common.c) +zephyr_library_sources_ifdef(CONFIG_AIROC_WIFI_BUS_SDIO airoc_whd_hal_sdio.c) +zephyr_library_sources_ifdef(CONFIG_AIROC_WIFI_BUS_SPI airoc_whd_hal_spi.c) zephyr_compile_definitions(CYBSP_WIFI_CAPABLE) zephyr_compile_definitions(CY_RTOS_AWARE) diff --git a/drivers/wifi/infineon/Kconfig.airoc b/drivers/wifi/infineon/Kconfig.airoc index 319253378d3181..b87e1da60dee10 100644 --- a/drivers/wifi/infineon/Kconfig.airoc +++ b/drivers/wifi/infineon/Kconfig.airoc @@ -6,9 +6,8 @@ menuconfig WIFI_AIROC bool "Infineon AIROC SoC Wi-Fi support" select THREAD_CUSTOM_DATA select WIFI_OFFLOAD + select NET_L2_ETHERNET select NET_L2_WIFI_MGMT - select SDIO_STACK - select SDHC select WIFI_USE_NATIVE_NETWORKING select USE_INFINEON_ABSTRACTION_RTOS depends on DT_HAS_INFINEON_AIROC_WIFI_ENABLED @@ -17,6 +16,24 @@ menuconfig WIFI_AIROC if WIFI_AIROC +choice + prompt "Bus Support" + default AIROC_WIFI_BUS_SDIO + +config AIROC_WIFI_BUS_SDIO + bool "SDIO Interface" + select SDIO_STACK + select SDHC + +config AIROC_WIFI_BUS_SPI + bool "SPI Interface" + select SPI + +endchoice + +config AIROC_WIFI_COUNTRY + string "Country code, see whd_country_code_t in whd_types.h" + config AIROC_WIFI_EVENT_TASK_STACK_SIZE int "Event Task Stack Size" default 4096 @@ -37,6 +54,17 @@ config AIROC_WIFI_CUSTOM user must to provide path to FW, CLM and NVRAM for custom or vendor CYW43xx modules. +if AIROC_WIFI_BUS_SPI + +config AIROC_WIFI_BUS_SPI_DATA_IRQ_MUX + bool "Data and IRQ GPIO is shared" + help + Enable logic when the SPI data GPIO is shared + with the host wake interrupt, which requires + switching GPIO modes for that pin. + +endif # AIROC_WIFI_BUS_SPI + choice AIROC_PART prompt "Select AIROC part" depends on !AIROC_WIFI_CUSTOM diff --git a/drivers/wifi/infineon/airoc_whd_hal_common.c b/drivers/wifi/infineon/airoc_whd_hal_common.c new file mode 100644 index 00000000000000..fd1a33ce7c6b52 --- /dev/null +++ b/drivers/wifi/infineon/airoc_whd_hal_common.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or + * an affiliate of Cypress Semiconductor Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "airoc_whd_hal_common.h" +#include "airoc_wifi.h" + +#include +#include + +#define DT_DRV_COMPAT infineon_airoc_wifi + +LOG_MODULE_DECLARE(infineon_airoc_wifi, CONFIG_WIFI_LOG_LEVEL); + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************** + * Function + ******************************************************/ + +int airoc_wifi_power_on(const struct device *dev) +{ +#if DT_INST_NODE_HAS_PROP(0, wifi_reg_on_gpios) + int ret; + const struct airoc_wifi_config *config = dev->config; + + /* Check WIFI REG_ON gpio instance */ + if (!device_is_ready(config->wifi_reg_on_gpio.port)) { + LOG_ERR("Error: failed to configure wifi_reg_on %s pin %d", + config->wifi_reg_on_gpio.port->name, config->wifi_reg_on_gpio.pin); + return -EIO; + } + + /* Configure wifi_reg_on as output */ + ret = gpio_pin_configure_dt(&config->wifi_reg_on_gpio, GPIO_OUTPUT); + if (ret) { + LOG_ERR("Error %d: failed to configure wifi_reg_on %s pin %d", ret, + config->wifi_reg_on_gpio.port->name, config->wifi_reg_on_gpio.pin); + return ret; + } + ret = gpio_pin_set_dt(&config->wifi_reg_on_gpio, 0); + if (ret) { + return ret; + } + + /* Allow CBUCK regulator to discharge */ + k_msleep(WLAN_CBUCK_DISCHARGE_MS); + + /* WIFI power on */ + ret = gpio_pin_set_dt(&config->wifi_reg_on_gpio, 1); + if (ret) { + return ret; + } + k_msleep(WLAN_POWER_UP_DELAY_MS); +#endif /* DT_INST_NODE_HAS_PROP(0, reg_on_gpios) */ + + return 0; +} + +/* + * Implement WHD memory wrappers + */ + +void *whd_mem_malloc(size_t size) +{ + return k_malloc(size); +} + +void *whd_mem_calloc(size_t nitems, size_t size) +{ + return k_calloc(nitems, size); +} + +void whd_mem_free(void *ptr) +{ + k_free(ptr); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/drivers/wifi/infineon/airoc_whd_hal_common.h b/drivers/wifi/infineon/airoc_whd_hal_common.h new file mode 100644 index 00000000000000..8b7100d1012c51 --- /dev/null +++ b/drivers/wifi/infineon/airoc_whd_hal_common.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or + * an affiliate of Cypress Semiconductor Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/** Defines the amount of stack memory available for the wifi thread. */ +#if !defined(CY_WIFI_THREAD_STACK_SIZE) +#define CY_WIFI_THREAD_STACK_SIZE (5120) +#endif + +/** Defines the priority of the thread that services wifi packets. Legal values are defined by the + * RTOS being used. + */ +#if !defined(CY_WIFI_THREAD_PRIORITY) +#define CY_WIFI_THREAD_PRIORITY (CY_RTOS_PRIORITY_HIGH) +#endif + +/** Map Zephyr country codes (2 octets) to whd_country_code_t. */ +#if defined(CONFIG_AIROC_WIFI_COUNTRY) +#if defined(CONFIG_BIG_ENDIAN) +#define AIROC_MAP_COUNTRY_CODE(code) ((code[1]) + (code[0] << 8)) +#else +#define AIROC_MAP_COUNTRY_CODE(code) ((code[0]) + (code[1] << 8)) +#endif +#endif + +/** Defines the country this will operate in for wifi initialization parameters. See the + * wifi-host-driver's whd_country_code_t for legal options. + */ +#if !defined(CY_WIFI_COUNTRY) +#define CY_WIFI_COUNTRY (WHD_COUNTRY_AUSTRALIA) +#endif + +/** Defines the priority of the interrupt that handles out-of-band notifications from the wifi + * chip. Legal values are defined by the MCU running this code. + */ +#if !defined(CY_WIFI_OOB_INTR_PRIORITY) +#define CY_WIFI_OOB_INTR_PRIORITY (2) +#endif + +/** Defines whether to use the out-of-band pin to allow the WIFI chip to wake up the MCU. */ +#if defined(CY_WIFI_HOST_WAKE_SW_FORCE) +#define CY_USE_OOB_INTR (CY_WIFI_HOST_WAKE_SW_FORCE) +#else +#define CY_USE_OOB_INTR (1u) +#endif /* defined(CY_WIFI_HOST_WAKE_SW_FORCE) */ + +#define CY_WIFI_HOST_WAKE_IRQ_EVENT GPIO_INT_TRIG_LOW +#define DEFAULT_OOB_PIN (0) +#define WLAN_POWER_UP_DELAY_MS (250) +#define WLAN_CBUCK_DISCHARGE_MS (10) + +extern whd_resource_source_t resource_ops; + +int airoc_wifi_power_on(const struct device *dev); diff --git a/drivers/wifi/infineon/airoc_whd_hal.c b/drivers/wifi/infineon/airoc_whd_hal_sdio.c similarity index 76% rename from drivers/wifi/infineon/airoc_whd_hal.c rename to drivers/wifi/infineon/airoc_whd_hal_sdio.c index b3a68102851f10..eeb15924ec26dc 100644 --- a/drivers/wifi/infineon/airoc_whd_hal.c +++ b/drivers/wifi/infineon/airoc_whd_hal_sdio.c @@ -5,118 +5,38 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include +#include "airoc_wifi.h" +#include "airoc_whd_hal_common.h" #include #include #include #include -#include #define DT_DRV_COMPAT infineon_airoc_wifi -LOG_MODULE_REGISTER(infineon_airoc, CONFIG_WIFI_LOG_LEVEL); +LOG_MODULE_DECLARE(infineon_airoc_wifi, CONFIG_WIFI_LOG_LEVEL); #ifdef __cplusplus extern "C" { #endif -/** Defines the amount of stack memory available for the wifi thread. */ -#if !defined(CY_WIFI_THREAD_STACK_SIZE) -#define CY_WIFI_THREAD_STACK_SIZE (5120) -#endif - -/** Defines the priority of the thread that services wifi packets. Legal values are defined by the - * RTOS being used. - */ -#if !defined(CY_WIFI_THREAD_PRIORITY) -#define CY_WIFI_THREAD_PRIORITY (CY_RTOS_PRIORITY_HIGH) -#endif - -/** Defines the country this will operate in for wifi initialization parameters. See the - * wifi-host-driver's whd_country_code_t for legal options. - */ -#if !defined(CY_WIFI_COUNTRY) -#define CY_WIFI_COUNTRY (WHD_COUNTRY_AUSTRALIA) -#endif - -/** Defines the priority of the interrupt that handles out-of-band notifications from the wifi - * chip. Legal values are defined by the MCU running this code. - */ -#if !defined(CY_WIFI_OOB_INTR_PRIORITY) -#define CY_WIFI_OOB_INTR_PRIORITY (2) -#endif - -/** Defines whether to use the out-of-band pin to allow the WIFI chip to wake up the MCU. */ -#if defined(CY_WIFI_HOST_WAKE_SW_FORCE) -#define CY_USE_OOB_INTR (CY_WIFI_HOST_WAKE_SW_FORCE) -#else -#define CY_USE_OOB_INTR (1u) -#endif /* defined(CY_WIFI_HOST_WAKE_SW_FORCE) */ - -#define CY_WIFI_HOST_WAKE_IRQ_EVENT GPIO_INT_TRIG_LOW -#define DEFAULT_OOB_PIN (0) -#define WLAN_POWER_UP_DELAY_MS (250) -#define WLAN_CBUCK_DISCHARGE_MS (10) - -extern whd_resource_source_t resource_ops; - struct whd_bus_priv { whd_sdio_config_t sdio_config; whd_bus_stats_t whd_bus_stats; whd_sdio_t sdio_obj; }; -static whd_init_config_t init_config_default = { - .thread_stack_size = CY_WIFI_THREAD_STACK_SIZE, - .thread_stack_start = NULL, - .thread_priority = (uint32_t)CY_WIFI_THREAD_PRIORITY, - .country = CY_WIFI_COUNTRY -}; +static whd_init_config_t init_config_default = {.thread_stack_size = CY_WIFI_THREAD_STACK_SIZE, + .thread_stack_start = NULL, + .thread_priority = + (uint32_t)CY_WIFI_THREAD_PRIORITY, + .country = CY_WIFI_COUNTRY}; /****************************************************** * Function ******************************************************/ -int airoc_wifi_power_on(const struct device *dev) -{ -#if DT_INST_NODE_HAS_PROP(0, wifi_reg_on_gpios) - int ret; - const struct airoc_wifi_config *config = dev->config; - - /* Check WIFI REG_ON gpio instance */ - if (!device_is_ready(config->wifi_reg_on_gpio.port)) { - LOG_ERR("Error: failed to configure wifi_reg_on %s pin %d", - config->wifi_reg_on_gpio.port->name, config->wifi_reg_on_gpio.pin); - return -EIO; - } - - /* Configure wifi_reg_on as output */ - ret = gpio_pin_configure_dt(&config->wifi_reg_on_gpio, GPIO_OUTPUT); - if (ret) { - LOG_ERR("Error %d: failed to configure wifi_reg_on %s pin %d", ret, - config->wifi_reg_on_gpio.port->name, config->wifi_reg_on_gpio.pin); - return ret; - } - ret = gpio_pin_set_dt(&config->wifi_reg_on_gpio, 0); - if (ret) { - return ret; - } - - /* Allow CBUCK regulator to discharge */ - (void)k_msleep(WLAN_CBUCK_DISCHARGE_MS); - - /* WIFI power on */ - ret = gpio_pin_set_dt(&config->wifi_reg_on_gpio, 1); - if (ret) { - return ret; - } - (void)k_msleep(WLAN_POWER_UP_DELAY_MS); -#endif /* DT_INST_NODE_HAS_PROP(0, reg_on_gpios) */ - - return 0; -} - int airoc_wifi_init_primary(const struct device *dev, whd_interface_t *interface, whd_netif_funcs_t *netif_funcs, whd_buffer_funcs_t *buffer_if) { @@ -144,12 +64,12 @@ int airoc_wifi_init_primary(const struct device *dev, whd_interface_t *interface return -ENODEV; } - if (!device_is_ready(config->sdhc_dev)) { + if (!device_is_ready(config->bus_dev.bus_sdio)) { LOG_ERR("SDHC device is not ready"); return -ENODEV; } - ret = sd_init(config->sdhc_dev, &data->card); + ret = sd_init(config->bus_dev.bus_sdio, &data->card); if (ret) { return ret; } @@ -415,25 +335,6 @@ whd_result_t whd_bus_sdio_enable_oob_intr(whd_driver_t whd_driver, whd_bool_t en return WHD_SUCCESS; } -/* - * Implement WHD memory wrappers - */ - -void *whd_mem_malloc(size_t size) -{ - return k_malloc(size); -} - -void *whd_mem_calloc(size_t nitems, size_t size) -{ - return k_calloc(nitems, size); -} - -void whd_mem_free(void *ptr) -{ - k_free(ptr); -} - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/drivers/wifi/infineon/airoc_whd_hal_spi.c b/drivers/wifi/infineon/airoc_whd_hal_spi.c new file mode 100644 index 00000000000000..f68a5e728c61b8 --- /dev/null +++ b/drivers/wifi/infineon/airoc_whd_hal_spi.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2024 Stephen Boylan (stephen.boylan@beechwoods.com) + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "airoc_whd_hal_common.h" + +#include +#include +#include +#include +#include + +#define DT_DRV_COMPAT infineon_airoc_wifi +LOG_MODULE_DECLARE(infineon_airoc_wifi, CONFIG_WIFI_LOG_LEVEL); + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(CONFIG_AIROC_WIFI_BUS_SPI_DATA_IRQ_MUX) && defined(CONFIG_BOARD_RPI_PICO) +#define PICO_BUS_DEVICE DT_PARENT(DT_PARENT(DT_DRV_INST(0))) + +#if PICO_BUS_DEVICE == DT_NODELABEL(pio0) +#define PICO_PIO_HOST GPIO_FUNC_PIO0 +#elif PICO_BUS_DEVICE == DT_NODELABEL(pio1) +#define PICO_PIO_HOST GPIO_FUNC_PIO1 +#elif PICO_BUS_DEVICE == DT_NODELABEL(pio2) +#define PICO_PIO_HOST GPIO_FUNC_PIO2 +#endif + +#endif /* defined(CONFIG_AIROC_WIFI_BUS_SPI_DATA_IRQ_MUX) && defined(CONFIG_BOARD_RPI_PICO) */ + +struct whd_bus_priv { + whd_spi_config_t spi_config; + whd_spi_t spi_obj; +}; + +static whd_init_config_t init_config_default = {.thread_stack_size = CY_WIFI_THREAD_STACK_SIZE, + .thread_stack_start = NULL, + .thread_priority = + (uint32_t)CY_WIFI_THREAD_PRIORITY, + .country = CY_WIFI_COUNTRY}; + +/****************************************************** + * Function + ******************************************************/ + +int airoc_wifi_init_primary(const struct device *dev, whd_interface_t *interface, + whd_netif_funcs_t *netif_funcs, + whd_buffer_funcs_t *buffer_if) +{ + struct airoc_wifi_data *data = dev->data; + const struct airoc_wifi_config *config = dev->config; + + whd_spi_config_t whd_spi_config = { + .is_spi_normal_mode = WHD_FALSE, + }; + +#if DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios) + whd_oob_config_t oob_config = { + .host_oob_pin = (void *)&config->wifi_host_wake_gpio, + .dev_gpio_sel = DEFAULT_OOB_PIN, + .is_falling_edge = + (CY_WIFI_HOST_WAKE_IRQ_EVENT == GPIO_INT_TRIG_LOW) ? WHD_TRUE : WHD_FALSE, + .intr_priority = CY_WIFI_OOB_INTR_PRIORITY}; + whd_spi_config.oob_config = oob_config; +#endif +/* TODO: Should be CONFIG_BOARD_RPI_PICO_RP2040_W */ +#if defined(CONFIG_AIROC_WIFI_BUS_SPI_DATA_IRQ_MUX) + data->prev_irq_state = 0; +#endif + + /* Pull bus select line low before enabling WiFi chip */ + gpio_pin_configure_dt(&config->bus_select_gpio, GPIO_OUTPUT_INACTIVE); + gpio_pin_set_dt(&config->bus_select_gpio, 0); + + if (airoc_wifi_power_on(dev)) { + LOG_ERR("airoc_wifi_power_on returns fail"); + return -ENODEV; + } + + if (!spi_is_ready_dt(&config->bus_dev.bus_spi)) { + LOG_ERR("SPI device is not ready"); + return -ENODEV; + } + + /* Init wifi host driver (whd) */ + cy_rslt_t whd_ret = whd_init(&data->whd_drv, &init_config_default, + &resource_ops, buffer_if, netif_funcs); + if (whd_ret == CY_RSLT_SUCCESS) { + whd_ret = whd_bus_spi_attach(data->whd_drv, &whd_spi_config, + (whd_spi_t)&config->bus_dev.bus_spi); + + if (whd_ret == CY_RSLT_SUCCESS) { + whd_ret = whd_wifi_on(data->whd_drv, interface); + } + + if (whd_ret != CY_RSLT_SUCCESS) { + whd_deinit(*interface); + return -ENODEV; + } + } + return 0; +} + +/* + * Implement SPI Transfer wrapper + */ + +whd_result_t whd_bus_spi_transfer(whd_driver_t whd_driver, const uint8_t *tx, size_t tx_length, + uint8_t *rx, size_t rx_length, uint8_t write_fill) +{ + const struct spi_dt_spec *spi_obj = whd_driver->bus_priv->spi_obj; + int ret; + whd_result_t whd_ret = WHD_SUCCESS; + +#if defined(CONFIG_AIROC_WIFI_BUS_SPI_DATA_IRQ_MUX) + ret = whd_bus_spi_irq_enable(whd_driver, false); + if (ret) { + LOG_DBG("whd_bus_spi_irq_enable FAIL %d\n", ret); + whd_ret = WHD_WLAN_SDIO_ERROR; + } +#endif + + /* In some cases whd_bus_spi_protocol.c places the command at */ + /* the start of the rx buffer and passes NULL as the tx address, */ + /* reusing the same buffer (and overwriting the tx data). */ + if (tx == NULL && tx_length > 0 && rx_length >= tx_length) { + tx = rx; + } + + const struct spi_buf tx_buf = {.buf = (uint8_t *)tx, .len = tx_length}; + const struct spi_buf_set tx_set = {.buffers = &tx_buf, .count = 1}; + struct spi_buf rx_buf[2]; + struct spi_buf_set rx_set = {.buffers = rx_buf, .count = 2}; + + if (rx != NULL) { + rx += tx_length; + } + + if (rx_length >= tx_length) { + rx_length -= tx_length; + } else { + rx_length = 0; + } + + if (spi_obj->config.operation & SPI_HALF_DUPLEX) { + rx_buf[0].buf = rx; + rx_buf[0].len = rx_length; + rx_set.count = 1; + } else { + /* Talking to a half-duplex device via a full-duplex SPI driver, */ + /* it's necessary to ignore the data returned during send. The */ + /* SPI driver should not copy out the data if the buffer is NULL. */ + rx_buf[0].buf = NULL; + rx_buf[0].len = tx_length; + rx_buf[1].buf = rx; + rx_buf[1].len = rx_length; + } + + ret = spi_transceive_dt(spi_obj, &tx_set, &rx_set); + if (ret) { + LOG_DBG("spi_transceive FAIL %d\n", ret); + whd_ret = WHD_WLAN_SDIO_ERROR; + } + +#if defined(CONFIG_AIROC_WIFI_BUS_SPI_DATA_IRQ_MUX) + ret = whd_bus_spi_irq_enable(whd_driver, true); + if (ret) { + LOG_DBG("whd_bus_spi_irq_enable FAIL %d\n", ret); + whd_ret = WHD_WLAN_SDIO_ERROR; + } +#endif + + return whd_ret; +} + +/* + * Implement OOB functionality + */ + +void whd_bus_spi_oob_irq_handler(const struct device *port, struct gpio_callback *cb, + gpio_port_pins_t pins) +{ +#if DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios) + struct airoc_wifi_data *data = CONTAINER_OF(cb, struct airoc_wifi_data, host_oob_pin_cb); + + /* Get OOB pin info */ + const whd_oob_config_t *oob_config = &data->whd_drv->bus_priv->spi_config.oob_config; + const struct gpio_dt_spec *host_oob_pin = oob_config->host_oob_pin; + + /* Check OOB state is correct */ + int expected_event = (oob_config->is_falling_edge == WHD_TRUE) ? 0 : 1; + + if (!(pins & BIT(host_oob_pin->pin)) || (gpio_pin_get_dt(host_oob_pin) != expected_event)) { + WPRINT_WHD_ERROR(("Unexpected interrupt event %d\n", expected_event)); + return; + } + + /* Call thread notify to wake up WHD thread */ + whd_thread_notify_irq(data->whd_drv); + +#endif /* DT_INST_NODE_HAS_PROP(0, wifi-host-wake-gpios) */ +} + +whd_result_t whd_bus_spi_irq_register(whd_driver_t whd_driver) +{ +#if DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios) + int ret; + const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); + struct airoc_wifi_data *data = dev->data; + + /* Get OOB pin info */ + const whd_oob_config_t *oob_config = &whd_driver->bus_priv->spi_config.oob_config; + const struct gpio_dt_spec *host_oob_pin = oob_config->host_oob_pin; + + /* Check if OOB pin is ready */ + if (!gpio_is_ready_dt(host_oob_pin)) { + WPRINT_WHD_ERROR(("%s: Failed at gpio_is_ready_dt for host_oob_pin\n", __func__)); + return WHD_HAL_ERROR; + } + + /* Configure OOB pin as input */ + ret = gpio_pin_configure_dt(host_oob_pin, GPIO_INPUT); + if (ret != 0) { + WPRINT_WHD_ERROR(( + " %s: Failed at gpio_pin_configure_dt for host_oob_pin, result code = %d\n", + __func__, ret)); + return WHD_HAL_ERROR; + } + + /* Initialize/add OOB pin callback */ + gpio_init_callback(&data->host_oob_pin_cb, whd_bus_spi_oob_irq_handler, + BIT(host_oob_pin->pin)); + + ret = gpio_add_callback_dt(host_oob_pin, &data->host_oob_pin_cb); + if (ret != 0) { + WPRINT_WHD_ERROR( + ("%s: Failed at gpio_add_callback_dt for host_oob_pin, result code = %d\n", + __func__, ret)); + return WHD_HAL_ERROR; + } +#endif /* DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios) */ + + return WHD_SUCCESS; +} + +whd_result_t whd_bus_spi_irq_enable(whd_driver_t whd_driver, whd_bool_t enable) +{ +#if DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios) + int ret; + const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); + struct airoc_wifi_data *data = dev->data; + const whd_oob_config_t *oob_config = &whd_driver->bus_priv->spi_config.oob_config; + struct gpio_dt_spec *gpio = (struct gpio_dt_spec *)oob_config->host_oob_pin; + + if (enable) { + ret = gpio_pin_configure_dt(gpio, GPIO_INPUT); + __ASSERT(ret == 0, "gpio_pin_configure_dt failed, return code %d", ret); + ret = gpio_pin_interrupt_configure_dt( + gpio, (oob_config->is_falling_edge == WHD_TRUE) ? GPIO_INT_EDGE_FALLING + : GPIO_INT_EDGE_RISING); + __ASSERT(ret == 0, "gpio_pin_interrupt_configure_dt failed, return code %d", ret); + +#if defined(CONFIG_AIROC_WIFI_BUS_SPI_DATA_IRQ_MUX) + int state = gpio_pin_get_dt(gpio); + int expected_event = (oob_config->is_falling_edge == WHD_TRUE) ? 0 : 1; + + /* Notify only if interrupt wasn't assert before. This code assumes that if */ + /* the interrupt was previously asserted, then the thread has already been */ + /* notified. */ + if (state == expected_event && state != data->prev_irq_state) { + whd_thread_notify_irq(whd_driver); + } +#endif + } else { + data->prev_irq_state = gpio_pin_get_dt(gpio); + ret = gpio_pin_interrupt_configure_dt(gpio, GPIO_INT_DISABLE); + __ASSERT(ret == 0, "gpio_pin_interrupt_configure_dt failed, return code %d", ret); +/* TODO: Should be CONFIG_BOARD_RPI_PICO_RP2040_W */ +#if defined(CONFIG_AIROC_WIFI_BUS_SPI_DATA_IRQ_MUX) && defined(CONFIG_BOARD_RPI_PICO) + gpio_set_function(gpio->pin, PICO_PIO_HOST); +#endif + } + +#endif /* DT_INST_NODE_HAS_PROP(0, wifi_host_wake_gpios) */ + + return WHD_SUCCESS; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/drivers/wifi/infineon/airoc_wifi.c b/drivers/wifi/infineon/airoc_wifi.c index 330227fd359c2b..c2a60f9d353d57 100644 --- a/drivers/wifi/infineon/airoc_wifi.c +++ b/drivers/wifi/infineon/airoc_wifi.c @@ -14,6 +14,7 @@ #include #include #include +#include LOG_MODULE_REGISTER(infineon_airoc_wifi, CONFIG_WIFI_LOG_LEVEL); @@ -77,7 +78,12 @@ static void airoc_event_task(void); static struct airoc_wifi_data airoc_wifi_data = {0}; static struct airoc_wifi_config airoc_wifi_config = { - .sdhc_dev = DEVICE_DT_GET(DT_INST_PARENT(0)), +#if defined(CONFIG_AIROC_WIFI_BUS_SDIO) + .bus_dev.bus_sdio = DEVICE_DT_GET(DT_INST_PARENT(0)), +#elif defined(CONFIG_AIROC_WIFI_BUS_SPI) + .bus_dev.bus_spi = SPI_DT_SPEC_GET(DT_DRV_INST(0), AIROC_WIFI_SPI_OPERATION, 0), + .bus_select_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(0), bus_select_gpios, {0}), +#endif .wifi_reg_on_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(0), wifi_reg_on_gpios, {0}), .wifi_host_wake_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(0), wifi_host_wake_gpios, {0}), .wifi_dev_wake_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(0), wifi_dev_wake_gpios, {0}), diff --git a/drivers/wifi/infineon/airoc_wifi.h b/drivers/wifi/infineon/airoc_wifi.h index 1bf688e5d72c1e..9b2f4982e3016e 100644 --- a/drivers/wifi/infineon/airoc_wifi.h +++ b/drivers/wifi/infineon/airoc_wifi.h @@ -6,16 +6,36 @@ */ #include -#include -#include #include #include + +#ifdef CONFIG_AIROC_WIFI_BUS_SDIO +#include +#include +#endif +#ifdef CONFIG_AIROC_WIFI_BUS_SPI +#include +#include +#endif + #include +#if defined(CONFIG_AIROC_WIFI_BUS_SPI) +#define AIROC_WIFI_SPI_OPERATION (SPI_WORD_SET(DT_PROP_OR(DT_DRV_INST(0), spi_word_size, 8)) \ + | (DT_PROP(DT_DRV_INST(0), spi_half_duplex) \ + ? SPI_HALF_DUPLEX : SPI_FULL_DUPLEX) \ + | SPI_TRANSFER_MSB) +#endif + struct airoc_wifi_data { +#if defined(CONFIG_AIROC_WIFI_BUS_SDIO) struct sd_card card; struct sdio_func sdio_func1; struct sdio_func sdio_func2; +#endif +#if defined(CONFIG_AIROC_WIFI_BUS_SPI_DATA_IRQ_MUX) + uint8_t prev_irq_state; +#endif struct net_if *iface; bool second_interface_init; bool is_ap_up; @@ -34,11 +54,22 @@ struct airoc_wifi_data { uint8_t frame_buf[NET_ETH_MAX_FRAME_SIZE]; }; +union airoc_wifi_bus { +#if defined(CONFIG_AIROC_WIFI_BUS_SDIO) + const struct device *bus_sdio; +#elif defined(CONFIG_AIROC_WIFI_BUS_SPI) + const struct spi_dt_spec bus_spi; +#endif +}; + struct airoc_wifi_config { - const struct device *sdhc_dev; + const union airoc_wifi_bus bus_dev; struct gpio_dt_spec wifi_reg_on_gpio; struct gpio_dt_spec wifi_host_wake_gpio; struct gpio_dt_spec wifi_dev_wake_gpio; +#if defined(CONFIG_AIROC_WIFI_BUS_SPI) + struct gpio_dt_spec bus_select_gpio; +#endif }; /** diff --git a/drivers/wifi/infineon/cybsp.h b/drivers/wifi/infineon/cybsp.h index dfa23ffbae53f6..ef3712443d79d8 100644 --- a/drivers/wifi/infineon/cybsp.h +++ b/drivers/wifi/infineon/cybsp.h @@ -5,4 +5,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -/* This is enpty/stub file used in WHD */ +/* required by whd_* header files. */ + +/* See cybsp_types.h, which is usually required by this file, but not available */ +/* here unless we pull in board-specific headers from the upstream driver. */ +#define CYBSP_SDIO_INTERFACE (0) +#define CYBSP_SPI_INTERFACE (1) + +#ifdef CONFIG_AIROC_WIFI_BUS_SDIO +#define CYBSP_WIFI_INTERFACE_TYPE CYBSP_SDIO_INTERFACE +#elif CONFIG_AIROC_WIFI_BUS_SPI +#define CYBSP_WIFI_INTERFACE_TYPE CYBSP_SPI_INTERFACE +#endif diff --git a/dts/bindings/wifi/infineon,airoc-wifi-common.yaml b/dts/bindings/wifi/infineon,airoc-wifi-common.yaml new file mode 100644 index 00000000000000..94d0e6b8a3b020 --- /dev/null +++ b/dts/bindings/wifi/infineon,airoc-wifi-common.yaml @@ -0,0 +1,20 @@ +include: [base.yaml, pinctrl-device.yaml] + +properties: + wifi-reg-on-gpios: + description: | + Power-up/down gpio to control the internal regulators used + by the WiFi section of AIROC Wi-Fi device. + type: phandle-array + + wifi-host-wake-gpios: + description: | + Host wake-up gpio. Signal from the AIROC Wi-Fi device + to the host indicating that the device requires attention. + type: phandle-array + + wifi-dev-wake-gpios: + description: | + WiFi device wake-up gpio. Signal from the host to the + AIROC Wi-Fi device indicating that the host requires attention. + type: phandle-array diff --git a/dts/bindings/wifi/infineon,airoc-wifi-spi.yaml b/dts/bindings/wifi/infineon,airoc-wifi-spi.yaml new file mode 100644 index 00000000000000..bf5711b02fde4e --- /dev/null +++ b/dts/bindings/wifi/infineon,airoc-wifi-spi.yaml @@ -0,0 +1,33 @@ +description: | + AIROC Wi-Fi Connectivity over SPI. + +compatible: "infineon,airoc-wifi" + +include: [spi-device.yaml, "infineon,airoc-wifi-common.yaml"] + +properties: + wifi-host-wake-gpios: + required: true + + bus-select-gpios: + required: true + description: | + Select bus mode. This gpio must be held low before + wifi-reg-on-gpios goes high to select SPI bus mode. + type: phandle-array + + spi-half-duplex: + description: + Use half-duplex communication; if not present, full- + duplex operation is assumed. + type: boolean + + spi-word-size: + description: + SPI word size in bits. + type: int + enum: + - 8 + - 16 + - 32 + default: 8 diff --git a/modules/hal_infineon/wifi-host-driver/CMakeLists.txt b/modules/hal_infineon/wifi-host-driver/CMakeLists.txt index 010518f1642d31..7f6d9420862bb2 100644 --- a/modules/hal_infineon/wifi-host-driver/CMakeLists.txt +++ b/modules/hal_infineon/wifi-host-driver/CMakeLists.txt @@ -49,7 +49,10 @@ zephyr_library_sources(${hal_wifi_dir}/WiFi_Host_Driver/src/whd_wifi_p2p.c) # src/bus_protocols zephyr_library_sources(${hal_wifi_dir}/WiFi_Host_Driver/src/bus_protocols/whd_bus.c) zephyr_library_sources(${hal_wifi_dir}/WiFi_Host_Driver/src/bus_protocols/whd_bus_common.c) -zephyr_library_sources(${hal_wifi_dir}/WiFi_Host_Driver/src/bus_protocols/whd_bus_sdio_protocol.c) +zephyr_library_sources_ifdef(CONFIG_AIROC_WIFI_BUS_SDIO + ${hal_wifi_dir}/WiFi_Host_Driver/src/bus_protocols/whd_bus_sdio_protocol.c) +zephyr_library_sources_ifdef(CONFIG_AIROC_WIFI_BUS_SPI + ${hal_wifi_dir}/WiFi_Host_Driver/src/bus_protocols/whd_bus_spi_protocol.c) # resources/resource_imp zephyr_library_sources(${hal_wifi_dir}/WiFi_Host_Driver/resources/resource_imp/whd_resources.c)