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