18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * cs.c -- Kernel Card Services - core services 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The initial developer of the original code is David A. Hinds 68c2ecf20Sopenharmony_ci * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds 78c2ecf20Sopenharmony_ci * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * (C) 1999 David A. Hinds 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/major.h> 188c2ecf20Sopenharmony_ci#include <linux/errno.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/mm.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/timer.h> 238c2ecf20Sopenharmony_ci#include <linux/ioport.h> 248c2ecf20Sopenharmony_ci#include <linux/delay.h> 258c2ecf20Sopenharmony_ci#include <linux/pm.h> 268c2ecf20Sopenharmony_ci#include <linux/device.h> 278c2ecf20Sopenharmony_ci#include <linux/kthread.h> 288c2ecf20Sopenharmony_ci#include <linux/freezer.h> 298c2ecf20Sopenharmony_ci#include <asm/irq.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 328c2ecf20Sopenharmony_ci#include <pcmcia/cistpl.h> 338c2ecf20Sopenharmony_ci#include <pcmcia/cisreg.h> 348c2ecf20Sopenharmony_ci#include <pcmcia/ds.h> 358c2ecf20Sopenharmony_ci#include "cs_internal.h" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Module parameters */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); 418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Linux Kernel Card Services"); 428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciINT_MODULE_PARM(setup_delay, 10); /* centiseconds */ 478c2ecf20Sopenharmony_ciINT_MODULE_PARM(resume_delay, 20); /* centiseconds */ 488c2ecf20Sopenharmony_ciINT_MODULE_PARM(shutdown_delay, 3); /* centiseconds */ 498c2ecf20Sopenharmony_ciINT_MODULE_PARM(vcc_settle, 40); /* centiseconds */ 508c2ecf20Sopenharmony_ciINT_MODULE_PARM(reset_time, 10); /* usecs */ 518c2ecf20Sopenharmony_ciINT_MODULE_PARM(unreset_delay, 10); /* centiseconds */ 528c2ecf20Sopenharmony_ciINT_MODULE_PARM(unreset_check, 10); /* centiseconds */ 538c2ecf20Sopenharmony_ciINT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Access speed for attribute memory windows */ 568c2ecf20Sopenharmony_ciINT_MODULE_PARM(cis_speed, 300); /* ns */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cisocket_state_t dead_socket = { 608c2ecf20Sopenharmony_ci .csc_mask = SS_DETECT, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dead_socket); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* List of all sockets, protected by a rwsem */ 668c2ecf20Sopenharmony_ciLIST_HEAD(pcmcia_socket_list); 678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_socket_list); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ciDECLARE_RWSEM(pcmcia_socket_list_rwsem); 708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_socket_list_rwsem); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct device *dev = get_device(&skt->dev); 768c2ecf20Sopenharmony_ci if (!dev) 778c2ecf20Sopenharmony_ci return NULL; 788c2ecf20Sopenharmony_ci return dev_get_drvdata(dev); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_get_socket); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_civoid pcmcia_put_socket(struct pcmcia_socket *skt) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci put_device(&skt->dev); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_put_socket); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void pcmcia_release_socket(struct device *dev) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct pcmcia_socket *socket = dev_get_drvdata(dev); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci complete(&socket->socket_released); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int pccardd(void *__skt); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/** 1008c2ecf20Sopenharmony_ci * pcmcia_register_socket - add a new pcmcia socket device 1018c2ecf20Sopenharmony_ci * @socket: the &socket to register 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ciint pcmcia_register_socket(struct pcmcia_socket *socket) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct task_struct *tsk; 1068c2ecf20Sopenharmony_ci int ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (!socket || !socket->ops || !socket->dev.parent || !socket->resource_ops) 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* try to obtain a socket number [yes, it gets ugly if we 1148c2ecf20Sopenharmony_ci * register more than 2^sizeof(unsigned int) pcmcia 1158c2ecf20Sopenharmony_ci * sockets... but the socket number is deprecated 1168c2ecf20Sopenharmony_ci * anyways, so I don't care] */ 1178c2ecf20Sopenharmony_ci down_write(&pcmcia_socket_list_rwsem); 1188c2ecf20Sopenharmony_ci if (list_empty(&pcmcia_socket_list)) 1198c2ecf20Sopenharmony_ci socket->sock = 0; 1208c2ecf20Sopenharmony_ci else { 1218c2ecf20Sopenharmony_ci unsigned int found, i = 1; 1228c2ecf20Sopenharmony_ci struct pcmcia_socket *tmp; 1238c2ecf20Sopenharmony_ci do { 1248c2ecf20Sopenharmony_ci found = 1; 1258c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &pcmcia_socket_list, socket_list) { 1268c2ecf20Sopenharmony_ci if (tmp->sock == i) 1278c2ecf20Sopenharmony_ci found = 0; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci i++; 1308c2ecf20Sopenharmony_ci } while (!found); 1318c2ecf20Sopenharmony_ci socket->sock = i - 1; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci list_add_tail(&socket->socket_list, &pcmcia_socket_list); 1348c2ecf20Sopenharmony_ci up_write(&pcmcia_socket_list_rwsem); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#ifndef CONFIG_CARDBUS 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * If we do not support Cardbus, ensure that 1398c2ecf20Sopenharmony_ci * the Cardbus socket capability is disabled. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci socket->features &= ~SS_CAP_CARDBUS; 1428c2ecf20Sopenharmony_ci#endif 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* set proper values in socket->dev */ 1458c2ecf20Sopenharmony_ci dev_set_drvdata(&socket->dev, socket); 1468c2ecf20Sopenharmony_ci socket->dev.class = &pcmcia_socket_class; 1478c2ecf20Sopenharmony_ci dev_set_name(&socket->dev, "pcmcia_socket%u", socket->sock); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* base address = 0, map = 0 */ 1508c2ecf20Sopenharmony_ci socket->cis_mem.flags = 0; 1518c2ecf20Sopenharmony_ci socket->cis_mem.speed = cis_speed; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&socket->cis_cache); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci init_completion(&socket->socket_released); 1568c2ecf20Sopenharmony_ci init_completion(&socket->thread_done); 1578c2ecf20Sopenharmony_ci mutex_init(&socket->skt_mutex); 1588c2ecf20Sopenharmony_ci mutex_init(&socket->ops_mutex); 1598c2ecf20Sopenharmony_ci spin_lock_init(&socket->thread_lock); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (socket->resource_ops->init) { 1628c2ecf20Sopenharmony_ci mutex_lock(&socket->ops_mutex); 1638c2ecf20Sopenharmony_ci ret = socket->resource_ops->init(socket); 1648c2ecf20Sopenharmony_ci mutex_unlock(&socket->ops_mutex); 1658c2ecf20Sopenharmony_ci if (ret) 1668c2ecf20Sopenharmony_ci goto err; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci tsk = kthread_run(pccardd, socket, "pccardd"); 1708c2ecf20Sopenharmony_ci if (IS_ERR(tsk)) { 1718c2ecf20Sopenharmony_ci ret = PTR_ERR(tsk); 1728c2ecf20Sopenharmony_ci goto err; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci wait_for_completion(&socket->thread_done); 1768c2ecf20Sopenharmony_ci if (!socket->thread) { 1778c2ecf20Sopenharmony_ci dev_warn(&socket->dev, 1788c2ecf20Sopenharmony_ci "PCMCIA: warning: socket thread did not start\n"); 1798c2ecf20Sopenharmony_ci return -EIO; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci pcmcia_parse_events(socket, SS_DETECT); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* 1858c2ecf20Sopenharmony_ci * Let's try to get the PCMCIA module for 16-bit PCMCIA support. 1868c2ecf20Sopenharmony_ci * If it fails, it doesn't matter -- we still have 32-bit CardBus 1878c2ecf20Sopenharmony_ci * support to offer, so this is not a failure mode. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci request_module_nowait("pcmcia"); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci err: 1948c2ecf20Sopenharmony_ci down_write(&pcmcia_socket_list_rwsem); 1958c2ecf20Sopenharmony_ci list_del(&socket->socket_list); 1968c2ecf20Sopenharmony_ci up_write(&pcmcia_socket_list_rwsem); 1978c2ecf20Sopenharmony_ci return ret; 1988c2ecf20Sopenharmony_ci} /* pcmcia_register_socket */ 1998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_register_socket); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/** 2038c2ecf20Sopenharmony_ci * pcmcia_unregister_socket - remove a pcmcia socket device 2048c2ecf20Sopenharmony_ci * @socket: the &socket to unregister 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_civoid pcmcia_unregister_socket(struct pcmcia_socket *socket) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci if (!socket) 2098c2ecf20Sopenharmony_ci return; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci dev_dbg(&socket->dev, "pcmcia_unregister_socket(0x%p)\n", socket->ops); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (socket->thread) 2148c2ecf20Sopenharmony_ci kthread_stop(socket->thread); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* remove from our own list */ 2178c2ecf20Sopenharmony_ci down_write(&pcmcia_socket_list_rwsem); 2188c2ecf20Sopenharmony_ci list_del(&socket->socket_list); 2198c2ecf20Sopenharmony_ci up_write(&pcmcia_socket_list_rwsem); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* wait for sysfs to drop all references */ 2228c2ecf20Sopenharmony_ci if (socket->resource_ops->exit) { 2238c2ecf20Sopenharmony_ci mutex_lock(&socket->ops_mutex); 2248c2ecf20Sopenharmony_ci socket->resource_ops->exit(socket); 2258c2ecf20Sopenharmony_ci mutex_unlock(&socket->ops_mutex); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci wait_for_completion(&socket->socket_released); 2288c2ecf20Sopenharmony_ci} /* pcmcia_unregister_socket */ 2298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_unregister_socket); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistruct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct pcmcia_socket *s; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci down_read(&pcmcia_socket_list_rwsem); 2378c2ecf20Sopenharmony_ci list_for_each_entry(s, &pcmcia_socket_list, socket_list) 2388c2ecf20Sopenharmony_ci if (s->sock == nr) { 2398c2ecf20Sopenharmony_ci up_read(&pcmcia_socket_list_rwsem); 2408c2ecf20Sopenharmony_ci return s; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci up_read(&pcmcia_socket_list_rwsem); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return NULL; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_get_socket_by_nr); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int socket_reset(struct pcmcia_socket *skt) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci int status, i; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci dev_dbg(&skt->dev, "reset\n"); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci skt->socket.flags |= SS_OUTPUT_ENA | SS_RESET; 2568c2ecf20Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 2578c2ecf20Sopenharmony_ci udelay((long)reset_time); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci skt->socket.flags &= ~SS_RESET; 2608c2ecf20Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci msleep(unreset_delay * 10); 2638c2ecf20Sopenharmony_ci for (i = 0; i < unreset_limit; i++) { 2648c2ecf20Sopenharmony_ci skt->ops->get_status(skt, &status); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (!(status & SS_DETECT)) 2678c2ecf20Sopenharmony_ci return -ENODEV; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (status & SS_READY) 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci msleep(unreset_check * 10); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci dev_err(&skt->dev, "time out after reset\n"); 2768c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/* 2808c2ecf20Sopenharmony_ci * socket_setup() and socket_shutdown() are called by the main event handler 2818c2ecf20Sopenharmony_ci * when card insertion and removal events are received. 2828c2ecf20Sopenharmony_ci * socket_setup() turns on socket power and resets the socket, in two stages. 2838c2ecf20Sopenharmony_ci * socket_shutdown() unconfigures a socket and turns off socket power. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_cistatic void socket_shutdown(struct pcmcia_socket *s) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci int status; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci dev_dbg(&s->dev, "shutdown\n"); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (s->callback) 2928c2ecf20Sopenharmony_ci s->callback->remove(s); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci mutex_lock(&s->ops_mutex); 2958c2ecf20Sopenharmony_ci s->state &= SOCKET_INUSE | SOCKET_PRESENT; 2968c2ecf20Sopenharmony_ci msleep(shutdown_delay * 10); 2978c2ecf20Sopenharmony_ci s->state &= SOCKET_INUSE; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* Blank out the socket state */ 3008c2ecf20Sopenharmony_ci s->socket = dead_socket; 3018c2ecf20Sopenharmony_ci s->ops->init(s); 3028c2ecf20Sopenharmony_ci s->ops->set_socket(s, &s->socket); 3038c2ecf20Sopenharmony_ci s->lock_count = 0; 3048c2ecf20Sopenharmony_ci kfree(s->fake_cis); 3058c2ecf20Sopenharmony_ci s->fake_cis = NULL; 3068c2ecf20Sopenharmony_ci s->functions = 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* From here on we can be sure that only we (that is, the 3098c2ecf20Sopenharmony_ci * pccardd thread) accesses this socket, and all (16-bit) 3108c2ecf20Sopenharmony_ci * PCMCIA interactions are gone. Therefore, release 3118c2ecf20Sopenharmony_ci * ops_mutex so that we don't get a sysfs-related lockdep 3128c2ecf20Sopenharmony_ci * warning. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci mutex_unlock(&s->ops_mutex); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci#ifdef CONFIG_CARDBUS 3178c2ecf20Sopenharmony_ci cb_free(s); 3188c2ecf20Sopenharmony_ci#endif 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* give socket some time to power down */ 3218c2ecf20Sopenharmony_ci msleep(100); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci s->ops->get_status(s, &status); 3248c2ecf20Sopenharmony_ci if (status & SS_POWERON) { 3258c2ecf20Sopenharmony_ci dev_err(&s->dev, 3268c2ecf20Sopenharmony_ci "*** DANGER *** unable to remove socket power\n"); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci s->state &= ~SOCKET_INUSE; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int socket_setup(struct pcmcia_socket *skt, int initial_delay) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci int status, i; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci dev_dbg(&skt->dev, "setup\n"); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci skt->ops->get_status(skt, &status); 3398c2ecf20Sopenharmony_ci if (!(status & SS_DETECT)) 3408c2ecf20Sopenharmony_ci return -ENODEV; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci msleep(initial_delay * 10); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 3458c2ecf20Sopenharmony_ci skt->ops->get_status(skt, &status); 3468c2ecf20Sopenharmony_ci if (!(status & SS_DETECT)) 3478c2ecf20Sopenharmony_ci return -ENODEV; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (!(status & SS_PENDING)) 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci msleep(100); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (status & SS_PENDING) { 3568c2ecf20Sopenharmony_ci dev_err(&skt->dev, "voltage interrogation timed out\n"); 3578c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (status & SS_CARDBUS) { 3618c2ecf20Sopenharmony_ci if (!(skt->features & SS_CAP_CARDBUS)) { 3628c2ecf20Sopenharmony_ci dev_err(&skt->dev, "cardbus cards are not supported\n"); 3638c2ecf20Sopenharmony_ci return -EINVAL; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci skt->state |= SOCKET_CARDBUS; 3668c2ecf20Sopenharmony_ci } else 3678c2ecf20Sopenharmony_ci skt->state &= ~SOCKET_CARDBUS; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* 3708c2ecf20Sopenharmony_ci * Decode the card voltage requirements, and apply power to the card. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci if (status & SS_3VCARD) 3738c2ecf20Sopenharmony_ci skt->socket.Vcc = skt->socket.Vpp = 33; 3748c2ecf20Sopenharmony_ci else if (!(status & SS_XVCARD)) 3758c2ecf20Sopenharmony_ci skt->socket.Vcc = skt->socket.Vpp = 50; 3768c2ecf20Sopenharmony_ci else { 3778c2ecf20Sopenharmony_ci dev_err(&skt->dev, "unsupported voltage key\n"); 3788c2ecf20Sopenharmony_ci return -EIO; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (skt->power_hook) 3828c2ecf20Sopenharmony_ci skt->power_hook(skt, HOOK_POWER_PRE); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci skt->socket.flags = 0; 3858c2ecf20Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* 3888c2ecf20Sopenharmony_ci * Wait "vcc_settle" for the supply to stabilise. 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_ci msleep(vcc_settle * 10); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci skt->ops->get_status(skt, &status); 3938c2ecf20Sopenharmony_ci if (!(status & SS_POWERON)) { 3948c2ecf20Sopenharmony_ci dev_err(&skt->dev, "unable to apply power\n"); 3958c2ecf20Sopenharmony_ci return -EIO; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci status = socket_reset(skt); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (skt->power_hook) 4018c2ecf20Sopenharmony_ci skt->power_hook(skt, HOOK_POWER_POST); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return status; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci/* 4078c2ecf20Sopenharmony_ci * Handle card insertion. Setup the socket, reset the card, 4088c2ecf20Sopenharmony_ci * and then tell the rest of PCMCIA that a card is present. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_cistatic int socket_insert(struct pcmcia_socket *skt) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci int ret; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci dev_dbg(&skt->dev, "insert\n"); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci mutex_lock(&skt->ops_mutex); 4178c2ecf20Sopenharmony_ci if (skt->state & SOCKET_INUSE) { 4188c2ecf20Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 4198c2ecf20Sopenharmony_ci return -EINVAL; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci skt->state |= SOCKET_INUSE; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci ret = socket_setup(skt, setup_delay); 4248c2ecf20Sopenharmony_ci if (ret == 0) { 4258c2ecf20Sopenharmony_ci skt->state |= SOCKET_PRESENT; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci dev_notice(&skt->dev, "pccard: %s card inserted into slot %d\n", 4288c2ecf20Sopenharmony_ci (skt->state & SOCKET_CARDBUS) ? "CardBus" : "PCMCIA", 4298c2ecf20Sopenharmony_ci skt->sock); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci#ifdef CONFIG_CARDBUS 4328c2ecf20Sopenharmony_ci if (skt->state & SOCKET_CARDBUS) { 4338c2ecf20Sopenharmony_ci cb_alloc(skt); 4348c2ecf20Sopenharmony_ci skt->state |= SOCKET_CARDBUS_CONFIG; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci#endif 4378c2ecf20Sopenharmony_ci dev_dbg(&skt->dev, "insert done\n"); 4388c2ecf20Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (!(skt->state & SOCKET_CARDBUS) && (skt->callback)) 4418c2ecf20Sopenharmony_ci skt->callback->add(skt); 4428c2ecf20Sopenharmony_ci } else { 4438c2ecf20Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 4448c2ecf20Sopenharmony_ci socket_shutdown(skt); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return ret; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int socket_suspend(struct pcmcia_socket *skt) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci if ((skt->state & SOCKET_SUSPEND) && !(skt->state & SOCKET_IN_RESUME)) 4538c2ecf20Sopenharmony_ci return -EBUSY; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci mutex_lock(&skt->ops_mutex); 4568c2ecf20Sopenharmony_ci /* store state on first suspend, but not after spurious wakeups */ 4578c2ecf20Sopenharmony_ci if (!(skt->state & SOCKET_IN_RESUME)) 4588c2ecf20Sopenharmony_ci skt->suspended_state = skt->state; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci skt->socket = dead_socket; 4618c2ecf20Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 4628c2ecf20Sopenharmony_ci if (skt->ops->suspend) 4638c2ecf20Sopenharmony_ci skt->ops->suspend(skt); 4648c2ecf20Sopenharmony_ci skt->state |= SOCKET_SUSPEND; 4658c2ecf20Sopenharmony_ci skt->state &= ~SOCKET_IN_RESUME; 4668c2ecf20Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int socket_early_resume(struct pcmcia_socket *skt) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci mutex_lock(&skt->ops_mutex); 4738c2ecf20Sopenharmony_ci skt->socket = dead_socket; 4748c2ecf20Sopenharmony_ci skt->ops->init(skt); 4758c2ecf20Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 4768c2ecf20Sopenharmony_ci if (skt->state & SOCKET_PRESENT) 4778c2ecf20Sopenharmony_ci skt->resume_status = socket_setup(skt, resume_delay); 4788c2ecf20Sopenharmony_ci skt->state |= SOCKET_IN_RESUME; 4798c2ecf20Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic int socket_late_resume(struct pcmcia_socket *skt) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci int ret = 0; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci mutex_lock(&skt->ops_mutex); 4888c2ecf20Sopenharmony_ci skt->state &= ~(SOCKET_SUSPEND | SOCKET_IN_RESUME); 4898c2ecf20Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (!(skt->state & SOCKET_PRESENT)) { 4928c2ecf20Sopenharmony_ci ret = socket_insert(skt); 4938c2ecf20Sopenharmony_ci if (ret == -ENODEV) 4948c2ecf20Sopenharmony_ci ret = 0; 4958c2ecf20Sopenharmony_ci return ret; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (skt->resume_status) { 4998c2ecf20Sopenharmony_ci socket_shutdown(skt); 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (skt->suspended_state != skt->state) { 5048c2ecf20Sopenharmony_ci dev_dbg(&skt->dev, 5058c2ecf20Sopenharmony_ci "suspend state 0x%x != resume state 0x%x\n", 5068c2ecf20Sopenharmony_ci skt->suspended_state, skt->state); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci socket_shutdown(skt); 5098c2ecf20Sopenharmony_ci return socket_insert(skt); 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (!(skt->state & SOCKET_CARDBUS) && (skt->callback)) 5138c2ecf20Sopenharmony_ci ret = skt->callback->early_resume(skt); 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci/* 5188c2ecf20Sopenharmony_ci * Finalize the resume. In case of a cardbus socket, we have 5198c2ecf20Sopenharmony_ci * to rebind the devices as we can't be certain that it has been 5208c2ecf20Sopenharmony_ci * replaced, or not. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_cistatic int socket_complete_resume(struct pcmcia_socket *skt) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci int ret = 0; 5258c2ecf20Sopenharmony_ci#ifdef CONFIG_CARDBUS 5268c2ecf20Sopenharmony_ci if (skt->state & SOCKET_CARDBUS) { 5278c2ecf20Sopenharmony_ci /* We can't be sure the CardBus card is the same 5288c2ecf20Sopenharmony_ci * as the one previously inserted. Therefore, remove 5298c2ecf20Sopenharmony_ci * and re-add... */ 5308c2ecf20Sopenharmony_ci cb_free(skt); 5318c2ecf20Sopenharmony_ci ret = cb_alloc(skt); 5328c2ecf20Sopenharmony_ci if (ret) 5338c2ecf20Sopenharmony_ci cb_free(skt); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci#endif 5368c2ecf20Sopenharmony_ci return ret; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci/* 5408c2ecf20Sopenharmony_ci * Resume a socket. If a card is present, verify its CIS against 5418c2ecf20Sopenharmony_ci * our cached copy. If they are different, the card has been 5428c2ecf20Sopenharmony_ci * replaced, and we need to tell the drivers. 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_cistatic int socket_resume(struct pcmcia_socket *skt) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci int err; 5478c2ecf20Sopenharmony_ci if (!(skt->state & SOCKET_SUSPEND)) 5488c2ecf20Sopenharmony_ci return -EBUSY; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci socket_early_resume(skt); 5518c2ecf20Sopenharmony_ci err = socket_late_resume(skt); 5528c2ecf20Sopenharmony_ci if (!err) 5538c2ecf20Sopenharmony_ci err = socket_complete_resume(skt); 5548c2ecf20Sopenharmony_ci return err; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic void socket_remove(struct pcmcia_socket *skt) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci dev_notice(&skt->dev, "pccard: card ejected from slot %d\n", skt->sock); 5608c2ecf20Sopenharmony_ci socket_shutdown(skt); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/* 5648c2ecf20Sopenharmony_ci * Process a socket card detect status change. 5658c2ecf20Sopenharmony_ci * 5668c2ecf20Sopenharmony_ci * If we don't have a card already present, delay the detect event for 5678c2ecf20Sopenharmony_ci * about 20ms (to be on the safe side) before reading the socket status. 5688c2ecf20Sopenharmony_ci * 5698c2ecf20Sopenharmony_ci * Some i82365-based systems send multiple SS_DETECT events during card 5708c2ecf20Sopenharmony_ci * insertion, and the "card present" status bit seems to bounce. This 5718c2ecf20Sopenharmony_ci * will probably be true with GPIO-based card detection systems after 5728c2ecf20Sopenharmony_ci * the product has aged. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_cistatic void socket_detect_change(struct pcmcia_socket *skt) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci if (!(skt->state & SOCKET_SUSPEND)) { 5778c2ecf20Sopenharmony_ci int status; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (!(skt->state & SOCKET_PRESENT)) 5808c2ecf20Sopenharmony_ci msleep(20); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci skt->ops->get_status(skt, &status); 5838c2ecf20Sopenharmony_ci if ((skt->state & SOCKET_PRESENT) && 5848c2ecf20Sopenharmony_ci !(status & SS_DETECT)) 5858c2ecf20Sopenharmony_ci socket_remove(skt); 5868c2ecf20Sopenharmony_ci if (!(skt->state & SOCKET_PRESENT) && 5878c2ecf20Sopenharmony_ci (status & SS_DETECT)) 5888c2ecf20Sopenharmony_ci socket_insert(skt); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic int pccardd(void *__skt) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct pcmcia_socket *skt = __skt; 5958c2ecf20Sopenharmony_ci int ret; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci skt->thread = current; 5988c2ecf20Sopenharmony_ci skt->socket = dead_socket; 5998c2ecf20Sopenharmony_ci skt->ops->init(skt); 6008c2ecf20Sopenharmony_ci skt->ops->set_socket(skt, &skt->socket); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* register with the device core */ 6038c2ecf20Sopenharmony_ci ret = device_register(&skt->dev); 6048c2ecf20Sopenharmony_ci if (ret) { 6058c2ecf20Sopenharmony_ci dev_warn(&skt->dev, "PCMCIA: unable to register socket\n"); 6068c2ecf20Sopenharmony_ci skt->thread = NULL; 6078c2ecf20Sopenharmony_ci complete(&skt->thread_done); 6088c2ecf20Sopenharmony_ci put_device(&skt->dev); 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci ret = pccard_sysfs_add_socket(&skt->dev); 6128c2ecf20Sopenharmony_ci if (ret) 6138c2ecf20Sopenharmony_ci dev_warn(&skt->dev, "err %d adding socket attributes\n", ret); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci complete(&skt->thread_done); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* wait for userspace to catch up */ 6188c2ecf20Sopenharmony_ci msleep(250); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci set_freezable(); 6218c2ecf20Sopenharmony_ci for (;;) { 6228c2ecf20Sopenharmony_ci unsigned long flags; 6238c2ecf20Sopenharmony_ci unsigned int events; 6248c2ecf20Sopenharmony_ci unsigned int sysfs_events; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci spin_lock_irqsave(&skt->thread_lock, flags); 6278c2ecf20Sopenharmony_ci events = skt->thread_events; 6288c2ecf20Sopenharmony_ci skt->thread_events = 0; 6298c2ecf20Sopenharmony_ci sysfs_events = skt->sysfs_events; 6308c2ecf20Sopenharmony_ci skt->sysfs_events = 0; 6318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&skt->thread_lock, flags); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci mutex_lock(&skt->skt_mutex); 6348c2ecf20Sopenharmony_ci if (events & SS_DETECT) 6358c2ecf20Sopenharmony_ci socket_detect_change(skt); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (sysfs_events) { 6388c2ecf20Sopenharmony_ci if (sysfs_events & PCMCIA_UEVENT_EJECT) 6398c2ecf20Sopenharmony_ci socket_remove(skt); 6408c2ecf20Sopenharmony_ci if (sysfs_events & PCMCIA_UEVENT_INSERT) 6418c2ecf20Sopenharmony_ci socket_insert(skt); 6428c2ecf20Sopenharmony_ci if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) && 6438c2ecf20Sopenharmony_ci !(skt->state & SOCKET_CARDBUS)) { 6448c2ecf20Sopenharmony_ci if (skt->callback) 6458c2ecf20Sopenharmony_ci ret = skt->callback->suspend(skt); 6468c2ecf20Sopenharmony_ci else 6478c2ecf20Sopenharmony_ci ret = 0; 6488c2ecf20Sopenharmony_ci if (!ret) { 6498c2ecf20Sopenharmony_ci socket_suspend(skt); 6508c2ecf20Sopenharmony_ci msleep(100); 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci if ((sysfs_events & PCMCIA_UEVENT_RESUME) && 6548c2ecf20Sopenharmony_ci !(skt->state & SOCKET_CARDBUS)) { 6558c2ecf20Sopenharmony_ci ret = socket_resume(skt); 6568c2ecf20Sopenharmony_ci if (!ret && skt->callback) 6578c2ecf20Sopenharmony_ci skt->callback->resume(skt); 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci if ((sysfs_events & PCMCIA_UEVENT_REQUERY) && 6608c2ecf20Sopenharmony_ci !(skt->state & SOCKET_CARDBUS)) { 6618c2ecf20Sopenharmony_ci if (!ret && skt->callback) 6628c2ecf20Sopenharmony_ci skt->callback->requery(skt); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci mutex_unlock(&skt->skt_mutex); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (events || sysfs_events) 6688c2ecf20Sopenharmony_ci continue; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 6718c2ecf20Sopenharmony_ci if (kthread_should_stop()) 6728c2ecf20Sopenharmony_ci break; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci schedule(); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci try_to_freeze(); 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci /* make sure we are running before we exit */ 6798c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci /* shut down socket, if a device is still present */ 6828c2ecf20Sopenharmony_ci if (skt->state & SOCKET_PRESENT) { 6838c2ecf20Sopenharmony_ci mutex_lock(&skt->skt_mutex); 6848c2ecf20Sopenharmony_ci socket_remove(skt); 6858c2ecf20Sopenharmony_ci mutex_unlock(&skt->skt_mutex); 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* remove from the device core */ 6898c2ecf20Sopenharmony_ci pccard_sysfs_remove_socket(&skt->dev); 6908c2ecf20Sopenharmony_ci device_unregister(&skt->dev); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return 0; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci/* 6968c2ecf20Sopenharmony_ci * Yenta (at least) probes interrupts before registering the socket and 6978c2ecf20Sopenharmony_ci * starting the handler thread. 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_civoid pcmcia_parse_events(struct pcmcia_socket *s, u_int events) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci unsigned long flags; 7028c2ecf20Sopenharmony_ci dev_dbg(&s->dev, "parse_events: events %08x\n", events); 7038c2ecf20Sopenharmony_ci if (s->thread) { 7048c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->thread_lock, flags); 7058c2ecf20Sopenharmony_ci s->thread_events |= events; 7068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->thread_lock, flags); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci wake_up_process(s->thread); 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci} /* pcmcia_parse_events */ 7118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_parse_events); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci/** 7148c2ecf20Sopenharmony_ci * pcmcia_parse_uevents() - tell pccardd to issue manual commands 7158c2ecf20Sopenharmony_ci * @s: the PCMCIA socket we wan't to command 7168c2ecf20Sopenharmony_ci * @events: events to pass to pccardd 7178c2ecf20Sopenharmony_ci * 7188c2ecf20Sopenharmony_ci * userspace-issued insert, eject, suspend and resume commands must be 7198c2ecf20Sopenharmony_ci * handled by pccardd to avoid any sysfs-related deadlocks. Valid events 7208c2ecf20Sopenharmony_ci * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), 7218c2ecf20Sopenharmony_ci * PCMCIA_UEVENT_RESUME (for resume), PCMCIA_UEVENT_SUSPEND (for suspend) 7228c2ecf20Sopenharmony_ci * and PCMCIA_UEVENT_REQUERY (for re-querying the PCMCIA card). 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_civoid pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci unsigned long flags; 7278c2ecf20Sopenharmony_ci dev_dbg(&s->dev, "parse_uevents: events %08x\n", events); 7288c2ecf20Sopenharmony_ci if (s->thread) { 7298c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->thread_lock, flags); 7308c2ecf20Sopenharmony_ci s->sysfs_events |= events; 7318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->thread_lock, flags); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci wake_up_process(s->thread); 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_parse_uevents); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci/* register pcmcia_callback */ 7408c2ecf20Sopenharmony_ciint pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci int ret = 0; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* s->skt_mutex also protects s->callback */ 7458c2ecf20Sopenharmony_ci mutex_lock(&s->skt_mutex); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (c) { 7488c2ecf20Sopenharmony_ci /* registration */ 7498c2ecf20Sopenharmony_ci if (s->callback) { 7508c2ecf20Sopenharmony_ci ret = -EBUSY; 7518c2ecf20Sopenharmony_ci goto err; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci s->callback = c; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if ((s->state & (SOCKET_PRESENT|SOCKET_CARDBUS)) == SOCKET_PRESENT) 7578c2ecf20Sopenharmony_ci s->callback->add(s); 7588c2ecf20Sopenharmony_ci } else 7598c2ecf20Sopenharmony_ci s->callback = NULL; 7608c2ecf20Sopenharmony_ci err: 7618c2ecf20Sopenharmony_ci mutex_unlock(&s->skt_mutex); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return ret; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pccard_register_pcmcia); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci/* I'm not sure which "reset" function this is supposed to use, 7698c2ecf20Sopenharmony_ci * but for now, it uses the low-level interface's reset, not the 7708c2ecf20Sopenharmony_ci * CIS register. 7718c2ecf20Sopenharmony_ci */ 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ciint pcmcia_reset_card(struct pcmcia_socket *skt) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci int ret; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci dev_dbg(&skt->dev, "resetting socket\n"); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci mutex_lock(&skt->skt_mutex); 7808c2ecf20Sopenharmony_ci do { 7818c2ecf20Sopenharmony_ci if (!(skt->state & SOCKET_PRESENT)) { 7828c2ecf20Sopenharmony_ci dev_dbg(&skt->dev, "can't reset, not present\n"); 7838c2ecf20Sopenharmony_ci ret = -ENODEV; 7848c2ecf20Sopenharmony_ci break; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci if (skt->state & SOCKET_SUSPEND) { 7878c2ecf20Sopenharmony_ci dev_dbg(&skt->dev, "can't reset, suspended\n"); 7888c2ecf20Sopenharmony_ci ret = -EBUSY; 7898c2ecf20Sopenharmony_ci break; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci if (skt->state & SOCKET_CARDBUS) { 7928c2ecf20Sopenharmony_ci dev_dbg(&skt->dev, "can't reset, is cardbus\n"); 7938c2ecf20Sopenharmony_ci ret = -EPERM; 7948c2ecf20Sopenharmony_ci break; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (skt->callback) 7988c2ecf20Sopenharmony_ci skt->callback->suspend(skt); 7998c2ecf20Sopenharmony_ci mutex_lock(&skt->ops_mutex); 8008c2ecf20Sopenharmony_ci ret = socket_reset(skt); 8018c2ecf20Sopenharmony_ci mutex_unlock(&skt->ops_mutex); 8028c2ecf20Sopenharmony_ci if ((ret == 0) && (skt->callback)) 8038c2ecf20Sopenharmony_ci skt->callback->resume(skt); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci ret = 0; 8068c2ecf20Sopenharmony_ci } while (0); 8078c2ecf20Sopenharmony_ci mutex_unlock(&skt->skt_mutex); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci return ret; 8108c2ecf20Sopenharmony_ci} /* reset_card */ 8118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_reset_card); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic int pcmcia_socket_uevent(struct device *dev, 8158c2ecf20Sopenharmony_ci struct kobj_uevent_env *env) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (add_uevent_var(env, "SOCKET_NO=%u", s->sock)) 8208c2ecf20Sopenharmony_ci return -ENOMEM; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci return 0; 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic struct completion pcmcia_unload; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic void pcmcia_release_socket_class(struct class *data) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci complete(&pcmcia_unload); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cistatic int __pcmcia_pm_op(struct device *dev, 8378c2ecf20Sopenharmony_ci int (*callback) (struct pcmcia_socket *skt)) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev); 8408c2ecf20Sopenharmony_ci int ret; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci mutex_lock(&s->skt_mutex); 8438c2ecf20Sopenharmony_ci ret = callback(s); 8448c2ecf20Sopenharmony_ci mutex_unlock(&s->skt_mutex); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci return ret; 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic int pcmcia_socket_dev_suspend_noirq(struct device *dev) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci return __pcmcia_pm_op(dev, socket_suspend); 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic int pcmcia_socket_dev_resume_noirq(struct device *dev) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci return __pcmcia_pm_op(dev, socket_early_resume); 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int __used pcmcia_socket_dev_resume(struct device *dev) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci return __pcmcia_pm_op(dev, socket_late_resume); 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic void __used pcmcia_socket_dev_complete(struct device *dev) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci WARN(__pcmcia_pm_op(dev, socket_complete_resume), 8678c2ecf20Sopenharmony_ci "failed to complete resume"); 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_cistatic const struct dev_pm_ops pcmcia_socket_pm_ops = { 8718c2ecf20Sopenharmony_ci /* dev_resume may be called with IRQs enabled */ 8728c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(NULL, 8738c2ecf20Sopenharmony_ci pcmcia_socket_dev_resume) 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci /* late suspend must be called with IRQs disabled */ 8768c2ecf20Sopenharmony_ci .suspend_noirq = pcmcia_socket_dev_suspend_noirq, 8778c2ecf20Sopenharmony_ci .freeze_noirq = pcmcia_socket_dev_suspend_noirq, 8788c2ecf20Sopenharmony_ci .poweroff_noirq = pcmcia_socket_dev_suspend_noirq, 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* early resume must be called with IRQs disabled */ 8818c2ecf20Sopenharmony_ci .resume_noirq = pcmcia_socket_dev_resume_noirq, 8828c2ecf20Sopenharmony_ci .thaw_noirq = pcmcia_socket_dev_resume_noirq, 8838c2ecf20Sopenharmony_ci .restore_noirq = pcmcia_socket_dev_resume_noirq, 8848c2ecf20Sopenharmony_ci .complete = pcmcia_socket_dev_complete, 8858c2ecf20Sopenharmony_ci}; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci#define PCMCIA_SOCKET_CLASS_PM_OPS (&pcmcia_socket_pm_ops) 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci#else /* CONFIG_PM */ 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci#define PCMCIA_SOCKET_CLASS_PM_OPS NULL 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistruct class pcmcia_socket_class = { 8968c2ecf20Sopenharmony_ci .name = "pcmcia_socket", 8978c2ecf20Sopenharmony_ci .dev_uevent = pcmcia_socket_uevent, 8988c2ecf20Sopenharmony_ci .dev_release = pcmcia_release_socket, 8998c2ecf20Sopenharmony_ci .class_release = pcmcia_release_socket_class, 9008c2ecf20Sopenharmony_ci .pm = PCMCIA_SOCKET_CLASS_PM_OPS, 9018c2ecf20Sopenharmony_ci}; 9028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_socket_class); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic int __init init_pcmcia_cs(void) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci init_completion(&pcmcia_unload); 9088c2ecf20Sopenharmony_ci return class_register(&pcmcia_socket_class); 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic void __exit exit_pcmcia_cs(void) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci class_unregister(&pcmcia_socket_class); 9148c2ecf20Sopenharmony_ci wait_for_completion(&pcmcia_unload); 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cisubsys_initcall(init_pcmcia_cs); 9188c2ecf20Sopenharmony_cimodule_exit(exit_pcmcia_cs); 9198c2ecf20Sopenharmony_ci 920