2007-09-18 23:12:50 +04:00
/*
* Sonics Silicon Backplane
* PCMCIA - Hostbus related functions
*
* Copyright 2006 Johannes Berg < johannes @ sipsolutions . net >
2011-07-04 22:50:05 +04:00
* Copyright 2007 - 2008 Michael Buesch < m @ bues . ch >
2007-09-18 23:12:50 +04:00
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
2018-07-31 22:56:38 +03:00
# include "ssb_private.h"
2007-09-18 23:12:50 +04:00
# 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/cistpl.h>
# include <pcmcia/ciscode.h>
# include <pcmcia/ds.h>
# include <pcmcia/cisreg.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 )
{
int res ;
2010-07-24 14:23:21 +04:00
res = pcmcia_write_config_byte ( bus - > host_pcmcia , offset , value ) ;
2008-08-03 12:07:45 +04:00
if ( unlikely ( res ! = 0 ) )
2008-03-10 19:26:32 +03:00
return - EBUSY ;
return 0 ;
}
/* Read from a PCMCIA configuration register. */
static int ssb_pcmcia_cfg_read ( struct ssb_bus * bus , u8 offset , u8 * value )
{
int res ;
2010-07-24 14:23:21 +04:00
res = pcmcia_read_config_byte ( bus - > host_pcmcia , offset , value ) ;
2008-08-03 12:07:45 +04:00
if ( unlikely ( res ! = 0 ) )
2008-03-10 19:26:32 +03:00
return - EBUSY ;
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 :
2018-07-31 22:56:38 +03:00
pr_err ( " 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
}
2015-09-21 10:55:26 +03:00
static int ssb_pcmcia_switch_core ( struct ssb_bus * bus , struct ssb_device * dev )
2007-09-18 23:12:50 +04:00
{
int err ;
# if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG
2018-07-31 22:56:38 +03:00
pr_info ( " Switching to %s core, index %d \n " ,
ssb_core_name ( dev - > id . coreid ) , dev - > core_index ) ;
2007-09-18 23:12:50 +04:00
# 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
2018-07-31 23:15:09 +03:00
WARN_ON ( ( seg ! = 0 ) & & ( seg ! = 1 ) ) ;
2007-09-18 23:12:50 +04:00
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 :
2018-07-31 22:56:38 +03:00
pr_err ( " 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-04-02 21:46:56 +04:00
# ifdef CONFIG_SSB_BLOCKIO
static void ssb_pcmcia_block_read ( struct ssb_device * dev , void * buffer ,
size_t count , u16 offset , u8 reg_width )
{
struct ssb_bus * bus = dev - > bus ;
unsigned long flags ;
void __iomem * addr = bus - > mmio + offset ;
int err ;
spin_lock_irqsave ( & bus - > bar_lock , flags ) ;
err = select_core_and_segment ( dev , & offset ) ;
if ( unlikely ( err ) ) {
memset ( buffer , 0xFF , count ) ;
goto unlock ;
}
switch ( reg_width ) {
case sizeof ( u8 ) : {
u8 * buf = buffer ;
while ( count ) {
* buf = __raw_readb ( addr ) ;
buf + + ;
count - - ;
}
break ;
}
case sizeof ( u16 ) : {
__le16 * buf = buffer ;
2018-07-31 23:15:09 +03:00
WARN_ON ( count & 1 ) ;
2008-04-02 21:46:56 +04:00
while ( count ) {
* buf = ( __force __le16 ) __raw_readw ( addr ) ;
buf + + ;
count - = 2 ;
}
break ;
}
case sizeof ( u32 ) : {
__le16 * buf = buffer ;
2018-07-31 23:15:09 +03:00
WARN_ON ( count & 3 ) ;
2008-04-02 21:46:56 +04:00
while ( count ) {
* buf = ( __force __le16 ) __raw_readw ( addr ) ;
buf + + ;
* buf = ( __force __le16 ) __raw_readw ( addr + 2 ) ;
buf + + ;
count - = 4 ;
}
break ;
}
default :
2018-07-31 23:15:09 +03:00
WARN_ON ( 1 ) ;
2008-04-02 21:46:56 +04:00
}
unlock :
spin_unlock_irqrestore ( & bus - > bar_lock , flags ) ;
}
# endif /* CONFIG_SSB_BLOCKIO */
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 ) ;
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 ) ;
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 ) ;
}
spin_unlock_irqrestore ( & bus - > bar_lock , flags ) ;
2007-09-18 23:12:50 +04:00
}
2008-04-02 21:46:56 +04:00
# ifdef CONFIG_SSB_BLOCKIO
static void ssb_pcmcia_block_write ( struct ssb_device * dev , const void * buffer ,
size_t count , u16 offset , u8 reg_width )
{
struct ssb_bus * bus = dev - > bus ;
unsigned long flags ;
void __iomem * addr = bus - > mmio + offset ;
int err ;
spin_lock_irqsave ( & bus - > bar_lock , flags ) ;
err = select_core_and_segment ( dev , & offset ) ;
if ( unlikely ( err ) )
goto unlock ;
switch ( reg_width ) {
case sizeof ( u8 ) : {
const u8 * buf = buffer ;
while ( count ) {
__raw_writeb ( * buf , addr ) ;
buf + + ;
count - - ;
}
break ;
}
case sizeof ( u16 ) : {
const __le16 * buf = buffer ;
2018-07-31 23:15:09 +03:00
WARN_ON ( count & 1 ) ;
2008-04-02 21:46:56 +04:00
while ( count ) {
__raw_writew ( ( __force u16 ) ( * buf ) , addr ) ;
buf + + ;
count - = 2 ;
}
break ;
}
case sizeof ( u32 ) : {
const __le16 * buf = buffer ;
2018-07-31 23:15:09 +03:00
WARN_ON ( count & 3 ) ;
2008-04-02 21:46:56 +04:00
while ( count ) {
__raw_writew ( ( __force u16 ) ( * buf ) , addr ) ;
buf + + ;
__raw_writew ( ( __force u16 ) ( * buf ) , addr + 2 ) ;
buf + + ;
count - = 4 ;
}
break ;
}
default :
2018-07-31 23:15:09 +03:00
WARN_ON ( 1 ) ;
2008-04-02 21:46:56 +04:00
}
unlock :
spin_unlock_irqrestore ( & bus - > bar_lock , flags ) ;
}
# 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_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-04-02 21:46:56 +04:00
# ifdef CONFIG_SSB_BLOCKIO
. block_read = ssb_pcmcia_block_read ,
. block_write = ssb_pcmcia_block_write ,
# endif
2007-09-18 23:12:50 +04:00
} ;
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 ;
2018-07-31 22:56:38 +03:00
pr_notice ( " Writing SPROM. Do NOT turn off the power! Please stand by... \n " ) ;
2008-03-10 19:26:32 +03:00
err = ssb_pcmcia_sprom_command ( bus , SSB_PCMCIA_SPROMCTL_WRITEEN ) ;
if ( err ) {
2018-07-31 22:56:38 +03:00
pr_notice ( " Could not enable SPROM write access \n " ) ;
2008-03-10 19:26:32 +03:00
return - EBUSY ;
}
2018-07-31 22:56:38 +03:00
pr_notice ( " [ 0%% " ) ;
2008-03-10 19:26:32 +03:00
msleep ( 500 ) ;
for ( i = 0 ; i < size ; i + + ) {
if ( i = = size / 4 )
2018-07-31 22:56:38 +03:00
pr_cont ( " 25%% " ) ;
2008-03-10 19:26:32 +03:00
else if ( i = = size / 2 )
2018-07-31 22:56:38 +03:00
pr_cont ( " 50%% " ) ;
2008-03-10 19:26:32 +03:00
else if ( i = = ( size * 3 ) / 4 )
2018-07-31 22:56:38 +03:00
pr_cont ( " 75%% " ) ;
2008-03-10 19:26:32 +03:00
else if ( i % 2 )
2018-07-31 22:56:38 +03:00
pr_cont ( " . " ) ;
2008-03-10 19:26:32 +03:00
err = ssb_pcmcia_sprom_write ( bus , i , sprom [ i ] ) ;
if ( err ) {
2018-07-31 22:56:38 +03:00
pr_notice ( " Failed to write to SPROM \n " ) ;
2008-03-10 19:26:32 +03:00
failed = 1 ;
break ;
}
}
err = ssb_pcmcia_sprom_command ( bus , SSB_PCMCIA_SPROMCTL_WRITEDIS ) ;
if ( err ) {
2018-07-31 22:56:38 +03:00
pr_notice ( " Could not disable SPROM write access \n " ) ;
2008-03-10 19:26:32 +03:00
failed = 1 ;
}
msleep ( 500 ) ;
if ( ! failed ) {
2018-07-31 22:56:38 +03:00
pr_cont ( " 100%% ] \n " ) ;
pr_notice ( " SPROM written \n " ) ;
2008-03-10 19:26:32 +03:00
}
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 )
2009-10-19 01:56:41 +04:00
static int ssb_pcmcia_get_mac ( struct pcmcia_device * p_dev ,
tuple_t * tuple ,
void * priv )
2007-09-18 23:12:50 +04:00
{
2009-10-19 01:56:41 +04:00
struct ssb_sprom * sprom = priv ;
if ( tuple - > TupleData [ 0 ] ! = CISTPL_FUNCE_LAN_NODE_ID )
return - EINVAL ;
if ( tuple - > TupleDataLen ! = ETH_ALEN + 2 )
return - EINVAL ;
if ( tuple - > TupleData [ 1 ] ! = ETH_ALEN )
return - EINVAL ;
memcpy ( sprom - > il0mac , & tuple - > TupleData [ 2 ] , ETH_ALEN ) ;
return 0 ;
} ;
static int ssb_pcmcia_do_get_invariants ( struct pcmcia_device * p_dev ,
tuple_t * tuple ,
void * priv )
{
struct ssb_init_invariants * iv = priv ;
2008-03-10 19:26:32 +03:00
struct ssb_sprom * sprom = & iv - > sprom ;
struct ssb_boardinfo * bi = & iv - > boardinfo ;
const char * error_description ;
2009-10-19 01:56:41 +04:00
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 ) & &
( tuple - > TupleDataLen ! = 10 ) ,
" 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 " ) ;
2012-02-28 03:56:05 +04:00
sprom - > antenna_gain . a0 = tuple - > TupleData [ 1 ] ;
sprom - > antenna_gain . a1 = tuple - > TupleData [ 1 ] ;
sprom - > antenna_gain . a2 = tuple - > TupleData [ 1 ] ;
sprom - > antenna_gain . a3 = tuple - > TupleData [ 1 ] ;
2009-10-19 01:56:41 +04:00
break ;
case SSB_PCMCIA_CIS_BFLAGS :
GOTO_ERROR_ON ( ( tuple - > TupleDataLen ! = 3 ) & &
( tuple - > TupleDataLen ! = 5 ) ,
" 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 ;
}
return - ENOSPC ; /* continue with next entry */
error :
2018-07-31 22:56:38 +03:00
pr_err ( " PCMCIA: Failed to fetch device invariants: %s \n " ,
error_description ) ;
2009-10-19 01:56:41 +04:00
return - ENODEV ;
}
int ssb_pcmcia_get_invariants ( struct ssb_bus * bus ,
struct ssb_init_invariants * iv )
{
struct ssb_sprom * sprom = & iv - > sprom ;
int res ;
2008-03-10 19:26:32 +03:00
memset ( sprom , 0xFF , sizeof ( * sprom ) ) ;
sprom - > revision = 1 ;
sprom - > boardflags_lo = 0 ;
sprom - > boardflags_hi = 0 ;
/* First fetch the MAC address. */
2009-10-19 01:56:41 +04:00
res = pcmcia_loop_tuple ( bus - > host_pcmcia , CISTPL_FUNCE ,
ssb_pcmcia_get_mac , sprom ) ;
if ( res ! = 0 ) {
2018-07-31 22:56:38 +03:00
pr_err ( " PCMCIA: Failed to fetch MAC address \n " ) ;
2009-10-19 01:56:41 +04:00
return - ENODEV ;
2008-03-10 19:26:32 +03:00
}
/* Fetch the vendor specific tuples. */
2009-10-19 01:56:41 +04:00
res = pcmcia_loop_tuple ( bus - > host_pcmcia , SSB_PCMCIA_CIS ,
2011-02-05 01:34:45 +03:00
ssb_pcmcia_do_get_invariants , iv ) ;
2009-10-19 01:56:41 +04:00
if ( ( res = = 0 ) | | ( res = = - ENOSPC ) )
return 0 ;
2008-03-10 19:26:32 +03:00
2018-07-31 22:56:38 +03:00
pr_err ( " PCMCIA: Failed to fetch device invariants \n " ) ;
2008-03-10 19:26:32 +03:00
return - ENODEV ;
}
2021-06-03 11:22:18 +03:00
static ssize_t ssb_sprom_show ( struct device * pcmciadev ,
struct device_attribute * attr ,
char * buf )
2008-03-10 19:26:32 +03:00
{
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 ) ;
}
2021-06-03 11:22:18 +03:00
static ssize_t ssb_sprom_store ( struct device * pcmciadev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2008-03-10 19:26:32 +03:00
{
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 ) ;
}
2021-06-03 11:22:18 +03:00
static DEVICE_ATTR_ADMIN_RW ( ssb_sprom ) ;
2008-03-10 19:26:32 +03:00
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 :
2018-07-31 22:56:38 +03:00
pr_err ( " Failed to initialize PCMCIA host device \n " ) ;
2008-03-10 19:26:32 +03:00
return err ;
2007-09-18 23:12:50 +04:00
}