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

600 lines
16 KiB
C

/****************************************************************************
* drivers/wireless/bcm43xxx/ieee80211/bcmf_core.c
*
* Copyright (C) 2017-2018 Gregory Nutt. All rights reserved.
* Author: Simon Piriou <spiriou31@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <debug.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include "bcmf_core.h"
#include "bcmf_sdio.h"
#include "bcmf_sdio_regs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Agent registers (common for every core) */
#define BCMA_IOCTL 0x0408 /* IO control */
#define BCMA_IOST 0x0500 /* IO status */
#define BCMA_RESET_CTL 0x0800 /* Reset control */
#define BCMA_RESET_ST 0x0804
#define BCMA_IOCTL_CLK 0x0001
#define BCMA_IOCTL_FGC 0x0002
#define BCMA_IOCTL_CORE_BITS 0x3FFC
#define BCMA_IOCTL_PME_EN 0x4000
#define BCMA_IOCTL_BIST_EN 0x8000
#define BCMA_IOST_CORE_BITS 0x0FFF
#define BCMA_IOST_DMA64 0x1000
#define BCMA_IOST_GATED_CLK 0x2000
#define BCMA_IOST_BIST_ERROR 0x4000
#define BCMA_IOST_BIST_DONE 0x8000
#define BCMA_RESET_CTL_RESET 0x0001
/* SOCSRAM core registers */
#define SOCSRAM_BANKX_INDEX ((uint32_t) (0x18004000 + 0x10) )
#define SOCSRAM_BANKX_PDA ((uint32_t) (0x18004000 + 0x44) )
/* Transfer size properties */
#define BCMF_UPLOAD_TRANSFER_SIZE (64 * 256)
/* Define this to validate uploaded materials */
/* #define DBG_VALIDATE_UPLOAD */
/****************************************************************************
* Private Types
****************************************************************************/
#ifdef DBG_VALIDATE_UPLOAD
static uint8_t compare_buffer[BCMF_UPLOAD_TRANSFER_SIZE];
#endif
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int bcmf_core_set_backplane_window(FAR struct bcmf_sdio_dev_s *sbus,
uint32_t address);
static int bcmf_upload_binary(FAR struct bcmf_sdio_dev_s *sbusv,
uint32_t address, uint8_t *buf,
unsigned int len);
static int bcmf_upload_nvram(FAR struct bcmf_sdio_dev_s *sbus);
/****************************************************************************
* Private Functions
****************************************************************************/
int bcmf_core_set_backplane_window(FAR struct bcmf_sdio_dev_s *sbus,
uint32_t address)
{
int ret;
int i;
address &= ~SBSDIO_SB_OFT_ADDR_MASK;
for (i = 1; i < 4; i++)
{
uint8_t addr_part = (address >> (8*i)) & 0xff;
uint8_t cur_addr_part = (sbus->backplane_current_addr >> (8*i)) & 0xff;
if (addr_part != cur_addr_part)
{
/* Update current backplane base address */
ret = bcmf_write_reg(sbus, 1, SBSDIO_FUNC1_SBADDRLOW + i - 1,
addr_part);
if (ret != OK)
{
return ret;
}
sbus->backplane_current_addr &= ~(0xff << (8*i));
sbus->backplane_current_addr |= addr_part << (8*i);
}
}
return OK;
}
int bcmf_upload_binary(FAR struct bcmf_sdio_dev_s *sbus, uint32_t address,
uint8_t *buf, unsigned int len)
{
unsigned int size;
#ifdef DBG_VALIDATE_UPLOAD
uint32_t validate_address = address;
uint8_t *validate_buffer = buf;
unsigned int validate_len = len;
#endif
while (len > 0)
{
/* Set the backplane window to include the start address */
int ret = bcmf_core_set_backplane_window(sbus, address);
if (ret != OK)
{
wlerr("Backplane setting failed at %08x\n", address);
return ret;
}
if (len > BCMF_UPLOAD_TRANSFER_SIZE)
{
size = BCMF_UPLOAD_TRANSFER_SIZE;
}
else
{
size = len;
}
/* Transfer firmware data */
ret = bcmf_transfer_bytes(sbus, true, 1,
address & SBSDIO_SB_OFT_ADDR_MASK, buf, size);
if (ret != OK)
{
wlerr("transfer failed %d %x %d\n", ret, address, size);
return ret;
}
len -= size;
address += size;
buf += size;
}
#ifdef DBG_VALIDATE_UPLOAD
wlwarn("Validating....\n");
while (validate_len > 0)
{
/* Set the backplane window to include the start address */
int ret = bcmf_core_set_backplane_window(sbus, validate_address);
if (ret != OK)
{
wlerr("Backplane setting failed at %08x\n", validate_address);
return ret;
}
if (validate_len > BCMF_UPLOAD_TRANSFER_SIZE)
{
size = BCMF_UPLOAD_TRANSFER_SIZE;
}
else
{
size = validate_len;
}
/* Transfer firmware data */
ret = bcmf_transfer_bytes(sbus, false, 1,
validate_address & SBSDIO_SB_OFT_ADDR_MASK,
compare_buffer, size);
if (ret != OK)
{
wlerr("validate transfer failed %d %x %d\n", ret, validate_address,
size);
return ret;
}
if (memcmp(validate_buffer, compare_buffer, size))
{
wlerr("Match failed at address base %08x\n", validate_address);
return -EILSEQ;
}
validate_len -= size;
validate_address += size;
validate_buffer += size;
}
wlwarn("Validation passed\n");
#endif
return OK;
}
#ifdef CONFIG_IEEE80211_BROADCOM_FWFILES
int bcmf_upload_file(FAR struct bcmf_sdio_dev_s *sbus, uint32_t address,
FAR const char *path)
{
struct file finfo;
FAR uint8_t *buf;
size_t total_read;
ssize_t nread;
int ret;
/* Open the file in the detached state */
ret = file_open(&finfo, path, O_RDONLY | O_BINARY);
if (ret < 0)
{
wlerr("ERROR: Failed to open the FILE MTD file %s: %d\n", path, ret);
return ret;
}
/* Allocate an I/O buffer */
buf = (FAR uint8_t *)kmm_malloc(BCMF_UPLOAD_TRANSFER_SIZE);
if (buf == NULL)
{
wlerr("ERROR: Failed allocate an I/O buffer\n");
ret = -ENOMEM;
goto errout_with_file;
}
/* Loop until the firmware has been loaded */
do
{
/* Set the backplane window to include the start address */
nread = file_read(&finfo, buf, BCMF_UPLOAD_TRANSFER_SIZE);
if (nread < 0)
{
ret = (int)nread;
wlerr("ERROR: Failed to read file: %d\n", ret);
goto errout_with_buf;
}
if (nread == 0)
{
break;
}
wlinfo("Read %ld bytes\n", (long)nread);
ret = bcmf_core_set_backplane_window(sbus, address);
if (ret < 0)
{
wlerr("ERROR: bcmf_core_set_backplane_window() failed: %d\n", ret);
goto errout_with_buf;
}
total_read = nread;
/* Transfer firmware data */
ret = bcmf_transfer_bytes(sbus, true, 1,
address & SBSDIO_SB_OFT_ADDR_MASK, buf,
total_read);
if (ret < 0)
{
wlerr("ERROR: Transfer failed address=%lx total_read=%lu: %d\n",
(unsigned long)address, (unsigned long)total_read, ret);
goto errout_with_buf;
}
address += total_read;
}
while (nread == BCMF_UPLOAD_TRANSFER_SIZE);
file_close(&finfo);
kmm_free(buf);
wlinfo("Upload complete\n");
return OK;
errout_with_buf:
kmm_free(buf);
errout_with_file:
file_close(&finfo);
return ret;
}
#endif
int bcmf_upload_nvram(FAR struct bcmf_sdio_dev_s *sbus)
{
int ret;
uint32_t nvram_sz;
uint32_t token;
/* Round up the size of the image */
nvram_sz = (*sbus->chip->nvram_image_size + 63) & (-64);
wlinfo("nvram size is %d %d bytes\n", nvram_sz,
*sbus->chip->nvram_image_size);
/* Write image */
ret = bcmf_upload_binary(sbus, sbus->chip->ram_size - 4 - nvram_sz,
sbus->chip->nvram_image,
*sbus->chip->nvram_image_size);
if (ret != OK)
{
return ret;
}
/* Generate length token */
token = nvram_sz / 4;
token = (~token << 16) | (token & 0x0000ffff);
/* Write the length token to the last word */
ret = bcmf_write_sbreg(sbus, sbus->chip->ram_size - 4,
(FAR uint8_t *)&token, 4);
if (ret != OK)
{
return ret;
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bcmf_read_sbreg
****************************************************************************/
int bcmf_read_sbreg(FAR struct bcmf_sdio_dev_s *sbus, uint32_t address,
FAR uint8_t *reg, unsigned int len)
{
int ret = bcmf_core_set_backplane_window(sbus, address);
if (ret != OK)
{
return ret;
}
address &= SBSDIO_SB_OFT_ADDR_MASK;
/* Map to 32-bit access if len == 4 */
if (len == 4)
{
address |= SBSDIO_SB_ACCESS_2_4B_FLAG;
}
return bcmf_transfer_bytes(sbus, false, 1, address, reg, len);
}
/****************************************************************************
* Name: bcmf_write_sbreg
****************************************************************************/
int bcmf_write_sbreg(FAR struct bcmf_sdio_dev_s *sbus, uint32_t address,
FAR uint8_t *reg, unsigned int len)
{
int ret = bcmf_core_set_backplane_window(sbus, address);
if (ret != OK)
{
return ret;
}
address &= SBSDIO_SB_OFT_ADDR_MASK;
/* Map to 32-bit access if len == 4 */
if (len == 4)
{
address |= SBSDIO_SB_ACCESS_2_4B_FLAG;
}
return bcmf_transfer_bytes(sbus, true, 1, address, reg, len);
}
/****************************************************************************
* Name: bcmf_core_upload_firmware
****************************************************************************/
int bcmf_core_upload_firmware(FAR struct bcmf_sdio_dev_s *sbus)
{
int ret;
wlinfo("upload firmware\n");
/* Disable ARMCM3 core and reset SOCRAM core to set device in firmware
* upload mode
*/
bcmf_core_disable(sbus, WLAN_ARMCM3_CORE_ID);
bcmf_core_reset(sbus, SOCSRAM_CORE_ID);
/* Do chip specific initialization */
if (sbus->cur_chip_id == SDIO_DEVICE_ID_BROADCOM_43430)
{
/* Disable remap for SRAM_3. Only for 4343x */
bcmf_write_sbregw(sbus, SOCSRAM_BANKX_INDEX, 0x3);
bcmf_write_sbregw(sbus, SOCSRAM_BANKX_PDA, 0);
}
up_mdelay(50);
/* Flash chip firmware */
#ifdef CONFIG_IEEE80211_BROADCOM_FWFILES
ret = bcmf_upload_file(sbus, 0, CONFIG_IEEE80211_BROADCOM_FWFILENAME);
#else
wlinfo("firmware size is %d bytes\n", *sbus->chip->firmware_image_size);
ret = bcmf_upload_binary(sbus, 0, sbus->chip->firmware_image,
*sbus->chip->firmware_image_size);
#endif
if (ret < 0)
{
wlerr("ERROR: Failed to upload firmware\n");
return ret;
}
/* Flash NVRAM configuration file */
wlinfo("upload nvram configuration\n");
ret = bcmf_upload_nvram(sbus);
if (ret < 0)
{
wlerr("ERROR: Failed to upload NVRAM\n");
return ret;
}
/* Firmware upload done, restart ARMCM3 core */
up_mdelay(10);
bcmf_core_reset(sbus, WLAN_ARMCM3_CORE_ID);
/* Check ARMCM3 core is running */
up_mdelay(10);
if (!bcmf_core_isup(sbus, WLAN_ARMCM3_CORE_ID))
{
wlerr("Cannot start ARMCM3 core\n");
return -ETIMEDOUT;
}
return OK;
}
bool bcmf_core_isup(FAR struct bcmf_sdio_dev_s *sbus, unsigned int core)
{
uint32_t value = 0;
uint32_t base;
if (core >= MAX_CORE_ID)
{
wlerr("Invalid core id %d\n", core);
return false;
}
base = sbus->chip->core_base[core];
bcmf_read_sbregw(sbus, base + BCMA_IOCTL, &value);
if ((value & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) != BCMA_IOCTL_CLK)
{
return false;
}
bcmf_read_sbregw(sbus, base + BCMA_RESET_CTL, &value);
return (value & BCMA_RESET_CTL_RESET) == 0;
}
void bcmf_core_disable(FAR struct bcmf_sdio_dev_s *sbus, unsigned int core)
{
uint8_t value;
if (core >= MAX_CORE_ID)
{
wlerr("Invalid core id %d\n", core);
return;
}
uint32_t base = sbus->chip->core_base[core];
/* Check if core is already in reset state */
bcmf_read_sbregb(sbus, base + BCMA_RESET_CTL, &value);
if ((value & BCMA_RESET_CTL_RESET) != 0)
{
/* Core already disabled */
return;
}
/* Ensure no backplane operation is pending */
up_mdelay(10);
/* Set core in reset state */
bcmf_write_sbregb(sbus, base + BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
up_udelay(1);
/* Write 0 to the IO control and read it back */
bcmf_write_sbregb(sbus, base + BCMA_IOCTL, 0);
bcmf_read_sbregb(sbus, base + BCMA_IOCTL, &value);
up_udelay(10);
}
void bcmf_core_reset(FAR struct bcmf_sdio_dev_s *sbus, unsigned int core)
{
uint32_t value;
uint32_t base;
if (core >= MAX_CORE_ID)
{
wlerr("Invalid core id %d\n", core);
return;
}
base = sbus->chip->core_base[core];
/* Put core in reset state */
bcmf_core_disable(sbus, core);
/* Run initialization sequence */
bcmf_write_sbregb(sbus, base + BCMA_IOCTL, BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
bcmf_read_sbregw(sbus, base + BCMA_IOCTL, &value);
bcmf_write_sbregb(sbus, base + BCMA_RESET_CTL, 0);
bcmf_read_sbregw(sbus, base + BCMA_RESET_CTL, &value);
up_udelay(1);
bcmf_write_sbregb(sbus, base + BCMA_IOCTL, BCMA_IOCTL_CLK);
bcmf_read_sbregw(sbus, base + BCMA_IOCTL, &value);
up_udelay(1);
}