2005-04-16 15:20:36 -07:00
/*
* drivers / pcmcia / m32r_pcc . c
*
* Device driver for the PCMCIA functionality of M32R .
*
* Copyright ( c ) 2001 , 2002 , 2003 , 2004
* Hiroyuki Kondo , Naoto Sugai , Hayato Fujiwara
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/string.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/timer.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/workqueue.h>
# include <linux/interrupt.h>
2005-10-29 19:07:23 +01:00
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
# include <asm/irq.h>
# include <asm/io.h>
# include <asm/bitops.h>
# include <asm/system.h>
# include <asm/addrspace.h>
# include <pcmcia/cs_types.h>
# include <pcmcia/ss.h>
# include <pcmcia/cs.h>
/* XXX: should be moved into asm/irq.h */
# define PCC0_IRQ 24
# define PCC1_IRQ 25
# include "m32r_pcc.h"
# define CHAOS_PCC_DEBUG
# ifdef CHAOS_PCC_DEBUG
static volatile u_short dummy_readbuf ;
# endif
# define PCC_DEBUG_DBEX
# ifdef DEBUG
static int m32r_pcc_debug ;
module_param ( m32r_pcc_debug , int , 0644 ) ;
# define debug(lvl, fmt, arg...) do { \
if ( m32r_pcc_debug > ( lvl ) ) \
printk ( KERN_DEBUG " m32r_pcc: " fmt , # # arg ) ; \
} while ( 0 )
# else
# define debug(n, args...) do { } while (0)
# endif
/* Poll status interval -- 0 means default to interrupt */
static int poll_interval = 0 ;
typedef enum pcc_space { as_none = 0 , as_comm , as_attr , as_io } pcc_as_t ;
typedef struct pcc_socket {
u_short type , flags ;
struct pcmcia_socket socket ;
unsigned int number ;
kio_addr_t ioaddr ;
u_long mapaddr ;
u_long base ; /* PCC register base */
u_char cs_irq , intr ;
pccard_io_map io_map [ MAX_IO_WIN ] ;
pccard_mem_map mem_map [ MAX_WIN ] ;
u_char io_win ;
u_char mem_win ;
pcc_as_t current_space ;
u_char last_iodbex ;
# ifdef CHAOS_PCC_DEBUG
u_char last_iosize ;
# endif
# ifdef CONFIG_PROC_FS
struct proc_dir_entry * proc ;
# endif
} pcc_socket_t ;
static int pcc_sockets = 0 ;
static pcc_socket_t socket [ M32R_MAX_PCC ] = {
{ 0 , } , /* ... */
} ;
/*====================================================================*/
static unsigned int pcc_get ( u_short , unsigned int ) ;
static void pcc_set ( u_short , unsigned int , unsigned int ) ;
static DEFINE_SPINLOCK ( pcc_lock ) ;
void pcc_iorw ( int sock , unsigned long port , void * buf , size_t size , size_t nmemb , int wr , int flag )
{
u_long addr ;
u_long flags ;
int need_ex ;
# ifdef PCC_DEBUG_DBEX
int _dbex ;
# endif
pcc_socket_t * t = & socket [ sock ] ;
# ifdef CHAOS_PCC_DEBUG
int map_changed = 0 ;
# endif
/* Need lock ? */
spin_lock_irqsave ( & pcc_lock , flags ) ;
/*
* Check if need dbex
*/
need_ex = ( size > 1 & & flag = = 0 ) ? PCMOD_DBEX : 0 ;
# ifdef PCC_DEBUG_DBEX
_dbex = need_ex ;
need_ex = 0 ;
# endif
/*
* calculate access address
*/
addr = t - > mapaddr + port - t - > ioaddr + KSEG1 ; /* XXX */
/*
* Check current mapping
*/
if ( t - > current_space ! = as_io | | t - > last_iodbex ! = need_ex ) {
u_long cbsz ;
/*
* Disable first
*/
pcc_set ( sock , PCCR , 0 ) ;
/*
* Set mode and io address
*/
cbsz = ( t - > flags & MAP_16BIT ) ? 0 : PCMOD_CBSZ ;
pcc_set ( sock , PCMOD , PCMOD_AS_IO | cbsz | need_ex ) ;
pcc_set ( sock , PCADR , addr & 0x1ff00000 ) ;
/*
* Enable and read it
*/
pcc_set ( sock , PCCR , 1 ) ;
# ifdef CHAOS_PCC_DEBUG
#if 0
map_changed = ( t - > current_space = = as_attr & & size = = 2 ) ; /* XXX */
# else
map_changed = 1 ;
# endif
# endif
t - > current_space = as_io ;
}
/*
* access to IO space
*/
if ( size = = 1 ) {
/* Byte */
unsigned char * bp = ( unsigned char * ) buf ;
# ifdef CHAOS_DEBUG
if ( map_changed ) {
dummy_readbuf = readb ( addr ) ;
}
# endif
if ( wr ) {
/* write Byte */
while ( nmemb - - ) {
writeb ( * bp + + , addr ) ;
}
} else {
/* read Byte */
while ( nmemb - - ) {
* bp + + = readb ( addr ) ;
}
}
} else {
/* Word */
unsigned short * bp = ( unsigned short * ) buf ;
# ifdef CHAOS_PCC_DEBUG
if ( map_changed ) {
dummy_readbuf = readw ( addr ) ;
}
# endif
if ( wr ) {
/* write Word */
while ( nmemb - - ) {
# ifdef PCC_DEBUG_DBEX
if ( _dbex ) {
unsigned char * cp = ( unsigned char * ) bp ;
unsigned short tmp ;
tmp = cp [ 1 ] < < 8 | cp [ 0 ] ;
writew ( tmp , addr ) ;
bp + + ;
} else
# endif
writew ( * bp + + , addr ) ;
}
} else {
/* read Word */
while ( nmemb - - ) {
# ifdef PCC_DEBUG_DBEX
if ( _dbex ) {
unsigned char * cp = ( unsigned char * ) bp ;
unsigned short tmp ;
tmp = readw ( addr ) ;
cp [ 0 ] = tmp & 0xff ;
cp [ 1 ] = ( tmp > > 8 ) & 0xff ;
bp + + ;
} else
# endif
* bp + + = readw ( addr ) ;
}
}
}
# if 1
/* addr is no longer used */
if ( ( addr = pcc_get ( sock , PCIRC ) ) & PCIRC_BWERR ) {
printk ( " m32r_pcc: BWERR detected : port 0x%04lx : iosize %dbit \n " ,
port , size * 8 ) ;
pcc_set ( sock , PCIRC , addr ) ;
}
# endif
/*
* save state
*/
t - > last_iosize = size ;
t - > last_iodbex = need_ex ;
/* Need lock ? */
spin_unlock_irqrestore ( & pcc_lock , flags ) ;
return ;
}
void pcc_ioread ( int sock , unsigned long port , void * buf , size_t size , size_t nmemb , int flag ) {
pcc_iorw ( sock , port , buf , size , nmemb , 0 , flag ) ;
}
void pcc_iowrite ( int sock , unsigned long port , void * buf , size_t size , size_t nmemb , int flag ) {
pcc_iorw ( sock , port , buf , size , nmemb , 1 , flag ) ;
}
/*====================================================================*/
# define IS_REGISTERED 0x2000
# define IS_ALIVE 0x8000
typedef struct pcc_t {
char * name ;
u_short flags ;
} pcc_t ;
static pcc_t pcc [ ] = {
{ " xnux2 " , 0 } , { " xnux2 " , 0 } ,
} ;
static irqreturn_t pcc_interrupt ( int , void * , struct pt_regs * ) ;
/*====================================================================*/
static struct timer_list poll_timer ;
static unsigned int pcc_get ( u_short sock , unsigned int reg )
{
return inl ( socket [ sock ] . base + reg ) ;
}
static void pcc_set ( u_short sock , unsigned int reg , unsigned int data )
{
outl ( data , socket [ sock ] . base + reg ) ;
}
/*======================================================================
See if a card is present , powered up , in IO mode , and already
bound to a ( non PC Card ) Linux driver . We leave these alone .
We make an exception for cards that seem to be serial devices .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int __init is_alive ( u_short sock )
{
unsigned int stat ;
unsigned int f ;
stat = pcc_get ( sock , PCIRC ) ;
f = ( stat & ( PCIRC_CDIN1 | PCIRC_CDIN2 ) ) > > 16 ;
if ( ! f ) {
printk ( " m32r_pcc: No Card is detected at socket %d : stat = 0x%08x \n " , stat , sock ) ;
return 0 ;
}
if ( f ! = 3 )
printk ( " m32r_pcc: Insertion fail (%.8x) at socket %d \n " , stat , sock ) ;
else
printk ( " m32r_pcc: Card is Inserted at socket %d(%.8x) \n " , sock , stat ) ;
return 0 ;
}
static void add_pcc_socket ( ulong base , int irq , ulong mapaddr , kio_addr_t ioaddr )
{
pcc_socket_t * t = & socket [ pcc_sockets ] ;
/* add sockets */
t - > ioaddr = ioaddr ;
t - > mapaddr = mapaddr ;
t - > base = base ;
# ifdef CHAOS_PCC_DEBUG
t - > flags = MAP_16BIT ;
# else
t - > flags = 0 ;
# endif
if ( is_alive ( pcc_sockets ) )
t - > flags | = IS_ALIVE ;
/* add pcc */
if ( t - > base > 0 ) {
request_region ( t - > base , 0x20 , " m32r-pcc " ) ;
}
printk ( KERN_INFO " %s " , pcc [ pcc_sockets ] . name ) ;
printk ( " pcc at 0x%08lx \n " , t - > base ) ;
/* Update socket interrupt information, capabilities */
t - > socket . features | = ( SS_CAP_PCCARD | SS_CAP_STATIC_MAP ) ;
t - > socket . map_size = M32R_PCC_MAPSIZE ;
t - > socket . io_offset = ioaddr ; /* use for io access offset */
t - > socket . irq_mask = 0 ;
t - > socket . pci_irq = 2 + pcc_sockets ; /* XXX */
request_irq ( irq , pcc_interrupt , 0 , " m32r-pcc " , pcc_interrupt ) ;
pcc_sockets + + ;
return ;
}
/*====================================================================*/
static irqreturn_t pcc_interrupt ( int irq , void * dev , struct pt_regs * regs )
{
int i , j , irc ;
u_int events , active ;
int handled = 0 ;
debug ( 4 , " m32r: pcc_interrupt(%d) \n " , irq ) ;
for ( j = 0 ; j < 20 ; j + + ) {
active = 0 ;
for ( i = 0 ; i < pcc_sockets ; i + + ) {
if ( ( socket [ i ] . cs_irq ! = irq ) & &
( socket [ i ] . socket . pci_irq ! = irq ) )
continue ;
handled = 1 ;
irc = pcc_get ( i , PCIRC ) ;
irc > > = 16 ;
debug ( 2 , " m32r-pcc:interrput: socket %d pcirc 0x%02x " , i , irc ) ;
if ( ! irc )
continue ;
events = ( irc ) ? SS_DETECT : 0 ;
events | = ( pcc_get ( i , PCCR ) & PCCR_PCEN ) ? SS_READY : 0 ;
debug ( 2 , " event 0x%02x \n " , events ) ;
if ( events )
pcmcia_parse_events ( & socket [ i ] . socket , events ) ;
active | = events ;
active = 0 ;
}
if ( ! active ) break ;
}
if ( j = = 20 )
printk ( KERN_NOTICE " m32r-pcc: infinite loop in interrupt handler \n " ) ;
debug ( 4 , " m32r-pcc: interrupt done \n " ) ;
return IRQ_RETVAL ( handled ) ;
} /* pcc_interrupt */
static void pcc_interrupt_wrapper ( u_long data )
{
pcc_interrupt ( 0 , NULL , NULL ) ;
init_timer ( & poll_timer ) ;
poll_timer . expires = jiffies + poll_interval ;
add_timer ( & poll_timer ) ;
}
/*====================================================================*/
static int _pcc_get_status ( u_short sock , u_int * value )
{
u_int status ;
status = pcc_get ( sock , PCIRC ) ;
* value = ( ( status & PCIRC_CDIN1 ) & & ( status & PCIRC_CDIN2 ) )
? SS_DETECT : 0 ;
status = pcc_get ( sock , PCCR ) ;
#if 0
* value | = ( status & PCCR_PCEN ) ? SS_READY : 0 ;
# else
* value | = SS_READY ; /* XXX: always */
# endif
status = pcc_get ( sock , PCCSIGCR ) ;
* value | = ( status & PCCSIGCR_VEN ) ? SS_POWERON : 0 ;
debug ( 3 , " m32r-pcc: GetStatus(%d) = %#4.4x \n " , sock , * value ) ;
return 0 ;
} /* _get_status */
/*====================================================================*/
static int _pcc_set_socket ( u_short sock , socket_state_t * state )
{
u_long reg = 0 ;
debug ( 3 , " m32r-pcc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
" io_irq %d, csc_mask %#2.2x) " , sock , state - > flags ,
state - > Vcc , state - > Vpp , state - > io_irq , state - > csc_mask ) ;
if ( state - > Vcc ) {
/*
* 5 V only
*/
if ( state - > Vcc = = 50 ) {
reg | = PCCSIGCR_VEN ;
} else {
return - EINVAL ;
}
}
if ( state - > flags & SS_RESET ) {
debug ( 3 , " :RESET \n " ) ;
reg | = PCCSIGCR_CRST ;
}
if ( state - > flags & SS_OUTPUT_ENA ) {
debug ( 3 , " :OUTPUT_ENA \n " ) ;
/* bit clear */
} else {
reg | = PCCSIGCR_SEN ;
}
pcc_set ( sock , PCCSIGCR , reg ) ;
# ifdef DEBUG
if ( state - > flags & SS_IOCARD ) {
debug ( 3 , " :IOCARD " ) ;
}
if ( state - > flags & SS_PWR_AUTO ) {
debug ( 3 , " :PWR_AUTO " ) ;
}
if ( state - > csc_mask & SS_DETECT )
debug ( 3 , " :csc-SS_DETECT " ) ;
if ( state - > flags & SS_IOCARD ) {
if ( state - > csc_mask & SS_STSCHG )
debug ( 3 , " :STSCHG " ) ;
} else {
if ( state - > csc_mask & SS_BATDEAD )
debug ( 3 , " :BATDEAD " ) ;
if ( state - > csc_mask & SS_BATWARN )
debug ( 3 , " :BATWARN " ) ;
if ( state - > csc_mask & SS_READY )
debug ( 3 , " :READY " ) ;
}
debug ( 3 , " \n " ) ;
# endif
return 0 ;
} /* _set_socket */
/*====================================================================*/
static int _pcc_set_io_map ( u_short sock , struct pccard_io_map * io )
{
u_char map ;
debug ( 3 , " m32r-pcc: SetIOMap(%d, %d, %#2.2x, %d ns, "
" %#lx-%#lx) \n " , sock , io - > map , io - > flags ,
io - > speed , io - > start , io - > stop ) ;
map = io - > map ;
return 0 ;
} /* _set_io_map */
/*====================================================================*/
static int _pcc_set_mem_map ( u_short sock , struct pccard_mem_map * mem )
{
u_char map = mem - > map ;
u_long mode ;
u_long addr ;
pcc_socket_t * t = & socket [ sock ] ;
# ifdef CHAOS_PCC_DEBUG
#if 0
pcc_as_t last = t - > current_space ;
# endif
# endif
debug ( 3 , " m32r-pcc: SetMemMap(%d, %d, %#2.2x, %d ns, "
" %#lx, %#x) \n " , sock , map , mem - > flags ,
mem - > speed , mem - > static_start , mem - > card_start ) ;
/*
* sanity check
*/
if ( ( map > MAX_WIN ) | | ( mem - > card_start > 0x3ffffff ) ) {
return - EINVAL ;
}
/*
* de - activate
*/
if ( ( mem - > flags & MAP_ACTIVE ) = = 0 ) {
t - > current_space = as_none ;
return 0 ;
}
/*
* Disable first
*/
pcc_set ( sock , PCCR , 0 ) ;
/*
* Set mode
*/
if ( mem - > flags & MAP_ATTRIB ) {
mode = PCMOD_AS_ATTRIB | PCMOD_CBSZ ;
t - > current_space = as_attr ;
} else {
mode = 0 ; /* common memory */
t - > current_space = as_comm ;
}
pcc_set ( sock , PCMOD , mode ) ;
/*
* Set address
*/
addr = t - > mapaddr + ( mem - > card_start & M32R_PCC_MAPMASK ) ;
pcc_set ( sock , PCADR , addr ) ;
mem - > static_start = addr + mem - > card_start ;
/*
* Enable again
*/
pcc_set ( sock , PCCR , 1 ) ;
# ifdef CHAOS_PCC_DEBUG
#if 0
if ( last ! = as_attr ) {
# else
if ( 1 ) {
# endif
dummy_readbuf = * ( u_char * ) ( addr + KSEG1 ) ;
}
# endif
return 0 ;
} /* _set_mem_map */
#if 0 /* driver model ordering issue */
/*======================================================================
Routines for accessing socket information and register dumps via
/ proc / bus / pccard / . . .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static ssize_t show_info ( struct class_device * class_dev , char * buf )
{
pcc_socket_t * s = container_of ( class_dev , struct pcc_socket ,
socket . dev ) ;
return sprintf ( buf , " type: %s \n base addr: 0x%08lx \n " ,
pcc [ s - > type ] . name , s - > base ) ;
}
static ssize_t show_exca ( struct class_device * class_dev , char * buf )
{
/* FIXME */
return 0 ;
}
static CLASS_DEVICE_ATTR ( info , S_IRUGO , show_info , NULL ) ;
static CLASS_DEVICE_ATTR ( exca , S_IRUGO , show_exca , NULL ) ;
# endif
/*====================================================================*/
/* this is horribly ugly... proper locking needs to be done here at
* some time . . . */
# define LOCKED(x) do { \
int retval ; \
unsigned long flags ; \
spin_lock_irqsave ( & pcc_lock , flags ) ; \
retval = x ; \
spin_unlock_irqrestore ( & pcc_lock , flags ) ; \
return retval ; \
} while ( 0 )
static int pcc_get_status ( struct pcmcia_socket * s , u_int * value )
{
unsigned int sock = container_of ( s , struct pcc_socket , socket ) - > number ;
if ( socket [ sock ] . flags & IS_ALIVE ) {
* value = 0 ;
return - EINVAL ;
}
LOCKED ( _pcc_get_status ( sock , value ) ) ;
}
static int pcc_set_socket ( struct pcmcia_socket * s , socket_state_t * state )
{
unsigned int sock = container_of ( s , struct pcc_socket , socket ) - > number ;
if ( socket [ sock ] . flags & IS_ALIVE )
return - EINVAL ;
LOCKED ( _pcc_set_socket ( sock , state ) ) ;
}
static int pcc_set_io_map ( struct pcmcia_socket * s , struct pccard_io_map * io )
{
unsigned int sock = container_of ( s , struct pcc_socket , socket ) - > number ;
if ( socket [ sock ] . flags & IS_ALIVE )
return - EINVAL ;
LOCKED ( _pcc_set_io_map ( sock , io ) ) ;
}
static int pcc_set_mem_map ( struct pcmcia_socket * s , struct pccard_mem_map * mem )
{
unsigned int sock = container_of ( s , struct pcc_socket , socket ) - > number ;
if ( socket [ sock ] . flags & IS_ALIVE )
return - EINVAL ;
LOCKED ( _pcc_set_mem_map ( sock , mem ) ) ;
}
static int pcc_init ( struct pcmcia_socket * s )
{
debug ( 4 , " m32r-pcc: init call \n " ) ;
return 0 ;
}
static struct pccard_operations pcc_operations = {
. init = pcc_init ,
. get_status = pcc_get_status ,
. set_socket = pcc_set_socket ,
. set_io_map = pcc_set_io_map ,
. set_mem_map = pcc_set_mem_map ,
} ;
/*====================================================================*/
static struct device_driver pcc_driver = {
. name = " pcc " ,
. bus = & platform_bus_type ,
2005-10-28 09:52:56 -07:00
. suspend = pcmcia_socket_dev_suspend ,
. resume = pcmcia_socket_dev_resume ,
2005-04-16 15:20:36 -07:00
} ;
static struct platform_device pcc_device = {
. name = " pcc " ,
. id = 0 ,
} ;
/*====================================================================*/
static int __init init_m32r_pcc ( void )
{
int i , ret ;
ret = driver_register ( & pcc_driver ) ;
if ( ret )
return ret ;
ret = platform_device_register ( & pcc_device ) ;
if ( ret ) {
driver_unregister ( & pcc_driver ) ;
return ret ;
}
printk ( KERN_INFO " m32r PCC probe: \n " ) ;
pcc_sockets = 0 ;
add_pcc_socket ( M32R_PCC0_BASE , PCC0_IRQ , M32R_PCC0_MAPBASE , 0x1000 ) ;
# ifdef CONFIG_M32RPCC_SLOT2
add_pcc_socket ( M32R_PCC1_BASE , PCC1_IRQ , M32R_PCC1_MAPBASE , 0x2000 ) ;
# endif
if ( pcc_sockets = = 0 ) {
printk ( " socket is not found. \n " ) ;
platform_device_unregister ( & pcc_device ) ;
driver_unregister ( & pcc_driver ) ;
return - ENODEV ;
}
/* Set up interrupt handler(s) */
for ( i = 0 ; i < pcc_sockets ; i + + ) {
socket [ i ] . socket . dev . dev = & pcc_device . dev ;
socket [ i ] . socket . ops = & pcc_operations ;
socket [ i ] . socket . resource_ops = & pccard_static_ops ;
socket [ i ] . socket . owner = THIS_MODULE ;
socket [ i ] . number = i ;
ret = pcmcia_register_socket ( & socket [ i ] . socket ) ;
if ( ! ret )
socket [ i ] . flags | = IS_REGISTERED ;
#if 0 /* driver model ordering issue */
class_device_create_file ( & socket [ i ] . socket . dev ,
& class_device_attr_info ) ;
class_device_create_file ( & socket [ i ] . socket . dev ,
& class_device_attr_exca ) ;
# endif
}
/* Finally, schedule a polling interrupt */
if ( poll_interval ! = 0 ) {
poll_timer . function = pcc_interrupt_wrapper ;
poll_timer . data = 0 ;
init_timer ( & poll_timer ) ;
poll_timer . expires = jiffies + poll_interval ;
add_timer ( & poll_timer ) ;
}
return 0 ;
} /* init_m32r_pcc */
static void __exit exit_m32r_pcc ( void )
{
int i ;
for ( i = 0 ; i < pcc_sockets ; i + + )
if ( socket [ i ] . flags & IS_REGISTERED )
pcmcia_unregister_socket ( & socket [ i ] . socket ) ;
platform_device_unregister ( & pcc_device ) ;
if ( poll_interval ! = 0 )
del_timer_sync ( & poll_timer ) ;
driver_unregister ( & pcc_driver ) ;
} /* exit_m32r_pcc */
module_init ( init_m32r_pcc ) ;
module_exit ( exit_m32r_pcc ) ;
MODULE_LICENSE ( " Dual MPL/GPL " ) ;
/*====================================================================*/