2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
/*
* Sharp SL - C7xx Series PCMCIA routines
*
* Copyright ( c ) 2004 - 2005 Richard Purdie
*
* Based on Sharp ' s 2.4 kernel patches and pxa2xx_mainstone . c
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
2005-10-31 02:39:02 +03:00
# include <asm/mach-types.h>
2005-04-17 02:20:36 +04:00
# include <asm/irq.h>
# include <asm/hardware/scoop.h>
2020-05-28 23:57:40 +03:00
# include <pcmcia/soc_common.h>
2005-04-17 02:20:36 +04:00
# define NO_KEEP_VS 0x0001
2005-11-08 22:15:43 +03:00
# define SCOOP_DEV platform_scoop_config->devs
2006-01-05 23:44:57 +03:00
static void sharpsl_pcmcia_init_reset ( struct soc_pcmcia_socket * skt )
2005-04-17 02:20:36 +04:00
{
2006-01-05 23:44:57 +03:00
struct scoop_pcmcia_dev * scoopdev = & SCOOP_DEV [ skt - > nr ] ;
2005-09-05 23:49:54 +04:00
reset_scoop ( scoopdev - > dev ) ;
2006-01-05 23:44:57 +03:00
/* Shared power controls need to be handled carefully */
if ( platform_scoop_config - > power_ctrl )
platform_scoop_config - > power_ctrl ( scoopdev - > dev , 0x0000 , skt - > nr ) ;
else
write_scoop_reg ( scoopdev - > dev , SCOOP_CPR , 0x0000 ) ;
2005-09-05 23:49:54 +04:00
scoopdev - > keep_vs = NO_KEEP_VS ;
scoopdev - > keep_rd = 0 ;
2005-04-17 02:20:36 +04:00
}
static int sharpsl_pcmcia_hw_init ( struct soc_pcmcia_socket * skt )
{
2005-11-08 22:15:43 +03:00
if ( SCOOP_DEV [ skt - > nr ] . cd_irq > = 0 ) {
2012-01-14 02:56:32 +04:00
skt - > stat [ SOC_STAT_CD ] . irq = SCOOP_DEV [ skt - > nr ] . cd_irq ;
skt - > stat [ SOC_STAT_CD ] . name = SCOOP_DEV [ skt - > nr ] . cd_irq_str ;
2005-04-17 02:20:36 +04:00
}
2009-03-30 01:45:26 +04:00
skt - > socket . pci_irq = SCOOP_DEV [ skt - > nr ] . irq ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void sharpsl_pcmcia_socket_state ( struct soc_pcmcia_socket * skt ,
struct pcmcia_state * state )
{
unsigned short cpr , csr ;
2005-11-08 22:15:43 +03:00
struct device * scoop = SCOOP_DEV [ skt - > nr ] . dev ;
2005-04-17 02:20:36 +04:00
2005-11-08 22:15:43 +03:00
cpr = read_scoop_reg ( SCOOP_DEV [ skt - > nr ] . dev , SCOOP_CPR ) ;
2005-04-17 02:20:36 +04:00
2005-09-05 23:49:54 +04:00
write_scoop_reg ( scoop , SCOOP_IRM , 0x00FF ) ;
write_scoop_reg ( scoop , SCOOP_ISR , 0x0000 ) ;
write_scoop_reg ( scoop , SCOOP_IRM , 0x0000 ) ;
csr = read_scoop_reg ( scoop , SCOOP_CSR ) ;
2005-04-17 02:20:36 +04:00
if ( csr & 0x0004 ) {
/* card eject */
2005-09-05 23:49:54 +04:00
write_scoop_reg ( scoop , SCOOP_CDR , 0x0000 ) ;
2005-11-08 22:15:43 +03:00
SCOOP_DEV [ skt - > nr ] . keep_vs = NO_KEEP_VS ;
2005-04-17 02:20:36 +04:00
}
2005-11-08 22:15:43 +03:00
else if ( ! ( SCOOP_DEV [ skt - > nr ] . keep_vs & NO_KEEP_VS ) ) {
2005-04-17 02:20:36 +04:00
/* keep vs1,vs2 */
2005-09-05 23:49:54 +04:00
write_scoop_reg ( scoop , SCOOP_CDR , 0x0000 ) ;
2005-11-08 22:15:43 +03:00
csr | = SCOOP_DEV [ skt - > nr ] . keep_vs ;
2005-04-17 02:20:36 +04:00
}
else if ( cpr & 0x0003 ) {
/* power on */
2005-09-05 23:49:54 +04:00
write_scoop_reg ( scoop , SCOOP_CDR , 0x0000 ) ;
2005-11-08 22:15:43 +03:00
SCOOP_DEV [ skt - > nr ] . keep_vs = ( csr & 0x00C0 ) ;
2005-04-17 02:20:36 +04:00
}
else {
/* card detect */
2005-11-08 22:15:43 +03:00
if ( ( machine_is_spitz ( ) | | machine_is_borzoi ( ) ) & & skt - > nr = = 1 ) {
write_scoop_reg ( scoop , SCOOP_CDR , 0x0000 ) ;
} else {
write_scoop_reg ( scoop , SCOOP_CDR , 0x0002 ) ;
}
2005-04-17 02:20:36 +04:00
}
state - > detect = ( csr & 0x0004 ) ? 0 : 1 ;
state - > ready = ( csr & 0x0002 ) ? 1 : 0 ;
state - > bvd1 = ( csr & 0x0010 ) ? 1 : 0 ;
state - > bvd2 = ( csr & 0x0020 ) ? 1 : 0 ;
state - > wrprot = ( csr & 0x0008 ) ? 1 : 0 ;
state - > vs_3v = ( csr & 0x0040 ) ? 0 : 1 ;
state - > vs_Xv = ( csr & 0x0080 ) ? 0 : 1 ;
if ( ( cpr & 0x0080 ) & & ( ( cpr & 0x8040 ) ! = 0x8040 ) ) {
printk ( KERN_ERR " sharpsl_pcmcia_socket_state(): CPR=%04X, Low voltage! \n " , cpr ) ;
}
}
static int sharpsl_pcmcia_configure_socket ( struct soc_pcmcia_socket * skt ,
const socket_state_t * state )
{
unsigned long flags ;
2005-11-08 22:15:43 +03:00
struct device * scoop = SCOOP_DEV [ skt - > nr ] . dev ;
2005-04-17 02:20:36 +04:00
unsigned short cpr , ncpr , ccr , nccr , mcr , nmcr , imr , nimr ;
switch ( state - > Vcc ) {
case 0 : break ;
case 33 : break ;
case 50 : break ;
default :
printk ( KERN_ERR " sharpsl_pcmcia_configure_socket(): bad Vcc %u \n " , state - > Vcc ) ;
return - 1 ;
}
if ( ( state - > Vpp ! = state - > Vcc ) & & ( state - > Vpp ! = 0 ) ) {
printk ( KERN_ERR " CF slot cannot support Vpp %u \n " , state - > Vpp ) ;
return - 1 ;
}
local_irq_save ( flags ) ;
2005-09-05 23:49:54 +04:00
nmcr = ( mcr = read_scoop_reg ( scoop , SCOOP_MCR ) ) & ~ 0x0010 ;
ncpr = ( cpr = read_scoop_reg ( scoop , SCOOP_CPR ) ) & ~ 0x0083 ;
nccr = ( ccr = read_scoop_reg ( scoop , SCOOP_CCR ) ) & ~ 0x0080 ;
nimr = ( imr = read_scoop_reg ( scoop , SCOOP_IMR ) ) & ~ 0x003E ;
2005-04-17 02:20:36 +04:00
2005-11-08 22:15:43 +03:00
if ( ( machine_is_spitz ( ) | | machine_is_borzoi ( ) | | machine_is_akita ( ) ) & & skt - > nr = = 0 ) {
ncpr | = ( state - > Vcc = = 33 ) ? 0x0002 :
( state - > Vcc = = 50 ) ? 0x0002 : 0 ;
} else {
ncpr | = ( state - > Vcc = = 33 ) ? 0x0001 :
( state - > Vcc = = 50 ) ? 0x0002 : 0 ;
}
2005-04-17 02:20:36 +04:00
nmcr | = ( state - > flags & SS_IOCARD ) ? 0x0010 : 0 ;
ncpr | = ( state - > flags & SS_OUTPUT_ENA ) ? 0x0080 : 0 ;
nccr | = ( state - > flags & SS_RESET ) ? 0x0080 : 0 ;
nimr | = ( ( skt - > status & SS_DETECT ) ? 0x0004 : 0 ) |
( ( skt - > status & SS_READY ) ? 0x0002 : 0 ) |
( ( skt - > status & SS_BATDEAD ) ? 0x0010 : 0 ) |
( ( skt - > status & SS_BATWARN ) ? 0x0020 : 0 ) |
( ( skt - > status & SS_STSCHG ) ? 0x0010 : 0 ) |
( ( skt - > status & SS_WRPROT ) ? 0x0008 : 0 ) ;
if ( ! ( ncpr & 0x0003 ) ) {
2005-11-08 22:15:43 +03:00
SCOOP_DEV [ skt - > nr ] . keep_rd = 0 ;
} else if ( ! SCOOP_DEV [ skt - > nr ] . keep_rd ) {
2005-04-17 02:20:36 +04:00
if ( nccr & 0x0080 )
2005-11-08 22:15:43 +03:00
SCOOP_DEV [ skt - > nr ] . keep_rd = 1 ;
2005-04-17 02:20:36 +04:00
else
nccr | = 0x0080 ;
}
if ( mcr ! = nmcr )
2005-09-05 23:49:54 +04:00
write_scoop_reg ( scoop , SCOOP_MCR , nmcr ) ;
2005-11-08 22:15:43 +03:00
if ( cpr ! = ncpr ) {
if ( platform_scoop_config - > power_ctrl )
platform_scoop_config - > power_ctrl ( scoop , ncpr , skt - > nr ) ;
else
write_scoop_reg ( scoop , SCOOP_CPR , ncpr ) ;
}
2005-04-17 02:20:36 +04:00
if ( ccr ! = nccr )
2005-09-05 23:49:54 +04:00
write_scoop_reg ( scoop , SCOOP_CCR , nccr ) ;
2005-04-17 02:20:36 +04:00
if ( imr ! = nimr )
2005-09-05 23:49:54 +04:00
write_scoop_reg ( scoop , SCOOP_IMR , nimr ) ;
2005-04-17 02:20:36 +04:00
local_irq_restore ( flags ) ;
return 0 ;
}
static void sharpsl_pcmcia_socket_init ( struct soc_pcmcia_socket * skt )
{
2006-01-05 23:44:57 +03:00
sharpsl_pcmcia_init_reset ( skt ) ;
2005-09-05 23:49:54 +04:00
/* Enable interrupt */
2005-11-08 22:15:43 +03:00
write_scoop_reg ( SCOOP_DEV [ skt - > nr ] . dev , SCOOP_IMR , 0x00C0 ) ;
write_scoop_reg ( SCOOP_DEV [ skt - > nr ] . dev , SCOOP_MCR , 0x0101 ) ;
SCOOP_DEV [ skt - > nr ] . keep_vs = NO_KEEP_VS ;
2005-04-17 02:20:36 +04:00
}
static void sharpsl_pcmcia_socket_suspend ( struct soc_pcmcia_socket * skt )
{
2006-01-05 23:44:57 +03:00
sharpsl_pcmcia_init_reset ( skt ) ;
2005-04-17 02:20:36 +04:00
}
2012-04-30 17:50:56 +04:00
static struct pcmcia_low_level sharpsl_pcmcia_ops = {
2005-11-08 22:15:43 +03:00
. owner = THIS_MODULE ,
. hw_init = sharpsl_pcmcia_hw_init ,
. socket_state = sharpsl_pcmcia_socket_state ,
. configure_socket = sharpsl_pcmcia_configure_socket ,
. socket_init = sharpsl_pcmcia_socket_init ,
. socket_suspend = sharpsl_pcmcia_socket_suspend ,
. first = 0 ,
. nr = 0 ,
2005-04-17 02:20:36 +04:00
} ;
2005-10-31 02:39:02 +03:00
# ifdef CONFIG_SA1100_COLLIE
2005-11-08 22:15:43 +03:00
# include "sa11xx_base.h"
2012-11-19 22:23:12 +04:00
int pcmcia_collie_init ( struct device * dev )
2005-10-31 02:39:02 +03:00
{
int ret = - ENODEV ;
if ( machine_is_collie ( ) )
ret = sa11xx_drv_pcmcia_probe ( dev , & sharpsl_pcmcia_ops , 0 , 1 ) ;
return ret ;
}
# else
2005-11-08 22:15:43 +03:00
static struct platform_device * sharpsl_pcmcia_device ;
2005-04-17 02:20:36 +04:00
static int __init sharpsl_pcmcia_init ( void )
{
int ret ;
2009-04-20 20:48:28 +04:00
if ( ! platform_scoop_config )
return - ENODEV ;
2006-01-10 20:16:12 +03:00
sharpsl_pcmcia_ops . nr = platform_scoop_config - > num_devs ;
sharpsl_pcmcia_device = platform_device_alloc ( " pxa2xx-pcmcia " , - 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ! sharpsl_pcmcia_device )
return - ENOMEM ;
2005-09-05 23:49:54 +04:00
2008-04-24 18:28:11 +04:00
ret = platform_device_add_data ( sharpsl_pcmcia_device ,
& sharpsl_pcmcia_ops , sizeof ( sharpsl_pcmcia_ops ) ) ;
if ( ret = = 0 ) {
sharpsl_pcmcia_device - > dev . parent = platform_scoop_config - > devs [ 0 ] . dev ;
ret = platform_device_add ( sharpsl_pcmcia_device ) ;
}
2005-04-17 02:20:36 +04:00
if ( ret )
2006-01-10 20:16:12 +03:00
platform_device_put ( sharpsl_pcmcia_device ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static void __exit sharpsl_pcmcia_exit ( void )
{
platform_device_unregister ( sharpsl_pcmcia_device ) ;
}
2005-09-03 22:39:25 +04:00
fs_initcall ( sharpsl_pcmcia_init ) ;
2005-04-17 02:20:36 +04:00
module_exit ( sharpsl_pcmcia_exit ) ;
2005-10-31 02:39:02 +03:00
# endif
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " Sharp SL Series PCMCIA Support " ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-08-18 06:40:39 +04:00
MODULE_ALIAS ( " platform:pxa2xx-pcmcia " ) ;