162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ds.c -- 16-bit PCMCIA core support
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 * (C) 2003 - 2010	Dominik Brodowski
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/list.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/workqueue.h>
2062306a36Sopenharmony_ci#include <linux/crc32.h>
2162306a36Sopenharmony_ci#include <linux/firmware.h>
2262306a36Sopenharmony_ci#include <linux/kref.h>
2362306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2462306a36Sopenharmony_ci#include <linux/slab.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <pcmcia/cistpl.h>
2762306a36Sopenharmony_ci#include <pcmcia/ds.h>
2862306a36Sopenharmony_ci#include <pcmcia/ss.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "cs_internal.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*====================================================================*/
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* Module parameters */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ciMODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
3762306a36Sopenharmony_ciMODULE_DESCRIPTION("PCMCIA Driver Services");
3862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*====================================================================*/
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void pcmcia_check_driver(struct pcmcia_driver *p_drv)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	const struct pcmcia_device_id *did = p_drv->id_table;
4662306a36Sopenharmony_ci	unsigned int i;
4762306a36Sopenharmony_ci	u32 hash;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (!p_drv->probe || !p_drv->remove)
5062306a36Sopenharmony_ci		printk(KERN_DEBUG "pcmcia: %s lacks a requisite callback "
5162306a36Sopenharmony_ci		       "function\n", p_drv->name);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	while (did && did->match_flags) {
5462306a36Sopenharmony_ci		for (i = 0; i < 4; i++) {
5562306a36Sopenharmony_ci			if (!did->prod_id[i])
5662306a36Sopenharmony_ci				continue;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci			hash = crc32(0, did->prod_id[i], strlen(did->prod_id[i]));
5962306a36Sopenharmony_ci			if (hash == did->prod_id_hash[i])
6062306a36Sopenharmony_ci				continue;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci			printk(KERN_DEBUG "pcmcia: %s: invalid hash for "
6362306a36Sopenharmony_ci			       "product string \"%s\": is 0x%x, should "
6462306a36Sopenharmony_ci			       "be 0x%x\n", p_drv->name, did->prod_id[i],
6562306a36Sopenharmony_ci			       did->prod_id_hash[i], hash);
6662306a36Sopenharmony_ci			printk(KERN_DEBUG "pcmcia: see "
6762306a36Sopenharmony_ci				"Documentation/pcmcia/devicetable.rst for "
6862306a36Sopenharmony_ci				"details\n");
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci		did++;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/*======================================================================*/
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct pcmcia_dynid {
8162306a36Sopenharmony_ci	struct list_head		node;
8262306a36Sopenharmony_ci	struct pcmcia_device_id		id;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/**
8662306a36Sopenharmony_ci * new_id_store() - add a new PCMCIA device ID to this driver and re-probe devices
8762306a36Sopenharmony_ci * @driver: target device driver
8862306a36Sopenharmony_ci * @buf: buffer for scanning device ID data
8962306a36Sopenharmony_ci * @count: input size
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * Adds a new dynamic PCMCIA device ID to this driver,
9262306a36Sopenharmony_ci * and causes the driver to probe for all devices again.
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_cistatic ssize_t
9562306a36Sopenharmony_cinew_id_store(struct device_driver *driver, const char *buf, size_t count)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct pcmcia_dynid *dynid;
9862306a36Sopenharmony_ci	struct pcmcia_driver *pdrv = to_pcmcia_drv(driver);
9962306a36Sopenharmony_ci	__u16 match_flags, manf_id, card_id;
10062306a36Sopenharmony_ci	__u8 func_id, function, device_no;
10162306a36Sopenharmony_ci	__u32 prod_id_hash[4] = {0, 0, 0, 0};
10262306a36Sopenharmony_ci	int fields = 0;
10362306a36Sopenharmony_ci	int retval = 0;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	fields = sscanf(buf, "%hx %hx %hx %hhx %hhx %hhx %x %x %x %x",
10662306a36Sopenharmony_ci			&match_flags, &manf_id, &card_id, &func_id, &function, &device_no,
10762306a36Sopenharmony_ci			&prod_id_hash[0], &prod_id_hash[1], &prod_id_hash[2], &prod_id_hash[3]);
10862306a36Sopenharmony_ci	if (fields < 6)
10962306a36Sopenharmony_ci		return -EINVAL;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	dynid = kzalloc(sizeof(struct pcmcia_dynid), GFP_KERNEL);
11262306a36Sopenharmony_ci	if (!dynid)
11362306a36Sopenharmony_ci		return -ENOMEM;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	dynid->id.match_flags = match_flags;
11662306a36Sopenharmony_ci	dynid->id.manf_id = manf_id;
11762306a36Sopenharmony_ci	dynid->id.card_id = card_id;
11862306a36Sopenharmony_ci	dynid->id.func_id = func_id;
11962306a36Sopenharmony_ci	dynid->id.function = function;
12062306a36Sopenharmony_ci	dynid->id.device_no = device_no;
12162306a36Sopenharmony_ci	memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	mutex_lock(&pdrv->dynids.lock);
12462306a36Sopenharmony_ci	list_add_tail(&dynid->node, &pdrv->dynids.list);
12562306a36Sopenharmony_ci	mutex_unlock(&pdrv->dynids.lock);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	retval = driver_attach(&pdrv->drv);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (retval)
13062306a36Sopenharmony_ci		return retval;
13162306a36Sopenharmony_ci	return count;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_cistatic DRIVER_ATTR_WO(new_id);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void
13662306a36Sopenharmony_cipcmcia_free_dynids(struct pcmcia_driver *drv)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct pcmcia_dynid *dynid, *n;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	mutex_lock(&drv->dynids.lock);
14162306a36Sopenharmony_ci	list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
14262306a36Sopenharmony_ci		list_del(&dynid->node);
14362306a36Sopenharmony_ci		kfree(dynid);
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci	mutex_unlock(&drv->dynids.lock);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int
14962306a36Sopenharmony_cipcmcia_create_newid_file(struct pcmcia_driver *drv)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	int error = 0;
15262306a36Sopenharmony_ci	if (drv->probe != NULL)
15362306a36Sopenharmony_ci		error = driver_create_file(&drv->drv, &driver_attr_new_id);
15462306a36Sopenharmony_ci	return error;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic void
15862306a36Sopenharmony_cipcmcia_remove_newid_file(struct pcmcia_driver *drv)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	driver_remove_file(&drv->drv, &driver_attr_new_id);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/**
16462306a36Sopenharmony_ci * pcmcia_register_driver - register a PCMCIA driver with the bus core
16562306a36Sopenharmony_ci * @driver: the &driver being registered
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci * Registers a PCMCIA driver with the PCMCIA bus core.
16862306a36Sopenharmony_ci */
16962306a36Sopenharmony_ciint pcmcia_register_driver(struct pcmcia_driver *driver)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	int error;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (!driver)
17462306a36Sopenharmony_ci		return -EINVAL;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	pcmcia_check_driver(driver);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* initialize common fields */
17962306a36Sopenharmony_ci	driver->drv.bus = &pcmcia_bus_type;
18062306a36Sopenharmony_ci	driver->drv.owner = driver->owner;
18162306a36Sopenharmony_ci	driver->drv.name = driver->name;
18262306a36Sopenharmony_ci	mutex_init(&driver->dynids.lock);
18362306a36Sopenharmony_ci	INIT_LIST_HEAD(&driver->dynids.list);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	pr_debug("registering driver %s\n", driver->name);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	error = driver_register(&driver->drv);
18862306a36Sopenharmony_ci	if (error < 0)
18962306a36Sopenharmony_ci		return error;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	error = pcmcia_create_newid_file(driver);
19262306a36Sopenharmony_ci	if (error)
19362306a36Sopenharmony_ci		driver_unregister(&driver->drv);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return error;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_register_driver);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/**
20062306a36Sopenharmony_ci * pcmcia_unregister_driver - unregister a PCMCIA driver with the bus core
20162306a36Sopenharmony_ci * @driver: the &driver being unregistered
20262306a36Sopenharmony_ci */
20362306a36Sopenharmony_civoid pcmcia_unregister_driver(struct pcmcia_driver *driver)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	pr_debug("unregistering driver %s\n", driver->name);
20662306a36Sopenharmony_ci	pcmcia_remove_newid_file(driver);
20762306a36Sopenharmony_ci	driver_unregister(&driver->drv);
20862306a36Sopenharmony_ci	pcmcia_free_dynids(driver);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_unregister_driver);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/* pcmcia_device handling */
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct device *tmp_dev;
21862306a36Sopenharmony_ci	tmp_dev = get_device(&p_dev->dev);
21962306a36Sopenharmony_ci	if (!tmp_dev)
22062306a36Sopenharmony_ci		return NULL;
22162306a36Sopenharmony_ci	return to_pcmcia_dev(tmp_dev);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic void pcmcia_put_dev(struct pcmcia_device *p_dev)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	if (p_dev)
22762306a36Sopenharmony_ci		put_device(&p_dev->dev);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void pcmcia_release_function(struct kref *ref)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct config_t *c = container_of(ref, struct config_t, ref);
23362306a36Sopenharmony_ci	pr_debug("releasing config_t\n");
23462306a36Sopenharmony_ci	kfree(c);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void pcmcia_release_dev(struct device *dev)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
24062306a36Sopenharmony_ci	int i;
24162306a36Sopenharmony_ci	dev_dbg(dev, "releasing device\n");
24262306a36Sopenharmony_ci	pcmcia_put_socket(p_dev->socket);
24362306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
24462306a36Sopenharmony_ci		kfree(p_dev->prod_id[i]);
24562306a36Sopenharmony_ci	kfree(p_dev->devname);
24662306a36Sopenharmony_ci	kref_put(&p_dev->function_config->ref, pcmcia_release_function);
24762306a36Sopenharmony_ci	kfree(p_dev);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic int pcmcia_device_probe(struct device *dev)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct pcmcia_device *p_dev;
25462306a36Sopenharmony_ci	struct pcmcia_driver *p_drv;
25562306a36Sopenharmony_ci	struct pcmcia_socket *s;
25662306a36Sopenharmony_ci	cistpl_config_t cis_config;
25762306a36Sopenharmony_ci	int ret = 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	dev = get_device(dev);
26062306a36Sopenharmony_ci	if (!dev)
26162306a36Sopenharmony_ci		return -ENODEV;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	p_dev = to_pcmcia_dev(dev);
26462306a36Sopenharmony_ci	p_drv = to_pcmcia_drv(dev->driver);
26562306a36Sopenharmony_ci	s = p_dev->socket;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	dev_dbg(dev, "trying to bind to %s\n", p_drv->name);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if ((!p_drv->probe) || (!p_dev->function_config) ||
27062306a36Sopenharmony_ci	    (!try_module_get(p_drv->owner))) {
27162306a36Sopenharmony_ci		ret = -EINVAL;
27262306a36Sopenharmony_ci		goto put_dev;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* set up some more device information */
27662306a36Sopenharmony_ci	ret = pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_CONFIG,
27762306a36Sopenharmony_ci				&cis_config);
27862306a36Sopenharmony_ci	if (!ret) {
27962306a36Sopenharmony_ci		p_dev->config_base = cis_config.base;
28062306a36Sopenharmony_ci		p_dev->config_regs = cis_config.rmask[0];
28162306a36Sopenharmony_ci		dev_dbg(dev, "base %x, regs %x", p_dev->config_base,
28262306a36Sopenharmony_ci			p_dev->config_regs);
28362306a36Sopenharmony_ci	} else {
28462306a36Sopenharmony_ci		dev_info(dev,
28562306a36Sopenharmony_ci			 "pcmcia: could not parse base and rmask0 of CIS\n");
28662306a36Sopenharmony_ci		p_dev->config_base = 0;
28762306a36Sopenharmony_ci		p_dev->config_regs = 0;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	ret = p_drv->probe(p_dev);
29162306a36Sopenharmony_ci	if (ret) {
29262306a36Sopenharmony_ci		dev_dbg(dev, "binding to %s failed with %d\n",
29362306a36Sopenharmony_ci			   p_drv->name, ret);
29462306a36Sopenharmony_ci		goto put_module;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci	dev_dbg(dev, "%s bound: Vpp %d.%d, idx %x, IRQ %d", p_drv->name,
29762306a36Sopenharmony_ci		p_dev->vpp/10, p_dev->vpp%10, p_dev->config_index, p_dev->irq);
29862306a36Sopenharmony_ci	dev_dbg(dev, "resources: ioport %pR %pR iomem %pR %pR %pR",
29962306a36Sopenharmony_ci		p_dev->resource[0], p_dev->resource[1], p_dev->resource[2],
30062306a36Sopenharmony_ci		p_dev->resource[3], p_dev->resource[4]);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
30362306a36Sopenharmony_ci	if ((s->pcmcia_pfc) &&
30462306a36Sopenharmony_ci	    (p_dev->socket->device_count == 1) && (p_dev->device_no == 0))
30562306a36Sopenharmony_ci		pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
30662306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ciput_module:
30962306a36Sopenharmony_ci	if (ret)
31062306a36Sopenharmony_ci		module_put(p_drv->owner);
31162306a36Sopenharmony_ciput_dev:
31262306a36Sopenharmony_ci	if (ret)
31362306a36Sopenharmony_ci		put_device(dev);
31462306a36Sopenharmony_ci	return ret;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/*
31962306a36Sopenharmony_ci * Removes a PCMCIA card from the device tree and socket list.
32062306a36Sopenharmony_ci */
32162306a36Sopenharmony_cistatic void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *leftover)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct pcmcia_device	*p_dev;
32462306a36Sopenharmony_ci	struct pcmcia_device	*tmp;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	dev_dbg(leftover ? &leftover->dev : &s->dev,
32762306a36Sopenharmony_ci		   "pcmcia_card_remove(%d) %s\n", s->sock,
32862306a36Sopenharmony_ci		   leftover ? leftover->devname : "");
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
33162306a36Sopenharmony_ci	if (!leftover)
33262306a36Sopenharmony_ci		s->device_count = 0;
33362306a36Sopenharmony_ci	else
33462306a36Sopenharmony_ci		s->device_count = 1;
33562306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* unregister all pcmcia_devices registered with this socket, except leftover */
33862306a36Sopenharmony_ci	list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) {
33962306a36Sopenharmony_ci		if (p_dev == leftover)
34062306a36Sopenharmony_ci			continue;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		mutex_lock(&s->ops_mutex);
34362306a36Sopenharmony_ci		list_del(&p_dev->socket_device_list);
34462306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "unregistering device\n");
34762306a36Sopenharmony_ci		device_unregister(&p_dev->dev);
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	return;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic void pcmcia_device_remove(struct device *dev)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct pcmcia_device *p_dev;
35662306a36Sopenharmony_ci	struct pcmcia_driver *p_drv;
35762306a36Sopenharmony_ci	int i;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	p_dev = to_pcmcia_dev(dev);
36062306a36Sopenharmony_ci	p_drv = to_pcmcia_drv(dev->driver);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	dev_dbg(dev, "removing device\n");
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* If we're removing the primary module driving a
36562306a36Sopenharmony_ci	 * pseudo multi-function card, we need to unbind
36662306a36Sopenharmony_ci	 * all devices
36762306a36Sopenharmony_ci	 */
36862306a36Sopenharmony_ci	if ((p_dev->socket->pcmcia_pfc) &&
36962306a36Sopenharmony_ci	    (p_dev->socket->device_count > 0) &&
37062306a36Sopenharmony_ci	    (p_dev->device_no == 0))
37162306a36Sopenharmony_ci		pcmcia_card_remove(p_dev->socket, p_dev);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* detach the "instance" */
37462306a36Sopenharmony_ci	if (p_drv->remove)
37562306a36Sopenharmony_ci		p_drv->remove(p_dev);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* check for proper unloading */
37862306a36Sopenharmony_ci	if (p_dev->_irq || p_dev->_io || p_dev->_locked)
37962306a36Sopenharmony_ci		dev_info(dev,
38062306a36Sopenharmony_ci			 "pcmcia: driver %s did not release config properly\n",
38162306a36Sopenharmony_ci			 p_drv->name);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	for (i = 0; i < MAX_WIN; i++)
38462306a36Sopenharmony_ci		if (p_dev->_win & CLIENT_WIN_REQ(i))
38562306a36Sopenharmony_ci			dev_info(dev,
38662306a36Sopenharmony_ci				 "pcmcia: driver %s did not release window properly\n",
38762306a36Sopenharmony_ci				 p_drv->name);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* references from pcmcia_device_probe */
39062306a36Sopenharmony_ci	pcmcia_put_dev(p_dev);
39162306a36Sopenharmony_ci	module_put(p_drv->owner);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/*
39662306a36Sopenharmony_ci * pcmcia_device_query -- determine information about a pcmcia device
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_cistatic int pcmcia_device_query(struct pcmcia_device *p_dev)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	cistpl_manfid_t manf_id;
40162306a36Sopenharmony_ci	cistpl_funcid_t func_id;
40262306a36Sopenharmony_ci	cistpl_vers_1_t	*vers1;
40362306a36Sopenharmony_ci	unsigned int i;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	vers1 = kmalloc(sizeof(*vers1), GFP_KERNEL);
40662306a36Sopenharmony_ci	if (!vers1)
40762306a36Sopenharmony_ci		return -ENOMEM;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL,
41062306a36Sopenharmony_ci			       CISTPL_MANFID, &manf_id)) {
41162306a36Sopenharmony_ci		mutex_lock(&p_dev->socket->ops_mutex);
41262306a36Sopenharmony_ci		p_dev->manf_id = manf_id.manf;
41362306a36Sopenharmony_ci		p_dev->card_id = manf_id.card;
41462306a36Sopenharmony_ci		p_dev->has_manf_id = 1;
41562306a36Sopenharmony_ci		p_dev->has_card_id = 1;
41662306a36Sopenharmony_ci		mutex_unlock(&p_dev->socket->ops_mutex);
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (!pccard_read_tuple(p_dev->socket, p_dev->func,
42062306a36Sopenharmony_ci			       CISTPL_FUNCID, &func_id)) {
42162306a36Sopenharmony_ci		mutex_lock(&p_dev->socket->ops_mutex);
42262306a36Sopenharmony_ci		p_dev->func_id = func_id.func;
42362306a36Sopenharmony_ci		p_dev->has_func_id = 1;
42462306a36Sopenharmony_ci		mutex_unlock(&p_dev->socket->ops_mutex);
42562306a36Sopenharmony_ci	} else {
42662306a36Sopenharmony_ci		/* rule of thumb: cards with no FUNCID, but with
42762306a36Sopenharmony_ci		 * common memory device geometry information, are
42862306a36Sopenharmony_ci		 * probably memory cards (from pcmcia-cs) */
42962306a36Sopenharmony_ci		cistpl_device_geo_t *devgeo;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		devgeo = kmalloc(sizeof(*devgeo), GFP_KERNEL);
43262306a36Sopenharmony_ci		if (!devgeo) {
43362306a36Sopenharmony_ci			kfree(vers1);
43462306a36Sopenharmony_ci			return -ENOMEM;
43562306a36Sopenharmony_ci		}
43662306a36Sopenharmony_ci		if (!pccard_read_tuple(p_dev->socket, p_dev->func,
43762306a36Sopenharmony_ci				      CISTPL_DEVICE_GEO, devgeo)) {
43862306a36Sopenharmony_ci			dev_dbg(&p_dev->dev,
43962306a36Sopenharmony_ci				   "mem device geometry probably means "
44062306a36Sopenharmony_ci				   "FUNCID_MEMORY\n");
44162306a36Sopenharmony_ci			mutex_lock(&p_dev->socket->ops_mutex);
44262306a36Sopenharmony_ci			p_dev->func_id = CISTPL_FUNCID_MEMORY;
44362306a36Sopenharmony_ci			p_dev->has_func_id = 1;
44462306a36Sopenharmony_ci			mutex_unlock(&p_dev->socket->ops_mutex);
44562306a36Sopenharmony_ci		}
44662306a36Sopenharmony_ci		kfree(devgeo);
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1,
45062306a36Sopenharmony_ci			       vers1)) {
45162306a36Sopenharmony_ci		mutex_lock(&p_dev->socket->ops_mutex);
45262306a36Sopenharmony_ci		for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) {
45362306a36Sopenharmony_ci			char *tmp;
45462306a36Sopenharmony_ci			unsigned int length;
45562306a36Sopenharmony_ci			char *new;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci			tmp = vers1->str + vers1->ofs[i];
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci			length = strlen(tmp) + 1;
46062306a36Sopenharmony_ci			if ((length < 2) || (length > 255))
46162306a36Sopenharmony_ci				continue;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci			new = kstrdup(tmp, GFP_KERNEL);
46462306a36Sopenharmony_ci			if (!new)
46562306a36Sopenharmony_ci				continue;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci			tmp = p_dev->prod_id[i];
46862306a36Sopenharmony_ci			p_dev->prod_id[i] = new;
46962306a36Sopenharmony_ci			kfree(tmp);
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci		mutex_unlock(&p_dev->socket->ops_mutex);
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	kfree(vers1);
47562306a36Sopenharmony_ci	return 0;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s,
48062306a36Sopenharmony_ci					       unsigned int function)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct pcmcia_device *p_dev, *tmp_dev;
48362306a36Sopenharmony_ci	int i;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	s = pcmcia_get_socket(s);
48662306a36Sopenharmony_ci	if (!s)
48762306a36Sopenharmony_ci		return NULL;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	pr_debug("adding device to %d, function %d\n", s->sock, function);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
49262306a36Sopenharmony_ci	if (!p_dev)
49362306a36Sopenharmony_ci		goto err_put;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
49662306a36Sopenharmony_ci	p_dev->device_no = (s->device_count++);
49762306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/* max of 2 PFC devices */
50062306a36Sopenharmony_ci	if ((p_dev->device_no >= 2) && (function == 0))
50162306a36Sopenharmony_ci		goto err_free;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/* max of 4 devices overall */
50462306a36Sopenharmony_ci	if (p_dev->device_no >= 4)
50562306a36Sopenharmony_ci		goto err_free;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	p_dev->socket = s;
50862306a36Sopenharmony_ci	p_dev->func   = function;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	p_dev->dev.bus = &pcmcia_bus_type;
51162306a36Sopenharmony_ci	p_dev->dev.parent = s->dev.parent;
51262306a36Sopenharmony_ci	p_dev->dev.release = pcmcia_release_dev;
51362306a36Sopenharmony_ci	/* by default don't allow DMA */
51462306a36Sopenharmony_ci	p_dev->dma_mask = 0;
51562306a36Sopenharmony_ci	p_dev->dev.dma_mask = &p_dev->dma_mask;
51662306a36Sopenharmony_ci	p_dev->devname = kasprintf(GFP_KERNEL, "pcmcia%s", dev_name(&p_dev->dev));
51762306a36Sopenharmony_ci	if (!p_dev->devname)
51862306a36Sopenharmony_ci		goto err_free;
51962306a36Sopenharmony_ci	dev_dbg(&p_dev->dev, "devname is %s\n", p_dev->devname);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/*
52462306a36Sopenharmony_ci	 * p_dev->function_config must be the same for all card functions.
52562306a36Sopenharmony_ci	 * Note that this is serialized by ops_mutex, so that only one
52662306a36Sopenharmony_ci	 * such struct will be created.
52762306a36Sopenharmony_ci	 */
52862306a36Sopenharmony_ci	list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list)
52962306a36Sopenharmony_ci		if (p_dev->func == tmp_dev->func) {
53062306a36Sopenharmony_ci			p_dev->function_config = tmp_dev->function_config;
53162306a36Sopenharmony_ci			p_dev->irq = tmp_dev->irq;
53262306a36Sopenharmony_ci			kref_get(&p_dev->function_config->ref);
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Add to the list in pcmcia_bus_socket */
53662306a36Sopenharmony_ci	list_add(&p_dev->socket_device_list, &s->devices_list);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	if (pcmcia_setup_irq(p_dev))
53962306a36Sopenharmony_ci		dev_warn(&p_dev->dev,
54062306a36Sopenharmony_ci			"IRQ setup failed -- device might not work\n");
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (!p_dev->function_config) {
54362306a36Sopenharmony_ci		config_t *c;
54462306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "creating config_t\n");
54562306a36Sopenharmony_ci		c = kzalloc(sizeof(struct config_t), GFP_KERNEL);
54662306a36Sopenharmony_ci		if (!c) {
54762306a36Sopenharmony_ci			mutex_unlock(&s->ops_mutex);
54862306a36Sopenharmony_ci			goto err_unreg;
54962306a36Sopenharmony_ci		}
55062306a36Sopenharmony_ci		p_dev->function_config = c;
55162306a36Sopenharmony_ci		kref_init(&c->ref);
55262306a36Sopenharmony_ci		for (i = 0; i < MAX_IO_WIN; i++) {
55362306a36Sopenharmony_ci			c->io[i].name = p_dev->devname;
55462306a36Sopenharmony_ci			c->io[i].flags = IORESOURCE_IO;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci		for (i = 0; i < MAX_WIN; i++) {
55762306a36Sopenharmony_ci			c->mem[i].name = p_dev->devname;
55862306a36Sopenharmony_ci			c->mem[i].flags = IORESOURCE_MEM;
55962306a36Sopenharmony_ci		}
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci	for (i = 0; i < MAX_IO_WIN; i++)
56262306a36Sopenharmony_ci		p_dev->resource[i] = &p_dev->function_config->io[i];
56362306a36Sopenharmony_ci	for (; i < (MAX_IO_WIN + MAX_WIN); i++)
56462306a36Sopenharmony_ci		p_dev->resource[i] = &p_dev->function_config->mem[i-MAX_IO_WIN];
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	dev_notice(&p_dev->dev, "pcmcia: registering new device %s (IRQ: %d)\n",
56962306a36Sopenharmony_ci		   p_dev->devname, p_dev->irq);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	pcmcia_device_query(p_dev);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	dev_set_name(&p_dev->dev, "%d.%d", p_dev->socket->sock, p_dev->device_no);
57462306a36Sopenharmony_ci	if (device_register(&p_dev->dev)) {
57562306a36Sopenharmony_ci		mutex_lock(&s->ops_mutex);
57662306a36Sopenharmony_ci		list_del(&p_dev->socket_device_list);
57762306a36Sopenharmony_ci		s->device_count--;
57862306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
57962306a36Sopenharmony_ci		put_device(&p_dev->dev);
58062306a36Sopenharmony_ci		return NULL;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	return p_dev;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci err_unreg:
58662306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
58762306a36Sopenharmony_ci	list_del(&p_dev->socket_device_list);
58862306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci err_free:
59162306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
59262306a36Sopenharmony_ci	s->device_count--;
59362306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
59662306a36Sopenharmony_ci		kfree(p_dev->prod_id[i]);
59762306a36Sopenharmony_ci	kfree(p_dev->devname);
59862306a36Sopenharmony_ci	kfree(p_dev);
59962306a36Sopenharmony_ci err_put:
60062306a36Sopenharmony_ci	pcmcia_put_socket(s);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	return NULL;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic int pcmcia_card_add(struct pcmcia_socket *s)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	cistpl_longlink_mfc_t mfc;
60962306a36Sopenharmony_ci	unsigned int no_funcs, i, no_chains;
61062306a36Sopenharmony_ci	int ret = -EAGAIN;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
61362306a36Sopenharmony_ci	if (!(s->resource_setup_done)) {
61462306a36Sopenharmony_ci		dev_dbg(&s->dev,
61562306a36Sopenharmony_ci			   "no resources available, delaying card_add\n");
61662306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
61762306a36Sopenharmony_ci		return -EAGAIN; /* try again, but later... */
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (pcmcia_validate_mem(s)) {
62162306a36Sopenharmony_ci		dev_dbg(&s->dev, "validating mem resources failed, "
62262306a36Sopenharmony_ci		       "delaying card_add\n");
62362306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
62462306a36Sopenharmony_ci		return -EAGAIN; /* try again, but later... */
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	ret = pccard_validate_cis(s, &no_chains);
62962306a36Sopenharmony_ci	if (ret || !no_chains) {
63062306a36Sopenharmony_ci#if defined(CONFIG_MTD_PCMCIA_ANONYMOUS)
63162306a36Sopenharmony_ci		/* Set up as an anonymous card. If we don't have anonymous
63262306a36Sopenharmony_ci		   memory support then just error the card as there is no
63362306a36Sopenharmony_ci		   point trying to second guess.
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		   Note: some cards have just a device entry, it may be
63662306a36Sopenharmony_ci		   worth extending support to cover these in future */
63762306a36Sopenharmony_ci		if (ret == -EIO) {
63862306a36Sopenharmony_ci			dev_info(&s->dev, "no CIS, assuming an anonymous memory card.\n");
63962306a36Sopenharmony_ci			pcmcia_replace_cis(s, "\xFF", 1);
64062306a36Sopenharmony_ci			no_chains = 1;
64162306a36Sopenharmony_ci			ret = 0;
64262306a36Sopenharmony_ci		} else
64362306a36Sopenharmony_ci#endif
64462306a36Sopenharmony_ci		{
64562306a36Sopenharmony_ci			dev_dbg(&s->dev, "invalid CIS or invalid resources\n");
64662306a36Sopenharmony_ci			return -ENODEV;
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc))
65162306a36Sopenharmony_ci		no_funcs = mfc.nfn;
65262306a36Sopenharmony_ci	else
65362306a36Sopenharmony_ci		no_funcs = 1;
65462306a36Sopenharmony_ci	s->functions = no_funcs;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	for (i = 0; i < no_funcs; i++)
65762306a36Sopenharmony_ci		pcmcia_device_add(s, i);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	return ret;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic int pcmcia_requery_callback(struct device *dev, void *_data)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
66662306a36Sopenharmony_ci	if (!p_dev->dev.driver) {
66762306a36Sopenharmony_ci		dev_dbg(dev, "update device information\n");
66862306a36Sopenharmony_ci		pcmcia_device_query(p_dev);
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	return 0;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic void pcmcia_requery(struct pcmcia_socket *s)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	int has_pfc;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT))
68062306a36Sopenharmony_ci		return;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (s->functions == 0) {
68362306a36Sopenharmony_ci		pcmcia_card_add(s);
68462306a36Sopenharmony_ci		return;
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	/* some device information might have changed because of a CIS
68862306a36Sopenharmony_ci	 * update or because we can finally read it correctly... so
68962306a36Sopenharmony_ci	 * determine it again, overwriting old values if necessary. */
69062306a36Sopenharmony_ci	bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery_callback);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* if the CIS changed, we need to check whether the number of
69362306a36Sopenharmony_ci	 * functions changed. */
69462306a36Sopenharmony_ci	if (s->fake_cis) {
69562306a36Sopenharmony_ci		int old_funcs, new_funcs;
69662306a36Sopenharmony_ci		cistpl_longlink_mfc_t mfc;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		/* does this cis override add or remove functions? */
69962306a36Sopenharmony_ci		old_funcs = s->functions;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC,
70262306a36Sopenharmony_ci					&mfc))
70362306a36Sopenharmony_ci			new_funcs = mfc.nfn;
70462306a36Sopenharmony_ci		else
70562306a36Sopenharmony_ci			new_funcs = 1;
70662306a36Sopenharmony_ci		if (old_funcs != new_funcs) {
70762306a36Sopenharmony_ci			/* we need to re-start */
70862306a36Sopenharmony_ci			pcmcia_card_remove(s, NULL);
70962306a36Sopenharmony_ci			s->functions = 0;
71062306a36Sopenharmony_ci			pcmcia_card_add(s);
71162306a36Sopenharmony_ci		}
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	/* If the PCMCIA device consists of two pseudo devices,
71562306a36Sopenharmony_ci	 * call pcmcia_device_add() -- which will fail if both
71662306a36Sopenharmony_ci	 * devices are already registered. */
71762306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
71862306a36Sopenharmony_ci	has_pfc = s->pcmcia_pfc;
71962306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
72062306a36Sopenharmony_ci	if (has_pfc)
72162306a36Sopenharmony_ci		pcmcia_device_add(s, 0);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* we re-scan all devices, not just the ones connected to this
72462306a36Sopenharmony_ci	 * socket. This does not matter, though. */
72562306a36Sopenharmony_ci	if (bus_rescan_devices(&pcmcia_bus_type))
72662306a36Sopenharmony_ci		dev_warn(&s->dev, "rescanning the bus failed\n");
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci#ifdef CONFIG_PCMCIA_LOAD_CIS
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci/**
73362306a36Sopenharmony_ci * pcmcia_load_firmware - load CIS from userspace if device-provided is broken
73462306a36Sopenharmony_ci * @dev: the pcmcia device which needs a CIS override
73562306a36Sopenharmony_ci * @filename: requested filename in /lib/firmware/
73662306a36Sopenharmony_ci *
73762306a36Sopenharmony_ci * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if
73862306a36Sopenharmony_ci * the one provided by the card is broken. The firmware files reside in
73962306a36Sopenharmony_ci * /lib/firmware/ in userspace.
74062306a36Sopenharmony_ci */
74162306a36Sopenharmony_cistatic int pcmcia_load_firmware(struct pcmcia_device *dev, char *filename)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct pcmcia_socket *s = dev->socket;
74462306a36Sopenharmony_ci	const struct firmware *fw;
74562306a36Sopenharmony_ci	int ret = -ENOMEM;
74662306a36Sopenharmony_ci	cistpl_longlink_mfc_t mfc;
74762306a36Sopenharmony_ci	int old_funcs, new_funcs = 1;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (!filename)
75062306a36Sopenharmony_ci		return -EINVAL;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	dev_dbg(&dev->dev, "trying to load CIS file %s\n", filename);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (request_firmware(&fw, filename, &dev->dev) == 0) {
75562306a36Sopenharmony_ci		if (fw->size >= CISTPL_MAX_CIS_SIZE) {
75662306a36Sopenharmony_ci			ret = -EINVAL;
75762306a36Sopenharmony_ci			dev_err(&dev->dev, "pcmcia: CIS override is too big\n");
75862306a36Sopenharmony_ci			goto release;
75962306a36Sopenharmony_ci		}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		if (!pcmcia_replace_cis(s, fw->data, fw->size))
76262306a36Sopenharmony_ci			ret = 0;
76362306a36Sopenharmony_ci		else {
76462306a36Sopenharmony_ci			dev_err(&dev->dev, "pcmcia: CIS override failed\n");
76562306a36Sopenharmony_ci			goto release;
76662306a36Sopenharmony_ci		}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci		/* we need to re-start if the number of functions changed */
76962306a36Sopenharmony_ci		old_funcs = s->functions;
77062306a36Sopenharmony_ci		if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC,
77162306a36Sopenharmony_ci					&mfc))
77262306a36Sopenharmony_ci			new_funcs = mfc.nfn;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci		if (old_funcs != new_funcs)
77562306a36Sopenharmony_ci			ret = -EBUSY;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		/* update information */
77862306a36Sopenharmony_ci		pcmcia_device_query(dev);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		/* requery (as number of functions might have changed) */
78162306a36Sopenharmony_ci		pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci release:
78462306a36Sopenharmony_ci	release_firmware(fw);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	return ret;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci#else /* !CONFIG_PCMCIA_LOAD_CIS */
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic inline int pcmcia_load_firmware(struct pcmcia_device *dev,
79262306a36Sopenharmony_ci				       char *filename)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	return -ENODEV;
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci#endif
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic inline int pcmcia_devmatch(struct pcmcia_device *dev,
80162306a36Sopenharmony_ci				  const struct pcmcia_device_id *did)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID) {
80462306a36Sopenharmony_ci		if ((!dev->has_manf_id) || (dev->manf_id != did->manf_id))
80562306a36Sopenharmony_ci			return 0;
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID) {
80962306a36Sopenharmony_ci		if ((!dev->has_card_id) || (dev->card_id != did->card_id))
81062306a36Sopenharmony_ci			return 0;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION) {
81462306a36Sopenharmony_ci		if (dev->func != did->function)
81562306a36Sopenharmony_ci			return 0;
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1) {
81962306a36Sopenharmony_ci		if (!dev->prod_id[0])
82062306a36Sopenharmony_ci			return 0;
82162306a36Sopenharmony_ci		if (strcmp(did->prod_id[0], dev->prod_id[0]))
82262306a36Sopenharmony_ci			return 0;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2) {
82662306a36Sopenharmony_ci		if (!dev->prod_id[1])
82762306a36Sopenharmony_ci			return 0;
82862306a36Sopenharmony_ci		if (strcmp(did->prod_id[1], dev->prod_id[1]))
82962306a36Sopenharmony_ci			return 0;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3) {
83362306a36Sopenharmony_ci		if (!dev->prod_id[2])
83462306a36Sopenharmony_ci			return 0;
83562306a36Sopenharmony_ci		if (strcmp(did->prod_id[2], dev->prod_id[2]))
83662306a36Sopenharmony_ci			return 0;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4) {
84062306a36Sopenharmony_ci		if (!dev->prod_id[3])
84162306a36Sopenharmony_ci			return 0;
84262306a36Sopenharmony_ci		if (strcmp(did->prod_id[3], dev->prod_id[3]))
84362306a36Sopenharmony_ci			return 0;
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) {
84762306a36Sopenharmony_ci		dev_dbg(&dev->dev, "this is a pseudo-multi-function device\n");
84862306a36Sopenharmony_ci		mutex_lock(&dev->socket->ops_mutex);
84962306a36Sopenharmony_ci		dev->socket->pcmcia_pfc = 1;
85062306a36Sopenharmony_ci		mutex_unlock(&dev->socket->ops_mutex);
85162306a36Sopenharmony_ci		if (dev->device_no != did->device_no)
85262306a36Sopenharmony_ci			return 0;
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) {
85662306a36Sopenharmony_ci		int ret;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		if ((!dev->has_func_id) || (dev->func_id != did->func_id))
85962306a36Sopenharmony_ci			return 0;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		/* if this is a pseudo-multi-function device,
86262306a36Sopenharmony_ci		 * we need explicit matches */
86362306a36Sopenharmony_ci		if (dev->socket->pcmcia_pfc)
86462306a36Sopenharmony_ci			return 0;
86562306a36Sopenharmony_ci		if (dev->device_no)
86662306a36Sopenharmony_ci			return 0;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		/* also, FUNC_ID matching needs to be activated by userspace
86962306a36Sopenharmony_ci		 * after it has re-checked that there is no possible module
87062306a36Sopenharmony_ci		 * with a prod_id/manf_id/card_id match.
87162306a36Sopenharmony_ci		 */
87262306a36Sopenharmony_ci		mutex_lock(&dev->socket->ops_mutex);
87362306a36Sopenharmony_ci		ret = dev->allow_func_id_match;
87462306a36Sopenharmony_ci		mutex_unlock(&dev->socket->ops_mutex);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		if (!ret) {
87762306a36Sopenharmony_ci			dev_dbg(&dev->dev,
87862306a36Sopenharmony_ci				"skipping FUNC_ID match until userspace ACK\n");
87962306a36Sopenharmony_ci			return 0;
88062306a36Sopenharmony_ci		}
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
88462306a36Sopenharmony_ci		dev_dbg(&dev->dev, "device needs a fake CIS\n");
88562306a36Sopenharmony_ci		if (!dev->socket->fake_cis)
88662306a36Sopenharmony_ci			if (pcmcia_load_firmware(dev, did->cisfile))
88762306a36Sopenharmony_ci				return 0;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) {
89162306a36Sopenharmony_ci		int i;
89262306a36Sopenharmony_ci		for (i = 0; i < 4; i++)
89362306a36Sopenharmony_ci			if (dev->prod_id[i])
89462306a36Sopenharmony_ci				return 0;
89562306a36Sopenharmony_ci		if (dev->has_manf_id || dev->has_card_id || dev->has_func_id)
89662306a36Sopenharmony_ci			return 0;
89762306a36Sopenharmony_ci	}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	return 1;
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic int pcmcia_bus_match(struct device *dev, struct device_driver *drv)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
90662306a36Sopenharmony_ci	struct pcmcia_driver *p_drv = to_pcmcia_drv(drv);
90762306a36Sopenharmony_ci	const struct pcmcia_device_id *did = p_drv->id_table;
90862306a36Sopenharmony_ci	struct pcmcia_dynid *dynid;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	/* match dynamic devices first */
91162306a36Sopenharmony_ci	mutex_lock(&p_drv->dynids.lock);
91262306a36Sopenharmony_ci	list_for_each_entry(dynid, &p_drv->dynids.list, node) {
91362306a36Sopenharmony_ci		dev_dbg(dev, "trying to match to %s\n", drv->name);
91462306a36Sopenharmony_ci		if (pcmcia_devmatch(p_dev, &dynid->id)) {
91562306a36Sopenharmony_ci			dev_dbg(dev, "matched to %s\n", drv->name);
91662306a36Sopenharmony_ci			mutex_unlock(&p_drv->dynids.lock);
91762306a36Sopenharmony_ci			return 1;
91862306a36Sopenharmony_ci		}
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci	mutex_unlock(&p_drv->dynids.lock);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	while (did && did->match_flags) {
92362306a36Sopenharmony_ci		dev_dbg(dev, "trying to match to %s\n", drv->name);
92462306a36Sopenharmony_ci		if (pcmcia_devmatch(p_dev, did)) {
92562306a36Sopenharmony_ci			dev_dbg(dev, "matched to %s\n", drv->name);
92662306a36Sopenharmony_ci			return 1;
92762306a36Sopenharmony_ci		}
92862306a36Sopenharmony_ci		did++;
92962306a36Sopenharmony_ci	}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	return 0;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic int pcmcia_bus_uevent(const struct device *dev, struct kobj_uevent_env *env)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	const struct pcmcia_device *p_dev;
93762306a36Sopenharmony_ci	int i;
93862306a36Sopenharmony_ci	u32 hash[4] = { 0, 0, 0, 0};
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (!dev)
94162306a36Sopenharmony_ci		return -ENODEV;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	p_dev = to_pcmcia_dev(dev);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	/* calculate hashes */
94662306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
94762306a36Sopenharmony_ci		if (!p_dev->prod_id[i])
94862306a36Sopenharmony_ci			continue;
94962306a36Sopenharmony_ci		hash[i] = crc32(0, p_dev->prod_id[i], strlen(p_dev->prod_id[i]));
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	if (add_uevent_var(env, "SOCKET_NO=%u", p_dev->socket->sock))
95362306a36Sopenharmony_ci		return -ENOMEM;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (add_uevent_var(env, "DEVICE_NO=%02X", p_dev->device_no))
95662306a36Sopenharmony_ci		return -ENOMEM;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (add_uevent_var(env, "MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X"
95962306a36Sopenharmony_ci			   "pa%08Xpb%08Xpc%08Xpd%08X",
96062306a36Sopenharmony_ci			   p_dev->has_manf_id ? p_dev->manf_id : 0,
96162306a36Sopenharmony_ci			   p_dev->has_card_id ? p_dev->card_id : 0,
96262306a36Sopenharmony_ci			   p_dev->has_func_id ? p_dev->func_id : 0,
96362306a36Sopenharmony_ci			   p_dev->func,
96462306a36Sopenharmony_ci			   p_dev->device_no,
96562306a36Sopenharmony_ci			   hash[0],
96662306a36Sopenharmony_ci			   hash[1],
96762306a36Sopenharmony_ci			   hash[2],
96862306a36Sopenharmony_ci			   hash[3]))
96962306a36Sopenharmony_ci		return -ENOMEM;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	return 0;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci/************************ runtime PM support ***************************/
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cistatic int pcmcia_dev_suspend(struct device *dev);
97762306a36Sopenharmony_cistatic int pcmcia_dev_resume(struct device *dev);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cistatic int runtime_suspend(struct device *dev)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	int rc;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	device_lock(dev);
98462306a36Sopenharmony_ci	rc = pcmcia_dev_suspend(dev);
98562306a36Sopenharmony_ci	device_unlock(dev);
98662306a36Sopenharmony_ci	return rc;
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_cistatic int runtime_resume(struct device *dev)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	int rc;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	device_lock(dev);
99462306a36Sopenharmony_ci	rc = pcmcia_dev_resume(dev);
99562306a36Sopenharmony_ci	device_unlock(dev);
99662306a36Sopenharmony_ci	return rc;
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci/************************ per-device sysfs output ***************************/
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci#define pcmcia_device_attr(field, test, format)				\
100262306a36Sopenharmony_cistatic ssize_t field##_show (struct device *dev, struct device_attribute *attr, char *buf)		\
100362306a36Sopenharmony_ci{									\
100462306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);		\
100562306a36Sopenharmony_ci	return p_dev->test ? sysfs_emit(buf, format, p_dev->field) : -ENODEV; \
100662306a36Sopenharmony_ci}									\
100762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(field);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci#define pcmcia_device_stringattr(name, field)					\
101062306a36Sopenharmony_cistatic ssize_t name##_show (struct device *dev, struct device_attribute *attr, char *buf)		\
101162306a36Sopenharmony_ci{									\
101262306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);		\
101362306a36Sopenharmony_ci	return p_dev->field ? sysfs_emit(buf, "%s\n", p_dev->field) : -ENODEV; \
101462306a36Sopenharmony_ci}									\
101562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(name);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cipcmcia_device_attr(func_id, has_func_id, "0x%02x\n");
101862306a36Sopenharmony_cipcmcia_device_attr(manf_id, has_manf_id, "0x%04x\n");
101962306a36Sopenharmony_cipcmcia_device_attr(card_id, has_card_id, "0x%04x\n");
102062306a36Sopenharmony_cipcmcia_device_stringattr(prod_id1, prod_id[0]);
102162306a36Sopenharmony_cipcmcia_device_stringattr(prod_id2, prod_id[1]);
102262306a36Sopenharmony_cipcmcia_device_stringattr(prod_id3, prod_id[2]);
102362306a36Sopenharmony_cipcmcia_device_stringattr(prod_id4, prod_id[3]);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_cistatic ssize_t function_show(struct device *dev, struct device_attribute *attr,
102662306a36Sopenharmony_ci			     char *buf)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
102962306a36Sopenharmony_ci	return p_dev->socket ? sysfs_emit(buf, "0x%02x\n", p_dev->func) : -ENODEV;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(function);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic ssize_t resources_show(struct device *dev,
103462306a36Sopenharmony_ci			      struct device_attribute *attr, char *buf)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
103762306a36Sopenharmony_ci	int i, at = 0;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	for (i = 0; i < PCMCIA_NUM_RESOURCES; i++)
104062306a36Sopenharmony_ci		at += sysfs_emit_at(buf, at, "%pr\n", p_dev->resource[i]);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	return at;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(resources);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_cistatic ssize_t pm_state_show(struct device *dev, struct device_attribute *attr, char *buf)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	if (p_dev->suspended)
105162306a36Sopenharmony_ci		return sysfs_emit(buf, "off\n");
105262306a36Sopenharmony_ci	else
105362306a36Sopenharmony_ci		return sysfs_emit(buf, "on\n");
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic ssize_t pm_state_store(struct device *dev, struct device_attribute *attr,
105762306a36Sopenharmony_ci			      const char *buf, size_t count)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
106062306a36Sopenharmony_ci	int ret = 0;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	if (!count)
106362306a36Sopenharmony_ci		return -EINVAL;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	if ((!p_dev->suspended) && !strncmp(buf, "off", 3))
106662306a36Sopenharmony_ci		ret = runtime_suspend(dev);
106762306a36Sopenharmony_ci	else if (p_dev->suspended && !strncmp(buf, "on", 2))
106862306a36Sopenharmony_ci		ret = runtime_resume(dev);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	return ret ? ret : count;
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(pm_state);
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
107762306a36Sopenharmony_ci	int i;
107862306a36Sopenharmony_ci	u32 hash[4] = { 0, 0, 0, 0};
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/* calculate hashes */
108162306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
108262306a36Sopenharmony_ci		if (!p_dev->prod_id[i])
108362306a36Sopenharmony_ci			continue;
108462306a36Sopenharmony_ci		hash[i] = crc32(0, p_dev->prod_id[i],
108562306a36Sopenharmony_ci				strlen(p_dev->prod_id[i]));
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci	return sysfs_emit(buf, "pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02Xpa%08Xpb%08Xpc%08Xpd%08X\n",
108862306a36Sopenharmony_ci				p_dev->has_manf_id ? p_dev->manf_id : 0,
108962306a36Sopenharmony_ci				p_dev->has_card_id ? p_dev->card_id : 0,
109062306a36Sopenharmony_ci				p_dev->has_func_id ? p_dev->func_id : 0,
109162306a36Sopenharmony_ci				p_dev->func, p_dev->device_no,
109262306a36Sopenharmony_ci				hash[0], hash[1], hash[2], hash[3]);
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_cistatic ssize_t allow_func_id_match_store(struct device *dev,
109762306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	if (!count)
110262306a36Sopenharmony_ci		return -EINVAL;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	mutex_lock(&p_dev->socket->ops_mutex);
110562306a36Sopenharmony_ci	p_dev->allow_func_id_match = 1;
110662306a36Sopenharmony_ci	mutex_unlock(&p_dev->socket->ops_mutex);
110762306a36Sopenharmony_ci	pcmcia_parse_uevents(p_dev->socket, PCMCIA_UEVENT_REQUERY);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return count;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_cistatic DEVICE_ATTR_WO(allow_func_id_match);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_cistatic struct attribute *pcmcia_dev_attrs[] = {
111462306a36Sopenharmony_ci	&dev_attr_resources.attr,
111562306a36Sopenharmony_ci	&dev_attr_pm_state.attr,
111662306a36Sopenharmony_ci	&dev_attr_function.attr,
111762306a36Sopenharmony_ci	&dev_attr_func_id.attr,
111862306a36Sopenharmony_ci	&dev_attr_manf_id.attr,
111962306a36Sopenharmony_ci	&dev_attr_card_id.attr,
112062306a36Sopenharmony_ci	&dev_attr_prod_id1.attr,
112162306a36Sopenharmony_ci	&dev_attr_prod_id2.attr,
112262306a36Sopenharmony_ci	&dev_attr_prod_id3.attr,
112362306a36Sopenharmony_ci	&dev_attr_prod_id4.attr,
112462306a36Sopenharmony_ci	&dev_attr_modalias.attr,
112562306a36Sopenharmony_ci	&dev_attr_allow_func_id_match.attr,
112662306a36Sopenharmony_ci	NULL,
112762306a36Sopenharmony_ci};
112862306a36Sopenharmony_ciATTRIBUTE_GROUPS(pcmcia_dev);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci/* PM support, also needed for reset */
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic int pcmcia_dev_suspend(struct device *dev)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
113562306a36Sopenharmony_ci	struct pcmcia_driver *p_drv = NULL;
113662306a36Sopenharmony_ci	int ret = 0;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	mutex_lock(&p_dev->socket->ops_mutex);
113962306a36Sopenharmony_ci	if (p_dev->suspended) {
114062306a36Sopenharmony_ci		mutex_unlock(&p_dev->socket->ops_mutex);
114162306a36Sopenharmony_ci		return 0;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci	p_dev->suspended = 1;
114462306a36Sopenharmony_ci	mutex_unlock(&p_dev->socket->ops_mutex);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	dev_dbg(dev, "suspending\n");
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (dev->driver)
114962306a36Sopenharmony_ci		p_drv = to_pcmcia_drv(dev->driver);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	if (!p_drv)
115262306a36Sopenharmony_ci		goto out;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	if (p_drv->suspend) {
115562306a36Sopenharmony_ci		ret = p_drv->suspend(p_dev);
115662306a36Sopenharmony_ci		if (ret) {
115762306a36Sopenharmony_ci			dev_err(dev,
115862306a36Sopenharmony_ci				"pcmcia: device %s (driver %s) did not want to go to sleep (%d)\n",
115962306a36Sopenharmony_ci				p_dev->devname, p_drv->name, ret);
116062306a36Sopenharmony_ci			mutex_lock(&p_dev->socket->ops_mutex);
116162306a36Sopenharmony_ci			p_dev->suspended = 0;
116262306a36Sopenharmony_ci			mutex_unlock(&p_dev->socket->ops_mutex);
116362306a36Sopenharmony_ci			goto out;
116462306a36Sopenharmony_ci		}
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	if (p_dev->device_no == p_dev->func) {
116862306a36Sopenharmony_ci		dev_dbg(dev, "releasing configuration\n");
116962306a36Sopenharmony_ci		pcmcia_release_configuration(p_dev);
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci out:
117362306a36Sopenharmony_ci	return ret;
117462306a36Sopenharmony_ci}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cistatic int pcmcia_dev_resume(struct device *dev)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
118062306a36Sopenharmony_ci	struct pcmcia_driver *p_drv = NULL;
118162306a36Sopenharmony_ci	int ret = 0;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	mutex_lock(&p_dev->socket->ops_mutex);
118462306a36Sopenharmony_ci	if (!p_dev->suspended) {
118562306a36Sopenharmony_ci		mutex_unlock(&p_dev->socket->ops_mutex);
118662306a36Sopenharmony_ci		return 0;
118762306a36Sopenharmony_ci	}
118862306a36Sopenharmony_ci	p_dev->suspended = 0;
118962306a36Sopenharmony_ci	mutex_unlock(&p_dev->socket->ops_mutex);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	dev_dbg(dev, "resuming\n");
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	if (dev->driver)
119462306a36Sopenharmony_ci		p_drv = to_pcmcia_drv(dev->driver);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	if (!p_drv)
119762306a36Sopenharmony_ci		goto out;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	if (p_dev->device_no == p_dev->func) {
120062306a36Sopenharmony_ci		dev_dbg(dev, "requesting configuration\n");
120162306a36Sopenharmony_ci		ret = pcmcia_enable_device(p_dev);
120262306a36Sopenharmony_ci		if (ret)
120362306a36Sopenharmony_ci			goto out;
120462306a36Sopenharmony_ci	}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	if (p_drv->resume)
120762306a36Sopenharmony_ci		ret = p_drv->resume(p_dev);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci out:
121062306a36Sopenharmony_ci	return ret;
121162306a36Sopenharmony_ci}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_cistatic int pcmcia_bus_suspend_callback(struct device *dev, void *_data)
121562306a36Sopenharmony_ci{
121662306a36Sopenharmony_ci	struct pcmcia_socket *skt = _data;
121762306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	if (p_dev->socket != skt || p_dev->suspended)
122062306a36Sopenharmony_ci		return 0;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	return runtime_suspend(dev);
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cistatic int pcmcia_bus_resume_callback(struct device *dev, void *_data)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	struct pcmcia_socket *skt = _data;
122862306a36Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	if (p_dev->socket != skt || !p_dev->suspended)
123162306a36Sopenharmony_ci		return 0;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	runtime_resume(dev);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	return 0;
123662306a36Sopenharmony_ci}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_cistatic int pcmcia_bus_resume(struct pcmcia_socket *skt)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	dev_dbg(&skt->dev, "resuming socket %d\n", skt->sock);
124162306a36Sopenharmony_ci	bus_for_each_dev(&pcmcia_bus_type, NULL, skt, pcmcia_bus_resume_callback);
124262306a36Sopenharmony_ci	return 0;
124362306a36Sopenharmony_ci}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cistatic int pcmcia_bus_suspend(struct pcmcia_socket *skt)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	dev_dbg(&skt->dev, "suspending socket %d\n", skt->sock);
124862306a36Sopenharmony_ci	if (bus_for_each_dev(&pcmcia_bus_type, NULL, skt,
124962306a36Sopenharmony_ci			     pcmcia_bus_suspend_callback)) {
125062306a36Sopenharmony_ci		pcmcia_bus_resume(skt);
125162306a36Sopenharmony_ci		return -EIO;
125262306a36Sopenharmony_ci	}
125362306a36Sopenharmony_ci	return 0;
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_cistatic int pcmcia_bus_remove(struct pcmcia_socket *skt)
125762306a36Sopenharmony_ci{
125862306a36Sopenharmony_ci	atomic_set(&skt->present, 0);
125962306a36Sopenharmony_ci	pcmcia_card_remove(skt, NULL);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	mutex_lock(&skt->ops_mutex);
126262306a36Sopenharmony_ci	destroy_cis_cache(skt);
126362306a36Sopenharmony_ci	pcmcia_cleanup_irq(skt);
126462306a36Sopenharmony_ci	mutex_unlock(&skt->ops_mutex);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	return 0;
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic int pcmcia_bus_add(struct pcmcia_socket *skt)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	atomic_set(&skt->present, 1);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	mutex_lock(&skt->ops_mutex);
127462306a36Sopenharmony_ci	skt->pcmcia_pfc = 0;
127562306a36Sopenharmony_ci	destroy_cis_cache(skt); /* to be on the safe side... */
127662306a36Sopenharmony_ci	mutex_unlock(&skt->ops_mutex);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	pcmcia_card_add(skt);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	return 0;
128162306a36Sopenharmony_ci}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_cistatic int pcmcia_bus_early_resume(struct pcmcia_socket *skt)
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	if (!verify_cis_cache(skt))
128662306a36Sopenharmony_ci		return 0;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	dev_dbg(&skt->dev, "cis mismatch - different card\n");
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	/* first, remove the card */
129162306a36Sopenharmony_ci	pcmcia_bus_remove(skt);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	mutex_lock(&skt->ops_mutex);
129462306a36Sopenharmony_ci	destroy_cis_cache(skt);
129562306a36Sopenharmony_ci	kfree(skt->fake_cis);
129662306a36Sopenharmony_ci	skt->fake_cis = NULL;
129762306a36Sopenharmony_ci	skt->functions = 0;
129862306a36Sopenharmony_ci	mutex_unlock(&skt->ops_mutex);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	/* now, add the new card */
130162306a36Sopenharmony_ci	pcmcia_bus_add(skt);
130262306a36Sopenharmony_ci	return 0;
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci/*
130762306a36Sopenharmony_ci * NOTE: This is racy. There's no guarantee the card will still be
130862306a36Sopenharmony_ci * physically present, even if the call to this function returns
130962306a36Sopenharmony_ci * non-NULL. Furthermore, the device driver most likely is unbound
131062306a36Sopenharmony_ci * almost immediately, so the timeframe where pcmcia_dev_present
131162306a36Sopenharmony_ci * returns NULL is probably really really small.
131262306a36Sopenharmony_ci */
131362306a36Sopenharmony_cistruct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
131462306a36Sopenharmony_ci{
131562306a36Sopenharmony_ci	struct pcmcia_device *p_dev;
131662306a36Sopenharmony_ci	struct pcmcia_device *ret = NULL;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	p_dev = pcmcia_get_dev(_p_dev);
131962306a36Sopenharmony_ci	if (!p_dev)
132062306a36Sopenharmony_ci		return NULL;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	if (atomic_read(&p_dev->socket->present) != 0)
132362306a36Sopenharmony_ci		ret = p_dev;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	pcmcia_put_dev(p_dev);
132662306a36Sopenharmony_ci	return ret;
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_dev_present);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic struct pcmcia_callback pcmcia_bus_callback = {
133262306a36Sopenharmony_ci	.owner = THIS_MODULE,
133362306a36Sopenharmony_ci	.add = pcmcia_bus_add,
133462306a36Sopenharmony_ci	.remove = pcmcia_bus_remove,
133562306a36Sopenharmony_ci	.requery = pcmcia_requery,
133662306a36Sopenharmony_ci	.validate = pccard_validate_cis,
133762306a36Sopenharmony_ci	.suspend = pcmcia_bus_suspend,
133862306a36Sopenharmony_ci	.early_resume = pcmcia_bus_early_resume,
133962306a36Sopenharmony_ci	.resume = pcmcia_bus_resume,
134062306a36Sopenharmony_ci};
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_cistatic int pcmcia_bus_add_socket(struct device *dev)
134362306a36Sopenharmony_ci{
134462306a36Sopenharmony_ci	struct pcmcia_socket *socket = dev_get_drvdata(dev);
134562306a36Sopenharmony_ci	int ret;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	socket = pcmcia_get_socket(socket);
134862306a36Sopenharmony_ci	if (!socket) {
134962306a36Sopenharmony_ci		dev_err(dev, "PCMCIA obtaining reference to socket failed\n");
135062306a36Sopenharmony_ci		return -ENODEV;
135162306a36Sopenharmony_ci	}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr);
135462306a36Sopenharmony_ci	if (ret) {
135562306a36Sopenharmony_ci		dev_err(dev, "PCMCIA registration failed\n");
135662306a36Sopenharmony_ci		pcmcia_put_socket(socket);
135762306a36Sopenharmony_ci		return ret;
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	INIT_LIST_HEAD(&socket->devices_list);
136162306a36Sopenharmony_ci	socket->pcmcia_pfc = 0;
136262306a36Sopenharmony_ci	socket->device_count = 0;
136362306a36Sopenharmony_ci	atomic_set(&socket->present, 0);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	ret = pccard_register_pcmcia(socket, &pcmcia_bus_callback);
136662306a36Sopenharmony_ci	if (ret) {
136762306a36Sopenharmony_ci		dev_err(dev, "PCMCIA registration failed\n");
136862306a36Sopenharmony_ci		pcmcia_put_socket(socket);
136962306a36Sopenharmony_ci		return ret;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	return 0;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_cistatic void pcmcia_bus_remove_socket(struct device *dev)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	struct pcmcia_socket *socket = dev_get_drvdata(dev);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	if (!socket)
138062306a36Sopenharmony_ci		return;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	pccard_register_pcmcia(socket, NULL);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	/* unregister any unbound devices */
138562306a36Sopenharmony_ci	mutex_lock(&socket->skt_mutex);
138662306a36Sopenharmony_ci	pcmcia_card_remove(socket, NULL);
138762306a36Sopenharmony_ci	release_cis_mem(socket);
138862306a36Sopenharmony_ci	mutex_unlock(&socket->skt_mutex);
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	pcmcia_put_socket(socket);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	return;
139562306a36Sopenharmony_ci}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci/* the pcmcia_bus_interface is used to handle pcmcia socket devices */
139962306a36Sopenharmony_cistatic struct class_interface pcmcia_bus_interface __refdata = {
140062306a36Sopenharmony_ci	.class = &pcmcia_socket_class,
140162306a36Sopenharmony_ci	.add_dev = &pcmcia_bus_add_socket,
140262306a36Sopenharmony_ci	.remove_dev = &pcmcia_bus_remove_socket,
140362306a36Sopenharmony_ci};
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_cistatic const struct dev_pm_ops pcmcia_bus_pm_ops = {
140662306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pcmcia_dev_suspend, pcmcia_dev_resume)
140762306a36Sopenharmony_ci};
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_cistruct bus_type pcmcia_bus_type = {
141062306a36Sopenharmony_ci	.name = "pcmcia",
141162306a36Sopenharmony_ci	.uevent = pcmcia_bus_uevent,
141262306a36Sopenharmony_ci	.match = pcmcia_bus_match,
141362306a36Sopenharmony_ci	.dev_groups = pcmcia_dev_groups,
141462306a36Sopenharmony_ci	.probe = pcmcia_device_probe,
141562306a36Sopenharmony_ci	.remove = pcmcia_device_remove,
141662306a36Sopenharmony_ci	.pm = &pcmcia_bus_pm_ops,
141762306a36Sopenharmony_ci};
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_cistatic int __init init_pcmcia_bus(void)
142162306a36Sopenharmony_ci{
142262306a36Sopenharmony_ci	int ret;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	ret = bus_register(&pcmcia_bus_type);
142562306a36Sopenharmony_ci	if (ret < 0) {
142662306a36Sopenharmony_ci		printk(KERN_WARNING "pcmcia: bus_register error: %d\n", ret);
142762306a36Sopenharmony_ci		return ret;
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci	ret = class_interface_register(&pcmcia_bus_interface);
143062306a36Sopenharmony_ci	if (ret < 0) {
143162306a36Sopenharmony_ci		printk(KERN_WARNING
143262306a36Sopenharmony_ci			"pcmcia: class_interface_register error: %d\n", ret);
143362306a36Sopenharmony_ci		bus_unregister(&pcmcia_bus_type);
143462306a36Sopenharmony_ci		return ret;
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	return 0;
143862306a36Sopenharmony_ci}
143962306a36Sopenharmony_cifs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that
144062306a36Sopenharmony_ci			       * pcmcia_socket_class is already registered */
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_cistatic void __exit exit_pcmcia_bus(void)
144462306a36Sopenharmony_ci{
144562306a36Sopenharmony_ci	class_interface_unregister(&pcmcia_bus_interface);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	bus_unregister(&pcmcia_bus_type);
144862306a36Sopenharmony_ci}
144962306a36Sopenharmony_cimodule_exit(exit_pcmcia_bus);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ciMODULE_ALIAS("ds");
1453