2013-05-24 20:04:38 -04:00
/*
* Firmware I / O code for mac80211 ST - Ericsson CW1200 drivers
*
* Copyright ( c ) 2010 , ST - Ericsson
* Author : Dmitry Tarnyagin < dmitry . tarnyagin @ lockless . no >
*
* Based on :
* ST - Ericsson UMAC CW1200 driver which is
* Copyright ( c ) 2010 , ST - Ericsson
* Author : Ajitpal Singh < ajitpal . singh @ stericsson . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/vmalloc.h>
# include <linux/sched.h>
# include <linux/firmware.h>
# include "cw1200.h"
# include "fwio.h"
# include "hwio.h"
2013-06-01 08:08:42 -04:00
# include "hwbus.h"
2013-05-24 20:04:38 -04:00
# include "bh.h"
static int cw1200_get_hw_type ( u32 config_reg_val , int * major_revision )
{
int hw_type = - 1 ;
u32 silicon_type = ( config_reg_val > > 24 ) & 0x7 ;
u32 silicon_vers = ( config_reg_val > > 31 ) & 0x1 ;
switch ( silicon_type ) {
case 0x00 :
* major_revision = 1 ;
hw_type = HIF_9000_SILICON_VERSATILE ;
break ;
case 0x01 :
case 0x02 : /* CW1x00 */
case 0x04 : /* CW1x60 */
* major_revision = silicon_type ;
if ( silicon_vers )
hw_type = HIF_8601_VERSATILE ;
else
hw_type = HIF_8601_SILICON ;
break ;
default :
break ;
}
return hw_type ;
}
static int cw1200_load_firmware_cw1200 ( struct cw1200_common * priv )
{
int ret , block , num_blocks ;
unsigned i ;
u32 val32 ;
u32 put = 0 , get = 0 ;
u8 * buf = NULL ;
const char * fw_path ;
const struct firmware * firmware = NULL ;
/* Macroses are local. */
# define APB_WRITE(reg, val) \
do { \
ret = cw1200_apb_write_32 ( priv , CW1200_APB ( reg ) , ( val ) ) ; \
if ( ret < 0 ) \
2015-02-04 17:28:41 +01:00
goto exit ; \
} while ( 0 )
# define APB_WRITE2(reg, val) \
do { \
ret = cw1200_apb_write_32 ( priv , CW1200_APB ( reg ) , ( val ) ) ; \
if ( ret < 0 ) \
goto free_buffer ; \
2013-05-24 20:04:38 -04:00
} while ( 0 )
# define APB_READ(reg, val) \
do { \
ret = cw1200_apb_read_32 ( priv , CW1200_APB ( reg ) , & ( val ) ) ; \
if ( ret < 0 ) \
2015-02-04 17:28:41 +01:00
goto free_buffer ; \
2013-05-24 20:04:38 -04:00
} while ( 0 )
# define REG_WRITE(reg, val) \
do { \
ret = cw1200_reg_write_32 ( priv , ( reg ) , ( val ) ) ; \
if ( ret < 0 ) \
2015-02-04 17:28:41 +01:00
goto exit ; \
2013-05-24 20:04:38 -04:00
} while ( 0 )
# define REG_READ(reg, val) \
do { \
ret = cw1200_reg_read_32 ( priv , ( reg ) , & ( val ) ) ; \
if ( ret < 0 ) \
2015-02-04 17:28:41 +01:00
goto exit ; \
2013-05-24 20:04:38 -04:00
} while ( 0 )
switch ( priv - > hw_revision ) {
case CW1200_HW_REV_CUT10 :
fw_path = FIRMWARE_CUT10 ;
if ( ! priv - > sdd_path )
priv - > sdd_path = SDD_FILE_10 ;
break ;
case CW1200_HW_REV_CUT11 :
fw_path = FIRMWARE_CUT11 ;
if ( ! priv - > sdd_path )
priv - > sdd_path = SDD_FILE_11 ;
break ;
case CW1200_HW_REV_CUT20 :
fw_path = FIRMWARE_CUT20 ;
if ( ! priv - > sdd_path )
priv - > sdd_path = SDD_FILE_20 ;
break ;
case CW1200_HW_REV_CUT22 :
fw_path = FIRMWARE_CUT22 ;
if ( ! priv - > sdd_path )
priv - > sdd_path = SDD_FILE_22 ;
break ;
case CW1X60_HW_REV :
fw_path = FIRMWARE_CW1X60 ;
if ( ! priv - > sdd_path )
priv - > sdd_path = SDD_FILE_CW1X60 ;
break ;
default :
pr_err ( " Invalid silicon revision %d. \n " , priv - > hw_revision ) ;
return - EINVAL ;
}
/* Initialize common registers */
APB_WRITE ( DOWNLOAD_IMAGE_SIZE_REG , DOWNLOAD_ARE_YOU_HERE ) ;
APB_WRITE ( DOWNLOAD_PUT_REG , 0 ) ;
APB_WRITE ( DOWNLOAD_GET_REG , 0 ) ;
APB_WRITE ( DOWNLOAD_STATUS_REG , DOWNLOAD_PENDING ) ;
APB_WRITE ( DOWNLOAD_FLAGS_REG , 0 ) ;
/* Write the NOP Instruction */
REG_WRITE ( ST90TDS_SRAM_BASE_ADDR_REG_ID , 0xFFF20000 ) ;
REG_WRITE ( ST90TDS_AHB_DPORT_REG_ID , 0xEAFFFFFE ) ;
/* Release CPU from RESET */
REG_READ ( ST90TDS_CONFIG_REG_ID , val32 ) ;
val32 & = ~ ST90TDS_CONFIG_CPU_RESET_BIT ;
REG_WRITE ( ST90TDS_CONFIG_REG_ID , val32 ) ;
/* Enable Clock */
val32 & = ~ ST90TDS_CONFIG_CPU_CLK_DIS_BIT ;
REG_WRITE ( ST90TDS_CONFIG_REG_ID , val32 ) ;
/* Load a firmware file */
ret = request_firmware ( & firmware , fw_path , priv - > pdev ) ;
if ( ret ) {
pr_err ( " Can't load firmware file %s. \n " , fw_path ) ;
2015-02-04 17:28:41 +01:00
goto exit ;
2013-05-24 20:04:38 -04:00
}
buf = kmalloc ( DOWNLOAD_BLOCK_SIZE , GFP_KERNEL | GFP_DMA ) ;
if ( ! buf ) {
pr_err ( " Can't allocate firmware load buffer. \n " ) ;
ret = - ENOMEM ;
2015-02-04 17:28:41 +01:00
goto firmware_release ;
2013-05-24 20:04:38 -04:00
}
/* Check if the bootloader is ready */
for ( i = 0 ; i < 100 ; i + = 1 + i / 2 ) {
APB_READ ( DOWNLOAD_IMAGE_SIZE_REG , val32 ) ;
if ( val32 = = DOWNLOAD_I_AM_HERE )
break ;
mdelay ( i ) ;
} /* End of for loop */
if ( val32 ! = DOWNLOAD_I_AM_HERE ) {
pr_err ( " Bootloader is not ready. \n " ) ;
ret = - ETIMEDOUT ;
2015-02-04 17:28:41 +01:00
goto free_buffer ;
2013-05-24 20:04:38 -04:00
}
/* Calculcate number of download blocks */
num_blocks = ( firmware - > size - 1 ) / DOWNLOAD_BLOCK_SIZE + 1 ;
/* Updating the length in Download Ctrl Area */
val32 = firmware - > size ; /* Explicit cast from size_t to u32 */
2015-02-04 17:28:41 +01:00
APB_WRITE2 ( DOWNLOAD_IMAGE_SIZE_REG , val32 ) ;
2013-05-24 20:04:38 -04:00
/* Firmware downloading loop */
for ( block = 0 ; block < num_blocks ; block + + ) {
size_t tx_size ;
size_t block_size ;
/* check the download status */
APB_READ ( DOWNLOAD_STATUS_REG , val32 ) ;
if ( val32 ! = DOWNLOAD_PENDING ) {
pr_err ( " Bootloader reported error %d. \n " , val32 ) ;
ret = - EIO ;
2015-02-04 17:28:41 +01:00
goto free_buffer ;
2013-05-24 20:04:38 -04:00
}
/* loop until put - get <= 24K */
for ( i = 0 ; i < 100 ; i + + ) {
APB_READ ( DOWNLOAD_GET_REG , get ) ;
if ( ( put - get ) < =
( DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE ) )
break ;
mdelay ( i ) ;
}
if ( ( put - get ) > ( DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE ) ) {
pr_err ( " Timeout waiting for FIFO. \n " ) ;
ret = - ETIMEDOUT ;
2015-02-04 17:28:41 +01:00
goto free_buffer ;
2013-05-24 20:04:38 -04:00
}
/* calculate the block size */
2014-02-25 18:12:52 +01:00
tx_size = block_size = min_t ( size_t , firmware - > size - put ,
DOWNLOAD_BLOCK_SIZE ) ;
2013-05-24 20:04:38 -04:00
memcpy ( buf , & firmware - > data [ put ] , block_size ) ;
if ( block_size < DOWNLOAD_BLOCK_SIZE ) {
memset ( & buf [ block_size ] , 0 ,
DOWNLOAD_BLOCK_SIZE - block_size ) ;
tx_size = DOWNLOAD_BLOCK_SIZE ;
}
/* send the block to sram */
ret = cw1200_apb_write ( priv ,
CW1200_APB ( DOWNLOAD_FIFO_OFFSET +
( put & ( DOWNLOAD_FIFO_SIZE - 1 ) ) ) ,
buf , tx_size ) ;
if ( ret < 0 ) {
pr_err ( " Can't write firmware block @ %d! \n " ,
put & ( DOWNLOAD_FIFO_SIZE - 1 ) ) ;
2015-02-04 17:28:41 +01:00
goto free_buffer ;
2013-05-24 20:04:38 -04:00
}
/* update the put register */
put + = block_size ;
2015-02-04 17:28:41 +01:00
APB_WRITE2 ( DOWNLOAD_PUT_REG , put ) ;
2013-05-24 20:04:38 -04:00
} /* End of firmware download loop */
/* Wait for the download completion */
for ( i = 0 ; i < 300 ; i + = 1 + i / 2 ) {
APB_READ ( DOWNLOAD_STATUS_REG , val32 ) ;
if ( val32 ! = DOWNLOAD_PENDING )
break ;
mdelay ( i ) ;
}
if ( val32 ! = DOWNLOAD_SUCCESS ) {
pr_err ( " Wait for download completion failed: 0x%.8X \n " , val32 ) ;
ret = - ETIMEDOUT ;
2015-02-04 17:28:41 +01:00
goto free_buffer ;
2013-05-24 20:04:38 -04:00
} else {
pr_info ( " Firmware download completed. \n " ) ;
ret = 0 ;
}
2015-02-04 17:28:41 +01:00
free_buffer :
2013-05-24 20:04:38 -04:00
kfree ( buf ) ;
2015-02-04 17:28:41 +01:00
firmware_release :
2015-02-04 16:32:15 +01:00
release_firmware ( firmware ) ;
2015-02-04 17:28:41 +01:00
exit :
2013-05-24 20:04:38 -04:00
return ret ;
# undef APB_WRITE
2015-02-04 17:28:41 +01:00
# undef APB_WRITE2
2013-05-24 20:04:38 -04:00
# undef APB_READ
# undef REG_WRITE
# undef REG_READ
}
static int config_reg_read ( struct cw1200_common * priv , u32 * val )
{
switch ( priv - > hw_type ) {
case HIF_9000_SILICON_VERSATILE : {
u16 val16 ;
int ret = cw1200_reg_read_16 ( priv ,
ST90TDS_CONFIG_REG_ID ,
& val16 ) ;
if ( ret < 0 )
return ret ;
* val = val16 ;
return 0 ;
}
case HIF_8601_VERSATILE :
case HIF_8601_SILICON :
default :
cw1200_reg_read_32 ( priv , ST90TDS_CONFIG_REG_ID , val ) ;
break ;
}
return 0 ;
}
static int config_reg_write ( struct cw1200_common * priv , u32 val )
{
switch ( priv - > hw_type ) {
case HIF_9000_SILICON_VERSATILE :
return cw1200_reg_write_16 ( priv ,
ST90TDS_CONFIG_REG_ID ,
( u16 ) val ) ;
case HIF_8601_VERSATILE :
case HIF_8601_SILICON :
default :
return cw1200_reg_write_32 ( priv , ST90TDS_CONFIG_REG_ID , val ) ;
}
return 0 ;
}
int cw1200_load_firmware ( struct cw1200_common * priv )
{
int ret ;
int i ;
u32 val32 ;
u16 val16 ;
int major_revision = - 1 ;
/* Read CONFIG Register */
ret = cw1200_reg_read_32 ( priv , ST90TDS_CONFIG_REG_ID , & val32 ) ;
if ( ret < 0 ) {
pr_err ( " Can't read config register. \n " ) ;
goto out ;
}
if ( val32 = = 0 | | val32 = = 0xffffffff ) {
pr_err ( " Bad config register value (0x%08x) \n " , val32 ) ;
ret = - EIO ;
goto out ;
}
priv - > hw_type = cw1200_get_hw_type ( val32 , & major_revision ) ;
if ( priv - > hw_type < 0 ) {
pr_err ( " Can't deduce hardware type. \n " ) ;
ret = - ENOTSUPP ;
goto out ;
}
/* Set DPLL Reg value, and read back to confirm writes work */
ret = cw1200_reg_write_32 ( priv , ST90TDS_TSET_GEN_R_W_REG_ID ,
cw1200_dpll_from_clk ( priv - > hw_refclk ) ) ;
if ( ret < 0 ) {
pr_err ( " Can't write DPLL register. \n " ) ;
goto out ;
}
msleep ( 20 ) ;
ret = cw1200_reg_read_32 ( priv ,
ST90TDS_TSET_GEN_R_W_REG_ID , & val32 ) ;
if ( ret < 0 ) {
pr_err ( " Can't read DPLL register. \n " ) ;
goto out ;
}
if ( val32 ! = cw1200_dpll_from_clk ( priv - > hw_refclk ) ) {
pr_err ( " Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X. \n " ,
cw1200_dpll_from_clk ( priv - > hw_refclk ) , val32 ) ;
ret = - EIO ;
goto out ;
}
/* Set wakeup bit in device */
ret = cw1200_reg_read_16 ( priv , ST90TDS_CONTROL_REG_ID , & val16 ) ;
if ( ret < 0 ) {
pr_err ( " set_wakeup: can't read control register. \n " ) ;
goto out ;
}
ret = cw1200_reg_write_16 ( priv , ST90TDS_CONTROL_REG_ID ,
val16 | ST90TDS_CONT_WUP_BIT ) ;
if ( ret < 0 ) {
pr_err ( " set_wakeup: can't write control register. \n " ) ;
goto out ;
}
/* Wait for wakeup */
for ( i = 0 ; i < 300 ; i + = ( 1 + i / 2 ) ) {
ret = cw1200_reg_read_16 ( priv ,
ST90TDS_CONTROL_REG_ID , & val16 ) ;
if ( ret < 0 ) {
pr_err ( " wait_for_wakeup: can't read control register. \n " ) ;
goto out ;
}
if ( val16 & ST90TDS_CONT_RDY_BIT )
break ;
msleep ( i ) ;
}
if ( ( val16 & ST90TDS_CONT_RDY_BIT ) = = 0 ) {
pr_err ( " wait_for_wakeup: device is not responding. \n " ) ;
ret = - ETIMEDOUT ;
goto out ;
}
switch ( major_revision ) {
case 1 :
/* CW1200 Hardware detection logic : Check for CUT1.1 */
ret = cw1200_ahb_read_32 ( priv , CW1200_CUT_ID_ADDR , & val32 ) ;
if ( ret ) {
pr_err ( " HW detection: can't read CUT ID. \n " ) ;
goto out ;
}
switch ( val32 ) {
case CW1200_CUT_11_ID_STR :
pr_info ( " CW1x00 Cut 1.1 silicon detected. \n " ) ;
priv - > hw_revision = CW1200_HW_REV_CUT11 ;
break ;
default :
pr_info ( " CW1x00 Cut 1.0 silicon detected. \n " ) ;
priv - > hw_revision = CW1200_HW_REV_CUT10 ;
break ;
}
/* According to ST-E, CUT<2.0 has busted BA TID0-3.
Just disable it entirely . . .
*/
priv - > ba_rx_tid_mask = 0 ;
priv - > ba_tx_tid_mask = 0 ;
break ;
case 2 : {
u32 ar1 , ar2 , ar3 ;
ret = cw1200_ahb_read_32 ( priv , CW1200_CUT2_ID_ADDR , & ar1 ) ;
if ( ret ) {
pr_err ( " (1) HW detection: can't read CUT ID \n " ) ;
goto out ;
}
ret = cw1200_ahb_read_32 ( priv , CW1200_CUT2_ID_ADDR + 4 , & ar2 ) ;
if ( ret ) {
pr_err ( " (2) HW detection: can't read CUT ID. \n " ) ;
goto out ;
}
ret = cw1200_ahb_read_32 ( priv , CW1200_CUT2_ID_ADDR + 8 , & ar3 ) ;
if ( ret ) {
pr_err ( " (3) HW detection: can't read CUT ID. \n " ) ;
goto out ;
}
if ( ar1 = = CW1200_CUT_22_ID_STR1 & &
ar2 = = CW1200_CUT_22_ID_STR2 & &
ar3 = = CW1200_CUT_22_ID_STR3 ) {
pr_info ( " CW1x00 Cut 2.2 silicon detected. \n " ) ;
priv - > hw_revision = CW1200_HW_REV_CUT22 ;
} else {
pr_info ( " CW1x00 Cut 2.0 silicon detected. \n " ) ;
priv - > hw_revision = CW1200_HW_REV_CUT20 ;
}
break ;
}
case 4 :
pr_info ( " CW1x60 silicon detected. \n " ) ;
priv - > hw_revision = CW1X60_HW_REV ;
break ;
default :
pr_err ( " Unsupported silicon major revision %d. \n " ,
major_revision ) ;
ret = - ENOTSUPP ;
goto out ;
}
/* Checking for access mode */
ret = config_reg_read ( priv , & val32 ) ;
if ( ret < 0 ) {
pr_err ( " Can't read config register. \n " ) ;
goto out ;
}
if ( ! ( val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT ) ) {
pr_err ( " Device is already in QUEUE mode! \n " ) ;
ret = - EINVAL ;
goto out ;
}
switch ( priv - > hw_type ) {
case HIF_8601_SILICON :
if ( priv - > hw_revision = = CW1X60_HW_REV ) {
pr_err ( " Can't handle CW1160/1260 firmware load yet. \n " ) ;
ret = - ENOTSUPP ;
goto out ;
}
ret = cw1200_load_firmware_cw1200 ( priv ) ;
break ;
default :
pr_err ( " Can't perform firmware load for hw type %d. \n " ,
priv - > hw_type ) ;
ret = - ENOTSUPP ;
goto out ;
}
if ( ret < 0 ) {
pr_err ( " Firmware load error. \n " ) ;
goto out ;
}
/* Enable interrupt signalling */
2013-06-01 08:08:42 -04:00
priv - > hwbus_ops - > lock ( priv - > hwbus_priv ) ;
2013-09-23 16:00:03 -04:00
ret = __cw1200_irq_enable ( priv , 1 ) ;
2013-06-01 08:08:42 -04:00
priv - > hwbus_ops - > unlock ( priv - > hwbus_priv ) ;
2013-05-24 20:04:38 -04:00
if ( ret < 0 )
goto unsubscribe ;
/* Configure device for MESSSAGE MODE */
ret = config_reg_read ( priv , & val32 ) ;
if ( ret < 0 ) {
pr_err ( " Can't read config register. \n " ) ;
goto unsubscribe ;
}
ret = config_reg_write ( priv , val32 & ~ ST90TDS_CONFIG_ACCESS_MODE_BIT ) ;
if ( ret < 0 ) {
pr_err ( " Can't write config register. \n " ) ;
goto unsubscribe ;
}
/* Unless we read the CONFIG Register we are
* not able to get an interrupt
*/
mdelay ( 10 ) ;
config_reg_read ( priv , & val32 ) ;
out :
return ret ;
unsubscribe :
/* Disable interrupt signalling */
2013-06-01 08:08:42 -04:00
priv - > hwbus_ops - > lock ( priv - > hwbus_priv ) ;
2013-05-24 20:04:38 -04:00
ret = __cw1200_irq_enable ( priv , 0 ) ;
2013-06-01 08:08:42 -04:00
priv - > hwbus_ops - > unlock ( priv - > hwbus_priv ) ;
2013-05-24 20:04:38 -04:00
return ret ;
}