2007-09-18 23:12:50 +04:00
/*
* Sonics Silicon Backplane
* PCMCIA - Hostbus related functions
*
* Copyright 2006 Johannes Berg < johannes @ sipsolutions . net >
2008-03-10 19:26:32 +03:00
* Copyright 2007 - 2008 Michael Buesch < mb @ bu3sch . de >
2007-09-18 23:12:50 +04:00
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# include <linux/ssb/ssb.h>
# include <linux/delay.h>
2007-10-14 08:51:51 +04:00
# include <linux/io.h>
2008-03-10 19:26:32 +03:00
# include <linux/etherdevice.h>
2007-09-18 23:12:50 +04:00
# include <pcmcia/cs_types.h>
# include <pcmcia/cs.h>
# include <pcmcia/cistpl.h>
# include <pcmcia/ciscode.h>
# include <pcmcia/ds.h>
# include <pcmcia/cisreg.h>
# include "ssb_private.h"
/* Define the following to 1 to enable a printk on each coreswitch. */
# define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0
2008-03-10 19:26:32 +03:00
/* PCMCIA configuration registers */
# define SSB_PCMCIA_ADDRESS0 0x2E
# define SSB_PCMCIA_ADDRESS1 0x30
# define SSB_PCMCIA_ADDRESS2 0x32
# define SSB_PCMCIA_MEMSEG 0x34
# define SSB_PCMCIA_SPROMCTL 0x36
# define SSB_PCMCIA_SPROMCTL_IDLE 0
# define SSB_PCMCIA_SPROMCTL_WRITE 1
# define SSB_PCMCIA_SPROMCTL_READ 2
# define SSB_PCMCIA_SPROMCTL_WRITEEN 4
# define SSB_PCMCIA_SPROMCTL_WRITEDIS 7
# define SSB_PCMCIA_SPROMCTL_DONE 8
# define SSB_PCMCIA_SPROM_DATALO 0x38
# define SSB_PCMCIA_SPROM_DATAHI 0x3A
# define SSB_PCMCIA_SPROM_ADDRLO 0x3C
# define SSB_PCMCIA_SPROM_ADDRHI 0x3E
/* Hardware invariants CIS tuples */
# define SSB_PCMCIA_CIS 0x80
# define SSB_PCMCIA_CIS_ID 0x01
# define SSB_PCMCIA_CIS_BOARDREV 0x02
# define SSB_PCMCIA_CIS_PA 0x03
# define SSB_PCMCIA_CIS_PA_PA0B0_LO 0
# define SSB_PCMCIA_CIS_PA_PA0B0_HI 1
# define SSB_PCMCIA_CIS_PA_PA0B1_LO 2
# define SSB_PCMCIA_CIS_PA_PA0B1_HI 3
# define SSB_PCMCIA_CIS_PA_PA0B2_LO 4
# define SSB_PCMCIA_CIS_PA_PA0B2_HI 5
# define SSB_PCMCIA_CIS_PA_ITSSI 6
# define SSB_PCMCIA_CIS_PA_MAXPOW 7
# define SSB_PCMCIA_CIS_OEMNAME 0x04
# define SSB_PCMCIA_CIS_CCODE 0x05
# define SSB_PCMCIA_CIS_ANTENNA 0x06
# define SSB_PCMCIA_CIS_ANTGAIN 0x07
# define SSB_PCMCIA_CIS_BFLAGS 0x08
# define SSB_PCMCIA_CIS_LEDS 0x09
/* PCMCIA SPROM size. */
# define SSB_PCMCIA_SPROM_SIZE 256
# define SSB_PCMCIA_SPROM_SIZE_BYTES (SSB_PCMCIA_SPROM_SIZE * sizeof(u16))
/* Write to a PCMCIA configuration register. */
static int ssb_pcmcia_cfg_write ( struct ssb_bus * bus , u8 offset , u8 value )
{
conf_reg_t reg ;
int res ;
memset ( & reg , 0 , sizeof ( reg ) ) ;
reg . Offset = offset ;
reg . Action = CS_WRITE ;
reg . Value = value ;
res = pcmcia_access_configuration_register ( bus - > host_pcmcia , & reg ) ;
if ( unlikely ( res ! = CS_SUCCESS ) )
return - EBUSY ;
return 0 ;
}
/* Read from a PCMCIA configuration register. */
static int ssb_pcmcia_cfg_read ( struct ssb_bus * bus , u8 offset , u8 * value )
{
conf_reg_t reg ;
int res ;
memset ( & reg , 0 , sizeof ( reg ) ) ;
reg . Offset = offset ;
reg . Action = CS_READ ;
res = pcmcia_access_configuration_register ( bus - > host_pcmcia , & reg ) ;
if ( unlikely ( res ! = CS_SUCCESS ) )
return - EBUSY ;
* value = reg . Value ;
return 0 ;
}
2007-09-18 23:12:50 +04:00
int ssb_pcmcia_switch_coreidx ( struct ssb_bus * bus ,
u8 coreidx )
{
int err ;
int attempts = 0 ;
u32 cur_core ;
u32 addr ;
u32 read_addr ;
2008-03-10 19:26:32 +03:00
u8 val ;
2007-09-18 23:12:50 +04:00
addr = ( coreidx * SSB_CORE_SIZE ) + SSB_ENUM_BASE ;
while ( 1 ) {
2008-03-10 19:26:32 +03:00
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_ADDRESS0 ,
( addr & 0x0000F000 ) > > 12 ) ;
if ( err )
2007-09-18 23:12:50 +04:00
goto error ;
2008-03-10 19:26:32 +03:00
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_ADDRESS1 ,
( addr & 0x00FF0000 ) > > 16 ) ;
if ( err )
2007-09-18 23:12:50 +04:00
goto error ;
2008-03-10 19:26:32 +03:00
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_ADDRESS2 ,
( addr & 0xFF000000 ) > > 24 ) ;
if ( err )
2007-09-18 23:12:50 +04:00
goto error ;
read_addr = 0 ;
2008-03-10 19:26:32 +03:00
err = ssb_pcmcia_cfg_read ( bus , SSB_PCMCIA_ADDRESS0 , & val ) ;
if ( err )
2007-09-18 23:12:50 +04:00
goto error ;
2008-03-10 19:26:32 +03:00
read_addr | = ( ( u32 ) ( val & 0x0F ) ) < < 12 ;
err = ssb_pcmcia_cfg_read ( bus , SSB_PCMCIA_ADDRESS1 , & val ) ;
if ( err )
2007-09-18 23:12:50 +04:00
goto error ;
2008-03-10 19:26:32 +03:00
read_addr | = ( ( u32 ) val ) < < 16 ;
err = ssb_pcmcia_cfg_read ( bus , SSB_PCMCIA_ADDRESS2 , & val ) ;
if ( err )
2007-09-18 23:12:50 +04:00
goto error ;
2008-03-10 19:26:32 +03:00
read_addr | = ( ( u32 ) val ) < < 24 ;
2007-09-18 23:12:50 +04:00
cur_core = ( read_addr - SSB_ENUM_BASE ) / SSB_CORE_SIZE ;
if ( cur_core = = coreidx )
break ;
2008-03-10 19:26:32 +03:00
err = - ETIMEDOUT ;
2007-09-18 23:12:50 +04:00
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 ) ;
2008-03-10 19:26:32 +03:00
return err ;
2007-09-18 23:12:50 +04:00
}
int ssb_pcmcia_switch_core ( struct ssb_bus * bus ,
struct ssb_device * dev )
{
int err ;
# if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG
ssb_printk ( KERN_INFO PFX
" Switching to %s core, index %d \n " ,
ssb_core_name ( dev - > id . coreid ) ,
dev - > core_index ) ;
# endif
err = ssb_pcmcia_switch_coreidx ( bus , dev - > core_index ) ;
if ( ! err )
bus - > mapped_device = dev ;
return err ;
}
int ssb_pcmcia_switch_segment ( struct ssb_bus * bus , u8 seg )
{
int attempts = 0 ;
2008-03-10 19:26:32 +03:00
int err ;
u8 val ;
2007-09-18 23:12:50 +04:00
SSB_WARN_ON ( ( seg ! = 0 ) & & ( seg ! = 1 ) ) ;
while ( 1 ) {
2008-03-10 19:26:32 +03:00
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_MEMSEG , seg ) ;
if ( err )
2007-09-18 23:12:50 +04:00
goto error ;
2008-03-10 19:26:32 +03:00
err = ssb_pcmcia_cfg_read ( bus , SSB_PCMCIA_MEMSEG , & val ) ;
if ( err )
2007-09-18 23:12:50 +04:00
goto error ;
2008-03-10 19:26:32 +03:00
if ( val = = seg )
2007-09-18 23:12:50 +04:00
break ;
2008-03-10 19:26:32 +03:00
err = - ETIMEDOUT ;
2007-09-18 23:12:50 +04:00
if ( unlikely ( attempts + + > SSB_BAR0_MAX_RETRIES ) )
goto error ;
udelay ( 10 ) ;
}
bus - > mapped_pcmcia_seg = seg ;
2007-12-23 00:01:36 +03:00
return 0 ;
2007-09-18 23:12:50 +04:00
error :
ssb_printk ( KERN_ERR PFX " Failed to switch pcmcia segment \n " ) ;
2008-03-10 19:26:32 +03:00
return err ;
2007-09-18 23:12:50 +04:00
}
2007-11-07 21:03:35 +03:00
static int select_core_and_segment ( struct ssb_device * dev ,
u16 * offset )
2007-09-18 23:12:50 +04:00
{
2007-11-07 21:03:35 +03:00
struct ssb_bus * bus = dev - > bus ;
2007-09-18 23:12:50 +04:00
int err ;
2007-11-07 21:03:35 +03:00
u8 need_segment ;
if ( * offset > = 0x800 ) {
* offset - = 0x800 ;
need_segment = 1 ;
} else
need_segment = 0 ;
2007-09-18 23:12:50 +04:00
if ( unlikely ( dev ! = bus - > mapped_device ) ) {
err = ssb_pcmcia_switch_core ( bus , dev ) ;
if ( unlikely ( err ) )
return err ;
}
2007-11-07 21:03:35 +03:00
if ( unlikely ( need_segment ! = bus - > mapped_pcmcia_seg ) ) {
err = ssb_pcmcia_switch_segment ( bus , need_segment ) ;
2007-09-18 23:12:50 +04:00
if ( unlikely ( err ) )
return err ;
}
return 0 ;
}
2008-02-20 21:08:10 +03:00
static u8 ssb_pcmcia_read8 ( struct ssb_device * dev , u16 offset )
{
struct ssb_bus * bus = dev - > bus ;
unsigned long flags ;
int err ;
u8 value = 0xFF ;
spin_lock_irqsave ( & bus - > bar_lock , flags ) ;
err = select_core_and_segment ( dev , & offset ) ;
if ( likely ( ! err ) )
value = readb ( bus - > mmio + offset ) ;
spin_unlock_irqrestore ( & bus - > bar_lock , flags ) ;
return value ;
}
2007-09-18 23:12:50 +04:00
static u16 ssb_pcmcia_read16 ( struct ssb_device * dev , u16 offset )
{
struct ssb_bus * bus = dev - > bus ;
2007-12-23 00:01:36 +03:00
unsigned long flags ;
int err ;
u16 value = 0xFFFF ;
2007-09-18 23:12:50 +04:00
2007-12-23 00:01:36 +03:00
spin_lock_irqsave ( & bus - > bar_lock , flags ) ;
err = select_core_and_segment ( dev , & offset ) ;
if ( likely ( ! err ) )
value = readw ( bus - > mmio + offset ) ;
spin_unlock_irqrestore ( & bus - > bar_lock , flags ) ;
2007-09-18 23:12:50 +04:00
2007-12-23 00:01:36 +03:00
return value ;
2007-09-18 23:12:50 +04:00
}
static u32 ssb_pcmcia_read32 ( struct ssb_device * dev , u16 offset )
{
struct ssb_bus * bus = dev - > bus ;
2007-12-23 00:01:36 +03:00
unsigned long flags ;
int err ;
u32 lo = 0xFFFFFFFF , hi = 0xFFFFFFFF ;
2007-09-18 23:12:50 +04:00
2007-12-23 00:01:36 +03:00
spin_lock_irqsave ( & bus - > bar_lock , flags ) ;
err = select_core_and_segment ( dev , & offset ) ;
if ( likely ( ! err ) ) {
lo = readw ( bus - > mmio + offset ) ;
hi = readw ( bus - > mmio + offset + 2 ) ;
}
spin_unlock_irqrestore ( & bus - > bar_lock , flags ) ;
2007-09-18 23:12:50 +04:00
2007-11-07 21:03:35 +03:00
return ( lo | ( hi < < 16 ) ) ;
2007-09-18 23:12:50 +04:00
}
2008-02-20 21:08:10 +03:00
static void ssb_pcmcia_write8 ( struct ssb_device * dev , u16 offset , u8 value )
{
struct ssb_bus * bus = dev - > bus ;
unsigned long flags ;
int err ;
spin_lock_irqsave ( & bus - > bar_lock , flags ) ;
err = select_core_and_segment ( dev , & offset ) ;
if ( likely ( ! err ) )
writeb ( value , bus - > mmio + offset ) ;
mmiowb ( ) ;
spin_unlock_irqrestore ( & bus - > bar_lock , flags ) ;
}
2007-09-18 23:12:50 +04:00
static void ssb_pcmcia_write16 ( struct ssb_device * dev , u16 offset , u16 value )
{
struct ssb_bus * bus = dev - > bus ;
2007-12-23 00:01:36 +03:00
unsigned long flags ;
int err ;
2007-09-18 23:12:50 +04:00
2007-12-23 00:01:36 +03:00
spin_lock_irqsave ( & bus - > bar_lock , flags ) ;
err = select_core_and_segment ( dev , & offset ) ;
if ( likely ( ! err ) )
writew ( value , bus - > mmio + offset ) ;
mmiowb ( ) ;
spin_unlock_irqrestore ( & bus - > bar_lock , flags ) ;
2007-09-18 23:12:50 +04:00
}
static void ssb_pcmcia_write32 ( struct ssb_device * dev , u16 offset , u32 value )
{
struct ssb_bus * bus = dev - > bus ;
2007-12-23 00:01:36 +03:00
unsigned long flags ;
int err ;
2007-09-18 23:12:50 +04:00
2007-12-23 00:01:36 +03:00
spin_lock_irqsave ( & bus - > bar_lock , flags ) ;
err = select_core_and_segment ( dev , & offset ) ;
if ( likely ( ! err ) ) {
writew ( ( value & 0x0000FFFF ) , bus - > mmio + offset ) ;
writew ( ( ( value & 0xFFFF0000 ) > > 16 ) , bus - > mmio + offset + 2 ) ;
}
mmiowb ( ) ;
spin_unlock_irqrestore ( & bus - > bar_lock , flags ) ;
2007-09-18 23:12:50 +04:00
}
/* Not "static", as it's used in main.c */
const struct ssb_bus_ops ssb_pcmcia_ops = {
2008-02-20 21:08:10 +03:00
. read8 = ssb_pcmcia_read8 ,
2007-09-18 23:12:50 +04:00
. read16 = ssb_pcmcia_read16 ,
. read32 = ssb_pcmcia_read32 ,
2008-02-20 21:08:10 +03:00
. write8 = ssb_pcmcia_write8 ,
2007-09-18 23:12:50 +04:00
. write16 = ssb_pcmcia_write16 ,
. write32 = ssb_pcmcia_write32 ,
} ;
2008-03-10 19:26:32 +03:00
static int ssb_pcmcia_sprom_command ( struct ssb_bus * bus , u8 command )
{
unsigned int i ;
int err ;
u8 value ;
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_SPROMCTL , command ) ;
if ( err )
return err ;
for ( i = 0 ; i < 1000 ; i + + ) {
err = ssb_pcmcia_cfg_read ( bus , SSB_PCMCIA_SPROMCTL , & value ) ;
if ( err )
return err ;
if ( value & SSB_PCMCIA_SPROMCTL_DONE )
return 0 ;
udelay ( 10 ) ;
}
return - ETIMEDOUT ;
}
/* offset is the 16bit word offset */
static int ssb_pcmcia_sprom_read ( struct ssb_bus * bus , u16 offset , u16 * value )
{
int err ;
u8 lo , hi ;
offset * = 2 ; /* Make byte offset */
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_SPROM_ADDRLO ,
( offset & 0x00FF ) ) ;
if ( err )
return err ;
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_SPROM_ADDRHI ,
( offset & 0xFF00 ) > > 8 ) ;
if ( err )
return err ;
err = ssb_pcmcia_sprom_command ( bus , SSB_PCMCIA_SPROMCTL_READ ) ;
if ( err )
return err ;
err = ssb_pcmcia_cfg_read ( bus , SSB_PCMCIA_SPROM_DATALO , & lo ) ;
if ( err )
return err ;
err = ssb_pcmcia_cfg_read ( bus , SSB_PCMCIA_SPROM_DATAHI , & hi ) ;
if ( err )
return err ;
* value = ( lo | ( ( ( u16 ) hi ) < < 8 ) ) ;
return 0 ;
}
/* offset is the 16bit word offset */
static int ssb_pcmcia_sprom_write ( struct ssb_bus * bus , u16 offset , u16 value )
{
int err ;
offset * = 2 ; /* Make byte offset */
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_SPROM_ADDRLO ,
( offset & 0x00FF ) ) ;
if ( err )
return err ;
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_SPROM_ADDRHI ,
( offset & 0xFF00 ) > > 8 ) ;
if ( err )
return err ;
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_SPROM_DATALO ,
( value & 0x00FF ) ) ;
if ( err )
return err ;
err = ssb_pcmcia_cfg_write ( bus , SSB_PCMCIA_SPROM_DATAHI ,
( value & 0xFF00 ) > > 8 ) ;
if ( err )
return err ;
err = ssb_pcmcia_sprom_command ( bus , SSB_PCMCIA_SPROMCTL_WRITE ) ;
if ( err )
return err ;
msleep ( 20 ) ;
return 0 ;
}
/* Read the SPROM image. bufsize is in 16bit words. */
static int ssb_pcmcia_sprom_read_all ( struct ssb_bus * bus , u16 * sprom )
{
int err , i ;
for ( i = 0 ; i < SSB_PCMCIA_SPROM_SIZE ; i + + ) {
err = ssb_pcmcia_sprom_read ( bus , i , & sprom [ i ] ) ;
if ( err )
return err ;
}
return 0 ;
}
/* Write the SPROM image. size is in 16bit words. */
static int ssb_pcmcia_sprom_write_all ( struct ssb_bus * bus , const u16 * sprom )
{
int i , err ;
bool failed = 0 ;
size_t size = SSB_PCMCIA_SPROM_SIZE ;
ssb_printk ( KERN_NOTICE PFX
" Writing SPROM. Do NOT turn off the power! "
" Please stand by... \n " ) ;
err = ssb_pcmcia_sprom_command ( bus , SSB_PCMCIA_SPROMCTL_WRITEEN ) ;
if ( err ) {
ssb_printk ( KERN_NOTICE PFX
" Could not enable SPROM write access. \n " ) ;
return - EBUSY ;
}
ssb_printk ( KERN_NOTICE PFX " [ 0%% " ) ;
msleep ( 500 ) ;
for ( i = 0 ; i < size ; i + + ) {
if ( i = = size / 4 )
ssb_printk ( " 25%% " ) ;
else if ( i = = size / 2 )
ssb_printk ( " 50%% " ) ;
else if ( i = = ( size * 3 ) / 4 )
ssb_printk ( " 75%% " ) ;
else if ( i % 2 )
ssb_printk ( " . " ) ;
err = ssb_pcmcia_sprom_write ( bus , i , sprom [ i ] ) ;
if ( err ) {
ssb_printk ( " \n " KERN_NOTICE PFX
" Failed to write to SPROM. \n " ) ;
failed = 1 ;
break ;
}
}
err = ssb_pcmcia_sprom_command ( bus , SSB_PCMCIA_SPROMCTL_WRITEDIS ) ;
if ( err ) {
ssb_printk ( " \n " KERN_NOTICE PFX
" Could not disable SPROM write access. \n " ) ;
failed = 1 ;
}
msleep ( 500 ) ;
if ( ! failed ) {
ssb_printk ( " 100%% ] \n " ) ;
ssb_printk ( KERN_NOTICE PFX " SPROM written. \n " ) ;
}
return failed ? - EBUSY : 0 ;
}
static int ssb_pcmcia_sprom_check_crc ( const u16 * sprom , size_t size )
{
//TODO
return 0 ;
}
# define GOTO_ERROR_ON(condition, description) do { \
if ( unlikely ( condition ) ) { \
error_description = description ; \
goto error ; \
} \
} while ( 0 )
2007-09-18 23:12:50 +04:00
int ssb_pcmcia_get_invariants ( struct ssb_bus * bus ,
struct ssb_init_invariants * iv )
{
2008-03-10 19:26:32 +03:00
tuple_t tuple ;
int res ;
unsigned char buf [ 32 ] ;
struct ssb_sprom * sprom = & iv - > sprom ;
struct ssb_boardinfo * bi = & iv - > boardinfo ;
const char * error_description ;
memset ( sprom , 0xFF , sizeof ( * sprom ) ) ;
sprom - > revision = 1 ;
sprom - > boardflags_lo = 0 ;
sprom - > boardflags_hi = 0 ;
/* First fetch the MAC address. */
memset ( & tuple , 0 , sizeof ( tuple ) ) ;
tuple . DesiredTuple = CISTPL_FUNCE ;
tuple . TupleData = buf ;
tuple . TupleDataMax = sizeof ( buf ) ;
res = pcmcia_get_first_tuple ( bus - > host_pcmcia , & tuple ) ;
GOTO_ERROR_ON ( res ! = CS_SUCCESS , " MAC first tpl " ) ;
res = pcmcia_get_tuple_data ( bus - > host_pcmcia , & tuple ) ;
GOTO_ERROR_ON ( res ! = CS_SUCCESS , " MAC first tpl data " ) ;
while ( 1 ) {
GOTO_ERROR_ON ( tuple . TupleDataLen < 1 , " MAC tpl < 1 " ) ;
if ( tuple . TupleData [ 0 ] = = CISTPL_FUNCE_LAN_NODE_ID )
break ;
res = pcmcia_get_next_tuple ( bus - > host_pcmcia , & tuple ) ;
GOTO_ERROR_ON ( res ! = CS_SUCCESS , " MAC next tpl " ) ;
res = pcmcia_get_tuple_data ( bus - > host_pcmcia , & tuple ) ;
GOTO_ERROR_ON ( res ! = CS_SUCCESS , " MAC next tpl data " ) ;
}
GOTO_ERROR_ON ( tuple . TupleDataLen ! = ETH_ALEN + 2 , " MAC tpl size " ) ;
memcpy ( sprom - > il0mac , & tuple . TupleData [ 2 ] , ETH_ALEN ) ;
/* Fetch the vendor specific tuples. */
memset ( & tuple , 0 , sizeof ( tuple ) ) ;
tuple . DesiredTuple = SSB_PCMCIA_CIS ;
tuple . TupleData = buf ;
tuple . TupleDataMax = sizeof ( buf ) ;
res = pcmcia_get_first_tuple ( bus - > host_pcmcia , & tuple ) ;
GOTO_ERROR_ON ( res ! = CS_SUCCESS , " VEN first tpl " ) ;
res = pcmcia_get_tuple_data ( bus - > host_pcmcia , & tuple ) ;
GOTO_ERROR_ON ( res ! = CS_SUCCESS , " VEN first tpl data " ) ;
while ( 1 ) {
GOTO_ERROR_ON ( tuple . TupleDataLen < 1 , " VEN tpl < 1 " ) ;
switch ( tuple . TupleData [ 0 ] ) {
case SSB_PCMCIA_CIS_ID :
GOTO_ERROR_ON ( ( tuple . TupleDataLen ! = 5 ) & &
( tuple . TupleDataLen ! = 7 ) ,
" id tpl size " ) ;
bi - > vendor = tuple . TupleData [ 1 ] |
( ( u16 ) tuple . TupleData [ 2 ] < < 8 ) ;
break ;
case SSB_PCMCIA_CIS_BOARDREV :
GOTO_ERROR_ON ( tuple . TupleDataLen ! = 2 ,
" boardrev tpl size " ) ;
sprom - > board_rev = tuple . TupleData [ 1 ] ;
break ;
case SSB_PCMCIA_CIS_PA :
GOTO_ERROR_ON ( tuple . TupleDataLen ! = 9 ,
" pa tpl size " ) ;
sprom - > pa0b0 = tuple . TupleData [ 1 ] |
( ( u16 ) tuple . TupleData [ 2 ] < < 8 ) ;
sprom - > pa0b1 = tuple . TupleData [ 3 ] |
( ( u16 ) tuple . TupleData [ 4 ] < < 8 ) ;
sprom - > pa0b2 = tuple . TupleData [ 5 ] |
( ( u16 ) tuple . TupleData [ 6 ] < < 8 ) ;
sprom - > itssi_a = tuple . TupleData [ 7 ] ;
sprom - > itssi_bg = tuple . TupleData [ 7 ] ;
sprom - > maxpwr_a = tuple . TupleData [ 8 ] ;
sprom - > maxpwr_bg = tuple . TupleData [ 8 ] ;
break ;
case SSB_PCMCIA_CIS_OEMNAME :
/* We ignore this. */
break ;
case SSB_PCMCIA_CIS_CCODE :
GOTO_ERROR_ON ( tuple . TupleDataLen ! = 2 ,
" ccode tpl size " ) ;
sprom - > country_code = tuple . TupleData [ 1 ] ;
break ;
case SSB_PCMCIA_CIS_ANTENNA :
GOTO_ERROR_ON ( tuple . TupleDataLen ! = 2 ,
" ant tpl size " ) ;
sprom - > ant_available_a = tuple . TupleData [ 1 ] ;
sprom - > ant_available_bg = tuple . TupleData [ 1 ] ;
break ;
case SSB_PCMCIA_CIS_ANTGAIN :
GOTO_ERROR_ON ( tuple . TupleDataLen ! = 2 ,
" antg tpl size " ) ;
sprom - > antenna_gain . ghz24 . a0 = tuple . TupleData [ 1 ] ;
sprom - > antenna_gain . ghz24 . a1 = tuple . TupleData [ 1 ] ;
sprom - > antenna_gain . ghz24 . a2 = tuple . TupleData [ 1 ] ;
sprom - > antenna_gain . ghz24 . a3 = tuple . TupleData [ 1 ] ;
sprom - > antenna_gain . ghz5 . a0 = tuple . TupleData [ 1 ] ;
sprom - > antenna_gain . ghz5 . a1 = tuple . TupleData [ 1 ] ;
sprom - > antenna_gain . ghz5 . a2 = tuple . TupleData [ 1 ] ;
sprom - > antenna_gain . ghz5 . a3 = tuple . TupleData [ 1 ] ;
break ;
case SSB_PCMCIA_CIS_BFLAGS :
GOTO_ERROR_ON ( tuple . TupleDataLen ! = 3 ,
" bfl tpl size " ) ;
sprom - > boardflags_lo = tuple . TupleData [ 1 ] |
( ( u16 ) tuple . TupleData [ 2 ] < < 8 ) ;
break ;
case SSB_PCMCIA_CIS_LEDS :
GOTO_ERROR_ON ( tuple . TupleDataLen ! = 5 ,
" leds tpl size " ) ;
sprom - > gpio0 = tuple . TupleData [ 1 ] ;
sprom - > gpio1 = tuple . TupleData [ 2 ] ;
sprom - > gpio2 = tuple . TupleData [ 3 ] ;
sprom - > gpio3 = tuple . TupleData [ 4 ] ;
break ;
}
res = pcmcia_get_next_tuple ( bus - > host_pcmcia , & tuple ) ;
if ( res = = CS_NO_MORE_ITEMS )
break ;
GOTO_ERROR_ON ( res ! = CS_SUCCESS , " VEN next tpl " ) ;
res = pcmcia_get_tuple_data ( bus - > host_pcmcia , & tuple ) ;
GOTO_ERROR_ON ( res ! = CS_SUCCESS , " VEN next tpl data " ) ;
}
2007-09-18 23:12:50 +04:00
return 0 ;
2008-03-10 19:26:32 +03:00
error :
ssb_printk ( KERN_ERR PFX
" PCMCIA: Failed to fetch device invariants: %s \n " ,
error_description ) ;
return - ENODEV ;
}
static ssize_t ssb_pcmcia_attr_sprom_show ( struct device * pcmciadev ,
struct device_attribute * attr ,
char * buf )
{
struct pcmcia_device * pdev =
container_of ( pcmciadev , struct pcmcia_device , dev ) ;
struct ssb_bus * bus ;
bus = ssb_pcmcia_dev_to_bus ( pdev ) ;
if ( ! bus )
return - ENODEV ;
return ssb_attr_sprom_show ( bus , buf ,
ssb_pcmcia_sprom_read_all ) ;
}
static ssize_t ssb_pcmcia_attr_sprom_store ( struct device * pcmciadev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct pcmcia_device * pdev =
container_of ( pcmciadev , struct pcmcia_device , dev ) ;
struct ssb_bus * bus ;
bus = ssb_pcmcia_dev_to_bus ( pdev ) ;
if ( ! bus )
return - ENODEV ;
return ssb_attr_sprom_store ( bus , buf , count ,
ssb_pcmcia_sprom_check_crc ,
ssb_pcmcia_sprom_write_all ) ;
}
static DEVICE_ATTR ( ssb_sprom , 0600 ,
ssb_pcmcia_attr_sprom_show ,
ssb_pcmcia_attr_sprom_store ) ;
2008-03-28 12:34:55 +03:00
static int ssb_pcmcia_cor_setup ( struct ssb_bus * bus , u8 cor )
{
u8 val ;
int err ;
err = ssb_pcmcia_cfg_read ( bus , cor , & val ) ;
if ( err )
return err ;
val & = ~ COR_SOFT_RESET ;
val | = COR_FUNC_ENA | COR_IREQ_ENA | COR_LEVEL_REQ ;
err = ssb_pcmcia_cfg_write ( bus , cor , val ) ;
if ( err )
return err ;
msleep ( 40 ) ;
return 0 ;
}
2008-03-30 03:10:50 +04:00
/* Initialize the PCMCIA hardware. This is called on Init and Resume. */
int ssb_pcmcia_hardware_setup ( struct ssb_bus * bus )
{
int err ;
if ( bus - > bustype ! = SSB_BUSTYPE_PCMCIA )
return 0 ;
/* Switch segment to a known state and sync
* bus - > mapped_pcmcia_seg with hardware state . */
ssb_pcmcia_switch_segment ( bus , 0 ) ;
/* Init the COR register. */
err = ssb_pcmcia_cor_setup ( bus , CISREG_COR ) ;
if ( err )
return err ;
/* Some cards also need this register to get poked. */
err = ssb_pcmcia_cor_setup ( bus , CISREG_COR + 0x80 ) ;
if ( err )
return err ;
return 0 ;
}
2008-03-10 19:26:32 +03:00
void ssb_pcmcia_exit ( struct ssb_bus * bus )
{
if ( bus - > bustype ! = SSB_BUSTYPE_PCMCIA )
return ;
device_remove_file ( & bus - > host_pcmcia - > dev , & dev_attr_ssb_sprom ) ;
2007-09-18 23:12:50 +04:00
}
int ssb_pcmcia_init ( struct ssb_bus * bus )
{
int err ;
if ( bus - > bustype ! = SSB_BUSTYPE_PCMCIA )
return 0 ;
2008-03-30 03:10:50 +04:00
err = ssb_pcmcia_hardware_setup ( bus ) ;
2008-03-10 19:26:32 +03:00
if ( err )
goto error ;
bus - > sprom_size = SSB_PCMCIA_SPROM_SIZE ;
mutex_init ( & bus - > sprom_mutex ) ;
err = device_create_file ( & bus - > host_pcmcia - > dev , & dev_attr_ssb_sprom ) ;
if ( err )
2007-09-18 23:12:50 +04:00
goto error ;
return 0 ;
error :
2008-03-10 19:26:32 +03:00
ssb_printk ( KERN_ERR PFX " Failed to initialize PCMCIA host device \n " ) ;
return err ;
2007-09-18 23:12:50 +04:00
}