2007-09-18 23:12:50 +04:00
/*
* Sonics Silicon Backplane PCI - Hostbus related functions .
*
* Copyright ( C ) 2005 - 2006 Michael Buesch < mb @ bu3sch . de >
* Copyright ( C ) 2005 Martin Langer < martin - langer @ gmx . de >
* Copyright ( C ) 2005 Stefano Brivio < st3 @ riseup . net >
* Copyright ( C ) 2005 Danny van Dyk < kugelfang @ gentoo . org >
* Copyright ( C ) 2005 Andreas Jaggi < andreas . jaggi @ waterwave . ch >
*
* Derived from the Broadcom 4400 device driver .
* Copyright ( C ) 2002 David S . Miller ( davem @ redhat . com )
* Fixed by Pekka Pietikainen ( pp @ ee . oulu . fi )
* Copyright ( C ) 2006 Broadcom Corporation .
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# include <linux/ssb/ssb.h>
# include <linux/ssb/ssb_regs.h>
# include <linux/pci.h>
# include <linux/delay.h>
# include "ssb_private.h"
/* Define the following to 1 to enable a printk on each coreswitch. */
# define SSB_VERBOSE_PCICORESWITCH_DEBUG 0
/* Lowlevel coreswitching */
int ssb_pci_switch_coreidx ( struct ssb_bus * bus , u8 coreidx )
{
int err ;
int attempts = 0 ;
u32 cur_core ;
while ( 1 ) {
err = pci_write_config_dword ( bus - > host_pci , SSB_BAR0_WIN ,
( coreidx * SSB_CORE_SIZE )
+ SSB_ENUM_BASE ) ;
if ( err )
goto error ;
err = pci_read_config_dword ( bus - > host_pci , SSB_BAR0_WIN ,
& cur_core ) ;
if ( err )
goto error ;
cur_core = ( cur_core - SSB_ENUM_BASE )
/ SSB_CORE_SIZE ;
if ( cur_core = = coreidx )
break ;
if ( attempts + + > SSB_BAR0_MAX_RETRIES )
goto error ;
udelay ( 10 ) ;
}
return 0 ;
error :
ssb_printk ( KERN_ERR PFX " Failed to switch to core %u \n " , coreidx ) ;
return - ENODEV ;
}
int ssb_pci_switch_core ( struct ssb_bus * bus ,
struct ssb_device * dev )
{
int err ;
unsigned long flags ;
# if SSB_VERBOSE_PCICORESWITCH_DEBUG
ssb_printk ( KERN_INFO PFX
" Switching to %s core, index %d \n " ,
ssb_core_name ( dev - > id . coreid ) ,
dev - > core_index ) ;
# endif
spin_lock_irqsave ( & bus - > bar_lock , flags ) ;
err = ssb_pci_switch_coreidx ( bus , dev - > core_index ) ;
if ( ! err )
bus - > mapped_device = dev ;
spin_unlock_irqrestore ( & bus - > bar_lock , flags ) ;
return err ;
}
/* Enable/disable the on board crystal oscillator and/or PLL. */
int ssb_pci_xtal ( struct ssb_bus * bus , u32 what , int turn_on )
{
int err ;
u32 in , out , outenable ;
u16 pci_status ;
if ( bus - > bustype ! = SSB_BUSTYPE_PCI )
return 0 ;
err = pci_read_config_dword ( bus - > host_pci , SSB_GPIO_IN , & in ) ;
if ( err )
goto err_pci ;
err = pci_read_config_dword ( bus - > host_pci , SSB_GPIO_OUT , & out ) ;
if ( err )
goto err_pci ;
err = pci_read_config_dword ( bus - > host_pci , SSB_GPIO_OUT_ENABLE , & outenable ) ;
if ( err )
goto err_pci ;
outenable | = what ;
if ( turn_on ) {
/* Avoid glitching the clock if GPRS is already using it.
* We can ' t actually read the state of the PLLPD so we infer it
* by the value of XTAL_PU which * is * readable via gpioin .
*/
if ( ! ( in & SSB_GPIO_XTAL ) ) {
if ( what & SSB_GPIO_XTAL ) {
/* Turn the crystal on */
out | = SSB_GPIO_XTAL ;
if ( what & SSB_GPIO_PLL )
out | = SSB_GPIO_PLL ;
err = pci_write_config_dword ( bus - > host_pci , SSB_GPIO_OUT , out ) ;
if ( err )
goto err_pci ;
err = pci_write_config_dword ( bus - > host_pci , SSB_GPIO_OUT_ENABLE ,
outenable ) ;
if ( err )
goto err_pci ;
msleep ( 1 ) ;
}
if ( what & SSB_GPIO_PLL ) {
/* Turn the PLL on */
out & = ~ SSB_GPIO_PLL ;
err = pci_write_config_dword ( bus - > host_pci , SSB_GPIO_OUT , out ) ;
if ( err )
goto err_pci ;
msleep ( 5 ) ;
}
}
err = pci_read_config_word ( bus - > host_pci , PCI_STATUS , & pci_status ) ;
if ( err )
goto err_pci ;
pci_status & = ~ PCI_STATUS_SIG_TARGET_ABORT ;
err = pci_write_config_word ( bus - > host_pci , PCI_STATUS , pci_status ) ;
if ( err )
goto err_pci ;
} else {
if ( what & SSB_GPIO_XTAL ) {
/* Turn the crystal off */
out & = ~ SSB_GPIO_XTAL ;
}
if ( what & SSB_GPIO_PLL ) {
/* Turn the PLL off */
out | = SSB_GPIO_PLL ;
}
err = pci_write_config_dword ( bus - > host_pci , SSB_GPIO_OUT , out ) ;
if ( err )
goto err_pci ;
err = pci_write_config_dword ( bus - > host_pci , SSB_GPIO_OUT_ENABLE , outenable ) ;
if ( err )
goto err_pci ;
}
out :
return err ;
err_pci :
printk ( KERN_ERR PFX " Error: ssb_pci_xtal() could not access PCI config space! \n " ) ;
err = - EBUSY ;
goto out ;
}
/* Get the word-offset for a SSB_SPROM_XXX define. */
# define SPOFF(offset) (((offset) - SSB_SPROM_BASE) / sizeof(u16))
/* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
# define SPEX(_outvar, _offset, _mask, _shift) \
out - > _outvar = ( ( in [ SPOFF ( _offset ) ] & ( _mask ) ) > > ( _shift ) )
static inline u8 ssb_crc8 ( u8 crc , u8 data )
{
/* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */
static const u8 t [ ] = {
0x00 , 0xF7 , 0xB9 , 0x4E , 0x25 , 0xD2 , 0x9C , 0x6B ,
0x4A , 0xBD , 0xF3 , 0x04 , 0x6F , 0x98 , 0xD6 , 0x21 ,
0x94 , 0x63 , 0x2D , 0xDA , 0xB1 , 0x46 , 0x08 , 0xFF ,
0xDE , 0x29 , 0x67 , 0x90 , 0xFB , 0x0C , 0x42 , 0xB5 ,
0x7F , 0x88 , 0xC6 , 0x31 , 0x5A , 0xAD , 0xE3 , 0x14 ,
0x35 , 0xC2 , 0x8C , 0x7B , 0x10 , 0xE7 , 0xA9 , 0x5E ,
0xEB , 0x1C , 0x52 , 0xA5 , 0xCE , 0x39 , 0x77 , 0x80 ,
0xA1 , 0x56 , 0x18 , 0xEF , 0x84 , 0x73 , 0x3D , 0xCA ,
0xFE , 0x09 , 0x47 , 0xB0 , 0xDB , 0x2C , 0x62 , 0x95 ,
0xB4 , 0x43 , 0x0D , 0xFA , 0x91 , 0x66 , 0x28 , 0xDF ,
0x6A , 0x9D , 0xD3 , 0x24 , 0x4F , 0xB8 , 0xF6 , 0x01 ,
0x20 , 0xD7 , 0x99 , 0x6E , 0x05 , 0xF2 , 0xBC , 0x4B ,
0x81 , 0x76 , 0x38 , 0xCF , 0xA4 , 0x53 , 0x1D , 0xEA ,
0xCB , 0x3C , 0x72 , 0x85 , 0xEE , 0x19 , 0x57 , 0xA0 ,
0x15 , 0xE2 , 0xAC , 0x5B , 0x30 , 0xC7 , 0x89 , 0x7E ,
0x5F , 0xA8 , 0xE6 , 0x11 , 0x7A , 0x8D , 0xC3 , 0x34 ,
0xAB , 0x5C , 0x12 , 0xE5 , 0x8E , 0x79 , 0x37 , 0xC0 ,
0xE1 , 0x16 , 0x58 , 0xAF , 0xC4 , 0x33 , 0x7D , 0x8A ,
0x3F , 0xC8 , 0x86 , 0x71 , 0x1A , 0xED , 0xA3 , 0x54 ,
0x75 , 0x82 , 0xCC , 0x3B , 0x50 , 0xA7 , 0xE9 , 0x1E ,
0xD4 , 0x23 , 0x6D , 0x9A , 0xF1 , 0x06 , 0x48 , 0xBF ,
0x9E , 0x69 , 0x27 , 0xD0 , 0xBB , 0x4C , 0x02 , 0xF5 ,
0x40 , 0xB7 , 0xF9 , 0x0E , 0x65 , 0x92 , 0xDC , 0x2B ,
0x0A , 0xFD , 0xB3 , 0x44 , 0x2F , 0xD8 , 0x96 , 0x61 ,
0x55 , 0xA2 , 0xEC , 0x1B , 0x70 , 0x87 , 0xC9 , 0x3E ,
0x1F , 0xE8 , 0xA6 , 0x51 , 0x3A , 0xCD , 0x83 , 0x74 ,
0xC1 , 0x36 , 0x78 , 0x8F , 0xE4 , 0x13 , 0x5D , 0xAA ,
0x8B , 0x7C , 0x32 , 0xC5 , 0xAE , 0x59 , 0x17 , 0xE0 ,
0x2A , 0xDD , 0x93 , 0x64 , 0x0F , 0xF8 , 0xB6 , 0x41 ,
0x60 , 0x97 , 0xD9 , 0x2E , 0x45 , 0xB2 , 0xFC , 0x0B ,
0xBE , 0x49 , 0x07 , 0xF0 , 0x9B , 0x6C , 0x22 , 0xD5 ,
0xF4 , 0x03 , 0x4D , 0xBA , 0xD1 , 0x26 , 0x68 , 0x9F ,
} ;
return t [ crc ^ data ] ;
}
2007-11-10 01:56:25 +03:00
static u8 ssb_sprom_crc ( const u16 * sprom , u16 size )
2007-09-18 23:12:50 +04:00
{
int word ;
u8 crc = 0xFF ;
2007-11-10 01:56:25 +03:00
for ( word = 0 ; word < size - 1 ; word + + ) {
2007-09-18 23:12:50 +04:00
crc = ssb_crc8 ( crc , sprom [ word ] & 0x00FF ) ;
crc = ssb_crc8 ( crc , ( sprom [ word ] & 0xFF00 ) > > 8 ) ;
}
2007-11-10 01:56:25 +03:00
crc = ssb_crc8 ( crc , sprom [ size - 1 ] & 0x00FF ) ;
2007-09-18 23:12:50 +04:00
crc ^ = 0xFF ;
return crc ;
}
2008-03-10 19:26:32 +03:00
static int sprom_check_crc ( const u16 * sprom , size_t size )
2007-09-18 23:12:50 +04:00
{
u8 crc ;
u8 expected_crc ;
u16 tmp ;
2007-11-10 01:56:25 +03:00
crc = ssb_sprom_crc ( sprom , size ) ;
tmp = sprom [ size - 1 ] & SSB_SPROM_REVISION_CRC ;
2007-09-18 23:12:50 +04:00
expected_crc = tmp > > SSB_SPROM_REVISION_CRC_SHIFT ;
if ( crc ! = expected_crc )
return - EPROTO ;
return 0 ;
}
2008-03-10 19:26:32 +03:00
static int sprom_do_read ( struct ssb_bus * bus , u16 * sprom )
2007-09-18 23:12:50 +04:00
{
int i ;
2007-11-10 01:56:25 +03:00
for ( i = 0 ; i < bus - > sprom_size ; i + + )
2007-12-22 23:51:30 +03:00
sprom [ i ] = ioread16 ( bus - > mmio + SSB_SPROM_BASE + ( i * 2 ) ) ;
2008-03-10 19:26:32 +03:00
return 0 ;
2007-09-18 23:12:50 +04:00
}
static int sprom_do_write ( struct ssb_bus * bus , const u16 * sprom )
{
struct pci_dev * pdev = bus - > host_pci ;
int i , err ;
u32 spromctl ;
2007-11-10 01:56:25 +03:00
u16 size = bus - > sprom_size ;
2007-09-18 23:12:50 +04:00
ssb_printk ( KERN_NOTICE PFX " Writing SPROM. Do NOT turn off the power! Please stand by... \n " ) ;
err = pci_read_config_dword ( pdev , SSB_SPROMCTL , & spromctl ) ;
if ( err )
goto err_ctlreg ;
spromctl | = SSB_SPROMCTL_WE ;
err = pci_write_config_dword ( pdev , SSB_SPROMCTL , spromctl ) ;
if ( err )
goto err_ctlreg ;
ssb_printk ( KERN_NOTICE PFX " [ 0%% " ) ;
msleep ( 500 ) ;
2007-11-10 01:56:25 +03:00
for ( i = 0 ; i < size ; i + + ) {
if ( i = = size / 4 )
2007-09-18 23:12:50 +04:00
ssb_printk ( " 25%% " ) ;
2007-11-10 01:56:25 +03:00
else if ( i = = size / 2 )
2007-09-18 23:12:50 +04:00
ssb_printk ( " 50%% " ) ;
2007-11-10 01:56:25 +03:00
else if ( i = = ( size * 3 ) / 4 )
2007-09-18 23:12:50 +04:00
ssb_printk ( " 75%% " ) ;
else if ( i % 2 )
ssb_printk ( " . " ) ;
writew ( sprom [ i ] , bus - > mmio + SSB_SPROM_BASE + ( i * 2 ) ) ;
mmiowb ( ) ;
msleep ( 20 ) ;
}
err = pci_read_config_dword ( pdev , SSB_SPROMCTL , & spromctl ) ;
if ( err )
goto err_ctlreg ;
spromctl & = ~ SSB_SPROMCTL_WE ;
err = pci_write_config_dword ( pdev , SSB_SPROMCTL , spromctl ) ;
if ( err )
goto err_ctlreg ;
msleep ( 500 ) ;
ssb_printk ( " 100%% ] \n " ) ;
ssb_printk ( KERN_NOTICE PFX " SPROM written. \n " ) ;
return 0 ;
err_ctlreg :
ssb_printk ( KERN_ERR PFX " Could not access SPROM control register. \n " ) ;
return err ;
}
2007-12-22 23:51:30 +03:00
static s8 r123_extract_antgain ( u8 sprom_revision , const u16 * in ,
u16 mask , u16 shift )
{
u16 v ;
u8 gain ;
v = in [ SPOFF ( SSB_SPROM1_AGAIN ) ] ;
gain = ( v & mask ) > > shift ;
if ( gain = = 0xFF )
gain = 2 ; /* If unset use 2dBm */
if ( sprom_revision = = 1 ) {
/* Convert to Q5.2 */
gain < < = 2 ;
} else {
/* Q5.2 Fractional part is stored in 0xC0 */
gain = ( ( gain & 0xC0 ) > > 6 ) | ( ( gain & 0x3F ) < < 2 ) ;
}
return ( s8 ) gain ;
}
2007-11-10 01:56:25 +03:00
static void sprom_extract_r123 ( struct ssb_sprom * out , const u16 * in )
2007-09-18 23:12:50 +04:00
{
int i ;
u16 v ;
2007-12-22 23:51:30 +03:00
s8 gain ;
2007-11-10 01:56:25 +03:00
u16 loc [ 3 ] ;
2007-09-18 23:12:50 +04:00
2008-08-21 02:45:06 +04:00
if ( out - > revision = = 3 ) /* rev 3 moved MAC */
2007-11-10 01:56:25 +03:00
loc [ 0 ] = SSB_SPROM3_IL0MAC ;
2008-08-21 02:45:06 +04:00
else {
2007-11-10 01:56:25 +03:00
loc [ 0 ] = SSB_SPROM1_IL0MAC ;
loc [ 1 ] = SSB_SPROM1_ET0MAC ;
loc [ 2 ] = SSB_SPROM1_ET1MAC ;
}
for ( i = 0 ; i < 3 ; i + + ) {
v = in [ SPOFF ( loc [ 0 ] ) + i ] ;
* ( ( ( __be16 * ) out - > il0mac ) + i ) = cpu_to_be16 ( v ) ;
}
2008-08-21 02:45:06 +04:00
if ( out - > revision < 3 ) { /* only rev 1-2 have et0, et1 */
for ( i = 0 ; i < 3 ; i + + ) {
v = in [ SPOFF ( loc [ 1 ] ) + i ] ;
* ( ( ( __be16 * ) out - > et0mac ) + i ) = cpu_to_be16 ( v ) ;
}
for ( i = 0 ; i < 3 ; i + + ) {
v = in [ SPOFF ( loc [ 2 ] ) + i ] ;
* ( ( ( __be16 * ) out - > et1mac ) + i ) = cpu_to_be16 ( v ) ;
}
2007-09-18 23:12:50 +04:00
}
2007-11-10 01:56:25 +03:00
SPEX ( et0phyaddr , SSB_SPROM1_ETHPHY , SSB_SPROM1_ETHPHY_ET0A , 0 ) ;
SPEX ( et1phyaddr , SSB_SPROM1_ETHPHY , SSB_SPROM1_ETHPHY_ET1A ,
SSB_SPROM1_ETHPHY_ET1A_SHIFT ) ;
2007-12-22 23:51:30 +03:00
SPEX ( et0mdcport , SSB_SPROM1_ETHPHY , SSB_SPROM1_ETHPHY_ET0M , 14 ) ;
SPEX ( et1mdcport , SSB_SPROM1_ETHPHY , SSB_SPROM1_ETHPHY_ET1M , 15 ) ;
SPEX ( board_rev , SSB_SPROM1_BINF , SSB_SPROM1_BINF_BREV , 0 ) ;
2007-11-10 01:56:25 +03:00
SPEX ( country_code , SSB_SPROM1_BINF , SSB_SPROM1_BINF_CCODE ,
SSB_SPROM1_BINF_CCODE_SHIFT ) ;
2007-12-22 23:51:30 +03:00
SPEX ( ant_available_a , SSB_SPROM1_BINF , SSB_SPROM1_BINF_ANTA ,
SSB_SPROM1_BINF_ANTA_SHIFT ) ;
SPEX ( ant_available_bg , SSB_SPROM1_BINF , SSB_SPROM1_BINF_ANTBG ,
SSB_SPROM1_BINF_ANTBG_SHIFT ) ;
2007-11-10 01:56:25 +03:00
SPEX ( pa0b0 , SSB_SPROM1_PA0B0 , 0xFFFF , 0 ) ;
SPEX ( pa0b1 , SSB_SPROM1_PA0B1 , 0xFFFF , 0 ) ;
SPEX ( pa0b2 , SSB_SPROM1_PA0B2 , 0xFFFF , 0 ) ;
SPEX ( pa1b0 , SSB_SPROM1_PA1B0 , 0xFFFF , 0 ) ;
SPEX ( pa1b1 , SSB_SPROM1_PA1B1 , 0xFFFF , 0 ) ;
SPEX ( pa1b2 , SSB_SPROM1_PA1B2 , 0xFFFF , 0 ) ;
SPEX ( gpio0 , SSB_SPROM1_GPIOA , SSB_SPROM1_GPIOA_P0 , 0 ) ;
SPEX ( gpio1 , SSB_SPROM1_GPIOA , SSB_SPROM1_GPIOA_P1 ,
SSB_SPROM1_GPIOA_P1_SHIFT ) ;
SPEX ( gpio2 , SSB_SPROM1_GPIOB , SSB_SPROM1_GPIOB_P2 , 0 ) ;
SPEX ( gpio3 , SSB_SPROM1_GPIOB , SSB_SPROM1_GPIOB_P3 ,
SSB_SPROM1_GPIOB_P3_SHIFT ) ;
SPEX ( maxpwr_a , SSB_SPROM1_MAXPWR , SSB_SPROM1_MAXPWR_A ,
SSB_SPROM1_MAXPWR_A_SHIFT ) ;
SPEX ( maxpwr_bg , SSB_SPROM1_MAXPWR , SSB_SPROM1_MAXPWR_BG , 0 ) ;
SPEX ( itssi_a , SSB_SPROM1_ITSSI , SSB_SPROM1_ITSSI_A ,
SSB_SPROM1_ITSSI_A_SHIFT ) ;
SPEX ( itssi_bg , SSB_SPROM1_ITSSI , SSB_SPROM1_ITSSI_BG , 0 ) ;
SPEX ( boardflags_lo , SSB_SPROM1_BFLLO , 0xFFFF , 0 ) ;
2008-01-13 23:08:24 +03:00
if ( out - > revision > = 2 )
SPEX ( boardflags_hi , SSB_SPROM2_BFLHI , 0xFFFF , 0 ) ;
2007-12-22 23:51:30 +03:00
/* Extract the antenna gain values. */
gain = r123_extract_antgain ( out - > revision , in ,
SSB_SPROM1_AGAIN_BG ,
SSB_SPROM1_AGAIN_BG_SHIFT ) ;
out - > antenna_gain . ghz24 . a0 = gain ;
out - > antenna_gain . ghz24 . a1 = gain ;
out - > antenna_gain . ghz24 . a2 = gain ;
out - > antenna_gain . ghz24 . a3 = gain ;
gain = r123_extract_antgain ( out - > revision , in ,
SSB_SPROM1_AGAIN_A ,
SSB_SPROM1_AGAIN_A_SHIFT ) ;
out - > antenna_gain . ghz5 . a0 = gain ;
out - > antenna_gain . ghz5 . a1 = gain ;
out - > antenna_gain . ghz5 . a2 = gain ;
out - > antenna_gain . ghz5 . a3 = gain ;
2007-09-18 23:12:50 +04:00
}
2008-08-19 21:50:31 +04:00
static void sprom_extract_r45 ( struct ssb_sprom * out , const u16 * in )
2007-09-18 23:12:50 +04:00
{
2007-11-10 01:56:25 +03:00
int i ;
u16 v ;
2008-08-19 21:50:31 +04:00
u16 il0mac_offset ;
2007-11-10 01:56:25 +03:00
2008-08-19 21:50:31 +04:00
if ( out - > revision = = 4 )
il0mac_offset = SSB_SPROM4_IL0MAC ;
else
il0mac_offset = SSB_SPROM5_IL0MAC ;
2008-08-21 02:45:06 +04:00
/* extract the MAC address */
2007-11-10 01:56:25 +03:00
for ( i = 0 ; i < 3 ; i + + ) {
2008-08-19 21:50:31 +04:00
v = in [ SPOFF ( il0mac_offset ) + i ] ;
2007-11-10 01:56:25 +03:00
* ( ( ( __be16 * ) out - > il0mac ) + i ) = cpu_to_be16 ( v ) ;
}
SPEX ( et0phyaddr , SSB_SPROM4_ETHPHY , SSB_SPROM4_ETHPHY_ET0A , 0 ) ;
SPEX ( et1phyaddr , SSB_SPROM4_ETHPHY , SSB_SPROM4_ETHPHY_ET1A ,
SSB_SPROM4_ETHPHY_ET1A_SHIFT ) ;
2008-08-19 21:50:31 +04:00
if ( out - > revision = = 4 ) {
SPEX ( country_code , SSB_SPROM4_CCODE , 0xFFFF , 0 ) ;
SPEX ( boardflags_lo , SSB_SPROM4_BFLLO , 0xFFFF , 0 ) ;
SPEX ( boardflags_hi , SSB_SPROM4_BFLHI , 0xFFFF , 0 ) ;
} else {
SPEX ( country_code , SSB_SPROM5_CCODE , 0xFFFF , 0 ) ;
SPEX ( boardflags_lo , SSB_SPROM5_BFLLO , 0xFFFF , 0 ) ;
SPEX ( boardflags_hi , SSB_SPROM5_BFLHI , 0xFFFF , 0 ) ;
}
2007-12-22 23:51:30 +03:00
SPEX ( ant_available_a , SSB_SPROM4_ANTAVAIL , SSB_SPROM4_ANTAVAIL_A ,
SSB_SPROM4_ANTAVAIL_A_SHIFT ) ;
SPEX ( ant_available_bg , SSB_SPROM4_ANTAVAIL , SSB_SPROM4_ANTAVAIL_BG ,
SSB_SPROM4_ANTAVAIL_BG_SHIFT ) ;
2007-11-10 01:58:20 +03:00
SPEX ( maxpwr_bg , SSB_SPROM4_MAXP_BG , SSB_SPROM4_MAXP_BG_MASK , 0 ) ;
SPEX ( itssi_bg , SSB_SPROM4_MAXP_BG , SSB_SPROM4_ITSSI_BG ,
SSB_SPROM4_ITSSI_BG_SHIFT ) ;
SPEX ( maxpwr_a , SSB_SPROM4_MAXP_A , SSB_SPROM4_MAXP_A_MASK , 0 ) ;
SPEX ( itssi_a , SSB_SPROM4_MAXP_A , SSB_SPROM4_ITSSI_A ,
SSB_SPROM4_ITSSI_A_SHIFT ) ;
2008-08-19 21:50:31 +04:00
if ( out - > revision = = 4 ) {
SPEX ( gpio0 , SSB_SPROM4_GPIOA , SSB_SPROM4_GPIOA_P0 , 0 ) ;
SPEX ( gpio1 , SSB_SPROM4_GPIOA , SSB_SPROM4_GPIOA_P1 ,
SSB_SPROM4_GPIOA_P1_SHIFT ) ;
SPEX ( gpio2 , SSB_SPROM4_GPIOB , SSB_SPROM4_GPIOB_P2 , 0 ) ;
SPEX ( gpio3 , SSB_SPROM4_GPIOB , SSB_SPROM4_GPIOB_P3 ,
SSB_SPROM4_GPIOB_P3_SHIFT ) ;
} else {
SPEX ( gpio0 , SSB_SPROM5_GPIOA , SSB_SPROM5_GPIOA_P0 , 0 ) ;
SPEX ( gpio1 , SSB_SPROM5_GPIOA , SSB_SPROM5_GPIOA_P1 ,
SSB_SPROM5_GPIOA_P1_SHIFT ) ;
SPEX ( gpio2 , SSB_SPROM5_GPIOB , SSB_SPROM5_GPIOB_P2 , 0 ) ;
SPEX ( gpio3 , SSB_SPROM5_GPIOB , SSB_SPROM5_GPIOB_P3 ,
SSB_SPROM5_GPIOB_P3_SHIFT ) ;
}
2007-12-22 23:51:30 +03:00
/* Extract the antenna gain values. */
SPEX ( antenna_gain . ghz24 . a0 , SSB_SPROM4_AGAIN01 ,
SSB_SPROM4_AGAIN0 , SSB_SPROM4_AGAIN0_SHIFT ) ;
SPEX ( antenna_gain . ghz24 . a1 , SSB_SPROM4_AGAIN01 ,
SSB_SPROM4_AGAIN1 , SSB_SPROM4_AGAIN1_SHIFT ) ;
SPEX ( antenna_gain . ghz24 . a2 , SSB_SPROM4_AGAIN23 ,
SSB_SPROM4_AGAIN2 , SSB_SPROM4_AGAIN2_SHIFT ) ;
SPEX ( antenna_gain . ghz24 . a3 , SSB_SPROM4_AGAIN23 ,
SSB_SPROM4_AGAIN3 , SSB_SPROM4_AGAIN3_SHIFT ) ;
memcpy ( & out - > antenna_gain . ghz5 , & out - > antenna_gain . ghz24 ,
sizeof ( out - > antenna_gain . ghz5 ) ) ;
2007-11-10 01:56:25 +03:00
/* TODO - get remaining rev 4 stuff needed */
2007-09-18 23:12:50 +04:00
}
2008-12-25 02:39:28 +03:00
static void sprom_extract_r8 ( struct ssb_sprom * out , const u16 * in )
{
int i ;
u16 v ;
/* extract the MAC address */
for ( i = 0 ; i < 3 ; i + + ) {
v = in [ SPOFF ( SSB_SPROM1_IL0MAC ) + i ] ;
* ( ( ( __be16 * ) out - > il0mac ) + i ) = cpu_to_be16 ( v ) ;
}
SPEX ( country_code , SSB_SPROM8_CCODE , 0xFFFF , 0 ) ;
SPEX ( boardflags_lo , SSB_SPROM8_BFLLO , 0xFFFF , 0 ) ;
SPEX ( boardflags_hi , SSB_SPROM8_BFLHI , 0xFFFF , 0 ) ;
SPEX ( ant_available_a , SSB_SPROM8_ANTAVAIL , SSB_SPROM8_ANTAVAIL_A ,
SSB_SPROM8_ANTAVAIL_A_SHIFT ) ;
SPEX ( ant_available_bg , SSB_SPROM8_ANTAVAIL , SSB_SPROM8_ANTAVAIL_BG ,
SSB_SPROM8_ANTAVAIL_BG_SHIFT ) ;
SPEX ( maxpwr_bg , SSB_SPROM8_MAXP_BG , SSB_SPROM8_MAXP_BG_MASK , 0 ) ;
SPEX ( itssi_bg , SSB_SPROM8_MAXP_BG , SSB_SPROM8_ITSSI_BG ,
SSB_SPROM8_ITSSI_BG_SHIFT ) ;
SPEX ( maxpwr_a , SSB_SPROM8_MAXP_A , SSB_SPROM8_MAXP_A_MASK , 0 ) ;
SPEX ( itssi_a , SSB_SPROM8_MAXP_A , SSB_SPROM8_ITSSI_A ,
SSB_SPROM8_ITSSI_A_SHIFT ) ;
SPEX ( gpio0 , SSB_SPROM8_GPIOA , SSB_SPROM8_GPIOA_P0 , 0 ) ;
SPEX ( gpio1 , SSB_SPROM8_GPIOA , SSB_SPROM8_GPIOA_P1 ,
SSB_SPROM8_GPIOA_P1_SHIFT ) ;
SPEX ( gpio2 , SSB_SPROM8_GPIOB , SSB_SPROM8_GPIOB_P2 , 0 ) ;
SPEX ( gpio3 , SSB_SPROM8_GPIOB , SSB_SPROM8_GPIOB_P3 ,
SSB_SPROM8_GPIOB_P3_SHIFT ) ;
/* Extract the antenna gain values. */
SPEX ( antenna_gain . ghz24 . a0 , SSB_SPROM8_AGAIN01 ,
SSB_SPROM8_AGAIN0 , SSB_SPROM8_AGAIN0_SHIFT ) ;
SPEX ( antenna_gain . ghz24 . a1 , SSB_SPROM8_AGAIN01 ,
SSB_SPROM8_AGAIN1 , SSB_SPROM8_AGAIN1_SHIFT ) ;
SPEX ( antenna_gain . ghz24 . a2 , SSB_SPROM8_AGAIN23 ,
SSB_SPROM8_AGAIN2 , SSB_SPROM8_AGAIN2_SHIFT ) ;
SPEX ( antenna_gain . ghz24 . a3 , SSB_SPROM8_AGAIN23 ,
SSB_SPROM8_AGAIN3 , SSB_SPROM8_AGAIN3_SHIFT ) ;
memcpy ( & out - > antenna_gain . ghz5 , & out - > antenna_gain . ghz24 ,
sizeof ( out - > antenna_gain . ghz5 ) ) ;
/* TODO - get remaining rev 8 stuff needed */
}
2007-11-10 01:56:25 +03:00
static int sprom_extract ( struct ssb_bus * bus , struct ssb_sprom * out ,
const u16 * in , u16 size )
2007-09-18 23:12:50 +04:00
{
memset ( out , 0 , sizeof ( * out ) ) ;
2007-11-10 01:56:25 +03:00
out - > revision = in [ size - 1 ] & 0x00FF ;
2007-12-22 23:51:30 +03:00
ssb_dprintk ( KERN_DEBUG PFX " SPROM revision %d detected. \n " , out - > revision ) ;
2008-08-21 02:45:06 +04:00
memset ( out - > et0mac , 0xFF , 6 ) ; /* preset et0 and et1 mac */
memset ( out - > et1mac , 0xFF , 6 ) ;
2007-09-18 23:12:50 +04:00
if ( ( bus - > chip_id & 0xFF00 ) = = 0x4400 ) {
/* Workaround: The BCM44XX chip has a stupid revision
* number stored in the SPROM .
* Always extract r1 . */
2007-11-10 01:56:25 +03:00
out - > revision = 1 ;
sprom_extract_r123 ( out , in ) ;
} else if ( bus - > chip_id = = 0x4321 ) {
/* the BCM4328 has a chipid == 0x4321 and a rev 4 SPROM */
out - > revision = 4 ;
2008-08-19 21:50:31 +04:00
sprom_extract_r45 ( out , in ) ;
2007-09-18 23:12:50 +04:00
} else {
2008-12-25 02:39:28 +03:00
switch ( out - > revision ) {
case 1 :
case 2 :
case 3 :
2007-11-10 01:56:25 +03:00
sprom_extract_r123 ( out , in ) ;
2008-12-25 02:39:28 +03:00
break ;
case 4 :
case 5 :
2008-08-19 21:50:31 +04:00
sprom_extract_r45 ( out , in ) ;
2008-12-25 02:39:28 +03:00
break ;
case 8 :
sprom_extract_r8 ( out , in ) ;
break ;
default :
ssb_printk ( KERN_WARNING PFX " Unsupported SPROM "
" revision %d detected. Will extract "
" v1 \n " , out - > revision ) ;
sprom_extract_r123 ( out , in ) ;
}
2007-09-18 23:12:50 +04:00
}
2008-04-19 19:52:12 +04:00
if ( out - > boardflags_lo = = 0xFFFF )
out - > boardflags_lo = 0 ; /* per specs */
if ( out - > boardflags_hi = = 0xFFFF )
out - > boardflags_hi = 0 ; /* per specs */
2007-09-18 23:12:50 +04:00
return 0 ;
}
static int ssb_pci_sprom_get ( struct ssb_bus * bus ,
struct ssb_sprom * sprom )
{
2009-02-27 18:59:05 +03:00
const struct ssb_sprom * fallback ;
2007-09-18 23:12:50 +04:00
int err = - ENOMEM ;
u16 * buf ;
2007-11-10 01:56:25 +03:00
buf = kcalloc ( SSB_SPROMSIZE_WORDS_R123 , sizeof ( u16 ) , GFP_KERNEL ) ;
2007-09-18 23:12:50 +04:00
if ( ! buf )
goto out ;
2007-11-10 01:56:25 +03:00
bus - > sprom_size = SSB_SPROMSIZE_WORDS_R123 ;
2007-09-18 23:12:50 +04:00
sprom_do_read ( bus , buf ) ;
2007-11-10 01:56:25 +03:00
err = sprom_check_crc ( buf , bus - > sprom_size ) ;
2007-09-18 23:12:50 +04:00
if ( err ) {
2008-04-19 18:24:09 +04:00
/* try for a 440 byte SPROM - revision 4 and higher */
kfree ( buf ) ;
buf = kcalloc ( SSB_SPROMSIZE_WORDS_R4 , sizeof ( u16 ) ,
GFP_KERNEL ) ;
if ( ! buf )
goto out ;
bus - > sprom_size = SSB_SPROMSIZE_WORDS_R4 ;
sprom_do_read ( bus , buf ) ;
err = sprom_check_crc ( buf , bus - > sprom_size ) ;
2009-02-27 18:59:05 +03:00
if ( err ) {
/* All CRC attempts failed.
* Maybe there is no SPROM on the device ?
* If we have a fallback , use that . */
fallback = ssb_get_fallback_sprom ( ) ;
if ( fallback ) {
memcpy ( sprom , fallback , sizeof ( * sprom ) ) ;
err = 0 ;
goto out_free ;
}
2007-11-10 01:56:25 +03:00
ssb_printk ( KERN_WARNING PFX " WARNING: Invalid "
" SPROM CRC (corrupt SPROM) \n " ) ;
2009-02-27 18:59:05 +03:00
}
2007-09-18 23:12:50 +04:00
}
2007-11-10 01:56:25 +03:00
err = sprom_extract ( bus , sprom , buf , bus - > sprom_size ) ;
2007-09-18 23:12:50 +04:00
2009-02-27 18:59:05 +03:00
out_free :
2007-09-18 23:12:50 +04:00
kfree ( buf ) ;
out :
return err ;
}
static void ssb_pci_get_boardinfo ( struct ssb_bus * bus ,
struct ssb_boardinfo * bi )
{
pci_read_config_word ( bus - > host_pci , PCI_SUBSYSTEM_VENDOR_ID ,
& bi - > vendor ) ;
pci_read_config_word ( bus - > host_pci , PCI_SUBSYSTEM_ID ,
& bi - > type ) ;
pci_read_config_word ( bus - > host_pci , PCI_REVISION_ID ,
& bi - > rev ) ;
}
int ssb_pci_get_invariants ( struct ssb_bus * bus ,
struct ssb_init_invariants * iv )
{
int err ;
err = ssb_pci_sprom_get ( bus , & iv - > sprom ) ;
if ( err )
goto out ;
ssb_pci_get_boardinfo ( bus , & iv - > boardinfo ) ;
out :
return err ;
}
# ifdef CONFIG_SSB_DEBUG
static int ssb_pci_assert_buspower ( struct ssb_bus * bus )
{
if ( likely ( bus - > powered_up ) )
return 0 ;
printk ( KERN_ERR PFX " FATAL ERROR: Bus powered down "
" while accessing PCI MMIO space \n " ) ;
if ( bus - > power_warn_count < = 10 ) {
bus - > power_warn_count + + ;
dump_stack ( ) ;
}
return - ENODEV ;
}
# else /* DEBUG */
static inline int ssb_pci_assert_buspower ( struct ssb_bus * bus )
{
return 0 ;
}
# endif /* DEBUG */
2008-02-20 21:08:10 +03:00
static u8 ssb_pci_read8 ( struct ssb_device * dev , u16 offset )
{
struct ssb_bus * bus = dev - > bus ;
if ( unlikely ( ssb_pci_assert_buspower ( bus ) ) )
return 0xFF ;
if ( unlikely ( bus - > mapped_device ! = dev ) ) {
if ( unlikely ( ssb_pci_switch_core ( bus , dev ) ) )
return 0xFF ;
}
return ioread8 ( bus - > mmio + offset ) ;
}
2007-09-18 23:12:50 +04:00
static u16 ssb_pci_read16 ( struct ssb_device * dev , u16 offset )
{
struct ssb_bus * bus = dev - > bus ;
if ( unlikely ( ssb_pci_assert_buspower ( bus ) ) )
return 0xFFFF ;
if ( unlikely ( bus - > mapped_device ! = dev ) ) {
if ( unlikely ( ssb_pci_switch_core ( bus , dev ) ) )
return 0xFFFF ;
}
2007-09-19 20:53:44 +04:00
return ioread16 ( bus - > mmio + offset ) ;
2007-09-18 23:12:50 +04:00
}
static u32 ssb_pci_read32 ( struct ssb_device * dev , u16 offset )
{
struct ssb_bus * bus = dev - > bus ;
if ( unlikely ( ssb_pci_assert_buspower ( bus ) ) )
return 0xFFFFFFFF ;
if ( unlikely ( bus - > mapped_device ! = dev ) ) {
if ( unlikely ( ssb_pci_switch_core ( bus , dev ) ) )
return 0xFFFFFFFF ;
}
2007-09-19 20:53:44 +04:00
return ioread32 ( bus - > mmio + offset ) ;
2007-09-18 23:12:50 +04:00
}
2008-04-02 21:46:56 +04:00
# ifdef CONFIG_SSB_BLOCKIO
static void ssb_pci_block_read ( struct ssb_device * dev , void * buffer ,
size_t count , u16 offset , u8 reg_width )
{
struct ssb_bus * bus = dev - > bus ;
void __iomem * addr = bus - > mmio + offset ;
if ( unlikely ( ssb_pci_assert_buspower ( bus ) ) )
goto error ;
if ( unlikely ( bus - > mapped_device ! = dev ) ) {
if ( unlikely ( ssb_pci_switch_core ( bus , dev ) ) )
goto error ;
}
switch ( reg_width ) {
case sizeof ( u8 ) :
ioread8_rep ( addr , buffer , count ) ;
break ;
case sizeof ( u16 ) :
SSB_WARN_ON ( count & 1 ) ;
ioread16_rep ( addr , buffer , count > > 1 ) ;
break ;
case sizeof ( u32 ) :
SSB_WARN_ON ( count & 3 ) ;
ioread32_rep ( addr , buffer , count > > 2 ) ;
break ;
default :
SSB_WARN_ON ( 1 ) ;
}
return ;
error :
memset ( buffer , 0xFF , count ) ;
}
# endif /* CONFIG_SSB_BLOCKIO */
2008-02-20 21:08:10 +03:00
static void ssb_pci_write8 ( struct ssb_device * dev , u16 offset , u8 value )
{
struct ssb_bus * bus = dev - > bus ;
if ( unlikely ( ssb_pci_assert_buspower ( bus ) ) )
return ;
if ( unlikely ( bus - > mapped_device ! = dev ) ) {
if ( unlikely ( ssb_pci_switch_core ( bus , dev ) ) )
return ;
}
iowrite8 ( value , bus - > mmio + offset ) ;
}
2007-09-18 23:12:50 +04:00
static void ssb_pci_write16 ( struct ssb_device * dev , u16 offset , u16 value )
{
struct ssb_bus * bus = dev - > bus ;
if ( unlikely ( ssb_pci_assert_buspower ( bus ) ) )
return ;
if ( unlikely ( bus - > mapped_device ! = dev ) ) {
if ( unlikely ( ssb_pci_switch_core ( bus , dev ) ) )
return ;
}
2007-09-19 20:53:44 +04:00
iowrite16 ( value , bus - > mmio + offset ) ;
2007-09-18 23:12:50 +04:00
}
static void ssb_pci_write32 ( struct ssb_device * dev , u16 offset , u32 value )
{
struct ssb_bus * bus = dev - > bus ;
if ( unlikely ( ssb_pci_assert_buspower ( bus ) ) )
return ;
if ( unlikely ( bus - > mapped_device ! = dev ) ) {
if ( unlikely ( ssb_pci_switch_core ( bus , dev ) ) )
return ;
}
2007-09-19 20:53:44 +04:00
iowrite32 ( value , bus - > mmio + offset ) ;
2007-09-18 23:12:50 +04:00
}
2008-04-02 21:46:56 +04:00
# ifdef CONFIG_SSB_BLOCKIO
static void ssb_pci_block_write ( struct ssb_device * dev , const void * buffer ,
size_t count , u16 offset , u8 reg_width )
{
struct ssb_bus * bus = dev - > bus ;
void __iomem * addr = bus - > mmio + offset ;
if ( unlikely ( ssb_pci_assert_buspower ( bus ) ) )
return ;
if ( unlikely ( bus - > mapped_device ! = dev ) ) {
if ( unlikely ( ssb_pci_switch_core ( bus , dev ) ) )
return ;
}
switch ( reg_width ) {
case sizeof ( u8 ) :
iowrite8_rep ( addr , buffer , count ) ;
break ;
case sizeof ( u16 ) :
SSB_WARN_ON ( count & 1 ) ;
iowrite16_rep ( addr , buffer , count > > 1 ) ;
break ;
case sizeof ( u32 ) :
SSB_WARN_ON ( count & 3 ) ;
iowrite32_rep ( addr , buffer , count > > 2 ) ;
break ;
default :
SSB_WARN_ON ( 1 ) ;
}
}
# endif /* CONFIG_SSB_BLOCKIO */
2007-09-18 23:12:50 +04:00
/* Not "static", as it's used in main.c */
const struct ssb_bus_ops ssb_pci_ops = {
2008-02-20 21:08:10 +03:00
. read8 = ssb_pci_read8 ,
2007-09-18 23:12:50 +04:00
. read16 = ssb_pci_read16 ,
. read32 = ssb_pci_read32 ,
2008-02-20 21:08:10 +03:00
. write8 = ssb_pci_write8 ,
2007-09-18 23:12:50 +04:00
. write16 = ssb_pci_write16 ,
. write32 = ssb_pci_write32 ,
2008-04-02 21:46:56 +04:00
# ifdef CONFIG_SSB_BLOCKIO
. block_read = ssb_pci_block_read ,
. block_write = ssb_pci_block_write ,
# endif
2007-09-18 23:12:50 +04:00
} ;
static ssize_t ssb_pci_attr_sprom_show ( struct device * pcidev ,
struct device_attribute * attr ,
char * buf )
{
struct pci_dev * pdev = container_of ( pcidev , struct pci_dev , dev ) ;
struct ssb_bus * bus ;
bus = ssb_pci_dev_to_bus ( pdev ) ;
if ( ! bus )
2008-03-10 19:26:32 +03:00
return - ENODEV ;
2007-09-18 23:12:50 +04:00
2008-03-10 19:26:32 +03:00
return ssb_attr_sprom_show ( bus , buf , sprom_do_read ) ;
2007-09-18 23:12:50 +04:00
}
static ssize_t ssb_pci_attr_sprom_store ( struct device * pcidev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct pci_dev * pdev = container_of ( pcidev , struct pci_dev , dev ) ;
struct ssb_bus * bus ;
bus = ssb_pci_dev_to_bus ( pdev ) ;
if ( ! bus )
2008-03-10 19:26:32 +03:00
return - ENODEV ;
2007-09-18 23:12:50 +04:00
2008-03-10 19:26:32 +03:00
return ssb_attr_sprom_store ( bus , buf , count ,
sprom_check_crc , sprom_do_write ) ;
2007-09-18 23:12:50 +04:00
}
static DEVICE_ATTR ( ssb_sprom , 0600 ,
ssb_pci_attr_sprom_show ,
ssb_pci_attr_sprom_store ) ;
void ssb_pci_exit ( struct ssb_bus * bus )
{
struct pci_dev * pdev ;
if ( bus - > bustype ! = SSB_BUSTYPE_PCI )
return ;
pdev = bus - > host_pci ;
device_remove_file ( & pdev - > dev , & dev_attr_ssb_sprom ) ;
}
int ssb_pci_init ( struct ssb_bus * bus )
{
struct pci_dev * pdev ;
int err ;
if ( bus - > bustype ! = SSB_BUSTYPE_PCI )
return 0 ;
pdev = bus - > host_pci ;
2008-03-10 19:26:32 +03:00
mutex_init ( & bus - > sprom_mutex ) ;
2007-09-18 23:12:50 +04:00
err = device_create_file ( & pdev - > dev , & dev_attr_ssb_sprom ) ;
if ( err )
goto out ;
out :
return err ;
}