162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cs.c -- Kernel Card Services - core services 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The initial developer of the original code is David A. Hinds 662306a36Sopenharmony_ci * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds 762306a36Sopenharmony_ci * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * (C) 1999 David A. Hinds 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/moduleparam.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/major.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/mm.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/timer.h> 2362306a36Sopenharmony_ci#include <linux/ioport.h> 2462306a36Sopenharmony_ci#include <linux/delay.h> 2562306a36Sopenharmony_ci#include <linux/pm.h> 2662306a36Sopenharmony_ci#include <linux/device.h> 2762306a36Sopenharmony_ci#include <linux/kthread.h> 2862306a36Sopenharmony_ci#include <linux/freezer.h> 2962306a36Sopenharmony_ci#include <asm/irq.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <pcmcia/ss.h> 3262306a36Sopenharmony_ci#include <pcmcia/cistpl.h> 3362306a36Sopenharmony_ci#include <pcmcia/cisreg.h> 3462306a36Sopenharmony_ci#include <pcmcia/ds.h> 3562306a36Sopenharmony_ci#include "cs_internal.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Module parameters */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ciMODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); 4162306a36Sopenharmony_ciMODULE_DESCRIPTION("Linux Kernel Card Services"); 4262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciINT_MODULE_PARM(setup_delay, 10); /* centiseconds */ 4762306a36Sopenharmony_ciINT_MODULE_PARM(resume_delay, 20); /* centiseconds */ 4862306a36Sopenharmony_ciINT_MODULE_PARM(shutdown_delay, 3); /* centiseconds */ 4962306a36Sopenharmony_ciINT_MODULE_PARM(vcc_settle, 40); /* centiseconds */ 5062306a36Sopenharmony_ciINT_MODULE_PARM(reset_time, 10); /* usecs */ 5162306a36Sopenharmony_ciINT_MODULE_PARM(unreset_delay, 10); /* centiseconds */ 5262306a36Sopenharmony_ciINT_MODULE_PARM(unreset_check, 10); /* centiseconds */ 5362306a36Sopenharmony_ciINT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Access speed for attribute memory windows */ 5662306a36Sopenharmony_ciINT_MODULE_PARM(cis_speed, 300); /* ns */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cisocket_state_t dead_socket = { 6062306a36Sopenharmony_ci .csc_mask = SS_DETECT, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ciEXPORT_SYMBOL(dead_socket); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* List of all sockets, protected by a rwsem */ 6662306a36Sopenharmony_ciLIST_HEAD(pcmcia_socket_list); 6762306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_socket_list); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ciDECLARE_RWSEM(pcmcia_socket_list_rwsem); 7062306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_socket_list_rwsem); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistruct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct device *dev = get_device(&skt->dev); 7662306a36Sopenharmony_ci if (!dev) 7762306a36Sopenharmony_ci return NULL; 7862306a36Sopenharmony_ci return dev_get_drvdata(dev); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_get_socket); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_civoid pcmcia_put_socket(struct pcmcia_socket *skt) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci put_device(&skt->dev); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_put_socket); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void pcmcia_release_socket(struct device *dev) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct pcmcia_socket *socket = dev_get_drvdata(dev); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci complete(&socket->socket_released); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int pccardd(void *__skt); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/** 10062306a36Sopenharmony_ci * pcmcia_register_socket - add a new pcmcia socket device 10162306a36Sopenharmony_ci * @socket: the &socket to register 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ciint pcmcia_register_socket(struct pcmcia_socket *socket) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct task_struct *tsk; 10662306a36Sopenharmony_ci int ret; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (!socket || !socket->ops || !socket->dev.parent || !socket->resource_ops) 10962306a36Sopenharmony_ci return -EINVAL; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* try to obtain a socket number [yes, it gets ugly if we 11462306a36Sopenharmony_ci * register more than 2^sizeof(unsigned int) pcmcia 11562306a36Sopenharmony_ci * sockets... but the socket number is deprecated 11662306a36Sopenharmony_ci * anyways, so I don't care] */ 11762306a36Sopenharmony_ci down_write(&pcmcia_socket_list_rwsem); 11862306a36Sopenharmony_ci if (list_empty(&pcmcia_socket_list)) 11962306a36Sopenharmony_ci socket->sock = 0; 12062306a36Sopenharmony_ci else { 12162306a36Sopenharmony_ci unsigned int found, i = 1; 12262306a36Sopenharmony_ci struct pcmcia_socket *tmp; 12362306a36Sopenharmony_ci do { 12462306a36Sopenharmony_ci found = 1; 12562306a36Sopenharmony_ci list_for_each_entry(tmp, &pcmcia_socket_list, socket_list) { 12662306a36Sopenharmony_ci if (tmp->sock == i) 12762306a36Sopenharmony_ci found = 0; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci i++; 13062306a36Sopenharmony_ci } while (!found); 13162306a36Sopenharmony_ci socket->sock = i - 1; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci list_add_tail(&socket->socket_list, &pcmcia_socket_list); 13462306a36Sopenharmony_ci up_write(&pcmcia_socket_list_rwsem); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#ifndef CONFIG_CARDBUS 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * If we do not support Cardbus, ensure that 13962306a36Sopenharmony_ci * the Cardbus socket capability is disabled. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci socket->features &= ~SS_CAP_CARDBUS; 14262306a36Sopenharmony_ci#endif 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* set proper values in socket->dev */ 14562306a36Sopenharmony_ci dev_set_drvdata(&socket->dev, socket); 14662306a36Sopenharmony_ci socket->dev.class = &pcmcia_socket_class; 14762306a36Sopenharmony_ci dev_set_name(&socket->dev, "pcmcia_socket%u", socket->sock); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* base address = 0, map = 0 */ 15062306a36Sopenharmony_ci socket->cis_mem.flags = 0; 15162306a36Sopenharmony_ci socket->cis_mem.speed = cis_speed; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci INIT_LIST_HEAD(&socket->cis_cache); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci init_completion(&socket->socket_released); 15662306a36Sopenharmony_ci init_completion(&socket->thread_done); 15762306a36Sopenharmony_ci mutex_init(&socket->skt_mutex); 15862306a36Sopenharmony_ci mutex_init(&socket->ops_mutex); 15962306a36Sopenharmony_ci spin_lock_init(&socket->thread_lock); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (socket->resource_ops->init) { 16262306a36Sopenharmony_ci mutex_lock(&socket->ops_mutex); 16362306a36Sopenharmony_ci ret = socket->resource_ops->init(socket); 16462306a36Sopenharmony_ci mutex_unlock(&socket->ops_mutex); 16562306a36Sopenharmony_ci if (ret) 16662306a36Sopenharmony_ci goto err; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci tsk = kthread_run(pccardd, socket, "pccardd"); 17062306a36Sopenharmony_ci if (IS_ERR(tsk)) { 17162306a36Sopenharmony_ci ret = PTR_ERR(tsk); 17262306a36Sopenharmony_ci goto err; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci wait_for_completion(&socket->thread_done); 17662306a36Sopenharmony_ci if (!socket->thread) { 17762306a36Sopenharmony_ci dev_warn(&socket->dev, 17862306a36Sopenharmony_ci "PCMCIA: warning: socket thread did not start\n"); 17962306a36Sopenharmony_ci return -EIO; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci pcmcia_parse_events(socket, SS_DETECT); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * Let's try to get the PCMCIA module for 16-bit PCMCIA support. 18662306a36Sopenharmony_ci * If it fails, it doesn't matter -- we still have 32-bit CardBus 18762306a36Sopenharmony_ci * support to offer, so this is not a failure mode. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ci request_module_nowait("pcmcia"); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci err: 19462306a36Sopenharmony_ci down_write(&pcmcia_socket_list_rwsem); 19562306a36Sopenharmony_ci list_del(&socket->socket_list); 19662306a36Sopenharmony_ci up_write(&pcmcia_socket_list_rwsem); 19762306a36Sopenharmony_ci return ret; 19862306a36Sopenharmony_ci} /* pcmcia_register_socket */ 19962306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_register_socket); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/** 20362306a36Sopenharmony_ci * pcmcia_unregister_socket - remove a pcmcia socket device 20462306a36Sopenharmony_ci * @socket: the &socket to unregister 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_civoid pcmcia_unregister_socket(struct pcmcia_socket *socket) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci if (!socket) 20962306a36Sopenharmony_ci return; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci dev_dbg(&socket->dev, "pcmcia_unregister_socket(0x%p)\n", socket->ops); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (socket->thread) 21462306a36Sopenharmony_ci kthread_stop(socket->thread); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* remove from our own list */ 21762306a36Sopenharmony_ci down_write(&pcmcia_socket_list_rwsem); 21862306a36Sopenharmony_ci list_del(&socket->socket_list); 21962306a36Sopenharmony_ci up_write(&pcmcia_socket_list_rwsem); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* wait for sysfs to drop all references */ 22262306a36Sopenharmony_ci if (socket->resource_ops->exit) { 22362306a36Sopenharmony_ci mutex_lock(&socket->ops_mutex); 22462306a36Sopenharmony_ci socket->resource_ops->exit(socket); 22562306a36Sopenharmony_ci mutex_unlock(&socket->ops_mutex); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci wait_for_completion(&socket->socket_released); 22862306a36Sopenharmony_ci} /* pcmcia_unregister_socket */ 22962306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_unregister_socket); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistruct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct pcmcia_socket *s; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci down_read(&pcmcia_socket_list_rwsem); 23762306a36Sopenharmony_ci list_for_each_entry(s, &pcmcia_socket_list, socket_list) 23862306a36Sopenharmony_ci if (s->sock == nr) { 23962306a36Sopenharmony_ci up_read(&pcmcia_socket_list_rwsem); 24062306a36Sopenharmony_ci return s; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci up_read(&pcmcia_socket_list_rwsem); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return NULL; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_get_socket_by_nr); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int socket_reset(struct pcmcia_socket *skt) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int status, i; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci dev_dbg(&skt->dev, "reset\n"); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci skt->socket.flags |= SS_OUTPUT_ENA | SS_RESET; 25662306a36Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 25762306a36Sopenharmony_ci udelay((long)reset_time); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci skt->socket.flags &= ~SS_RESET; 26062306a36Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci msleep(unreset_delay * 10); 26362306a36Sopenharmony_ci for (i = 0; i < unreset_limit; i++) { 26462306a36Sopenharmony_ci skt->ops->get_status(skt, &status); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (!(status & SS_DETECT)) 26762306a36Sopenharmony_ci return -ENODEV; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (status & SS_READY) 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci msleep(unreset_check * 10); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci dev_err(&skt->dev, "time out after reset\n"); 27662306a36Sopenharmony_ci return -ETIMEDOUT; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* 28062306a36Sopenharmony_ci * socket_setup() and socket_shutdown() are called by the main event handler 28162306a36Sopenharmony_ci * when card insertion and removal events are received. 28262306a36Sopenharmony_ci * socket_setup() turns on socket power and resets the socket, in two stages. 28362306a36Sopenharmony_ci * socket_shutdown() unconfigures a socket and turns off socket power. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_cistatic void socket_shutdown(struct pcmcia_socket *s) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int status; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci dev_dbg(&s->dev, "shutdown\n"); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (s->callback) 29262306a36Sopenharmony_ci s->callback->remove(s); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci mutex_lock(&s->ops_mutex); 29562306a36Sopenharmony_ci s->state &= SOCKET_INUSE | SOCKET_PRESENT; 29662306a36Sopenharmony_ci msleep(shutdown_delay * 10); 29762306a36Sopenharmony_ci s->state &= SOCKET_INUSE; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Blank out the socket state */ 30062306a36Sopenharmony_ci s->socket = dead_socket; 30162306a36Sopenharmony_ci s->ops->init(s); 30262306a36Sopenharmony_ci s->ops->set_socket(s, &s->socket); 30362306a36Sopenharmony_ci s->lock_count = 0; 30462306a36Sopenharmony_ci kfree(s->fake_cis); 30562306a36Sopenharmony_ci s->fake_cis = NULL; 30662306a36Sopenharmony_ci s->functions = 0; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* From here on we can be sure that only we (that is, the 30962306a36Sopenharmony_ci * pccardd thread) accesses this socket, and all (16-bit) 31062306a36Sopenharmony_ci * PCMCIA interactions are gone. Therefore, release 31162306a36Sopenharmony_ci * ops_mutex so that we don't get a sysfs-related lockdep 31262306a36Sopenharmony_ci * warning. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci mutex_unlock(&s->ops_mutex); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci#ifdef CONFIG_CARDBUS 31762306a36Sopenharmony_ci cb_free(s); 31862306a36Sopenharmony_ci#endif 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* give socket some time to power down */ 32162306a36Sopenharmony_ci msleep(100); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci s->ops->get_status(s, &status); 32462306a36Sopenharmony_ci if (status & SS_POWERON) { 32562306a36Sopenharmony_ci dev_err(&s->dev, 32662306a36Sopenharmony_ci "*** DANGER *** unable to remove socket power\n"); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci s->state &= ~SOCKET_INUSE; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int socket_setup(struct pcmcia_socket *skt, int initial_delay) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int status, i; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci dev_dbg(&skt->dev, "setup\n"); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci skt->ops->get_status(skt, &status); 33962306a36Sopenharmony_ci if (!(status & SS_DETECT)) 34062306a36Sopenharmony_ci return -ENODEV; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci msleep(initial_delay * 10); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci for (i = 0; i < 100; i++) { 34562306a36Sopenharmony_ci skt->ops->get_status(skt, &status); 34662306a36Sopenharmony_ci if (!(status & SS_DETECT)) 34762306a36Sopenharmony_ci return -ENODEV; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!(status & SS_PENDING)) 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci msleep(100); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (status & SS_PENDING) { 35662306a36Sopenharmony_ci dev_err(&skt->dev, "voltage interrogation timed out\n"); 35762306a36Sopenharmony_ci return -ETIMEDOUT; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (status & SS_CARDBUS) { 36162306a36Sopenharmony_ci if (!(skt->features & SS_CAP_CARDBUS)) { 36262306a36Sopenharmony_ci dev_err(&skt->dev, "cardbus cards are not supported\n"); 36362306a36Sopenharmony_ci return -EINVAL; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci skt->state |= SOCKET_CARDBUS; 36662306a36Sopenharmony_ci } else 36762306a36Sopenharmony_ci skt->state &= ~SOCKET_CARDBUS; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* 37062306a36Sopenharmony_ci * Decode the card voltage requirements, and apply power to the card. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci if (status & SS_3VCARD) 37362306a36Sopenharmony_ci skt->socket.Vcc = skt->socket.Vpp = 33; 37462306a36Sopenharmony_ci else if (!(status & SS_XVCARD)) 37562306a36Sopenharmony_ci skt->socket.Vcc = skt->socket.Vpp = 50; 37662306a36Sopenharmony_ci else { 37762306a36Sopenharmony_ci dev_err(&skt->dev, "unsupported voltage key\n"); 37862306a36Sopenharmony_ci return -EIO; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (skt->power_hook) 38262306a36Sopenharmony_ci skt->power_hook(skt, HOOK_POWER_PRE); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci skt->socket.flags = 0; 38562306a36Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * Wait "vcc_settle" for the supply to stabilise. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci msleep(vcc_settle * 10); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci skt->ops->get_status(skt, &status); 39362306a36Sopenharmony_ci if (!(status & SS_POWERON)) { 39462306a36Sopenharmony_ci dev_err(&skt->dev, "unable to apply power\n"); 39562306a36Sopenharmony_ci return -EIO; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci status = socket_reset(skt); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (skt->power_hook) 40162306a36Sopenharmony_ci skt->power_hook(skt, HOOK_POWER_POST); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return status; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/* 40762306a36Sopenharmony_ci * Handle card insertion. Setup the socket, reset the card, 40862306a36Sopenharmony_ci * and then tell the rest of PCMCIA that a card is present. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_cistatic int socket_insert(struct pcmcia_socket *skt) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci int ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci dev_dbg(&skt->dev, "insert\n"); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci mutex_lock(&skt->ops_mutex); 41762306a36Sopenharmony_ci if (skt->state & SOCKET_INUSE) { 41862306a36Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 41962306a36Sopenharmony_ci return -EINVAL; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci skt->state |= SOCKET_INUSE; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ret = socket_setup(skt, setup_delay); 42462306a36Sopenharmony_ci if (ret == 0) { 42562306a36Sopenharmony_ci skt->state |= SOCKET_PRESENT; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci dev_notice(&skt->dev, "pccard: %s card inserted into slot %d\n", 42862306a36Sopenharmony_ci (skt->state & SOCKET_CARDBUS) ? "CardBus" : "PCMCIA", 42962306a36Sopenharmony_ci skt->sock); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci#ifdef CONFIG_CARDBUS 43262306a36Sopenharmony_ci if (skt->state & SOCKET_CARDBUS) { 43362306a36Sopenharmony_ci cb_alloc(skt); 43462306a36Sopenharmony_ci skt->state |= SOCKET_CARDBUS_CONFIG; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci#endif 43762306a36Sopenharmony_ci dev_dbg(&skt->dev, "insert done\n"); 43862306a36Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (!(skt->state & SOCKET_CARDBUS) && (skt->callback)) 44162306a36Sopenharmony_ci skt->callback->add(skt); 44262306a36Sopenharmony_ci } else { 44362306a36Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 44462306a36Sopenharmony_ci socket_shutdown(skt); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int socket_suspend(struct pcmcia_socket *skt) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci if ((skt->state & SOCKET_SUSPEND) && !(skt->state & SOCKET_IN_RESUME)) 45362306a36Sopenharmony_ci return -EBUSY; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci mutex_lock(&skt->ops_mutex); 45662306a36Sopenharmony_ci /* store state on first suspend, but not after spurious wakeups */ 45762306a36Sopenharmony_ci if (!(skt->state & SOCKET_IN_RESUME)) 45862306a36Sopenharmony_ci skt->suspended_state = skt->state; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci skt->socket = dead_socket; 46162306a36Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 46262306a36Sopenharmony_ci if (skt->ops->suspend) 46362306a36Sopenharmony_ci skt->ops->suspend(skt); 46462306a36Sopenharmony_ci skt->state |= SOCKET_SUSPEND; 46562306a36Sopenharmony_ci skt->state &= ~SOCKET_IN_RESUME; 46662306a36Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic int socket_early_resume(struct pcmcia_socket *skt) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci mutex_lock(&skt->ops_mutex); 47362306a36Sopenharmony_ci skt->socket = dead_socket; 47462306a36Sopenharmony_ci skt->ops->init(skt); 47562306a36Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 47662306a36Sopenharmony_ci if (skt->state & SOCKET_PRESENT) 47762306a36Sopenharmony_ci skt->resume_status = socket_setup(skt, resume_delay); 47862306a36Sopenharmony_ci skt->state |= SOCKET_IN_RESUME; 47962306a36Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int socket_late_resume(struct pcmcia_socket *skt) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci int ret = 0; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci mutex_lock(&skt->ops_mutex); 48862306a36Sopenharmony_ci skt->state &= ~(SOCKET_SUSPEND | SOCKET_IN_RESUME); 48962306a36Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (!(skt->state & SOCKET_PRESENT)) { 49262306a36Sopenharmony_ci ret = socket_insert(skt); 49362306a36Sopenharmony_ci if (ret == -ENODEV) 49462306a36Sopenharmony_ci ret = 0; 49562306a36Sopenharmony_ci return ret; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (skt->resume_status) { 49962306a36Sopenharmony_ci socket_shutdown(skt); 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (skt->suspended_state != skt->state) { 50462306a36Sopenharmony_ci dev_dbg(&skt->dev, 50562306a36Sopenharmony_ci "suspend state 0x%x != resume state 0x%x\n", 50662306a36Sopenharmony_ci skt->suspended_state, skt->state); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci socket_shutdown(skt); 50962306a36Sopenharmony_ci return socket_insert(skt); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (!(skt->state & SOCKET_CARDBUS) && (skt->callback)) 51362306a36Sopenharmony_ci ret = skt->callback->early_resume(skt); 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/* 51862306a36Sopenharmony_ci * Finalize the resume. In case of a cardbus socket, we have 51962306a36Sopenharmony_ci * to rebind the devices as we can't be certain that it has been 52062306a36Sopenharmony_ci * replaced, or not. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_cistatic int socket_complete_resume(struct pcmcia_socket *skt) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci int ret = 0; 52562306a36Sopenharmony_ci#ifdef CONFIG_CARDBUS 52662306a36Sopenharmony_ci if (skt->state & SOCKET_CARDBUS) { 52762306a36Sopenharmony_ci /* We can't be sure the CardBus card is the same 52862306a36Sopenharmony_ci * as the one previously inserted. Therefore, remove 52962306a36Sopenharmony_ci * and re-add... */ 53062306a36Sopenharmony_ci cb_free(skt); 53162306a36Sopenharmony_ci ret = cb_alloc(skt); 53262306a36Sopenharmony_ci if (ret) 53362306a36Sopenharmony_ci cb_free(skt); 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci#endif 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci/* 54062306a36Sopenharmony_ci * Resume a socket. If a card is present, verify its CIS against 54162306a36Sopenharmony_ci * our cached copy. If they are different, the card has been 54262306a36Sopenharmony_ci * replaced, and we need to tell the drivers. 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_cistatic int socket_resume(struct pcmcia_socket *skt) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci int err; 54762306a36Sopenharmony_ci if (!(skt->state & SOCKET_SUSPEND)) 54862306a36Sopenharmony_ci return -EBUSY; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci socket_early_resume(skt); 55162306a36Sopenharmony_ci err = socket_late_resume(skt); 55262306a36Sopenharmony_ci if (!err) 55362306a36Sopenharmony_ci err = socket_complete_resume(skt); 55462306a36Sopenharmony_ci return err; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic void socket_remove(struct pcmcia_socket *skt) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci dev_notice(&skt->dev, "pccard: card ejected from slot %d\n", skt->sock); 56062306a36Sopenharmony_ci socket_shutdown(skt); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/* 56462306a36Sopenharmony_ci * Process a socket card detect status change. 56562306a36Sopenharmony_ci * 56662306a36Sopenharmony_ci * If we don't have a card already present, delay the detect event for 56762306a36Sopenharmony_ci * about 20ms (to be on the safe side) before reading the socket status. 56862306a36Sopenharmony_ci * 56962306a36Sopenharmony_ci * Some i82365-based systems send multiple SS_DETECT events during card 57062306a36Sopenharmony_ci * insertion, and the "card present" status bit seems to bounce. This 57162306a36Sopenharmony_ci * will probably be true with GPIO-based card detection systems after 57262306a36Sopenharmony_ci * the product has aged. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_cistatic void socket_detect_change(struct pcmcia_socket *skt) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci if (!(skt->state & SOCKET_SUSPEND)) { 57762306a36Sopenharmony_ci int status; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!(skt->state & SOCKET_PRESENT)) 58062306a36Sopenharmony_ci msleep(20); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci skt->ops->get_status(skt, &status); 58362306a36Sopenharmony_ci if ((skt->state & SOCKET_PRESENT) && 58462306a36Sopenharmony_ci !(status & SS_DETECT)) 58562306a36Sopenharmony_ci socket_remove(skt); 58662306a36Sopenharmony_ci if (!(skt->state & SOCKET_PRESENT) && 58762306a36Sopenharmony_ci (status & SS_DETECT)) 58862306a36Sopenharmony_ci socket_insert(skt); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int pccardd(void *__skt) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct pcmcia_socket *skt = __skt; 59562306a36Sopenharmony_ci int ret; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci skt->thread = current; 59862306a36Sopenharmony_ci skt->socket = dead_socket; 59962306a36Sopenharmony_ci skt->ops->init(skt); 60062306a36Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* register with the device core */ 60362306a36Sopenharmony_ci ret = device_register(&skt->dev); 60462306a36Sopenharmony_ci if (ret) { 60562306a36Sopenharmony_ci dev_warn(&skt->dev, "PCMCIA: unable to register socket\n"); 60662306a36Sopenharmony_ci skt->thread = NULL; 60762306a36Sopenharmony_ci complete(&skt->thread_done); 60862306a36Sopenharmony_ci put_device(&skt->dev); 60962306a36Sopenharmony_ci return 0; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci ret = pccard_sysfs_add_socket(&skt->dev); 61262306a36Sopenharmony_ci if (ret) 61362306a36Sopenharmony_ci dev_warn(&skt->dev, "err %d adding socket attributes\n", ret); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci complete(&skt->thread_done); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* wait for userspace to catch up */ 61862306a36Sopenharmony_ci msleep(250); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci set_freezable(); 62162306a36Sopenharmony_ci for (;;) { 62262306a36Sopenharmony_ci unsigned long flags; 62362306a36Sopenharmony_ci unsigned int events; 62462306a36Sopenharmony_ci unsigned int sysfs_events; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci spin_lock_irqsave(&skt->thread_lock, flags); 62762306a36Sopenharmony_ci events = skt->thread_events; 62862306a36Sopenharmony_ci skt->thread_events = 0; 62962306a36Sopenharmony_ci sysfs_events = skt->sysfs_events; 63062306a36Sopenharmony_ci skt->sysfs_events = 0; 63162306a36Sopenharmony_ci spin_unlock_irqrestore(&skt->thread_lock, flags); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci mutex_lock(&skt->skt_mutex); 63462306a36Sopenharmony_ci if (events & SS_DETECT) 63562306a36Sopenharmony_ci socket_detect_change(skt); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (sysfs_events) { 63862306a36Sopenharmony_ci if (sysfs_events & PCMCIA_UEVENT_EJECT) 63962306a36Sopenharmony_ci socket_remove(skt); 64062306a36Sopenharmony_ci if (sysfs_events & PCMCIA_UEVENT_INSERT) 64162306a36Sopenharmony_ci socket_insert(skt); 64262306a36Sopenharmony_ci if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) && 64362306a36Sopenharmony_ci !(skt->state & SOCKET_CARDBUS)) { 64462306a36Sopenharmony_ci if (skt->callback) 64562306a36Sopenharmony_ci ret = skt->callback->suspend(skt); 64662306a36Sopenharmony_ci else 64762306a36Sopenharmony_ci ret = 0; 64862306a36Sopenharmony_ci if (!ret) { 64962306a36Sopenharmony_ci socket_suspend(skt); 65062306a36Sopenharmony_ci msleep(100); 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci if ((sysfs_events & PCMCIA_UEVENT_RESUME) && 65462306a36Sopenharmony_ci !(skt->state & SOCKET_CARDBUS)) { 65562306a36Sopenharmony_ci ret = socket_resume(skt); 65662306a36Sopenharmony_ci if (!ret && skt->callback) 65762306a36Sopenharmony_ci skt->callback->resume(skt); 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci if ((sysfs_events & PCMCIA_UEVENT_REQUERY) && 66062306a36Sopenharmony_ci !(skt->state & SOCKET_CARDBUS)) { 66162306a36Sopenharmony_ci if (!ret && skt->callback) 66262306a36Sopenharmony_ci skt->callback->requery(skt); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci mutex_unlock(&skt->skt_mutex); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (events || sysfs_events) 66862306a36Sopenharmony_ci continue; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 67162306a36Sopenharmony_ci if (kthread_should_stop()) 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci schedule(); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci try_to_freeze(); 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci /* make sure we are running before we exit */ 67962306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* shut down socket, if a device is still present */ 68262306a36Sopenharmony_ci if (skt->state & SOCKET_PRESENT) { 68362306a36Sopenharmony_ci mutex_lock(&skt->skt_mutex); 68462306a36Sopenharmony_ci socket_remove(skt); 68562306a36Sopenharmony_ci mutex_unlock(&skt->skt_mutex); 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* remove from the device core */ 68962306a36Sopenharmony_ci pccard_sysfs_remove_socket(&skt->dev); 69062306a36Sopenharmony_ci device_unregister(&skt->dev); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci/* 69662306a36Sopenharmony_ci * Yenta (at least) probes interrupts before registering the socket and 69762306a36Sopenharmony_ci * starting the handler thread. 69862306a36Sopenharmony_ci */ 69962306a36Sopenharmony_civoid pcmcia_parse_events(struct pcmcia_socket *s, u_int events) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci unsigned long flags; 70262306a36Sopenharmony_ci dev_dbg(&s->dev, "parse_events: events %08x\n", events); 70362306a36Sopenharmony_ci if (s->thread) { 70462306a36Sopenharmony_ci spin_lock_irqsave(&s->thread_lock, flags); 70562306a36Sopenharmony_ci s->thread_events |= events; 70662306a36Sopenharmony_ci spin_unlock_irqrestore(&s->thread_lock, flags); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci wake_up_process(s->thread); 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci} /* pcmcia_parse_events */ 71162306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_parse_events); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci/** 71462306a36Sopenharmony_ci * pcmcia_parse_uevents() - tell pccardd to issue manual commands 71562306a36Sopenharmony_ci * @s: the PCMCIA socket we wan't to command 71662306a36Sopenharmony_ci * @events: events to pass to pccardd 71762306a36Sopenharmony_ci * 71862306a36Sopenharmony_ci * userspace-issued insert, eject, suspend and resume commands must be 71962306a36Sopenharmony_ci * handled by pccardd to avoid any sysfs-related deadlocks. Valid events 72062306a36Sopenharmony_ci * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), 72162306a36Sopenharmony_ci * PCMCIA_UEVENT_RESUME (for resume), PCMCIA_UEVENT_SUSPEND (for suspend) 72262306a36Sopenharmony_ci * and PCMCIA_UEVENT_REQUERY (for re-querying the PCMCIA card). 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_civoid pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci unsigned long flags; 72762306a36Sopenharmony_ci dev_dbg(&s->dev, "parse_uevents: events %08x\n", events); 72862306a36Sopenharmony_ci if (s->thread) { 72962306a36Sopenharmony_ci spin_lock_irqsave(&s->thread_lock, flags); 73062306a36Sopenharmony_ci s->sysfs_events |= events; 73162306a36Sopenharmony_ci spin_unlock_irqrestore(&s->thread_lock, flags); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci wake_up_process(s->thread); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_parse_uevents); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci/* register pcmcia_callback */ 74062306a36Sopenharmony_ciint pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci int ret = 0; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* s->skt_mutex also protects s->callback */ 74562306a36Sopenharmony_ci mutex_lock(&s->skt_mutex); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (c) { 74862306a36Sopenharmony_ci /* registration */ 74962306a36Sopenharmony_ci if (s->callback) { 75062306a36Sopenharmony_ci ret = -EBUSY; 75162306a36Sopenharmony_ci goto err; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci s->callback = c; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if ((s->state & (SOCKET_PRESENT|SOCKET_CARDBUS)) == SOCKET_PRESENT) 75762306a36Sopenharmony_ci s->callback->add(s); 75862306a36Sopenharmony_ci } else 75962306a36Sopenharmony_ci s->callback = NULL; 76062306a36Sopenharmony_ci err: 76162306a36Sopenharmony_ci mutex_unlock(&s->skt_mutex); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci return ret; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ciEXPORT_SYMBOL(pccard_register_pcmcia); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci/* I'm not sure which "reset" function this is supposed to use, 76962306a36Sopenharmony_ci * but for now, it uses the low-level interface's reset, not the 77062306a36Sopenharmony_ci * CIS register. 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ciint pcmcia_reset_card(struct pcmcia_socket *skt) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci int ret; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci dev_dbg(&skt->dev, "resetting socket\n"); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci mutex_lock(&skt->skt_mutex); 78062306a36Sopenharmony_ci do { 78162306a36Sopenharmony_ci if (!(skt->state & SOCKET_PRESENT)) { 78262306a36Sopenharmony_ci dev_dbg(&skt->dev, "can't reset, not present\n"); 78362306a36Sopenharmony_ci ret = -ENODEV; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci if (skt->state & SOCKET_SUSPEND) { 78762306a36Sopenharmony_ci dev_dbg(&skt->dev, "can't reset, suspended\n"); 78862306a36Sopenharmony_ci ret = -EBUSY; 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci if (skt->state & SOCKET_CARDBUS) { 79262306a36Sopenharmony_ci dev_dbg(&skt->dev, "can't reset, is cardbus\n"); 79362306a36Sopenharmony_ci ret = -EPERM; 79462306a36Sopenharmony_ci break; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (skt->callback) 79862306a36Sopenharmony_ci skt->callback->suspend(skt); 79962306a36Sopenharmony_ci mutex_lock(&skt->ops_mutex); 80062306a36Sopenharmony_ci ret = socket_reset(skt); 80162306a36Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 80262306a36Sopenharmony_ci if ((ret == 0) && (skt->callback)) 80362306a36Sopenharmony_ci skt->callback->resume(skt); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci ret = 0; 80662306a36Sopenharmony_ci } while (0); 80762306a36Sopenharmony_ci mutex_unlock(&skt->skt_mutex); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci return ret; 81062306a36Sopenharmony_ci} /* reset_card */ 81162306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_reset_card); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic int pcmcia_socket_uevent(const struct device *dev, 81562306a36Sopenharmony_ci struct kobj_uevent_env *env) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci const struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (add_uevent_var(env, "SOCKET_NO=%u", s->sock)) 82062306a36Sopenharmony_ci return -ENOMEM; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return 0; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic struct completion pcmcia_unload; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic void pcmcia_release_socket_class(const struct class *data) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci complete(&pcmcia_unload); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci#ifdef CONFIG_PM 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic int __pcmcia_pm_op(struct device *dev, 83762306a36Sopenharmony_ci int (*callback) (struct pcmcia_socket *skt)) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev); 84062306a36Sopenharmony_ci int ret; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci mutex_lock(&s->skt_mutex); 84362306a36Sopenharmony_ci ret = callback(s); 84462306a36Sopenharmony_ci mutex_unlock(&s->skt_mutex); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return ret; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic int pcmcia_socket_dev_suspend_noirq(struct device *dev) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci return __pcmcia_pm_op(dev, socket_suspend); 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic int pcmcia_socket_dev_resume_noirq(struct device *dev) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci return __pcmcia_pm_op(dev, socket_early_resume); 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int __used pcmcia_socket_dev_resume(struct device *dev) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci return __pcmcia_pm_op(dev, socket_late_resume); 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic void __used pcmcia_socket_dev_complete(struct device *dev) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci WARN(__pcmcia_pm_op(dev, socket_complete_resume), 86762306a36Sopenharmony_ci "failed to complete resume"); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic const struct dev_pm_ops pcmcia_socket_pm_ops = { 87162306a36Sopenharmony_ci /* dev_resume may be called with IRQs enabled */ 87262306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(NULL, 87362306a36Sopenharmony_ci pcmcia_socket_dev_resume) 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* late suspend must be called with IRQs disabled */ 87662306a36Sopenharmony_ci .suspend_noirq = pcmcia_socket_dev_suspend_noirq, 87762306a36Sopenharmony_ci .freeze_noirq = pcmcia_socket_dev_suspend_noirq, 87862306a36Sopenharmony_ci .poweroff_noirq = pcmcia_socket_dev_suspend_noirq, 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* early resume must be called with IRQs disabled */ 88162306a36Sopenharmony_ci .resume_noirq = pcmcia_socket_dev_resume_noirq, 88262306a36Sopenharmony_ci .thaw_noirq = pcmcia_socket_dev_resume_noirq, 88362306a36Sopenharmony_ci .restore_noirq = pcmcia_socket_dev_resume_noirq, 88462306a36Sopenharmony_ci .complete = pcmcia_socket_dev_complete, 88562306a36Sopenharmony_ci}; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci#define PCMCIA_SOCKET_CLASS_PM_OPS (&pcmcia_socket_pm_ops) 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci#else /* CONFIG_PM */ 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci#define PCMCIA_SOCKET_CLASS_PM_OPS NULL 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci#endif /* CONFIG_PM */ 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistruct class pcmcia_socket_class = { 89662306a36Sopenharmony_ci .name = "pcmcia_socket", 89762306a36Sopenharmony_ci .dev_uevent = pcmcia_socket_uevent, 89862306a36Sopenharmony_ci .dev_release = pcmcia_release_socket, 89962306a36Sopenharmony_ci .class_release = pcmcia_release_socket_class, 90062306a36Sopenharmony_ci .pm = PCMCIA_SOCKET_CLASS_PM_OPS, 90162306a36Sopenharmony_ci}; 90262306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_socket_class); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic int __init init_pcmcia_cs(void) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci init_completion(&pcmcia_unload); 90862306a36Sopenharmony_ci return class_register(&pcmcia_socket_class); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic void __exit exit_pcmcia_cs(void) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci class_unregister(&pcmcia_socket_class); 91462306a36Sopenharmony_ci wait_for_completion(&pcmcia_unload); 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cisubsys_initcall(init_pcmcia_cs); 91862306a36Sopenharmony_cimodule_exit(exit_pcmcia_cs); 91962306a36Sopenharmony_ci 920