mbed-os/targets/TARGET_Cypress/TARGET_PSOC6/common/cybsp_wifi.c

338 lines
10 KiB
C

/***************************************************************************//**
* \file cybsp_utils.c
*
* \brief
* Provides utility functions that are used by board support packages.
*
********************************************************************************
* \copyright
* Copyright 2018-2019 Cypress Semiconductor Corporation
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
#if defined(TARGET_WHD)
#include "cybsp_api_wifi.h"
#include "cy_network_buffer.h"
#include "cmsis_os2.h"
#include "whd_bus_types.h"
#include "cyhal.h"
#include "cyhal_implementation.h"
#if defined(__cplusplus)
extern "C" {
#endif
#define THREAD_STACK_SIZE 5120
#define THREAD_PRIORITY osPriorityHigh
#define COUNTRY WHD_COUNTRY_AUSTRALIA
#define DEFAULT_OOB_PIN 0
#define WLAN_INTR_PRIORITY 1
#define WLAN_POWER_UP_DELAY_MS 250
#define SDIO_ENUMERATION_TRIES 500
#define SDIO_RETRY_DELAY_MS 1
#define SDIO_BUS_LEVEL_MAX_RETRIES 5
/* Determine whether to use the SDIO oob interrupt.
* When CY_SDIO_BUS_NEEDS_OOB_INTR is defined,
* use its value to determine enable status; otherwise,
* default to enable. Use CY_WIFI_HOST_WAKE_SW_FORCE
* to force the enable status.
*/
#if !defined(CY_WIFI_HOST_WAKE_SW_FORCE)
#if !defined(CY_SDIO_BUS_NEEDS_OOB_INTR)
#define CY_SDIO_BUS_USE_OOB_INTR (1u)
#else
#define CY_SDIO_BUS_USE_OOB_INTR CY_SDIO_BUS_NEEDS_OOB_INTR
#endif /* !defined(CY_SDIO_BUS_NEEDS_OOB_INTR) */
#else
#define CY_SDIO_BUS_USE_OOB_INTR CY_WIFI_HOST_WAKE_SW_FORCE
#endif /* defined(CY_WIFI_HOST_WAKE_SW_FORCE) */
/* Define the host-wake configuration.
* Choose the configurator settings over the HAL.
*/
#if (CY_SDIO_BUS_USE_OOB_INTR != 0)
/* Prefer configurator declarations over HAL */
#if defined(CYCFG_WIFI_HOST_WAKE_GPIO)
#define CY_WIFI_HOST_WAKE_GPIO CYCFG_WIFI_HOST_WAKE_GPIO
#else
#define CY_WIFI_HOST_WAKE_GPIO CYBSP_WIFI_HOST_WAKE
#endif
#if defined(CYCFG_WIFI_HOST_WAKE_GPIO_DM)
#define CY_WIFI_HOST_WAKE_GPIO_DM CYCFG_WIFI_HOST_WAKE_GPIO_DM
#else
#define CY_WIFI_HOST_WAKE_GPIO_DM CYBSP_WIFI_HOST_WAKE_GPIO_DM
#endif
#if defined(CYCFG_WIFI_HOST_WAKE_IRQ_EVENT)
#define CY_WIFI_HOST_WAKE_IRQ_EVENT CYCFG_WIFI_HOST_WAKE_IRQ_EVENT
#else
#define CY_WIFI_HOST_WAKE_IRQ_EVENT CYBSP_WIFI_HOST_WAKE_IRQ_EVENT
#endif
#else
/* Dummy macro declarations to appease compiler */
#define CY_WIFI_HOST_WAKE_GPIO 0
#define CY_WIFI_HOST_WAKE_GPIO_DM 0
#define CY_WIFI_HOST_WAKE_IRQ_EVENT 0
#endif /* (CY_SDIO_BUS_USE_OOB_INTR != 0) */
whd_driver_t whd_drv;
bool sdio_initialized = false;
cyhal_sdio_t sdio_obj;
static void cy_enable_oob_intr(whd_driver_t whd_driver, const whd_variant_t intr, whd_bool_t whd_enable);
static void cy_get_intr_config(whd_driver_t whd_driver, const whd_variant_t intr, whd_intr_config_t *config);
static whd_buffer_funcs_t buffer_ops =
{
.whd_host_buffer_get = cy_host_buffer_get,
.whd_buffer_release = cy_buffer_release,
.whd_buffer_get_current_piece_data_pointer = cy_buffer_get_current_piece_data_pointer,
.whd_buffer_get_current_piece_size = cy_buffer_get_current_piece_size,
.whd_buffer_set_size = cy_buffer_set_size,
.whd_buffer_add_remove_at_front = cy_buffer_add_remove_at_front,
};
static whd_netif_funcs_t netif_ops =
{
.whd_network_process_ethernet_data = cy_network_process_ethernet_data,
};
static whd_sdio_funcs_t sdio_ops =
{
.whd_enable_intr = cy_enable_oob_intr,
.whd_get_intr_config = cy_get_intr_config,
};
//TODO: Need to use resource implemenatation from abstraction layer.
extern whd_resource_source_t resource_ops;
static void whd_wake_host_irq_handler(void *arg, cyhal_gpio_irq_event_t event)
{
//TODO: Swtich out from deep sleep or LP mode.
whd_bus_sdio_oob_intr_asserted(arg);
}
static cy_rslt_t sdio_try_send_cmd(const cyhal_sdio_t *sdio_object, cyhal_transfer_t direction, \
cyhal_sdio_command_t command, uint32_t argument, uint32_t* response)
{
uint8_t loop_count = 0;
cy_rslt_t result = CYBSP_RSLT_WIFI_SDIO_ENUM_TIMEOUT;
do
{
result = cyhal_sdio_send_cmd(sdio_object, direction, command, argument, response);
if(result != CY_RSLT_SUCCESS)
{
Cy_SysLib_Delay(SDIO_RETRY_DELAY_MS);
}
loop_count++;
}
while(result != CY_RSLT_SUCCESS && loop_count <= SDIO_BUS_LEVEL_MAX_RETRIES);
return result;
}
static cy_rslt_t cybsp_sdio_enumerate(const cyhal_sdio_t *sdio_object)
{
cy_rslt_t result = CYBSP_RSLT_WIFI_SDIO_ENUM_TIMEOUT;
uint32_t loop_count = 0;
uint32_t rel_addr;
uint32_t response_ignored = 0;
uint32_t no_argument = 0;
do
{
/* Send CMD0 to set it to idle state */
sdio_try_send_cmd(sdio_object, CYHAL_WRITE, CYHAL_SDIO_CMD_GO_IDLE_STATE, no_argument, &response_ignored /*ignored*/);
/* CMD5. */
sdio_try_send_cmd(sdio_object, CYHAL_READ, CYHAL_SDIO_CMD_IO_SEND_OP_COND, no_argument, &response_ignored /*ignored*/);
/* Send CMD3 to get RCA. */
result = sdio_try_send_cmd(sdio_object, CYHAL_READ, CYHAL_SDIO_CMD_SEND_RELATIVE_ADDR, no_argument, &rel_addr);
if(result != CY_RSLT_SUCCESS)
{
Cy_SysLib_Delay(SDIO_RETRY_DELAY_MS);
}
loop_count++;
} while (result != CY_RSLT_SUCCESS && loop_count <= SDIO_ENUMERATION_TRIES);
if(result == CY_RSLT_SUCCESS)
{
/* Send CMD7 with the returned RCA to select the card */
result = sdio_try_send_cmd(sdio_object, CYHAL_WRITE, CYHAL_SDIO_CMD_SELECT_CARD, rel_addr, &response_ignored);
}
return result;
}
static cy_rslt_t init_sdio_whd(void)
{
/* WiFi into reset */
cy_rslt_t result = cyhal_gpio_init(CYBSP_WIFI_WL_REG_ON, CYHAL_GPIO_DIR_OUTPUT, CY_GPIO_DM_PULLUP, 0);
if(result == CY_RSLT_SUCCESS)
{
/* Init SDIO Host */
if (!sdio_initialized)
{
result = cybsp_sdio_init();
}
if(result == CY_RSLT_SUCCESS)
{
/* WiFi out of reset */
cyhal_gpio_write(CYBSP_WIFI_WL_REG_ON, true);
Cy_SysLib_Delay(WLAN_POWER_UP_DELAY_MS);
}
else
{
cyhal_gpio_free(CYBSP_WIFI_WL_REG_ON);
}
}
return result;
}
static void deinit_sdio_whd(void)
{
cyhal_sdio_free(&sdio_obj);
cyhal_gpio_free(CYBSP_WIFI_WL_REG_ON);
sdio_initialized = false;
}
static cy_rslt_t init_sdio_bus(void)
{
whd_sdio_config_t whd_sdio_config;
cyhal_sdio_cfg_t config;
cy_rslt_t result = cybsp_sdio_enumerate(&sdio_obj);
if(result == CY_RSLT_SUCCESS)
{
whd_sdio_config.flags = 0;
whd_sdio_config.sdio_1bit_mode = WHD_FALSE;
whd_sdio_config.high_speed_sdio_clock = WHD_FALSE;
if(CY_SDIO_BUS_USE_OOB_INTR != 0)
{
whd_sdio_config.flags |= WHD_BUS_SDIO_OOB_INTR;
whd_sdio_config.oob_intr.u32val = 0; /* reserved for multi whd instances */
}
whd_bus_sdio_attach(whd_drv, &whd_sdio_config, &sdio_obj, &sdio_ops);
/* Increase frequency to 25 MHz for better performance */
config.frequencyhal_hz = 25000000;
config.block_size = 0;
cyhal_sdio_configure(&sdio_obj, &config);
}
return result;
}
static cy_rslt_t init_wlan_wakeup(void)
{
/* assert(CY_SDIO_BUS_USE_OOB_INTR != 0) */
cy_rslt_t result = cyhal_gpio_init(CY_WIFI_HOST_WAKE_GPIO, CYHAL_GPIO_DIR_INPUT, CY_WIFI_HOST_WAKE_GPIO_DM, 0);
if(result == CY_RSLT_SUCCESS)
{
cyhal_gpio_register_irq(CY_WIFI_HOST_WAKE_GPIO, WLAN_INTR_PRIORITY, whd_wake_host_irq_handler, whd_drv);
}
return result;
}
static void cy_enable_oob_intr(whd_driver_t whd_driver, const whd_variant_t intr, whd_bool_t whd_enable)
{
/* assert(CY_SDIO_BUS_USE_OOB_INTR != 0) */
(void)whd_driver;
(void)intr;
//TODO: This needs to be enabled in the WHD after the rtos is initialized. The current location where this is called
//causes a crash in the interrupt handler since it tries to set data on the thread before it is initialized.
//Need to review where this should be called in the WHD.
// cyhal_gpio_irq_enable(CY_WIFI_HOST_WAKE_GPIO, CY_WIFI_HOST_WAKE_IRQ_EVENT,
// (whd_enable == WHD_TRUE) ? true : false);
}
static void cy_get_intr_config(whd_driver_t whd_driver, const whd_variant_t intr, whd_intr_config_t *config)
{
/* assert(CY_SDIO_BUS_USE_OOB_INTR != 0) */
(void)whd_driver;
(void)intr;
config->dev_gpio_sel = DEFAULT_OOB_PIN;
config->is_falling_edge = (CY_WIFI_HOST_WAKE_IRQ_EVENT == CYHAL_GPIO_IRQ_FALL)
? WHD_TRUE
: WHD_FALSE;
}
cy_rslt_t cybsp_sdio_init(void)
{
cy_rslt_t result = cyhal_sdio_init(&sdio_obj, CYBSP_WIFI_SDIO_CMD, CYBSP_WIFI_SDIO_CLK, CYBSP_WIFI_SDIO_D0, CYBSP_WIFI_SDIO_D1, CYBSP_WIFI_SDIO_D2, CYBSP_WIFI_SDIO_D3);
sdio_initialized = (result == CY_RSLT_SUCCESS);
return result;
}
cy_rslt_t cybsp_wifi_init(void)
{
cy_rslt_t result = init_sdio_whd();
if(result != CY_RSLT_SUCCESS)
{
return result;
}
whd_init_config_t whd_init_config;
whd_init_config.thread_stack_size = (uint32_t) THREAD_STACK_SIZE;
whd_init_config.thread_stack_start = (uint8_t *)malloc(THREAD_STACK_SIZE);
whd_init_config.thread_priority = (uint32_t) THREAD_PRIORITY;
whd_init_config.country = COUNTRY;
uint32_t ret = whd_init(&whd_drv, &whd_init_config, &resource_ops, &buffer_ops, &netif_ops);
if(ret == WHD_SUCCESS)
{
result = init_sdio_bus();
if (result == CY_RSLT_SUCCESS && CY_SDIO_BUS_USE_OOB_INTR != 0)
{
result = init_wlan_wakeup();
}
}
else
{
result = CYBSP_RSLT_WIFI_INIT_FAILED;
}
if (result != CY_RSLT_SUCCESS)
{
deinit_sdio_whd();
}
return result;
}
whd_driver_t* cybsp_get_wifi_driver(void)
{
return &whd_drv;
}
#if defined(__cplusplus)
}
#endif
#endif /* defined(TARGET_WHD) */