nuttx/drivers/wireless/ieee80211/bcm43xxx/bcmf_gspi.c

1136 lines
29 KiB
C

/****************************************************************************
* drivers/wireless/ieee80211/bcm43xxx/bcmf_gspi.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <debug.h>
#include <errno.h>
#include <assert.h>
#include <nuttx/kmalloc.h>
#include <nuttx/arch.h>
#include <nuttx/kthread.h>
#include <nuttx/wdog.h>
#include <nuttx/signal.h>
#include <nuttx/wireless/ieee80211/bcmf_board.h>
#include "bcmf_gspi.h"
#include "bcmf_gspi_f2_frame.h"
#include "bcmf_core.h"
#include "bcmf_sdpcm.h"
#include "bcmf_utils.h"
#include "bcmf_sdio_core.h"
#include "bcmf_sdio_regs.h"
#include "cyw_reg_def.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define BCMF_GSPI_READY_TRYS 10
#define BCMF_GSPI_THREAD_NAME "bcmf-gspi-thread"
#define BCMF_GSPI_THREAD_STACK_SIZE 2048
#define BCMF_GSPI_LOWPOWER_TIMEOUT_TICK SEC2TICK(2)
#ifdef CONFIG_IEEE80211_INFINEON_CYW43439
extern const struct bcmf_chip_data cyw43439_config_data;
#endif
#define REV16(x) (((x & 0x000000ff) << 8) \
| ((x & 0x0000ff00) >> 8) \
| ((x & 0x00ff0000) << 8) \
| ((x & 0xff000000) >> 8))
/* Chip-common registers */
#define CHIPCOMMON_GPIO_CONTROL ((uint32_t)(0x18000000 + 0x06c) )
#define CHIPCOMMON_SR_CONTROL0 ((uint32_t)(0x18000000 + 0x504) )
#define CHIPCOMMON_SR_CONTROL1 ((uint32_t)(0x18000000 + 0x508) )
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: bcmf_gspi_read_reg_32
*
* Description:
* Read a 32-bit register
*
****************************************************************************/
static inline uint32_t bcmf_gspi_read_reg_32(FAR gspi_dev_t *gspi,
enum gspi_cmd_func_e function,
uint32_t address)
{
uint32_t buffer;
gspi->read(gspi, true, function, address, 4, &buffer);
return buffer;
}
/****************************************************************************
* Name: bcmf_gspi_read_reg_16
*
* Description:
* Read a 32-bit register
*
****************************************************************************/
static inline uint16_t bcmf_gspi_read_reg_16(FAR gspi_dev_t *gspi,
enum gspi_cmd_func_e function,
uint32_t address)
{
uint32_t buffer;
gspi->read(gspi, true, function, address, 2, &buffer);
return buffer;
}
/****************************************************************************
* Name: bcmf_gspi_read_reg_8
*
* Description:
* Read a 32-bit register
*
****************************************************************************/
static inline uint8_t bcmf_gspi_read_reg_8(FAR gspi_dev_t *gspi,
enum gspi_cmd_func_e function,
uint32_t address)
{
uint32_t buffer;
gspi->read(gspi, true, function, address, 1, &buffer);
return buffer;
}
/****************************************************************************
* Name: bcmf_gspi_write_reg_32
*
* Description:
* Write a 32-bit register
*
****************************************************************************/
static inline void bcmf_gspi_write_reg_32(FAR gspi_dev_t *gspi,
enum gspi_cmd_func_e function,
uint32_t address,
uint32_t value)
{
gspi->write(gspi, true, function, address, 4, &value);
}
/****************************************************************************
* Name: bcmf_gspi_write_reg_16
*
* Description:
* Read a 32-bit register
*
****************************************************************************/
static inline void bcmf_gspi_write_reg_16(FAR gspi_dev_t *gspi,
enum gspi_cmd_func_e function,
uint32_t address,
uint32_t value)
{
gspi->write(gspi, true, function, address, 2, &value);
}
/****************************************************************************
* Name: bcmf_gspi_write_reg_8
*
* Description:
* Write a 8-bit register
*
****************************************************************************/
static inline void bcmf_gspi_write_reg_8(FAR gspi_dev_t *gspi,
enum gspi_cmd_func_e function,
uint32_t address,
uint32_t value)
{
gspi->write(gspi, true, function, address, 1, &value);
}
/****************************************************************************
* Name: bcmf_gspi_kso_enable
****************************************************************************/
static int bcmf_gspi_kso_enable(FAR bcmf_gspi_dev_t *gbus, bool enable)
{
FAR gspi_dev_t *gspi = gbus->gspi;
uint8_t value;
int loops;
if (!gbus->ready)
{
return -EPERM;
}
if (gbus->kso_enable == enable)
{
return OK;
}
if (enable)
{
wlinfo("enable\n");
loops = 200;
while (--loops > 0)
{
bcmf_gspi_write_reg_8(gspi,
gspi_f1_backplane,
SBSDIO_FUNC1_SLEEPCSR,
SBSDIO_FUNC1_SLEEPCSR_KSO_MASK
| SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK);
nxsig_usleep(100 * 1000);
value = bcmf_gspi_read_reg_8(gspi, 1, SBSDIO_FUNC1_SLEEPCSR);
if ((value & (SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)) != 0)
{
break;
}
}
if (loops <= 0)
{
return -ETIMEDOUT;
}
}
else
{
wlinfo("disable\n");
bcmf_gspi_write_reg_8(gspi, 1, SBSDIO_FUNC1_SLEEPCSR, 0);
}
gbus->kso_enable = enable;
return OK;
}
/****************************************************************************
* Name: bcmf_gspi_bus_sleep
****************************************************************************/
static int bcmf_gspi_bus_sleep(FAR bcmf_gspi_dev_t *gbus, bool sleep)
{
FAR gspi_dev_t *gspi = gbus->gspi;
uint8_t value;
int loops;
if (!gbus->ready)
{
return -EPERM;
}
if (gbus->sleeping == sleep)
{
return OK;
}
if (sleep)
{
wlinfo("enable\n");
gbus->sleeping = true;
bcmf_gspi_write_reg_8(gspi,
gspi_f1_backplane,
SBSDIO_FUNC1_CHIPCLKCSR,
0);
wlinfo("exit\n");
return OK;
}
else
{
wlinfo("disable\n");
loops = 200;
while (--loops > 0)
{
/* Request HT Avail */
bcmf_gspi_write_reg_8(gspi,
gspi_f1_backplane,
SBSDIO_FUNC1_CHIPCLKCSR,
SBSDIO_HT_AVAIL_REQ
| SBSDIO_FORCE_HT);
/* Wait for High Throughput clock */
nxsig_usleep(100 * 1000);
value = bcmf_gspi_read_reg_8(gspi, 1, SBSDIO_FUNC1_CHIPCLKCSR);
if (value & SBSDIO_HT_AVAIL)
{
/* High Throughput clock is ready */
break;
}
}
if (loops <= 0)
{
wlerr("HT clock not ready\n");
return -ETIMEDOUT;
}
gbus->sleeping = false;
}
return OK;
}
/****************************************************************************
* Name: bcmf_gspi_bus_lowpower
****************************************************************************/
static int bcmf_gspi_bus_lowpower(FAR bcmf_gspi_dev_t *gbus, bool enable)
{
return gbus->support_sr ? bcmf_gspi_kso_enable(gbus, !enable)
: bcmf_gspi_bus_sleep(gbus, enable);
}
/****************************************************************************
* Name: bcmf_gspi_thread_isr
****************************************************************************/
static int bcmf_gspi_thread_isr(int isr, void *context, void *arg)
{
FAR bcmf_gspi_dev_t *gbus = (FAR bcmf_gspi_dev_t *) arg;
FAR gspi_dev_t *gspi = gbus->gspi;
int semcount;
gbus->irq_pending = true;
nxsem_get_value(&gbus->thread_signal, &semcount);
if (semcount < 1)
{
nxsem_post(&gbus->thread_signal);
}
/* Disable interrupt until bcmf_gspi_thread runs */
gspi->interrupt_enable(gspi, false);
return OK;
}
/****************************************************************************
* Name: bcmf_gspi_thread
****************************************************************************/
static int bcmf_gspi_thread(int argc, char **argv)
{
FAR struct bcmf_dev_s *priv;
FAR bcmf_gspi_dev_t *gbus;
FAR gspi_dev_t *gspi;
uint32_t status;
uint16_t intr_flags;
int ret;
int length;
int wait_count = 0;
bool wait_for_event;
bool enter_low_power = false;
priv = (FAR struct bcmf_dev_s *)((uintptr_t)strtoul(argv[1], NULL, 16));
gbus = (FAR bcmf_gspi_dev_t *)priv->bus;
gspi = gbus->gspi;
wlinfo(">>>> entered\n");
nxsig_usleep(50 * 1000);
gbus->thread_run = true;
bcmf_gspi_bus_lowpower(gbus, false);
while (gbus->thread_run)
{
/* Preset the wait for event flag in case we have nothing to do */
wait_for_event = true;
/* Get the device status */
status = bcmf_gspi_read_reg_32(gspi, gspi_f0_bus, CYW_REG_STATUS);
#ifdef CONFIG_DEBUG_WIRELESS_ERROR
if (status & (1 << 2))
{
wlerr("CYW_REG_STATUS_FIFO_OVERFLOW\n");
}
if (status & (1 << 7))
{
wlerr("CYW_REG_STATUS_CMD_DATA_ERROR\n");
}
#endif
/* If we were woken by an device interrupt clear the interrupt bits. */
if (gbus->irq_pending)
{
gbus->irq_pending = false;
/* These call also update the status in gspi->status */
intr_flags = bcmf_gspi_read_reg_16(gspi,
gspi_f0_bus,
CYW_REG_INTERRUPT);
if (intr_flags != 0)
{
bcmf_gspi_write_reg_16(gspi,
gspi_f0_bus,
CYW_REG_INTERRUPT,
intr_flags);
}
}
/* If we have a packet available to read -- read it */
if (status & CYW_REG_STATUS_F2_PKT_AVAIL)
{
length = status & CYW_REG_STATUS_F2_PKT_LEN_MASK;
length >>= CYW_REG_STATUS_F2_PKT_LEN_SHIFT;
/* If we don't have a frame leave the loop */
if (length == 0) break;
/* Read and process frame. This updates gspi->status */
ret = bcmf_gspi_read_f2_frame(priv, length);
if (ret == OK)
{
wait_for_event = false;
}
else
{
wlerr("error reading f2 frame: %d\n", ret);
}
}
else
{
/* If we don't have anything to read, try sending a packet */
while ((status & CYW_REG_STATUS_F2_RECEIVE_RDY) == 0)
{
/* Oops! no room for a packet. We'll wait a bit to see
* if room shows up.
*/
wlinfo(">>>> not ready to receive\n");
if (++wait_count > 100)
{
wlerr("Chip cannot receive F2 frame\n");
break;
}
/* No room at the inn for an f2 frame -- wait a bit */
usleep(10000);
status = bcmf_gspi_read_reg_32(gspi,
gspi_f0_bus,
CYW_REG_STATUS);
}
/* reset the count for next time */
wait_count = 0;
/* We have space, send the frame */
ret = bcmf_gspi_send_f2_frame(priv);
if (ret == OK)
{
wait_for_event = false;
}
else
{
#ifdef CONFIG_DEBUG_WIRELESS_ERROR
if (ret != -ENODATA)
{
wlerr("error sending f2 frame: %d\n", ret);
}
#endif
}
}
/* No more transfer requests. Wait for something to happen. */
if (wait_for_event)
{
/* Wait for event (device interrupt or user request) */
gspi->interrupt_enable(gspi, true);
if (enter_low_power)
{
enter_low_power = false;
bcmf_gspi_bus_lowpower(gbus, true);
nxsem_wait_uninterruptible(&gbus->thread_signal);
bcmf_gspi_bus_lowpower(gbus, false);
}
else
{
ret = nxsem_tickwait_uninterruptible(
&gbus->thread_signal,
BCMF_GSPI_LOWPOWER_TIMEOUT_TICK);
if (ret == -ETIMEDOUT) enter_low_power = true;
}
}
}
wlinfo(">>>> exit\n");
return 0;
}
/****************************************************************************
* Name: bcmf_gspi_init_device
*
* Description:
* Keeps checking the 43439's test register looking for the test pattern.
* When found puts the chip in 32-bit mode.
*
* Note:
* Some calls to the 43439 in this function uses the "magic" rev16 mode
* to account for the 16-bit mode's default byte ordering.
****************************************************************************/
static int bcmf_gspi_init_device(FAR bcmf_gspi_dev_t *gbus)
{
FAR gspi_dev_t *gspi = gbus->gspi;
uint32_t buffer[2];
uint32_t data;
int ret;
int i;
wlinfo("entered.\n");
/* Look for the chip ready pattern. */
for (i = 0; i < BCMF_GSPI_READY_TRYS; ++i)
{
ret = gspi->read(gspi,
true,
gspi_f0_bus_rev16,
CYW_REG_TEST_RO,
4,
buffer);
if (ret != 0)
{
wlerr("Error looking for \"ready\" pattern: %d\n", ret);
return ret;
}
if (REV16(buffer[0]) == CYW_REG_TEST_RO_PATTERN) break;
}
if (i == BCMF_GSPI_READY_TRYS)
{
wlerr("Could not find cyw43439 \"ready\" pattern\n");
return -ENODEV;
}
data = CYW_REG_SETUP_WORD_LEN_32
| CYW_REG_SETUP_BIG_ENDIAN
| CYW_REG_SETUP_HIGH_SPEED
| CYW_REG_SETUP_INT_POLARITY
| CYW_REG_SETUP_WAKE_UP
| CYW_REG_STAT_ENA_INTR_STAT;
data = REV16(data);
gspi->write(gspi, true, gspi_f0_bus_rev16, CYW_REG_SETUP, 4, &data);
/* We are now in 32-bit bigendian mode -- we no longer do REV16s. */
/* Set a 4-byte response delay for F1 packets */
bcmf_gspi_write_reg_8(gspi, gspi_f0_bus, CYW_REG_RESP_DELAY_F1, 4);
wlinfo("complete\n");
return OK;
}
/****************************************************************************
* Name: bcmf_gspi_probe_chip
****************************************************************************/
static int bcmf_gspi_probe_chip(FAR bcmf_gspi_dev_t *gbus)
{
uint32_t value;
int chipid;
int ret;
wlinfo("entered\n");
ret = bcmf_read_sbregw(gbus, SI_ENUM_BASE, &value);
if (ret != OK)
{
wlerr("bcmf_read_sbregw failed\n");
return ret;
}
chipid = value & 0xffff;
gbus->cur_chip_id = chipid;
switch (chipid)
{
#ifdef CONFIG_IEEE80211_INFINEON_CYW43439
case SDIO_DEVICE_ID_INFINEON_CYW43439:
wlinfo("cyw%d chip detected\n", chipid);
gbus->chip = (struct bcmf_chip_data *)&cyw43439_config_data;
break;
#endif
default:
wlerr("chip 0x%08X is not supported\n", chipid);
return -ENODEV;
}
return OK;
}
/****************************************************************************
* Name: bcmf_gspi_setup_interrupts
****************************************************************************/
static int bcmf_gspi_setup_interrupts(FAR bcmf_gspi_dev_t *gbus)
{
FAR gspi_dev_t *gspi = gbus->gspi;
wlinfo("entered\n");
/* Set up device interrupt preferences */
bcmf_gspi_write_reg_16(gspi,
gspi_f0_bus,
CYW_REG_INTERRUPT,
CYW_REG_INTERRUPT_DATA_NOT_AVAIL
| CYW_REG_INTERRUPT_COMMAND_ERROR
| CYW_REG_INTERRUPT_DATA_ERROR
| CYW_REG_INTERRUPT_F1_OVERFLOW);
/* We only want an interrupt if an F2 packet is available */
bcmf_gspi_write_reg_16(gspi,
gspi_f0_bus,
CYW_REG_INTR_ENA,
CYW_REG_INTR_ENA_F2_PKT_AVAIL);
wlinfo("interrupt set up complete\n");
return OK;
}
/****************************************************************************
* Name: bcmf_gspi_init_alp_clock
****************************************************************************/
static int bcmf_gspi_init_alp_clock(FAR bcmf_gspi_dev_t *gbus)
{
FAR gspi_dev_t *gspi = gbus->gspi;
int loops;
uint8_t value;
wlinfo("entered\n");
/* Send Active Low-Power clock request */
bcmf_gspi_write_reg_8(gspi,
gspi_f1_backplane,
SBSDIO_FUNC1_CHIPCLKCSR,
SBSDIO_FORCE_HW_CLKREQ_OFF
| SBSDIO_ALP_AVAIL_REQ
| SBSDIO_FORCE_ALP);
loops = 10;
while (--loops > 0)
{
nxsig_usleep(10 * 1000);
value = bcmf_gspi_read_reg_8(gspi,
gspi_f1_backplane,
SBSDIO_FUNC1_CHIPCLKCSR);
if (value & SBSDIO_ALP_AVAIL)
{
/* Active Low-Power clock is ready */
break;
}
}
if (loops <= 0)
{
wlerr("failed to enable ALP\n");
return -ETIMEDOUT;
}
/* Clear Active Low-Power clock request */
bcmf_gspi_write_reg_8(gspi,
gspi_f1_backplane,
SBSDIO_FUNC1_CHIPCLKCSR,
0);
wlinfo("ALP initialization complete\n");
nxsig_usleep(100 * 1000);
return OK;
}
/****************************************************************************
* Name: bcmf_gspi_init_save_restore
****************************************************************************/
static int bcmf_gspi_init_save_restore(FAR bcmf_gspi_dev_t *gbus)
{
uint8_t data;
uint32_t srctrl = 0;
int ret;
wlinfo("entered\n");
ret = bcmf_read_sbregw(gbus, CHIPCOMMON_SR_CONTROL1, &srctrl);
if (ret != OK)
{
wlinfo("exit -- not SR capable.\n");
return OK; /* chip not sr capable */
}
if (srctrl != 0)
{
/* Configure WakeupCtrl register to set HtAvail request bit in
* chipClockCSR register after the sdiod core is powered on.
*/
bcmf_read_reg(gbus, 1, SBSDIO_FUNC1_WAKEUPCTRL, &data);
data |= SBSDIO_FUNC1_WCTRL_HTWAIT_MASK;
bcmf_write_reg(gbus, 1, SBSDIO_FUNC1_WAKEUPCTRL, data);
/* Set brcmCardCapability to noCmdDecode mode.
* It makes sdiod_aos to wakeup host for any activity of cmd line,
* even though module won't decode cmd or respond
*/
bcmf_write_reg(gbus, 0, SDIO_CCCR_BRCM_CARDCAP,
SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC);
bcmf_write_reg(gbus, 1, SBSDIO_FUNC1_CHIPCLKCSR,
SBSDIO_FORCE_HT);
/* Enable KeepSdioOn (KSO) bit for normal operation */
bcmf_gspi_kso_enable(gbus, true);
gbus->support_sr = true;
}
wlinfo("exit\n");
return OK;
}
/****************************************************************************
* Name: bcmf_gspi_hw_uninitialize
****************************************************************************/
static int bcmf_gspi_hw_uninitialize(FAR bcmf_gspi_dev_t *gbus)
{
FAR gspi_dev_t *gspi = gbus->gspi;
wlinfo("entered\n");
if (gbus->thread_id != 0)
{
gbus->thread_run = false;
nxsem_post(&gbus->thread_signal);
}
gspi->deinit(gspi);
return OK;
}
/****************************************************************************
* Name: bcmf_bus_gspi_initialize
*
* Description:
* Initialize a bcmf device connected via gSPI interface.
*
****************************************************************************/
static int bcmf_bus_gspi_initialize(FAR struct bcmf_dev_s *priv,
FAR struct gspi_dev_s *gspi)
{
FAR bcmf_gspi_dev_t *gbus;
int ret;
wlinfo("entered.\n");
/* Allocate gSPI bus structure */
gbus = (FAR bcmf_gspi_dev_t *)kmm_zalloc(sizeof(*gbus));
if (!gspi)
{
return -ENOMEM;
}
/* Initialize sdio bus device structure */
gbus->gspi = gspi;
gbus->ready = false;
gbus->sleeping = true;
/* FIX ME -- The interface needs some room to send F2 packets
* to the device before we've read the first F2
* packet from the device, so we set the max_seq
* to something small;
*/
gbus->max_seq = 4;
gbus->bus.txframe = bcmf_sdpcm_queue_frame;
gbus->bus.rxframe = bcmf_sdpcm_get_rx_frame;
gbus->bus.allocate_frame = bcmf_sdpcm_alloc_frame;
gbus->bus.free_frame = bcmf_sdpcm_free_frame;
gbus->bus.stop = NULL; /* TODO */
/* Init transmit frames queue */
if ((ret = nxsem_init(&gbus->queue_mutex, 0, 1)) != OK)
{
goto exit_free_bus;
}
list_initialize(&gbus->tx_queue);
list_initialize(&gbus->rx_queue);
/* Setup free buffer list */
bcmf_initialize_interface_frames();
/* Init thread semaphore */
if ((ret = nxsem_init(&gbus->thread_signal, 0, 0)) != OK)
{
goto exit_free_bus;
}
if ((ret = nxsem_set_protocol(&gbus->thread_signal, SEM_PRIO_NONE)) != OK)
{
goto exit_free_bus;
}
/* Register sdio bus */
priv->bus = &gbus->bus;
/* Save a back pointer so we can recover driver state */
gspi->priv = priv;
wlinfo("complete.\n");
return OK;
exit_free_bus:
wlinfo("failed.\n");
kmm_free(gbus);
priv->bus = NULL;
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bcmf_gspi_initialize
*
* Description:
* Initialize the drive with a gSPI connection.
****************************************************************************/
int bcmf_gspi_initialize(FAR struct gspi_dev_s *gspi)
{
int ret;
FAR struct bcmf_dev_s *priv;
ninfo("entered.\n");
priv = bcmf_allocate_device();
if (!priv)
{
return -ENOMEM;
}
/* Init sdio bus */
ret = bcmf_bus_gspi_initialize(priv, gspi);
if (ret != OK)
{
ret = -EIO;
goto exit_free_device;
}
/* Bus initialized, register network driver */
return bcmf_driver_initialize(priv);
exit_free_device:
bcmf_free_device(priv);
return ret;
}
/****************************************************************************
* Name: bcmf_bus_gspi_active
*
* Description:
* Activate (or deactivate) a bcmf device connected via gSPI interface.
****************************************************************************/
int bcmf_bus_gspi_active(FAR struct bcmf_dev_s *priv,
bool active)
{
FAR bcmf_gspi_dev_t *gbus = (FAR bcmf_gspi_dev_t *)priv->bus;
FAR gspi_dev_t *gspi = gbus->gspi;
int ret = OK;
FAR char *argv[2];
char arg1[32];
wlinfo("entered. active = %d\n", active);
if (!active)
{
goto exit_uninit_hw;
}
/* Initialize device hardware */
ret = gspi->init(gspi);
if (ret != OK)
{
return ret;
}
gbus->ready = active;
/* Probe device */
ret = bcmf_gspi_init_device(gbus);
if (ret != OK)
{
goto exit_uninit_hw;
}
/* Detect and configure for specific chip */
ret = bcmf_gspi_probe_chip(gbus);
if (ret != OK)
{
wlerr("bcmf_gspi_probe_chip failed\n");
goto exit_uninit_hw;
}
ret = bcmf_gspi_setup_interrupts(gbus);
if (ret != OK)
{
goto exit_uninit_hw;
}
/* Active the low power clock */
ret = bcmf_gspi_init_alp_clock(gbus);
if (ret != OK)
{
goto exit_uninit_hw;
}
/* Upload firmware */
ret = bcmf_core_upload_firmware(gbus);
if (ret != OK)
{
wlerr("bcmf_core_upload_firmware failed\n");
goto exit_uninit_hw;
}
/* Spawn bcmf daemon thread */
snprintf(arg1, sizeof(arg1), "%p", priv);
argv[0] = arg1;
argv[1] = NULL;
ret = kthread_create(BCMF_GSPI_THREAD_NAME,
CONFIG_IEEE80211_BROADCOM_SCHED_PRIORITY,
BCMF_GSPI_THREAD_STACK_SIZE,
bcmf_gspi_thread,
argv);
if (ret <= 0)
{
wlerr("Cannot spawn daemon thread\n");
ret = -EBADE;
goto exit_uninit_hw;
}
gbus->thread_id = (pid_t)ret;
ret = gspi->set_isr(gspi, bcmf_gspi_thread_isr, gbus);
if (ret != OK)
{
wlerr("set_isr failed\n");
goto exit_uninit_hw;
}
ret = bcmf_gspi_init_save_restore(gbus);
if (ret != OK)
{
goto exit_uninit_hw;
}
return OK;
exit_uninit_hw:
gbus->ready = false;
bcmf_gspi_hw_uninitialize(gbus);
return ret;
}
/****************************************************************************
* Name: bcmf_transfer_bytes
****************************************************************************/
/* FIXME: Low level bus data transfer function
* To avoid bus error, len will be aligned to:
* - upper power of 2 iflen is lesser than 64
* - upper 64 bytes block if len is greater than 64
*/
int bcmf_transfer_bytes(FAR bcmf_gspi_dev_t *gbus,
bool write,
uint8_t function,
uint32_t address,
uint8_t *buf,
unsigned int len)
{
FAR gspi_dev_t *gspi = gbus->gspi;
int ret;
DEBUGASSERT((((uintptr_t) buf) & 0x03) == 0); /* make sure buf is word aligned */
if (write)
{
ret = gspi->write(gspi,
true,
function,
address,
len,
(FAR uint32_t *) buf);
return ret;
}
/* -- read btytes -- */
ret = gspi->read(gspi,
true,
function,
address,
len,
(FAR uint32_t *) buf);
return ret;
}
/****************************************************************************
* Name: bcmf_read_reg
****************************************************************************/
int bcmf_read_reg(FAR bcmf_gspi_dev_t *gbus,
uint8_t function,
uint32_t address,
uint8_t *reg)
{
*reg = bcmf_gspi_read_reg_8(gbus->gspi, function, address);
return OK;
}
/****************************************************************************
* Name: bcmf_write_reg
****************************************************************************/
int bcmf_write_reg(FAR bcmf_gspi_dev_t *gbus,
uint8_t function,
uint32_t address,
uint8_t reg)
{
bcmf_gspi_write_reg_8(gbus->gspi, function, address, reg);
return OK;
}