2008-08-19 12:15:53 +02:00
/*
2009-11-14 13:39:13 +01:00
* Viper / Zeus PCMCIA support
2008-08-19 12:15:53 +02:00
* Copyright 2004 Arcom Control Systems
*
* Maintained by Marc Zyngier < maz @ misterjones . org >
*
* Based on :
* iPAQ h2200 PCMCIA support
* Copyright 2004 Koen Kooi < koen @ vestingbar . nl >
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file COPYING in the main directory of this archive for
* more details .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <linux/gpio.h>
# include <pcmcia/ss.h>
# include <asm/irq.h>
2009-11-14 13:39:13 +01:00
# include <mach/arcom-pcmcia.h>
2008-08-19 12:15:53 +02:00
# include "soc_common.h"
# include "pxa2xx_base.h"
2009-11-14 13:39:13 +01:00
static struct platform_device * arcom_pcmcia_dev ;
2008-08-19 12:15:53 +02:00
static struct pcmcia_irqs irqs [ ] = {
2009-11-14 13:39:13 +01:00
{
. sock = 0 ,
. str = " PCMCIA_CD " ,
} ,
2008-08-19 12:15:53 +02:00
} ;
2009-11-14 13:39:13 +01:00
static inline struct arcom_pcmcia_pdata * viper_get_pdata ( void )
{
return arcom_pcmcia_dev - > dev . platform_data ;
}
2008-08-19 12:15:53 +02:00
static int viper_pcmcia_hw_init ( struct soc_pcmcia_socket * skt )
{
2009-11-14 13:39:13 +01:00
struct arcom_pcmcia_pdata * pdata = viper_get_pdata ( ) ;
2008-08-19 12:15:53 +02:00
unsigned long flags ;
2009-11-14 13:39:13 +01:00
skt - > socket . pci_irq = gpio_to_irq ( pdata - > rdy_gpio ) ;
irqs [ 0 ] . irq = gpio_to_irq ( pdata - > cd_gpio ) ;
2008-08-19 12:15:53 +02:00
2009-11-14 13:39:13 +01:00
if ( gpio_request ( pdata - > cd_gpio , " CF detect " ) )
2008-08-19 12:15:53 +02:00
goto err_request_cd ;
2009-11-14 13:39:13 +01:00
if ( gpio_request ( pdata - > rdy_gpio , " CF ready " ) )
2008-08-19 12:15:53 +02:00
goto err_request_rdy ;
2009-11-14 13:39:13 +01:00
if ( gpio_request ( pdata - > pwr_gpio , " CF power " ) )
2008-08-19 12:15:53 +02:00
goto err_request_pwr ;
local_irq_save ( flags ) ;
2009-11-14 13:39:13 +01:00
if ( gpio_direction_output ( pdata - > pwr_gpio , 0 ) | |
gpio_direction_input ( pdata - > cd_gpio ) | |
gpio_direction_input ( pdata - > rdy_gpio ) ) {
2008-08-19 12:15:53 +02:00
local_irq_restore ( flags ) ;
goto err_dir ;
}
local_irq_restore ( flags ) ;
return soc_pcmcia_request_irqs ( skt , irqs , ARRAY_SIZE ( irqs ) ) ;
err_dir :
2009-11-14 13:39:13 +01:00
gpio_free ( pdata - > pwr_gpio ) ;
2008-08-19 12:15:53 +02:00
err_request_pwr :
2009-11-14 13:39:13 +01:00
gpio_free ( pdata - > rdy_gpio ) ;
2008-08-19 12:15:53 +02:00
err_request_rdy :
2009-11-14 13:39:13 +01:00
gpio_free ( pdata - > cd_gpio ) ;
2008-08-19 12:15:53 +02:00
err_request_cd :
2009-11-14 13:39:13 +01:00
dev_err ( & arcom_pcmcia_dev - > dev , " Failed to setup PCMCIA GPIOs \n " ) ;
2008-08-19 12:15:53 +02:00
return - 1 ;
}
/*
* Release all resources .
*/
static void viper_pcmcia_hw_shutdown ( struct soc_pcmcia_socket * skt )
{
2009-11-14 13:39:13 +01:00
struct arcom_pcmcia_pdata * pdata = viper_get_pdata ( ) ;
2008-08-19 12:15:53 +02:00
soc_pcmcia_free_irqs ( skt , irqs , ARRAY_SIZE ( irqs ) ) ;
2009-11-14 13:39:13 +01:00
gpio_free ( pdata - > pwr_gpio ) ;
gpio_free ( pdata - > rdy_gpio ) ;
gpio_free ( pdata - > cd_gpio ) ;
2008-08-19 12:15:53 +02:00
}
static void viper_pcmcia_socket_state ( struct soc_pcmcia_socket * skt ,
struct pcmcia_state * state )
{
2009-11-14 13:39:13 +01:00
struct arcom_pcmcia_pdata * pdata = viper_get_pdata ( ) ;
state - > detect = ! gpio_get_value ( pdata - > cd_gpio ) ;
state - > ready = ! ! gpio_get_value ( pdata - > rdy_gpio ) ;
2008-08-19 12:15:53 +02:00
state - > bvd1 = 1 ;
state - > bvd2 = 1 ;
state - > wrprot = 0 ;
state - > vs_3v = 1 ; /* Can only apply 3.3V */
state - > vs_Xv = 0 ;
}
static int viper_pcmcia_configure_socket ( struct soc_pcmcia_socket * skt ,
const socket_state_t * state )
{
2009-11-14 13:39:13 +01:00
struct arcom_pcmcia_pdata * pdata = viper_get_pdata ( ) ;
2008-08-19 12:15:53 +02:00
/* Silently ignore Vpp, output enable, speaker enable. */
2009-11-14 13:39:13 +01:00
pdata - > reset ( state - > flags & SS_RESET ) ;
2008-08-19 12:15:53 +02:00
/* Apply socket voltage */
switch ( state - > Vcc ) {
case 0 :
2009-11-14 13:39:13 +01:00
gpio_set_value ( pdata - > pwr_gpio , 0 ) ;
2008-08-19 12:15:53 +02:00
break ;
case 33 :
2009-11-14 13:39:13 +01:00
gpio_set_value ( pdata - > pwr_gpio , 1 ) ;
2008-08-19 12:15:53 +02:00
break ;
default :
2009-11-14 13:39:13 +01:00
dev_err ( & arcom_pcmcia_dev - > dev , " Unsupported Vcc:%d \n " , state - > Vcc ) ;
2008-08-19 12:15:53 +02:00
return - 1 ;
}
return 0 ;
}
2009-11-14 13:39:13 +01:00
static struct pcmcia_low_level viper_pcmcia_ops = {
2008-08-19 12:15:53 +02:00
. owner = THIS_MODULE ,
. hw_init = viper_pcmcia_hw_init ,
. hw_shutdown = viper_pcmcia_hw_shutdown ,
. socket_state = viper_pcmcia_socket_state ,
. configure_socket = viper_pcmcia_configure_socket ,
. nr = 1 ,
} ;
static struct platform_device * viper_pcmcia_device ;
2009-11-14 13:39:13 +01:00
static int viper_pcmcia_probe ( struct platform_device * pdev )
2008-08-19 12:15:53 +02:00
{
int ret ;
2009-11-14 13:39:13 +01:00
/* I can't imagine more than one device, but you never know... */
if ( arcom_pcmcia_dev )
return - EEXIST ;
if ( ! pdev - > dev . platform_data )
return - EINVAL ;
2008-08-19 12:15:53 +02:00
viper_pcmcia_device = platform_device_alloc ( " pxa2xx-pcmcia " , - 1 ) ;
if ( ! viper_pcmcia_device )
return - ENOMEM ;
2009-11-14 13:39:13 +01:00
arcom_pcmcia_dev = pdev ;
viper_pcmcia_device - > dev . parent = & pdev - > dev ;
2008-08-19 12:15:53 +02:00
ret = platform_device_add_data ( viper_pcmcia_device ,
& viper_pcmcia_ops ,
sizeof ( viper_pcmcia_ops ) ) ;
if ( ! ret )
ret = platform_device_add ( viper_pcmcia_device ) ;
2009-11-14 13:39:13 +01:00
if ( ret ) {
2008-08-19 12:15:53 +02:00
platform_device_put ( viper_pcmcia_device ) ;
2009-11-14 13:39:13 +01:00
arcom_pcmcia_dev = NULL ;
}
2008-08-19 12:15:53 +02:00
return ret ;
}
2009-11-14 13:39:13 +01:00
static int viper_pcmcia_remove ( struct platform_device * pdev )
2008-08-19 12:15:53 +02:00
{
platform_device_unregister ( viper_pcmcia_device ) ;
2009-11-14 13:39:13 +01:00
arcom_pcmcia_dev = NULL ;
return 0 ;
}
static struct platform_device_id viper_pcmcia_id_table [ ] = {
{ . name = " viper-pcmcia " , } ,
{ . name = " zeus-pcmcia " , } ,
{ } ,
} ;
static struct platform_driver viper_pcmcia_driver = {
. probe = viper_pcmcia_probe ,
. remove = viper_pcmcia_remove ,
. driver = {
. name = " arcom-pcmcia " ,
. owner = THIS_MODULE ,
} ,
. id_table = viper_pcmcia_id_table ,
} ;
static int __init viper_pcmcia_init ( void )
{
return platform_driver_register ( & viper_pcmcia_driver ) ;
}
static void __exit viper_pcmcia_exit ( void )
{
return platform_driver_unregister ( & viper_pcmcia_driver ) ;
2008-08-19 12:15:53 +02:00
}
module_init ( viper_pcmcia_init ) ;
module_exit ( viper_pcmcia_exit ) ;
2009-11-14 13:39:13 +01:00
MODULE_DEVICE_TABLE ( platform , viper_pcmcia_id_table ) ;
2008-08-19 12:15:53 +02:00
MODULE_LICENSE ( " GPL " ) ;