2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
/*
* cs . c - - Kernel Card Services - core services
*
* The initial developer of the original code is David A . Hinds
* < dahinds @ users . sourceforge . net > . Portions created by David A . Hinds
* are Copyright ( C ) 1999 David A . Hinds . All Rights Reserved .
*
* ( C ) 1999 David A . Hinds
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/major.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/timer.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/pm.h>
# include <linux/device.h>
2006-06-14 17:01:26 +04:00
# include <linux/kthread.h>
2006-12-07 07:34:23 +03:00
# include <linux/freezer.h>
2005-04-17 02:20:36 +04:00
# include <asm/irq.h>
# include <pcmcia/ss.h>
# include <pcmcia/cistpl.h>
# include <pcmcia/cisreg.h>
# include <pcmcia/ds.h>
# include "cs_internal.h"
/* Module parameters */
MODULE_AUTHOR ( " David Hinds <dahinds@users.sourceforge.net> " ) ;
2005-06-28 03:28:53 +04:00
MODULE_DESCRIPTION ( " Linux Kernel Card Services " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
# define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
INT_MODULE_PARM ( setup_delay , 10 ) ; /* centiseconds */
INT_MODULE_PARM ( resume_delay , 20 ) ; /* centiseconds */
INT_MODULE_PARM ( shutdown_delay , 3 ) ; /* centiseconds */
INT_MODULE_PARM ( vcc_settle , 40 ) ; /* centiseconds */
INT_MODULE_PARM ( reset_time , 10 ) ; /* usecs */
INT_MODULE_PARM ( unreset_delay , 10 ) ; /* centiseconds */
INT_MODULE_PARM ( unreset_check , 10 ) ; /* centiseconds */
INT_MODULE_PARM ( unreset_limit , 30 ) ; /* unreset_check's */
/* Access speed for attribute memory windows */
INT_MODULE_PARM ( cis_speed , 300 ) ; /* ns */
socket_state_t dead_socket = {
. csc_mask = SS_DETECT ,
} ;
2005-06-28 03:28:53 +04:00
EXPORT_SYMBOL ( dead_socket ) ;
2005-04-17 02:20:36 +04:00
/* List of all sockets, protected by a rwsem */
LIST_HEAD ( pcmcia_socket_list ) ;
EXPORT_SYMBOL ( pcmcia_socket_list ) ;
2005-06-28 03:28:53 +04:00
DECLARE_RWSEM ( pcmcia_socket_list_rwsem ) ;
EXPORT_SYMBOL ( pcmcia_socket_list_rwsem ) ;
2005-04-17 02:20:36 +04:00
2009-12-08 00:11:45 +03:00
struct pcmcia_socket * pcmcia_get_socket ( struct pcmcia_socket * skt )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct device * dev = get_device ( & skt - > dev ) ;
if ( ! dev )
2005-04-17 02:20:36 +04:00
return NULL ;
2010-01-03 00:59:15 +03:00
return dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( pcmcia_get_socket ) ;
void pcmcia_put_socket ( struct pcmcia_socket * skt )
{
2006-09-12 19:00:10 +04:00
put_device ( & skt - > dev ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( pcmcia_put_socket ) ;
2006-09-12 19:00:10 +04:00
static void pcmcia_release_socket ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct pcmcia_socket * socket = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
complete ( & socket - > socket_released ) ;
}
static int pccardd ( void * __skt ) ;
/**
* pcmcia_register_socket - add a new pcmcia socket device
2007-12-11 02:49:22 +03:00
* @ socket : the & socket to register
2005-04-17 02:20:36 +04:00
*/
int pcmcia_register_socket ( struct pcmcia_socket * socket )
{
2006-06-14 17:01:26 +04:00
struct task_struct * tsk ;
2005-04-17 02:20:36 +04:00
int ret ;
2006-09-12 19:00:10 +04:00
if ( ! socket | | ! socket - > ops | | ! socket - > dev . parent | | ! socket - > resource_ops )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2009-10-23 14:51:28 +04:00
dev_dbg ( & socket - > dev , " pcmcia_register_socket(0x%p) \n " , socket - > ops ) ;
2005-04-17 02:20:36 +04:00
/* try to obtain a socket number [yes, it gets ugly if we
2005-06-28 03:28:53 +04:00
* register more than 2 ^ sizeof ( unsigned int ) pcmcia
* sockets . . . but the socket number is deprecated
2005-04-17 02:20:36 +04:00
* anyways , so I don ' t care ] */
down_write ( & pcmcia_socket_list_rwsem ) ;
if ( list_empty ( & pcmcia_socket_list ) )
socket - > sock = 0 ;
else {
unsigned int found , i = 1 ;
struct pcmcia_socket * tmp ;
do {
found = 1 ;
list_for_each_entry ( tmp , & pcmcia_socket_list , socket_list ) {
if ( tmp - > sock = = i )
found = 0 ;
}
i + + ;
} while ( ! found ) ;
socket - > sock = i - 1 ;
}
list_add_tail ( & socket - > socket_list , & pcmcia_socket_list ) ;
up_write ( & pcmcia_socket_list_rwsem ) ;
2005-07-08 04:59:07 +04:00
# ifndef CONFIG_CARDBUS
/*
* If we do not support Cardbus , ensure that
* the Cardbus socket capability is disabled .
*/
socket - > features & = ~ SS_CAP_CARDBUS ;
# endif
2005-04-17 02:20:36 +04:00
/* set proper values in socket->dev */
2006-09-12 19:00:10 +04:00
dev_set_drvdata ( & socket - > dev , socket ) ;
2005-04-17 02:20:36 +04:00
socket - > dev . class = & pcmcia_socket_class ;
2008-11-01 13:46:06 +03:00
dev_set_name ( & socket - > dev , " pcmcia_socket%u " , socket - > sock ) ;
2005-04-17 02:20:36 +04:00
/* base address = 0, map = 0 */
socket - > cis_mem . flags = 0 ;
socket - > cis_mem . speed = cis_speed ;
INIT_LIST_HEAD ( & socket - > cis_cache ) ;
init_completion ( & socket - > socket_released ) ;
init_completion ( & socket - > thread_done ) ;
2006-01-10 23:20:36 +03:00
mutex_init ( & socket - > skt_mutex ) ;
2010-01-12 23:42:51 +03:00
mutex_init ( & socket - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
spin_lock_init ( & socket - > thread_lock ) ;
2008-11-09 23:47:47 +03:00
if ( socket - > resource_ops - > init ) {
2010-01-17 21:31:45 +03:00
mutex_lock ( & socket - > ops_mutex ) ;
2008-11-09 23:47:47 +03:00
ret = socket - > resource_ops - > init ( socket ) ;
2010-01-17 21:31:45 +03:00
mutex_unlock ( & socket - > ops_mutex ) ;
2008-11-09 23:47:47 +03:00
if ( ret )
goto err ;
}
2006-06-14 17:01:26 +04:00
tsk = kthread_run ( pccardd , socket , " pccardd " ) ;
if ( IS_ERR ( tsk ) ) {
ret = PTR_ERR ( tsk ) ;
2005-04-17 02:20:36 +04:00
goto err ;
2006-06-14 17:01:26 +04:00
}
2005-04-17 02:20:36 +04:00
wait_for_completion ( & socket - > thread_done ) ;
2006-06-14 17:01:26 +04:00
if ( ! socket - > thread ) {
2014-10-10 20:12:47 +04:00
dev_warn ( & socket - > dev ,
" PCMCIA: warning: socket thread did not start \n " ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2006-06-14 17:01:26 +04:00
2005-04-17 02:20:36 +04:00
pcmcia_parse_events ( socket , SS_DETECT ) ;
2009-11-07 14:26:17 +03:00
/*
* Let ' s try to get the PCMCIA module for 16 - bit PCMCIA support .
* If it fails , it doesn ' t matter - - we still have 32 - bit CardBus
* support to offer , so this is not a failure mode .
*/
request_module_nowait ( " pcmcia " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
err :
down_write ( & pcmcia_socket_list_rwsem ) ;
list_del ( & socket - > socket_list ) ;
up_write ( & pcmcia_socket_list_rwsem ) ;
return ret ;
} /* pcmcia_register_socket */
EXPORT_SYMBOL ( pcmcia_register_socket ) ;
/**
* pcmcia_unregister_socket - remove a pcmcia socket device
2007-12-11 02:49:22 +03:00
* @ socket : the & socket to unregister
2005-04-17 02:20:36 +04:00
*/
void pcmcia_unregister_socket ( struct pcmcia_socket * socket )
{
if ( ! socket )
return ;
2009-10-23 14:51:28 +04:00
dev_dbg ( & socket - > dev , " pcmcia_unregister_socket(0x%p) \n " , socket - > ops ) ;
2005-04-17 02:20:36 +04:00
2007-04-22 16:55:36 +04:00
if ( socket - > thread )
2006-06-14 17:01:26 +04:00
kthread_stop ( socket - > thread ) ;
2007-04-22 16:55:36 +04:00
2005-04-17 02:20:36 +04:00
/* remove from our own list */
down_write ( & pcmcia_socket_list_rwsem ) ;
list_del ( & socket - > socket_list ) ;
up_write ( & pcmcia_socket_list_rwsem ) ;
/* wait for sysfs to drop all references */
2010-01-17 21:31:45 +03:00
if ( socket - > resource_ops - > exit ) {
mutex_lock ( & socket - > ops_mutex ) ;
2010-01-06 13:23:58 +03:00
socket - > resource_ops - > exit ( socket ) ;
2010-01-17 21:31:45 +03:00
mutex_unlock ( & socket - > ops_mutex ) ;
}
2005-04-17 02:20:36 +04:00
wait_for_completion ( & socket - > socket_released ) ;
} /* pcmcia_unregister_socket */
EXPORT_SYMBOL ( pcmcia_unregister_socket ) ;
2009-12-08 00:11:45 +03:00
struct pcmcia_socket * pcmcia_get_socket_by_nr ( unsigned int nr )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_socket * s ;
down_read ( & pcmcia_socket_list_rwsem ) ;
list_for_each_entry ( s , & pcmcia_socket_list , socket_list )
if ( s - > sock = = nr ) {
up_read ( & pcmcia_socket_list_rwsem ) ;
return s ;
}
up_read ( & pcmcia_socket_list_rwsem ) ;
return NULL ;
}
EXPORT_SYMBOL ( pcmcia_get_socket_by_nr ) ;
static int socket_reset ( struct pcmcia_socket * skt )
{
int status , i ;
2009-10-23 14:51:28 +04:00
dev_dbg ( & skt - > dev , " reset \n " ) ;
2005-04-17 02:20:36 +04:00
skt - > socket . flags | = SS_OUTPUT_ENA | SS_RESET ;
skt - > ops - > set_socket ( skt , & skt - > socket ) ;
udelay ( ( long ) reset_time ) ;
skt - > socket . flags & = ~ SS_RESET ;
skt - > ops - > set_socket ( skt , & skt - > socket ) ;
msleep ( unreset_delay * 10 ) ;
for ( i = 0 ; i < unreset_limit ; i + + ) {
skt - > ops - > get_status ( skt , & status ) ;
if ( ! ( status & SS_DETECT ) )
2008-08-03 13:10:56 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
if ( status & SS_READY )
2008-08-03 12:07:45 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
msleep ( unreset_check * 10 ) ;
}
2014-10-10 20:12:47 +04:00
dev_err ( & skt - > dev , " time out after reset \n " ) ;
2008-08-03 13:15:45 +04:00
return - ETIMEDOUT ;
2005-04-17 02:20:36 +04:00
}
2007-12-11 02:49:22 +03:00
/*
2005-12-30 17:12:35 +03:00
* socket_setup ( ) and socket_shutdown ( ) are called by the main event handler
* when card insertion and removal events are received .
* socket_setup ( ) turns on socket power and resets the socket , in two stages .
* socket_shutdown ( ) unconfigures a socket and turns off socket power .
*/
static void socket_shutdown ( struct pcmcia_socket * s )
{
int status ;
2009-10-23 14:51:28 +04:00
dev_dbg ( & s - > dev , " shutdown \n " ) ;
2005-12-30 17:12:35 +03:00
2010-07-11 12:26:53 +04:00
if ( s - > callback )
s - > callback - > remove ( s ) ;
2010-01-16 02:26:33 +03:00
mutex_lock ( & s - > ops_mutex ) ;
2005-12-30 17:12:35 +03:00
s - > state & = SOCKET_INUSE | SOCKET_PRESENT ;
msleep ( shutdown_delay * 10 ) ;
s - > state & = SOCKET_INUSE ;
/* Blank out the socket state */
s - > socket = dead_socket ;
s - > ops - > init ( s ) ;
s - > ops - > set_socket ( s , & s - > socket ) ;
s - > lock_count = 0 ;
2010-01-02 14:28:04 +03:00
kfree ( s - > fake_cis ) ;
s - > fake_cis = NULL ;
2010-01-18 10:43:39 +03:00
s - > functions = 0 ;
/* From here on we can be sure that only we (that is, the
* pccardd thread ) accesses this socket , and all ( 16 - bit )
* PCMCIA interactions are gone . Therefore , release
* ops_mutex so that we don ' t get a sysfs - related lockdep
* warning .
*/
mutex_unlock ( & s - > ops_mutex ) ;
2005-12-30 17:12:35 +03:00
# ifdef CONFIG_CARDBUS
cb_free ( s ) ;
# endif
2007-07-31 11:38:08 +04:00
/* give socket some time to power down */
msleep ( 100 ) ;
2005-12-30 17:12:35 +03:00
s - > ops - > get_status ( s , & status ) ;
if ( status & SS_POWERON ) {
2014-10-10 20:12:47 +04:00
dev_err ( & s - > dev ,
" *** DANGER *** unable to remove socket power \n " ) ;
2005-12-30 17:12:35 +03:00
}
2010-01-03 01:19:45 +03:00
s - > state & = ~ SOCKET_INUSE ;
2005-12-30 17:12:35 +03:00
}
2005-04-17 02:20:36 +04:00
static int socket_setup ( struct pcmcia_socket * skt , int initial_delay )
{
int status , i ;
2009-10-23 14:51:28 +04:00
dev_dbg ( & skt - > dev , " setup \n " ) ;
2005-04-17 02:20:36 +04:00
skt - > ops - > get_status ( skt , & status ) ;
if ( ! ( status & SS_DETECT ) )
2008-08-03 13:10:56 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
msleep ( initial_delay * 10 ) ;
for ( i = 0 ; i < 100 ; i + + ) {
skt - > ops - > get_status ( skt , & status ) ;
if ( ! ( status & SS_DETECT ) )
2008-08-03 13:10:56 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
if ( ! ( status & SS_PENDING ) )
break ;
msleep ( 100 ) ;
}
if ( status & SS_PENDING ) {
2014-10-10 20:12:47 +04:00
dev_err ( & skt - > dev , " voltage interrogation timed out \n " ) ;
2008-08-03 13:15:45 +04:00
return - ETIMEDOUT ;
2005-04-17 02:20:36 +04:00
}
if ( status & SS_CARDBUS ) {
2005-07-08 04:59:07 +04:00
if ( ! ( skt - > features & SS_CAP_CARDBUS ) ) {
2014-10-10 20:12:47 +04:00
dev_err ( & skt - > dev , " cardbus cards are not supported \n " ) ;
2008-08-03 13:58:53 +04:00
return - EINVAL ;
2005-07-08 04:59:07 +04:00
}
2005-04-17 02:20:36 +04:00
skt - > state | = SOCKET_CARDBUS ;
2010-01-02 16:14:23 +03:00
} else
skt - > state & = ~ SOCKET_CARDBUS ;
2005-04-17 02:20:36 +04:00
/*
* Decode the card voltage requirements , and apply power to the card .
*/
if ( status & SS_3VCARD )
skt - > socket . Vcc = skt - > socket . Vpp = 33 ;
else if ( ! ( status & SS_XVCARD ) )
skt - > socket . Vcc = skt - > socket . Vpp = 50 ;
else {
2014-10-10 20:12:47 +04:00
dev_err ( & skt - > dev , " unsupported voltage key \n " ) ;
2008-08-03 13:58:53 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
}
2005-06-23 11:10:12 +04:00
if ( skt - > power_hook )
skt - > power_hook ( skt , HOOK_POWER_PRE ) ;
2005-04-17 02:20:36 +04:00
skt - > socket . flags = 0 ;
skt - > ops - > set_socket ( skt , & skt - > socket ) ;
/*
* Wait " vcc_settle " for the supply to stabilise .
*/
msleep ( vcc_settle * 10 ) ;
skt - > ops - > get_status ( skt , & status ) ;
if ( ! ( status & SS_POWERON ) ) {
2014-10-10 20:12:47 +04:00
dev_err ( & skt - > dev , " unable to apply power \n " ) ;
2008-08-03 13:58:53 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
}
2005-06-23 11:10:12 +04:00
status = socket_reset ( skt ) ;
if ( skt - > power_hook )
skt - > power_hook ( skt , HOOK_POWER_POST ) ;
return status ;
2005-04-17 02:20:36 +04:00
}
/*
* Handle card insertion . Setup the socket , reset the card ,
* and then tell the rest of PCMCIA that a card is present .
*/
static int socket_insert ( struct pcmcia_socket * skt )
{
int ret ;
2009-10-23 14:51:28 +04:00
dev_dbg ( & skt - > dev , " insert \n " ) ;
2005-04-17 02:20:36 +04:00
2010-01-16 02:26:33 +03:00
mutex_lock ( & skt - > ops_mutex ) ;
2010-01-17 20:13:31 +03:00
if ( skt - > state & SOCKET_INUSE ) {
mutex_unlock ( & skt - > ops_mutex ) ;
return - EINVAL ;
}
2010-01-03 01:19:45 +03:00
skt - > state | = SOCKET_INUSE ;
2005-04-17 02:20:36 +04:00
ret = socket_setup ( skt , setup_delay ) ;
2008-08-03 12:07:45 +04:00
if ( ret = = 0 ) {
2005-04-17 02:20:36 +04:00
skt - > state | = SOCKET_PRESENT ;
2005-11-13 01:34:06 +03:00
2014-10-10 20:12:47 +04:00
dev_notice ( & skt - > dev , " pccard: %s card inserted into slot %d \n " ,
2008-08-02 20:08:38 +04:00
( skt - > state & SOCKET_CARDBUS ) ? " CardBus " : " PCMCIA " ,
skt - > sock ) ;
2005-11-13 01:34:06 +03:00
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_CARDBUS
if ( skt - > state & SOCKET_CARDBUS ) {
cb_alloc ( skt ) ;
skt - > state | = SOCKET_CARDBUS_CONFIG ;
}
# endif
2009-10-23 14:51:28 +04:00
dev_dbg ( & skt - > dev , " insert done \n " ) ;
2010-01-16 02:26:33 +03:00
mutex_unlock ( & skt - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
2010-07-11 12:26:53 +04:00
if ( ! ( skt - > state & SOCKET_CARDBUS ) & & ( skt - > callback ) )
skt - > callback - > add ( skt ) ;
2005-04-17 02:20:36 +04:00
} else {
2010-01-16 02:26:33 +03:00
mutex_unlock ( & skt - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
socket_shutdown ( skt ) ;
}
return ret ;
}
static int socket_suspend ( struct pcmcia_socket * skt )
{
2018-02-21 15:24:16 +03:00
if ( ( skt - > state & SOCKET_SUSPEND ) & & ! ( skt - > state & SOCKET_IN_RESUME ) )
2008-08-03 13:40:19 +04:00
return - EBUSY ;
2005-04-17 02:20:36 +04:00
2010-01-16 02:26:33 +03:00
mutex_lock ( & skt - > ops_mutex ) ;
2018-02-21 15:24:16 +03:00
/* store state on first suspend, but not after spurious wakeups */
if ( ! ( skt - > state & SOCKET_IN_RESUME ) )
skt - > suspended_state = skt - > state ;
2010-01-02 16:14:23 +03:00
2005-04-17 02:20:36 +04:00
skt - > socket = dead_socket ;
skt - > ops - > set_socket ( skt , & skt - > socket ) ;
if ( skt - > ops - > suspend )
skt - > ops - > suspend ( skt ) ;
skt - > state | = SOCKET_SUSPEND ;
2018-02-21 15:24:16 +03:00
skt - > state & = ~ SOCKET_IN_RESUME ;
2010-01-16 02:26:33 +03:00
mutex_unlock ( & skt - > ops_mutex ) ;
2008-08-03 12:07:45 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-11-03 12:54:58 +03:00
static int socket_early_resume ( struct pcmcia_socket * skt )
2005-04-17 02:20:36 +04:00
{
2010-01-16 02:26:33 +03:00
mutex_lock ( & skt - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
skt - > socket = dead_socket ;
skt - > ops - > init ( skt ) ;
skt - > ops - > set_socket ( skt , & skt - > socket ) ;
2009-11-03 12:54:58 +03:00
if ( skt - > state & SOCKET_PRESENT )
skt - > resume_status = socket_setup ( skt , resume_delay ) ;
2018-02-21 15:24:16 +03:00
skt - > state | = SOCKET_IN_RESUME ;
2010-01-16 02:26:33 +03:00
mutex_unlock ( & skt - > ops_mutex ) ;
2009-11-03 12:54:58 +03:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2009-11-03 12:54:58 +03:00
static int socket_late_resume ( struct pcmcia_socket * skt )
{
2013-02-22 04:42:06 +04:00
int ret = 0 ;
2010-03-15 23:46:34 +03:00
2010-01-16 02:26:33 +03:00
mutex_lock ( & skt - > ops_mutex ) ;
2018-02-21 15:24:16 +03:00
skt - > state & = ~ ( SOCKET_SUSPEND | SOCKET_IN_RESUME ) ;
2010-01-16 02:26:33 +03:00
mutex_unlock ( & skt - > ops_mutex ) ;
2010-01-02 16:14:23 +03:00
2010-03-15 23:46:34 +03:00
if ( ! ( skt - > state & SOCKET_PRESENT ) ) {
ret = socket_insert ( skt ) ;
if ( ret = = - ENODEV )
ret = 0 ;
return ret ;
}
2010-01-02 16:14:23 +03:00
if ( skt - > resume_status ) {
socket_shutdown ( skt ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-01-02 16:14:23 +03:00
if ( skt - > suspended_state ! = skt - > state ) {
dev_dbg ( & skt - > dev ,
" suspend state 0x%x != resume state 0x%x \n " ,
skt - > suspended_state , skt - > state ) ;
2005-04-17 02:20:36 +04:00
socket_shutdown ( skt ) ;
2010-01-02 16:14:23 +03:00
return socket_insert ( skt ) ;
2005-04-17 02:20:36 +04:00
}
2013-02-22 04:42:06 +04:00
if ( ! ( skt - > state & SOCKET_CARDBUS ) & & ( skt - > callback ) )
ret = skt - > callback - > early_resume ( skt ) ;
return ret ;
}
/*
* Finalize the resume . In case of a cardbus socket , we have
* to rebind the devices as we can ' t be certain that it has been
* replaced , or not .
*/
static int socket_complete_resume ( struct pcmcia_socket * skt )
{
int ret = 0 ;
2010-01-02 16:14:23 +03:00
# ifdef CONFIG_CARDBUS
if ( skt - > state & SOCKET_CARDBUS ) {
/* We can't be sure the CardBus card is the same
* as the one previously inserted . Therefore , remove
* and re - add . . . */
cb_free ( skt ) ;
2013-02-22 04:42:06 +04:00
ret = cb_alloc ( skt ) ;
if ( ret )
cb_free ( skt ) ;
2010-01-02 16:14:23 +03:00
}
# endif
2013-02-22 04:42:06 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2009-11-03 12:54:58 +03:00
/*
* Resume a socket . If a card is present , verify its CIS against
* our cached copy . If they are different , the card has been
* replaced , and we need to tell the drivers .
*/
static int socket_resume ( struct pcmcia_socket * skt )
{
2013-02-22 04:42:06 +04:00
int err ;
2009-11-03 12:54:58 +03:00
if ( ! ( skt - > state & SOCKET_SUSPEND ) )
return - EBUSY ;
socket_early_resume ( skt ) ;
2013-02-22 04:42:06 +04:00
err = socket_late_resume ( skt ) ;
if ( ! err )
err = socket_complete_resume ( skt ) ;
return err ;
2009-11-03 12:54:58 +03:00
}
2005-04-17 02:20:36 +04:00
static void socket_remove ( struct pcmcia_socket * skt )
{
2014-10-10 20:12:47 +04:00
dev_notice ( & skt - > dev , " pccard: card ejected from slot %d \n " , skt - > sock ) ;
2005-04-17 02:20:36 +04:00
socket_shutdown ( skt ) ;
}
/*
* Process a socket card detect status change .
*
* If we don ' t have a card already present , delay the detect event for
* about 20 ms ( to be on the safe side ) before reading the socket status .
*
* Some i82365 - based systems send multiple SS_DETECT events during card
* insertion , and the " card present " status bit seems to bounce . This
* will probably be true with GPIO - based card detection systems after
* the product has aged .
*/
static void socket_detect_change ( struct pcmcia_socket * skt )
{
if ( ! ( skt - > state & SOCKET_SUSPEND ) ) {
int status ;
if ( ! ( skt - > state & SOCKET_PRESENT ) )
msleep ( 20 ) ;
skt - > ops - > get_status ( skt , & status ) ;
if ( ( skt - > state & SOCKET_PRESENT ) & &
! ( status & SS_DETECT ) )
socket_remove ( skt ) ;
if ( ! ( skt - > state & SOCKET_PRESENT ) & &
( status & SS_DETECT ) )
socket_insert ( skt ) ;
}
}
static int pccardd ( void * __skt )
{
struct pcmcia_socket * skt = __skt ;
int ret ;
skt - > thread = current ;
skt - > socket = dead_socket ;
skt - > ops - > init ( skt ) ;
skt - > ops - > set_socket ( skt , & skt - > socket ) ;
/* register with the device core */
2006-09-12 19:00:10 +04:00
ret = device_register ( & skt - > dev ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
2014-10-10 20:12:47 +04:00
dev_warn ( & skt - > dev , " PCMCIA: unable to register socket \n " ) ;
2005-04-17 02:20:36 +04:00
skt - > thread = NULL ;
2006-06-14 17:01:26 +04:00
complete ( & skt - > thread_done ) ;
2022-11-12 12:25:41 +03:00
put_device ( & skt - > dev ) ;
2006-06-14 17:01:26 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-04-28 12:03:20 +04:00
ret = pccard_sysfs_add_socket ( & skt - > dev ) ;
if ( ret )
dev_warn ( & skt - > dev , " err %d adding socket attributes \n " , ret ) ;
2005-04-17 02:20:36 +04:00
2005-09-10 00:03:23 +04:00
complete ( & skt - > thread_done ) ;
2010-02-17 13:02:22 +03:00
/* wait for userspace to catch up */
msleep ( 250 ) ;
2007-07-17 15:03:35 +04:00
set_freezable ( ) ;
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
unsigned long flags ;
unsigned int events ;
2010-01-17 20:13:31 +03:00
unsigned int sysfs_events ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & skt - > thread_lock , flags ) ;
events = skt - > thread_events ;
skt - > thread_events = 0 ;
2010-01-17 20:13:31 +03:00
sysfs_events = skt - > sysfs_events ;
skt - > sysfs_events = 0 ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & skt - > thread_lock , flags ) ;
2010-01-17 20:13:31 +03:00
mutex_lock ( & skt - > skt_mutex ) ;
2010-07-11 12:04:59 +04:00
if ( events & SS_DETECT )
socket_detect_change ( skt ) ;
2005-04-17 02:20:36 +04:00
2010-01-17 20:13:31 +03:00
if ( sysfs_events ) {
if ( sysfs_events & PCMCIA_UEVENT_EJECT )
socket_remove ( skt ) ;
if ( sysfs_events & PCMCIA_UEVENT_INSERT )
socket_insert ( skt ) ;
if ( ( sysfs_events & PCMCIA_UEVENT_SUSPEND ) & &
! ( skt - > state & SOCKET_CARDBUS ) ) {
if ( skt - > callback )
ret = skt - > callback - > suspend ( skt ) ;
else
ret = 0 ;
2010-05-03 17:10:09 +04:00
if ( ! ret ) {
2010-01-17 20:13:31 +03:00
socket_suspend ( skt ) ;
2010-05-03 17:10:09 +04:00
msleep ( 100 ) ;
}
}
if ( ( sysfs_events & PCMCIA_UEVENT_RESUME ) & &
! ( skt - > state & SOCKET_CARDBUS ) ) {
ret = socket_resume ( skt ) ;
if ( ! ret & & skt - > callback )
skt - > callback - > resume ( skt ) ;
2010-01-17 20:13:31 +03:00
}
2010-01-17 21:30:53 +03:00
if ( ( sysfs_events & PCMCIA_UEVENT_REQUERY ) & &
! ( skt - > state & SOCKET_CARDBUS ) ) {
if ( ! ret & & skt - > callback )
skt - > callback - > requery ( skt ) ;
}
2010-01-17 20:13:31 +03:00
}
mutex_unlock ( & skt - > skt_mutex ) ;
if ( events | | sysfs_events )
continue ;
2022-01-09 12:02:51 +03:00
set_current_state ( TASK_INTERRUPTIBLE ) ;
2006-06-14 17:01:26 +04:00
if ( kthread_should_stop ( ) )
2005-04-17 02:20:36 +04:00
break ;
2005-09-10 00:03:23 +04:00
schedule ( ) ;
2015-03-30 14:33:45 +03:00
2005-09-10 00:03:23 +04:00
try_to_freeze ( ) ;
2005-04-17 02:20:36 +04:00
}
2022-01-09 12:02:51 +03:00
/* make sure we are running before we exit */
__set_current_state ( TASK_RUNNING ) ;
2005-10-10 19:13:17 +04:00
2010-01-03 01:19:45 +03:00
/* shut down socket, if a device is still present */
if ( skt - > state & SOCKET_PRESENT ) {
mutex_lock ( & skt - > skt_mutex ) ;
socket_remove ( skt ) ;
mutex_unlock ( & skt - > skt_mutex ) ;
}
2005-04-17 02:20:36 +04:00
/* remove from the device core */
2008-04-28 12:03:20 +04:00
pccard_sysfs_remove_socket ( & skt - > dev ) ;
2006-09-12 19:00:10 +04:00
device_unregister ( & skt - > dev ) ;
2005-04-17 02:20:36 +04:00
2006-06-14 17:01:26 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Yenta ( at least ) probes interrupts before registering the socket and
* starting the handler thread .
*/
void pcmcia_parse_events ( struct pcmcia_socket * s , u_int events )
{
2006-06-30 12:31:13 +04:00
unsigned long flags ;
2009-10-23 14:51:28 +04:00
dev_dbg ( & s - > dev , " parse_events: events %08x \n " , events ) ;
2005-04-17 02:20:36 +04:00
if ( s - > thread ) {
2006-06-30 12:31:13 +04:00
spin_lock_irqsave ( & s - > thread_lock , flags ) ;
2005-04-17 02:20:36 +04:00
s - > thread_events | = events ;
2006-06-30 12:31:13 +04:00
spin_unlock_irqrestore ( & s - > thread_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2007-04-22 16:55:36 +04:00
wake_up_process ( s - > thread ) ;
2005-04-17 02:20:36 +04:00
}
} /* pcmcia_parse_events */
2005-06-28 03:28:53 +04:00
EXPORT_SYMBOL ( pcmcia_parse_events ) ;
2005-04-17 02:20:36 +04:00
2010-01-17 20:13:31 +03:00
/**
* pcmcia_parse_uevents ( ) - tell pccardd to issue manual commands
* @ s : the PCMCIA socket we wan ' t to command
* @ events : events to pass to pccardd
*
* userspace - issued insert , eject , suspend and resume commands must be
* handled by pccardd to avoid any sysfs - related deadlocks . Valid events
* are PCMCIA_UEVENT_EJECT ( for eject ) , PCMCIA_UEVENT__INSERT ( for insert ) ,
2010-01-17 21:30:53 +03:00
* PCMCIA_UEVENT_RESUME ( for resume ) , PCMCIA_UEVENT_SUSPEND ( for suspend )
* and PCMCIA_UEVENT_REQUERY ( for re - querying the PCMCIA card ) .
2010-01-17 20:13:31 +03:00
*/
void pcmcia_parse_uevents ( struct pcmcia_socket * s , u_int events )
{
unsigned long flags ;
dev_dbg ( & s - > dev , " parse_uevents: events %08x \n " , events ) ;
if ( s - > thread ) {
spin_lock_irqsave ( & s - > thread_lock , flags ) ;
s - > sysfs_events | = events ;
spin_unlock_irqrestore ( & s - > thread_lock , flags ) ;
wake_up_process ( s - > thread ) ;
}
}
EXPORT_SYMBOL ( pcmcia_parse_uevents ) ;
2005-04-17 02:20:36 +04:00
/* register pcmcia_callback */
int pccard_register_pcmcia ( struct pcmcia_socket * s , struct pcmcia_callback * c )
{
2009-12-08 00:11:45 +03:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2006-01-10 23:20:36 +03:00
/* s->skt_mutex also protects s->callback */
mutex_lock ( & s - > skt_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( c ) {
/* registration */
if ( s - > callback ) {
ret = - EBUSY ;
goto err ;
}
s - > callback = c ;
if ( ( s - > state & ( SOCKET_PRESENT | SOCKET_CARDBUS ) ) = = SOCKET_PRESENT )
2010-07-11 12:26:53 +04:00
s - > callback - > add ( s ) ;
2005-04-17 02:20:36 +04:00
} else
s - > callback = NULL ;
err :
2006-01-10 23:20:36 +03:00
mutex_unlock ( & s - > skt_mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
EXPORT_SYMBOL ( pccard_register_pcmcia ) ;
2005-06-28 03:28:53 +04:00
/* I'm not sure which "reset" function this is supposed to use,
* but for now , it uses the low - level interface ' s reset , not the
* CIS register .
*/
2005-04-17 02:20:36 +04:00
2008-08-31 17:20:26 +04:00
int pcmcia_reset_card ( struct pcmcia_socket * skt )
2005-04-17 02:20:36 +04:00
{
int ret ;
2005-06-28 03:28:53 +04:00
2009-10-23 14:51:28 +04:00
dev_dbg ( & skt - > dev , " resetting socket \n " ) ;
2005-04-17 02:20:36 +04:00
2006-01-10 23:20:36 +03:00
mutex_lock ( & skt - > skt_mutex ) ;
2005-04-17 02:20:36 +04:00
do {
if ( ! ( skt - > state & SOCKET_PRESENT ) ) {
2009-10-24 14:20:18 +04:00
dev_dbg ( & skt - > dev , " can't reset, not present \n " ) ;
2008-08-03 13:10:56 +04:00
ret = - ENODEV ;
2005-04-17 02:20:36 +04:00
break ;
}
if ( skt - > state & SOCKET_SUSPEND ) {
2009-10-24 14:20:18 +04:00
dev_dbg ( & skt - > dev , " can't reset, suspended \n " ) ;
2008-08-03 13:40:19 +04:00
ret = - EBUSY ;
2005-04-17 02:20:36 +04:00
break ;
}
if ( skt - > state & SOCKET_CARDBUS ) {
2009-10-24 14:20:18 +04:00
dev_dbg ( & skt - > dev , " can't reset, is cardbus \n " ) ;
2008-08-03 12:47:59 +04:00
ret = - EPERM ;
2005-04-17 02:20:36 +04:00
break ;
}
2010-07-11 12:04:59 +04:00
if ( skt - > callback )
skt - > callback - > suspend ( skt ) ;
mutex_lock ( & skt - > ops_mutex ) ;
ret = socket_reset ( skt ) ;
mutex_unlock ( & skt - > ops_mutex ) ;
if ( ( ret = = 0 ) & & ( skt - > callback ) )
skt - > callback - > resume ( skt ) ;
2005-04-17 02:20:36 +04:00
2008-08-03 12:07:45 +04:00
ret = 0 ;
2005-04-17 02:20:36 +04:00
} while ( 0 ) ;
2006-01-10 23:20:36 +03:00
mutex_unlock ( & skt - > skt_mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
} /* reset_card */
2008-08-31 17:20:26 +04:00
EXPORT_SYMBOL ( pcmcia_reset_card ) ;
2005-04-17 02:20:36 +04:00
2022-11-23 15:25:19 +03:00
static int pcmcia_socket_uevent ( const struct device * dev ,
2007-08-14 17:15:12 +04:00
struct kobj_uevent_env * env )
2005-06-28 03:28:05 +04:00
{
2022-11-23 15:25:19 +03:00
const struct pcmcia_socket * s = container_of ( dev , struct pcmcia_socket , dev ) ;
2005-06-28 03:28:05 +04:00
2007-08-14 17:15:12 +04:00
if ( add_uevent_var ( env , " SOCKET_NO=%u " , s - > sock ) )
2005-06-28 03:28:05 +04:00
return - ENOMEM ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2005-06-28 03:28:54 +04:00
static struct completion pcmcia_unload ;
2023-04-02 20:58:46 +03:00
static void pcmcia_release_socket_class ( const struct class * data )
2005-06-28 03:28:54 +04:00
{
complete ( & pcmcia_unload ) ;
}
2010-03-15 23:46:34 +03:00
# ifdef CONFIG_PM
static int __pcmcia_pm_op ( struct device * dev ,
int ( * callback ) ( struct pcmcia_socket * skt ) )
{
struct pcmcia_socket * s = container_of ( dev , struct pcmcia_socket , dev ) ;
int ret ;
mutex_lock ( & s - > skt_mutex ) ;
ret = callback ( s ) ;
mutex_unlock ( & s - > skt_mutex ) ;
return ret ;
}
static int pcmcia_socket_dev_suspend_noirq ( struct device * dev )
{
return __pcmcia_pm_op ( dev , socket_suspend ) ;
}
static int pcmcia_socket_dev_resume_noirq ( struct device * dev )
{
return __pcmcia_pm_op ( dev , socket_early_resume ) ;
}
2010-10-14 08:47:14 +04:00
static int __used pcmcia_socket_dev_resume ( struct device * dev )
2010-03-15 23:46:34 +03:00
{
return __pcmcia_pm_op ( dev , socket_late_resume ) ;
}
2013-02-22 04:42:06 +04:00
static void __used pcmcia_socket_dev_complete ( struct device * dev )
{
WARN ( __pcmcia_pm_op ( dev , socket_complete_resume ) ,
" failed to complete resume " ) ;
}
2010-03-15 23:46:34 +03:00
static const struct dev_pm_ops pcmcia_socket_pm_ops = {
/* dev_resume may be called with IRQs enabled */
SET_SYSTEM_SLEEP_PM_OPS ( NULL ,
pcmcia_socket_dev_resume )
/* late suspend must be called with IRQs disabled */
. suspend_noirq = pcmcia_socket_dev_suspend_noirq ,
. freeze_noirq = pcmcia_socket_dev_suspend_noirq ,
. poweroff_noirq = pcmcia_socket_dev_suspend_noirq ,
/* early resume must be called with IRQs disabled */
. resume_noirq = pcmcia_socket_dev_resume_noirq ,
. thaw_noirq = pcmcia_socket_dev_resume_noirq ,
. restore_noirq = pcmcia_socket_dev_resume_noirq ,
2013-02-22 04:42:06 +04:00
. complete = pcmcia_socket_dev_complete ,
2010-03-15 23:46:34 +03:00
} ;
# define PCMCIA_SOCKET_CLASS_PM_OPS (&pcmcia_socket_pm_ops)
# else /* CONFIG_PM */
# define PCMCIA_SOCKET_CLASS_PM_OPS NULL
# endif /* CONFIG_PM */
2005-04-17 02:20:36 +04:00
struct class pcmcia_socket_class = {
. name = " pcmcia_socket " ,
2006-09-12 19:00:10 +04:00
. dev_uevent = pcmcia_socket_uevent ,
. dev_release = pcmcia_release_socket ,
2005-06-28 03:28:54 +04:00
. class_release = pcmcia_release_socket_class ,
2010-03-15 23:46:34 +03:00
. pm = PCMCIA_SOCKET_CLASS_PM_OPS ,
2005-04-17 02:20:36 +04:00
} ;
EXPORT_SYMBOL ( pcmcia_socket_class ) ;
static int __init init_pcmcia_cs ( void )
{
2005-06-28 03:28:54 +04:00
init_completion ( & pcmcia_unload ) ;
2008-04-28 12:03:20 +04:00
return class_register ( & pcmcia_socket_class ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit exit_pcmcia_cs ( void )
{
2005-06-28 03:28:53 +04:00
class_unregister ( & pcmcia_socket_class ) ;
2005-06-28 03:28:54 +04:00
wait_for_completion ( & pcmcia_unload ) ;
2005-04-17 02:20:36 +04:00
}
subsys_initcall ( init_pcmcia_cs ) ;
module_exit ( exit_pcmcia_cs ) ;