nuttx/drivers/wireless/ieee80211/bcmf_core.c
2017-04-05 21:55:21 +02:00

391 lines
11 KiB
C

/****************************************************************************
* drivers/wireless/ieee80211/bcmf_core.c
*
* Copyright (C) 2017 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 <nuttx/arch.h>
#include "bcmf_core.h"
#include "bcmf_sdio.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
/* Transfer size properties */
#define BCMF_UPLOAD_TRANSFER_SIZE (64 * 256)
// TODO move in chip configuration data
#define CHIP_RAM_SIZE 0x3C000
extern const char bcmf_nvram_image[];
extern const unsigned int bcmf_nvram_image_len;
extern const uint8_t bcmf_firmware_image[];
extern const unsigned int bcmf_firmware_image_len;
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int bcmf_core_set_backplane_window(FAR struct bcmf_dev_s *priv,
uint32_t address);
static int bcmf_upload_binary(FAR struct bcmf_dev_s *priv,
uint32_t address, uint8_t *buf,
unsigned int len);
static int bcmf_upload_nvram(FAR struct bcmf_dev_s *priv);
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
int bcmf_core_set_backplane_window(FAR struct bcmf_dev_s *priv, 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 = (priv->backplane_current_addr >> (8*i)) & 0xff;
if (addr_part != cur_addr_part)
{
/* Update current backplane base address */
ret = bcmf_write_reg(priv, 1, SBSDIO_FUNC1_SBADDRLOW+i-1,
addr_part);
if (ret != OK)
{
return ret;
}
priv->backplane_current_addr &= ~(0xff << (8*i));
priv->backplane_current_addr |= addr_part << (8*i);
}
}
return OK;
}
int bcmf_upload_binary(FAR struct bcmf_dev_s *priv, uint32_t address,
uint8_t *buf, unsigned int len)
{
unsigned int size;
while (len > 0)
{
/* Set the backplane window to include the start address */
int ret = bcmf_core_set_backplane_window(priv, address);
if (ret != OK)
{
return ret;
}
if (len > BCMF_UPLOAD_TRANSFER_SIZE)
{
size = BCMF_UPLOAD_TRANSFER_SIZE;
}
else
{
size = len;
}
/* Transfer firmware data */
ret = bcmf_transfer_bytes(priv, true, 1,
address & SBSDIO_SB_OFT_ADDR_MASK, buf, size);
if (ret != OK)
{
_err("transfer failed %d %x %d\n", ret, address, size);
return ret;
}
len -= size;
address += size;
buf += size;
}
return OK;
}
int bcmf_upload_nvram(FAR struct bcmf_dev_s *priv)
{
int ret;
uint32_t nvram_sz;
uint32_t token;
/* Round up the size of the image */
nvram_sz = (bcmf_nvram_image_len + 63) & (-64);
_info("nvram size is %d %d bytes\n", nvram_sz, bcmf_nvram_image_len);
/* Write image */
ret = bcmf_upload_binary(priv, CHIP_RAM_SIZE - 4 - nvram_sz,
(uint8_t*)bcmf_nvram_image, bcmf_nvram_image_len);
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(priv, CHIP_RAM_SIZE - 4, (uint8_t*)&token, 4);
if ( ret != OK)
{
return ret;
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bcmf_read_sbreg
****************************************************************************/
int bcmf_read_sbreg(FAR struct bcmf_dev_s *priv, uint32_t address,
uint8_t *reg, unsigned int len)
{
int ret = bcmf_core_set_backplane_window(priv, address);
if (ret != OK)
{
return ret;
}
return bcmf_transfer_bytes(priv, false, 1,
address & SBSDIO_SB_OFT_ADDR_MASK, reg, len);
}
/****************************************************************************
* Name: bcmf_write_sbreg
****************************************************************************/
int bcmf_write_sbreg(FAR struct bcmf_dev_s *priv, uint32_t address,
uint8_t *reg, unsigned int len)
{
int ret = bcmf_core_set_backplane_window(priv, address);
if (ret != OK)
{
return ret;
}
return bcmf_transfer_bytes(priv, true, 1, address & SBSDIO_SB_OFT_ADDR_MASK,
reg, len);
}
/****************************************************************************
* Name: bcmf_core_upload_firmware
****************************************************************************/
int bcmf_core_upload_firmware(FAR struct bcmf_dev_s *priv)
{
int ret;
_info("upload firmware\n");
/* Disable ARMCM3 core and reset SOCRAM core
* to set device in firmware upload mode */
bcmf_core_disable(priv, WLAN_ARMCM3_CORE_ID);
bcmf_core_reset(priv, SOCSRAM_CORE_ID);
up_mdelay(50);
/* Flash chip firmware */
_info("firmware size is %d bytes\n", bcmf_firmware_image_len);
ret = bcmf_upload_binary(priv, 0, (uint8_t*)bcmf_firmware_image,
bcmf_firmware_image_len);
if (ret != OK)
{
_err("Failed to upload firmware\n");
return ret;
}
/* Flash NVRAM configuration file */
_info("upload nvram configuration\n");
ret = bcmf_upload_nvram(priv);
if (ret != OK)
{
_err("Failed to upload nvram\n");
return ret;
}
/* Firmware upload done, restart ARMCM3 core */
up_mdelay(10);
bcmf_core_reset(priv, WLAN_ARMCM3_CORE_ID);
/* Check ARMCM3 core is running */
up_mdelay(10);
if (!bcmf_core_isup(priv, WLAN_ARMCM3_CORE_ID))
{
_err("Cannot start ARMCM3 core\n");
return -ETIMEDOUT;
}
return OK;
}
bool bcmf_core_isup(FAR struct bcmf_dev_s *priv, unsigned int core)
{
uint32_t value = 0;
uint32_t base = priv->get_core_base_address(core);
bcmf_read_sbregw(priv, base + BCMA_IOCTL, &value);
if ((value & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) != BCMA_IOCTL_CLK)
{
return false;
}
bcmf_read_sbregw(priv, base + BCMA_RESET_CTL, &value);
return (value & BCMA_RESET_CTL_RESET) == 0;
}
void bcmf_core_disable(FAR struct bcmf_dev_s *priv, unsigned int core)
{
uint8_t value;
uint32_t base = priv->get_core_base_address(core);
/* Check if core is already in reset state */
bcmf_read_sbregb(priv, 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(priv, base + BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
up_udelay(1);
/* Write 0 to the IO control and read it back */
bcmf_write_sbregb(priv, base + BCMA_IOCTL, 0);
bcmf_read_sbregb(priv, base + BCMA_IOCTL, &value);
up_udelay(10);
}
void bcmf_core_reset(FAR struct bcmf_dev_s *priv, unsigned int core)
{
uint32_t value;
uint32_t base = priv->get_core_base_address(core);
/* Put core in reset state */
bcmf_core_disable(priv, core);
/* Run initialization sequence */
bcmf_write_sbregb(priv, base + BCMA_IOCTL, BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
bcmf_read_sbregw(priv, base + BCMA_IOCTL, &value);
bcmf_write_sbregb(priv, base + BCMA_RESET_CTL, 0);
bcmf_read_sbregw(priv, base + BCMA_RESET_CTL, &value);
up_udelay(1);
bcmf_write_sbregb(priv, base + BCMA_IOCTL, BCMA_IOCTL_CLK);
bcmf_read_sbregw(priv, base + BCMA_IOCTL, &value);
up_udelay(1);
}