2007-09-18 15:12:50 -04:00
/*
* Sonics Silicon Backplane
* PCMCIA - Hostbus related functions
*
* Copyright 2006 Johannes Berg < johannes @ sipsolutions . net >
* Copyright 2007 Michael Buesch < mb @ bu3sch . de >
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# include <linux/ssb/ssb.h>
# include <linux/delay.h>
2007-10-14 05:51:51 +01:00
# include <linux/io.h>
2007-09-18 15: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
int ssb_pcmcia_switch_coreidx ( struct ssb_bus * bus ,
u8 coreidx )
{
struct pcmcia_device * pdev = bus - > host_pcmcia ;
int err ;
int attempts = 0 ;
u32 cur_core ;
conf_reg_t reg ;
u32 addr ;
u32 read_addr ;
addr = ( coreidx * SSB_CORE_SIZE ) + SSB_ENUM_BASE ;
while ( 1 ) {
reg . Action = CS_WRITE ;
reg . Offset = 0x2E ;
reg . Value = ( addr & 0x0000F000 ) > > 12 ;
err = pcmcia_access_configuration_register ( pdev , & reg ) ;
if ( err ! = CS_SUCCESS )
goto error ;
reg . Offset = 0x30 ;
reg . Value = ( addr & 0x00FF0000 ) > > 16 ;
err = pcmcia_access_configuration_register ( pdev , & reg ) ;
if ( err ! = CS_SUCCESS )
goto error ;
reg . Offset = 0x32 ;
reg . Value = ( addr & 0xFF000000 ) > > 24 ;
err = pcmcia_access_configuration_register ( pdev , & reg ) ;
if ( err ! = CS_SUCCESS )
goto error ;
read_addr = 0 ;
reg . Action = CS_READ ;
reg . Offset = 0x2E ;
err = pcmcia_access_configuration_register ( pdev , & reg ) ;
if ( err ! = CS_SUCCESS )
goto error ;
2007-11-07 19:03:35 +01:00
read_addr | = ( ( u32 ) ( reg . Value & 0x0F ) ) < < 12 ;
2007-09-18 15:12:50 -04:00
reg . Offset = 0x30 ;
err = pcmcia_access_configuration_register ( pdev , & reg ) ;
if ( err ! = CS_SUCCESS )
goto error ;
2007-11-07 19:03:35 +01:00
read_addr | = ( ( u32 ) reg . Value ) < < 16 ;
2007-09-18 15:12:50 -04:00
reg . Offset = 0x32 ;
err = pcmcia_access_configuration_register ( pdev , & reg ) ;
if ( err ! = CS_SUCCESS )
goto error ;
2007-11-07 19:03:35 +01:00
read_addr | = ( ( u32 ) reg . Value ) < < 24 ;
2007-09-18 15:12:50 -04:00
cur_core = ( read_addr - 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_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 ;
conf_reg_t reg ;
2007-12-22 22:01:36 +01:00
int res ;
2007-09-18 15:12:50 -04:00
SSB_WARN_ON ( ( seg ! = 0 ) & & ( seg ! = 1 ) ) ;
reg . Offset = 0x34 ;
reg . Function = 0 ;
while ( 1 ) {
reg . Action = CS_WRITE ;
reg . Value = seg ;
res = pcmcia_access_configuration_register ( bus - > host_pcmcia , & reg ) ;
if ( unlikely ( res ! = CS_SUCCESS ) )
goto error ;
reg . Value = 0xFF ;
reg . Action = CS_READ ;
res = pcmcia_access_configuration_register ( bus - > host_pcmcia , & reg ) ;
if ( unlikely ( res ! = CS_SUCCESS ) )
goto error ;
if ( reg . Value = = seg )
break ;
if ( unlikely ( attempts + + > SSB_BAR0_MAX_RETRIES ) )
goto error ;
udelay ( 10 ) ;
}
bus - > mapped_pcmcia_seg = seg ;
2007-12-22 22:01:36 +01:00
return 0 ;
2007-09-18 15:12:50 -04:00
error :
ssb_printk ( KERN_ERR PFX " Failed to switch pcmcia segment \n " ) ;
2007-12-22 22:01:36 +01:00
return - ENODEV ;
2007-09-18 15:12:50 -04:00
}
2007-11-07 19:03:35 +01:00
static int select_core_and_segment ( struct ssb_device * dev ,
u16 * offset )
2007-09-18 15:12:50 -04:00
{
2007-11-07 19:03:35 +01:00
struct ssb_bus * bus = dev - > bus ;
2007-09-18 15:12:50 -04:00
int err ;
2007-11-07 19:03:35 +01:00
u8 need_segment ;
if ( * offset > = 0x800 ) {
* offset - = 0x800 ;
need_segment = 1 ;
} else
need_segment = 0 ;
2007-09-18 15: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 19:03:35 +01:00
if ( unlikely ( need_segment ! = bus - > mapped_pcmcia_seg ) ) {
err = ssb_pcmcia_switch_segment ( bus , need_segment ) ;
2007-09-18 15:12:50 -04:00
if ( unlikely ( err ) )
return err ;
}
return 0 ;
}
static u16 ssb_pcmcia_read16 ( struct ssb_device * dev , u16 offset )
{
struct ssb_bus * bus = dev - > bus ;
2007-12-22 22:01:36 +01:00
unsigned long flags ;
int err ;
u16 value = 0xFFFF ;
2007-09-18 15:12:50 -04:00
2007-12-22 22:01:36 +01: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 15:12:50 -04:00
2007-12-22 22:01:36 +01:00
return value ;
2007-09-18 15:12:50 -04:00
}
static u32 ssb_pcmcia_read32 ( struct ssb_device * dev , u16 offset )
{
struct ssb_bus * bus = dev - > bus ;
2007-12-22 22:01:36 +01:00
unsigned long flags ;
int err ;
u32 lo = 0xFFFFFFFF , hi = 0xFFFFFFFF ;
2007-09-18 15:12:50 -04:00
2007-12-22 22:01:36 +01: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 15:12:50 -04:00
2007-11-07 19:03:35 +01:00
return ( lo | ( hi < < 16 ) ) ;
2007-09-18 15: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-22 22:01:36 +01:00
unsigned long flags ;
int err ;
2007-09-18 15:12:50 -04:00
2007-12-22 22:01:36 +01: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 15: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-22 22:01:36 +01:00
unsigned long flags ;
int err ;
2007-09-18 15:12:50 -04:00
2007-12-22 22:01:36 +01: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 15:12:50 -04:00
}
/* Not "static", as it's used in main.c */
const struct ssb_bus_ops ssb_pcmcia_ops = {
. read16 = ssb_pcmcia_read16 ,
. read32 = ssb_pcmcia_read32 ,
. write16 = ssb_pcmcia_write16 ,
. write32 = ssb_pcmcia_write32 ,
} ;
2007-12-22 22:01:36 +01:00
# include <linux/etherdevice.h>
2007-09-18 15:12:50 -04:00
int ssb_pcmcia_get_invariants ( struct ssb_bus * bus ,
struct ssb_init_invariants * iv )
{
//TODO
2007-12-22 22:01:36 +01:00
random_ether_addr ( iv - > sprom . il0mac ) ;
2007-09-18 15:12:50 -04:00
return 0 ;
}
int ssb_pcmcia_init ( struct ssb_bus * bus )
{
conf_reg_t reg ;
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 IRQ routing */
reg . Action = CS_READ ;
reg . Function = 0 ;
if ( bus - > chip_id = = 0x4306 )
reg . Offset = 0x00 ;
else
reg . Offset = 0x80 ;
err = pcmcia_access_configuration_register ( bus - > host_pcmcia , & reg ) ;
if ( err ! = CS_SUCCESS )
goto error ;
reg . Action = CS_WRITE ;
reg . Value | = 0x04 | 0x01 ;
err = pcmcia_access_configuration_register ( bus - > host_pcmcia , & reg ) ;
if ( err ! = CS_SUCCESS )
goto error ;
return 0 ;
error :
return - ENODEV ;
}