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