18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ds.c -- 16-bit PCMCIA core support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * The initial developer of the original code is David A. Hinds
68c2ecf20Sopenharmony_ci * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
78c2ecf20Sopenharmony_ci * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * (C) 1999		David A. Hinds
108c2ecf20Sopenharmony_ci * (C) 2003 - 2010	Dominik Brodowski
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/list.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
208c2ecf20Sopenharmony_ci#include <linux/crc32.h>
218c2ecf20Sopenharmony_ci#include <linux/firmware.h>
228c2ecf20Sopenharmony_ci#include <linux/kref.h>
238c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <pcmcia/cistpl.h>
278c2ecf20Sopenharmony_ci#include <pcmcia/ds.h>
288c2ecf20Sopenharmony_ci#include <pcmcia/ss.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "cs_internal.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*====================================================================*/
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* Module parameters */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PCMCIA Driver Services");
388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*====================================================================*/
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic void pcmcia_check_driver(struct pcmcia_driver *p_drv)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	const struct pcmcia_device_id *did = p_drv->id_table;
468c2ecf20Sopenharmony_ci	unsigned int i;
478c2ecf20Sopenharmony_ci	u32 hash;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (!p_drv->probe || !p_drv->remove)
508c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "pcmcia: %s lacks a requisite callback "
518c2ecf20Sopenharmony_ci		       "function\n", p_drv->name);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	while (did && did->match_flags) {
548c2ecf20Sopenharmony_ci		for (i = 0; i < 4; i++) {
558c2ecf20Sopenharmony_ci			if (!did->prod_id[i])
568c2ecf20Sopenharmony_ci				continue;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci			hash = crc32(0, did->prod_id[i], strlen(did->prod_id[i]));
598c2ecf20Sopenharmony_ci			if (hash == did->prod_id_hash[i])
608c2ecf20Sopenharmony_ci				continue;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "pcmcia: %s: invalid hash for "
638c2ecf20Sopenharmony_ci			       "product string \"%s\": is 0x%x, should "
648c2ecf20Sopenharmony_ci			       "be 0x%x\n", p_drv->name, did->prod_id[i],
658c2ecf20Sopenharmony_ci			       did->prod_id_hash[i], hash);
668c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "pcmcia: see "
678c2ecf20Sopenharmony_ci				"Documentation/pcmcia/devicetable.rst for "
688c2ecf20Sopenharmony_ci				"details\n");
698c2ecf20Sopenharmony_ci		}
708c2ecf20Sopenharmony_ci		did++;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*======================================================================*/
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistruct pcmcia_dynid {
818c2ecf20Sopenharmony_ci	struct list_head		node;
828c2ecf20Sopenharmony_ci	struct pcmcia_device_id		id;
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/**
868c2ecf20Sopenharmony_ci * pcmcia_store_new_id - add a new PCMCIA device ID to this driver and re-probe devices
878c2ecf20Sopenharmony_ci * @driver: target device driver
888c2ecf20Sopenharmony_ci * @buf: buffer for scanning device ID data
898c2ecf20Sopenharmony_ci * @count: input size
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci * Adds a new dynamic PCMCIA device ID to this driver,
928c2ecf20Sopenharmony_ci * and causes the driver to probe for all devices again.
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_cistatic ssize_t
958c2ecf20Sopenharmony_cinew_id_store(struct device_driver *driver, const char *buf, size_t count)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct pcmcia_dynid *dynid;
988c2ecf20Sopenharmony_ci	struct pcmcia_driver *pdrv = to_pcmcia_drv(driver);
998c2ecf20Sopenharmony_ci	__u16 match_flags, manf_id, card_id;
1008c2ecf20Sopenharmony_ci	__u8 func_id, function, device_no;
1018c2ecf20Sopenharmony_ci	__u32 prod_id_hash[4] = {0, 0, 0, 0};
1028c2ecf20Sopenharmony_ci	int fields = 0;
1038c2ecf20Sopenharmony_ci	int retval = 0;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	fields = sscanf(buf, "%hx %hx %hx %hhx %hhx %hhx %x %x %x %x",
1068c2ecf20Sopenharmony_ci			&match_flags, &manf_id, &card_id, &func_id, &function, &device_no,
1078c2ecf20Sopenharmony_ci			&prod_id_hash[0], &prod_id_hash[1], &prod_id_hash[2], &prod_id_hash[3]);
1088c2ecf20Sopenharmony_ci	if (fields < 6)
1098c2ecf20Sopenharmony_ci		return -EINVAL;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	dynid = kzalloc(sizeof(struct pcmcia_dynid), GFP_KERNEL);
1128c2ecf20Sopenharmony_ci	if (!dynid)
1138c2ecf20Sopenharmony_ci		return -ENOMEM;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	dynid->id.match_flags = match_flags;
1168c2ecf20Sopenharmony_ci	dynid->id.manf_id = manf_id;
1178c2ecf20Sopenharmony_ci	dynid->id.card_id = card_id;
1188c2ecf20Sopenharmony_ci	dynid->id.func_id = func_id;
1198c2ecf20Sopenharmony_ci	dynid->id.function = function;
1208c2ecf20Sopenharmony_ci	dynid->id.device_no = device_no;
1218c2ecf20Sopenharmony_ci	memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	mutex_lock(&pdrv->dynids.lock);
1248c2ecf20Sopenharmony_ci	list_add_tail(&dynid->node, &pdrv->dynids.list);
1258c2ecf20Sopenharmony_ci	mutex_unlock(&pdrv->dynids.lock);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	retval = driver_attach(&pdrv->drv);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (retval)
1308c2ecf20Sopenharmony_ci		return retval;
1318c2ecf20Sopenharmony_ci	return count;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(new_id);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void
1368c2ecf20Sopenharmony_cipcmcia_free_dynids(struct pcmcia_driver *drv)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct pcmcia_dynid *dynid, *n;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	mutex_lock(&drv->dynids.lock);
1418c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
1428c2ecf20Sopenharmony_ci		list_del(&dynid->node);
1438c2ecf20Sopenharmony_ci		kfree(dynid);
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci	mutex_unlock(&drv->dynids.lock);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int
1498c2ecf20Sopenharmony_cipcmcia_create_newid_file(struct pcmcia_driver *drv)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	int error = 0;
1528c2ecf20Sopenharmony_ci	if (drv->probe != NULL)
1538c2ecf20Sopenharmony_ci		error = driver_create_file(&drv->drv, &driver_attr_new_id);
1548c2ecf20Sopenharmony_ci	return error;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic void
1588c2ecf20Sopenharmony_cipcmcia_remove_newid_file(struct pcmcia_driver *drv)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	driver_remove_file(&drv->drv, &driver_attr_new_id);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/**
1648c2ecf20Sopenharmony_ci * pcmcia_register_driver - register a PCMCIA driver with the bus core
1658c2ecf20Sopenharmony_ci * @driver: the &driver being registered
1668c2ecf20Sopenharmony_ci *
1678c2ecf20Sopenharmony_ci * Registers a PCMCIA driver with the PCMCIA bus core.
1688c2ecf20Sopenharmony_ci */
1698c2ecf20Sopenharmony_ciint pcmcia_register_driver(struct pcmcia_driver *driver)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	int error;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (!driver)
1748c2ecf20Sopenharmony_ci		return -EINVAL;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	pcmcia_check_driver(driver);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* initialize common fields */
1798c2ecf20Sopenharmony_ci	driver->drv.bus = &pcmcia_bus_type;
1808c2ecf20Sopenharmony_ci	driver->drv.owner = driver->owner;
1818c2ecf20Sopenharmony_ci	driver->drv.name = driver->name;
1828c2ecf20Sopenharmony_ci	mutex_init(&driver->dynids.lock);
1838c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&driver->dynids.list);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	pr_debug("registering driver %s\n", driver->name);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	error = driver_register(&driver->drv);
1888c2ecf20Sopenharmony_ci	if (error < 0)
1898c2ecf20Sopenharmony_ci		return error;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	error = pcmcia_create_newid_file(driver);
1928c2ecf20Sopenharmony_ci	if (error)
1938c2ecf20Sopenharmony_ci		driver_unregister(&driver->drv);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return error;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_register_driver);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/**
2008c2ecf20Sopenharmony_ci * pcmcia_unregister_driver - unregister a PCMCIA driver with the bus core
2018c2ecf20Sopenharmony_ci * @driver: the &driver being unregistered
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_civoid pcmcia_unregister_driver(struct pcmcia_driver *driver)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	pr_debug("unregistering driver %s\n", driver->name);
2068c2ecf20Sopenharmony_ci	pcmcia_remove_newid_file(driver);
2078c2ecf20Sopenharmony_ci	driver_unregister(&driver->drv);
2088c2ecf20Sopenharmony_ci	pcmcia_free_dynids(driver);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_unregister_driver);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/* pcmcia_device handling */
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct device *tmp_dev;
2188c2ecf20Sopenharmony_ci	tmp_dev = get_device(&p_dev->dev);
2198c2ecf20Sopenharmony_ci	if (!tmp_dev)
2208c2ecf20Sopenharmony_ci		return NULL;
2218c2ecf20Sopenharmony_ci	return to_pcmcia_dev(tmp_dev);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic void pcmcia_put_dev(struct pcmcia_device *p_dev)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	if (p_dev)
2278c2ecf20Sopenharmony_ci		put_device(&p_dev->dev);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic void pcmcia_release_function(struct kref *ref)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct config_t *c = container_of(ref, struct config_t, ref);
2338c2ecf20Sopenharmony_ci	pr_debug("releasing config_t\n");
2348c2ecf20Sopenharmony_ci	kfree(c);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void pcmcia_release_dev(struct device *dev)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
2408c2ecf20Sopenharmony_ci	int i;
2418c2ecf20Sopenharmony_ci	dev_dbg(dev, "releasing device\n");
2428c2ecf20Sopenharmony_ci	pcmcia_put_socket(p_dev->socket);
2438c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++)
2448c2ecf20Sopenharmony_ci		kfree(p_dev->prod_id[i]);
2458c2ecf20Sopenharmony_ci	kfree(p_dev->devname);
2468c2ecf20Sopenharmony_ci	kref_put(&p_dev->function_config->ref, pcmcia_release_function);
2478c2ecf20Sopenharmony_ci	kfree(p_dev);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int pcmcia_device_probe(struct device *dev)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev;
2548c2ecf20Sopenharmony_ci	struct pcmcia_driver *p_drv;
2558c2ecf20Sopenharmony_ci	struct pcmcia_socket *s;
2568c2ecf20Sopenharmony_ci	cistpl_config_t cis_config;
2578c2ecf20Sopenharmony_ci	int ret = 0;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	dev = get_device(dev);
2608c2ecf20Sopenharmony_ci	if (!dev)
2618c2ecf20Sopenharmony_ci		return -ENODEV;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	p_dev = to_pcmcia_dev(dev);
2648c2ecf20Sopenharmony_ci	p_drv = to_pcmcia_drv(dev->driver);
2658c2ecf20Sopenharmony_ci	s = p_dev->socket;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	dev_dbg(dev, "trying to bind to %s\n", p_drv->name);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if ((!p_drv->probe) || (!p_dev->function_config) ||
2708c2ecf20Sopenharmony_ci	    (!try_module_get(p_drv->owner))) {
2718c2ecf20Sopenharmony_ci		ret = -EINVAL;
2728c2ecf20Sopenharmony_ci		goto put_dev;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* set up some more device information */
2768c2ecf20Sopenharmony_ci	ret = pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_CONFIG,
2778c2ecf20Sopenharmony_ci				&cis_config);
2788c2ecf20Sopenharmony_ci	if (!ret) {
2798c2ecf20Sopenharmony_ci		p_dev->config_base = cis_config.base;
2808c2ecf20Sopenharmony_ci		p_dev->config_regs = cis_config.rmask[0];
2818c2ecf20Sopenharmony_ci		dev_dbg(dev, "base %x, regs %x", p_dev->config_base,
2828c2ecf20Sopenharmony_ci			p_dev->config_regs);
2838c2ecf20Sopenharmony_ci	} else {
2848c2ecf20Sopenharmony_ci		dev_info(dev,
2858c2ecf20Sopenharmony_ci			 "pcmcia: could not parse base and rmask0 of CIS\n");
2868c2ecf20Sopenharmony_ci		p_dev->config_base = 0;
2878c2ecf20Sopenharmony_ci		p_dev->config_regs = 0;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	ret = p_drv->probe(p_dev);
2918c2ecf20Sopenharmony_ci	if (ret) {
2928c2ecf20Sopenharmony_ci		dev_dbg(dev, "binding to %s failed with %d\n",
2938c2ecf20Sopenharmony_ci			   p_drv->name, ret);
2948c2ecf20Sopenharmony_ci		goto put_module;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s bound: Vpp %d.%d, idx %x, IRQ %d", p_drv->name,
2978c2ecf20Sopenharmony_ci		p_dev->vpp/10, p_dev->vpp%10, p_dev->config_index, p_dev->irq);
2988c2ecf20Sopenharmony_ci	dev_dbg(dev, "resources: ioport %pR %pR iomem %pR %pR %pR",
2998c2ecf20Sopenharmony_ci		p_dev->resource[0], p_dev->resource[1], p_dev->resource[2],
3008c2ecf20Sopenharmony_ci		p_dev->resource[3], p_dev->resource[4]);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
3038c2ecf20Sopenharmony_ci	if ((s->pcmcia_pfc) &&
3048c2ecf20Sopenharmony_ci	    (p_dev->socket->device_count == 1) && (p_dev->device_no == 0))
3058c2ecf20Sopenharmony_ci		pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
3068c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ciput_module:
3098c2ecf20Sopenharmony_ci	if (ret)
3108c2ecf20Sopenharmony_ci		module_put(p_drv->owner);
3118c2ecf20Sopenharmony_ciput_dev:
3128c2ecf20Sopenharmony_ci	if (ret)
3138c2ecf20Sopenharmony_ci		put_device(dev);
3148c2ecf20Sopenharmony_ci	return ret;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci/*
3198c2ecf20Sopenharmony_ci * Removes a PCMCIA card from the device tree and socket list.
3208c2ecf20Sopenharmony_ci */
3218c2ecf20Sopenharmony_cistatic void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *leftover)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct pcmcia_device	*p_dev;
3248c2ecf20Sopenharmony_ci	struct pcmcia_device	*tmp;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	dev_dbg(leftover ? &leftover->dev : &s->dev,
3278c2ecf20Sopenharmony_ci		   "pcmcia_card_remove(%d) %s\n", s->sock,
3288c2ecf20Sopenharmony_ci		   leftover ? leftover->devname : "");
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
3318c2ecf20Sopenharmony_ci	if (!leftover)
3328c2ecf20Sopenharmony_ci		s->device_count = 0;
3338c2ecf20Sopenharmony_ci	else
3348c2ecf20Sopenharmony_ci		s->device_count = 1;
3358c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* unregister all pcmcia_devices registered with this socket, except leftover */
3388c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) {
3398c2ecf20Sopenharmony_ci		if (p_dev == leftover)
3408c2ecf20Sopenharmony_ci			continue;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		mutex_lock(&s->ops_mutex);
3438c2ecf20Sopenharmony_ci		list_del(&p_dev->socket_device_list);
3448c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "unregistering device\n");
3478c2ecf20Sopenharmony_ci		device_unregister(&p_dev->dev);
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic int pcmcia_device_remove(struct device *dev)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev;
3568c2ecf20Sopenharmony_ci	struct pcmcia_driver *p_drv;
3578c2ecf20Sopenharmony_ci	int i;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	p_dev = to_pcmcia_dev(dev);
3608c2ecf20Sopenharmony_ci	p_drv = to_pcmcia_drv(dev->driver);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	dev_dbg(dev, "removing device\n");
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/* If we're removing the primary module driving a
3658c2ecf20Sopenharmony_ci	 * pseudo multi-function card, we need to unbind
3668c2ecf20Sopenharmony_ci	 * all devices
3678c2ecf20Sopenharmony_ci	 */
3688c2ecf20Sopenharmony_ci	if ((p_dev->socket->pcmcia_pfc) &&
3698c2ecf20Sopenharmony_ci	    (p_dev->socket->device_count > 0) &&
3708c2ecf20Sopenharmony_ci	    (p_dev->device_no == 0))
3718c2ecf20Sopenharmony_ci		pcmcia_card_remove(p_dev->socket, p_dev);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/* detach the "instance" */
3748c2ecf20Sopenharmony_ci	if (!p_drv)
3758c2ecf20Sopenharmony_ci		return 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (p_drv->remove)
3788c2ecf20Sopenharmony_ci		p_drv->remove(p_dev);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* check for proper unloading */
3818c2ecf20Sopenharmony_ci	if (p_dev->_irq || p_dev->_io || p_dev->_locked)
3828c2ecf20Sopenharmony_ci		dev_info(dev,
3838c2ecf20Sopenharmony_ci			 "pcmcia: driver %s did not release config properly\n",
3848c2ecf20Sopenharmony_ci			 p_drv->name);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_WIN; i++)
3878c2ecf20Sopenharmony_ci		if (p_dev->_win & CLIENT_WIN_REQ(i))
3888c2ecf20Sopenharmony_ci			dev_info(dev,
3898c2ecf20Sopenharmony_ci				 "pcmcia: driver %s did not release window properly\n",
3908c2ecf20Sopenharmony_ci				 p_drv->name);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* references from pcmcia_probe_device */
3938c2ecf20Sopenharmony_ci	pcmcia_put_dev(p_dev);
3948c2ecf20Sopenharmony_ci	module_put(p_drv->owner);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/*
4018c2ecf20Sopenharmony_ci * pcmcia_device_query -- determine information about a pcmcia device
4028c2ecf20Sopenharmony_ci */
4038c2ecf20Sopenharmony_cistatic int pcmcia_device_query(struct pcmcia_device *p_dev)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	cistpl_manfid_t manf_id;
4068c2ecf20Sopenharmony_ci	cistpl_funcid_t func_id;
4078c2ecf20Sopenharmony_ci	cistpl_vers_1_t	*vers1;
4088c2ecf20Sopenharmony_ci	unsigned int i;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	vers1 = kmalloc(sizeof(*vers1), GFP_KERNEL);
4118c2ecf20Sopenharmony_ci	if (!vers1)
4128c2ecf20Sopenharmony_ci		return -ENOMEM;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL,
4158c2ecf20Sopenharmony_ci			       CISTPL_MANFID, &manf_id)) {
4168c2ecf20Sopenharmony_ci		mutex_lock(&p_dev->socket->ops_mutex);
4178c2ecf20Sopenharmony_ci		p_dev->manf_id = manf_id.manf;
4188c2ecf20Sopenharmony_ci		p_dev->card_id = manf_id.card;
4198c2ecf20Sopenharmony_ci		p_dev->has_manf_id = 1;
4208c2ecf20Sopenharmony_ci		p_dev->has_card_id = 1;
4218c2ecf20Sopenharmony_ci		mutex_unlock(&p_dev->socket->ops_mutex);
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (!pccard_read_tuple(p_dev->socket, p_dev->func,
4258c2ecf20Sopenharmony_ci			       CISTPL_FUNCID, &func_id)) {
4268c2ecf20Sopenharmony_ci		mutex_lock(&p_dev->socket->ops_mutex);
4278c2ecf20Sopenharmony_ci		p_dev->func_id = func_id.func;
4288c2ecf20Sopenharmony_ci		p_dev->has_func_id = 1;
4298c2ecf20Sopenharmony_ci		mutex_unlock(&p_dev->socket->ops_mutex);
4308c2ecf20Sopenharmony_ci	} else {
4318c2ecf20Sopenharmony_ci		/* rule of thumb: cards with no FUNCID, but with
4328c2ecf20Sopenharmony_ci		 * common memory device geometry information, are
4338c2ecf20Sopenharmony_ci		 * probably memory cards (from pcmcia-cs) */
4348c2ecf20Sopenharmony_ci		cistpl_device_geo_t *devgeo;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		devgeo = kmalloc(sizeof(*devgeo), GFP_KERNEL);
4378c2ecf20Sopenharmony_ci		if (!devgeo) {
4388c2ecf20Sopenharmony_ci			kfree(vers1);
4398c2ecf20Sopenharmony_ci			return -ENOMEM;
4408c2ecf20Sopenharmony_ci		}
4418c2ecf20Sopenharmony_ci		if (!pccard_read_tuple(p_dev->socket, p_dev->func,
4428c2ecf20Sopenharmony_ci				      CISTPL_DEVICE_GEO, devgeo)) {
4438c2ecf20Sopenharmony_ci			dev_dbg(&p_dev->dev,
4448c2ecf20Sopenharmony_ci				   "mem device geometry probably means "
4458c2ecf20Sopenharmony_ci				   "FUNCID_MEMORY\n");
4468c2ecf20Sopenharmony_ci			mutex_lock(&p_dev->socket->ops_mutex);
4478c2ecf20Sopenharmony_ci			p_dev->func_id = CISTPL_FUNCID_MEMORY;
4488c2ecf20Sopenharmony_ci			p_dev->has_func_id = 1;
4498c2ecf20Sopenharmony_ci			mutex_unlock(&p_dev->socket->ops_mutex);
4508c2ecf20Sopenharmony_ci		}
4518c2ecf20Sopenharmony_ci		kfree(devgeo);
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1,
4558c2ecf20Sopenharmony_ci			       vers1)) {
4568c2ecf20Sopenharmony_ci		mutex_lock(&p_dev->socket->ops_mutex);
4578c2ecf20Sopenharmony_ci		for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) {
4588c2ecf20Sopenharmony_ci			char *tmp;
4598c2ecf20Sopenharmony_ci			unsigned int length;
4608c2ecf20Sopenharmony_ci			char *new;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci			tmp = vers1->str + vers1->ofs[i];
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci			length = strlen(tmp) + 1;
4658c2ecf20Sopenharmony_ci			if ((length < 2) || (length > 255))
4668c2ecf20Sopenharmony_ci				continue;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci			new = kstrdup(tmp, GFP_KERNEL);
4698c2ecf20Sopenharmony_ci			if (!new)
4708c2ecf20Sopenharmony_ci				continue;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci			tmp = p_dev->prod_id[i];
4738c2ecf20Sopenharmony_ci			p_dev->prod_id[i] = new;
4748c2ecf20Sopenharmony_ci			kfree(tmp);
4758c2ecf20Sopenharmony_ci		}
4768c2ecf20Sopenharmony_ci		mutex_unlock(&p_dev->socket->ops_mutex);
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	kfree(vers1);
4808c2ecf20Sopenharmony_ci	return 0;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s,
4858c2ecf20Sopenharmony_ci					       unsigned int function)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev, *tmp_dev;
4888c2ecf20Sopenharmony_ci	int i;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	s = pcmcia_get_socket(s);
4918c2ecf20Sopenharmony_ci	if (!s)
4928c2ecf20Sopenharmony_ci		return NULL;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	pr_debug("adding device to %d, function %d\n", s->sock, function);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
4978c2ecf20Sopenharmony_ci	if (!p_dev)
4988c2ecf20Sopenharmony_ci		goto err_put;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
5018c2ecf20Sopenharmony_ci	p_dev->device_no = (s->device_count++);
5028c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* max of 2 PFC devices */
5058c2ecf20Sopenharmony_ci	if ((p_dev->device_no >= 2) && (function == 0))
5068c2ecf20Sopenharmony_ci		goto err_free;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/* max of 4 devices overall */
5098c2ecf20Sopenharmony_ci	if (p_dev->device_no >= 4)
5108c2ecf20Sopenharmony_ci		goto err_free;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	p_dev->socket = s;
5138c2ecf20Sopenharmony_ci	p_dev->func   = function;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	p_dev->dev.bus = &pcmcia_bus_type;
5168c2ecf20Sopenharmony_ci	p_dev->dev.parent = s->dev.parent;
5178c2ecf20Sopenharmony_ci	p_dev->dev.release = pcmcia_release_dev;
5188c2ecf20Sopenharmony_ci	/* by default don't allow DMA */
5198c2ecf20Sopenharmony_ci	p_dev->dma_mask = 0;
5208c2ecf20Sopenharmony_ci	p_dev->dev.dma_mask = &p_dev->dma_mask;
5218c2ecf20Sopenharmony_ci	p_dev->devname = kasprintf(GFP_KERNEL, "pcmcia%s", dev_name(&p_dev->dev));
5228c2ecf20Sopenharmony_ci	if (!p_dev->devname)
5238c2ecf20Sopenharmony_ci		goto err_free;
5248c2ecf20Sopenharmony_ci	dev_dbg(&p_dev->dev, "devname is %s\n", p_dev->devname);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/*
5298c2ecf20Sopenharmony_ci	 * p_dev->function_config must be the same for all card functions.
5308c2ecf20Sopenharmony_ci	 * Note that this is serialized by ops_mutex, so that only one
5318c2ecf20Sopenharmony_ci	 * such struct will be created.
5328c2ecf20Sopenharmony_ci	 */
5338c2ecf20Sopenharmony_ci	list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list)
5348c2ecf20Sopenharmony_ci		if (p_dev->func == tmp_dev->func) {
5358c2ecf20Sopenharmony_ci			p_dev->function_config = tmp_dev->function_config;
5368c2ecf20Sopenharmony_ci			p_dev->irq = tmp_dev->irq;
5378c2ecf20Sopenharmony_ci			kref_get(&p_dev->function_config->ref);
5388c2ecf20Sopenharmony_ci		}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/* Add to the list in pcmcia_bus_socket */
5418c2ecf20Sopenharmony_ci	list_add(&p_dev->socket_device_list, &s->devices_list);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	if (pcmcia_setup_irq(p_dev))
5448c2ecf20Sopenharmony_ci		dev_warn(&p_dev->dev,
5458c2ecf20Sopenharmony_ci			"IRQ setup failed -- device might not work\n");
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (!p_dev->function_config) {
5488c2ecf20Sopenharmony_ci		config_t *c;
5498c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "creating config_t\n");
5508c2ecf20Sopenharmony_ci		c = kzalloc(sizeof(struct config_t), GFP_KERNEL);
5518c2ecf20Sopenharmony_ci		if (!c) {
5528c2ecf20Sopenharmony_ci			mutex_unlock(&s->ops_mutex);
5538c2ecf20Sopenharmony_ci			goto err_unreg;
5548c2ecf20Sopenharmony_ci		}
5558c2ecf20Sopenharmony_ci		p_dev->function_config = c;
5568c2ecf20Sopenharmony_ci		kref_init(&c->ref);
5578c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_IO_WIN; i++) {
5588c2ecf20Sopenharmony_ci			c->io[i].name = p_dev->devname;
5598c2ecf20Sopenharmony_ci			c->io[i].flags = IORESOURCE_IO;
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_WIN; i++) {
5628c2ecf20Sopenharmony_ci			c->mem[i].name = p_dev->devname;
5638c2ecf20Sopenharmony_ci			c->mem[i].flags = IORESOURCE_MEM;
5648c2ecf20Sopenharmony_ci		}
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_IO_WIN; i++)
5678c2ecf20Sopenharmony_ci		p_dev->resource[i] = &p_dev->function_config->io[i];
5688c2ecf20Sopenharmony_ci	for (; i < (MAX_IO_WIN + MAX_WIN); i++)
5698c2ecf20Sopenharmony_ci		p_dev->resource[i] = &p_dev->function_config->mem[i-MAX_IO_WIN];
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	dev_notice(&p_dev->dev, "pcmcia: registering new device %s (IRQ: %d)\n",
5748c2ecf20Sopenharmony_ci		   p_dev->devname, p_dev->irq);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	pcmcia_device_query(p_dev);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	dev_set_name(&p_dev->dev, "%d.%d", p_dev->socket->sock, p_dev->device_no);
5798c2ecf20Sopenharmony_ci	if (device_register(&p_dev->dev)) {
5808c2ecf20Sopenharmony_ci		mutex_lock(&s->ops_mutex);
5818c2ecf20Sopenharmony_ci		list_del(&p_dev->socket_device_list);
5828c2ecf20Sopenharmony_ci		s->device_count--;
5838c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
5848c2ecf20Sopenharmony_ci		put_device(&p_dev->dev);
5858c2ecf20Sopenharmony_ci		return NULL;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	return p_dev;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci err_unreg:
5918c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
5928c2ecf20Sopenharmony_ci	list_del(&p_dev->socket_device_list);
5938c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci err_free:
5968c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
5978c2ecf20Sopenharmony_ci	s->device_count--;
5988c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++)
6018c2ecf20Sopenharmony_ci		kfree(p_dev->prod_id[i]);
6028c2ecf20Sopenharmony_ci	kfree(p_dev->devname);
6038c2ecf20Sopenharmony_ci	kfree(p_dev);
6048c2ecf20Sopenharmony_ci err_put:
6058c2ecf20Sopenharmony_ci	pcmcia_put_socket(s);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	return NULL;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_cistatic int pcmcia_card_add(struct pcmcia_socket *s)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	cistpl_longlink_mfc_t mfc;
6148c2ecf20Sopenharmony_ci	unsigned int no_funcs, i, no_chains;
6158c2ecf20Sopenharmony_ci	int ret = -EAGAIN;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
6188c2ecf20Sopenharmony_ci	if (!(s->resource_setup_done)) {
6198c2ecf20Sopenharmony_ci		dev_dbg(&s->dev,
6208c2ecf20Sopenharmony_ci			   "no resources available, delaying card_add\n");
6218c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
6228c2ecf20Sopenharmony_ci		return -EAGAIN; /* try again, but later... */
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	if (pcmcia_validate_mem(s)) {
6268c2ecf20Sopenharmony_ci		dev_dbg(&s->dev, "validating mem resources failed, "
6278c2ecf20Sopenharmony_ci		       "delaying card_add\n");
6288c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
6298c2ecf20Sopenharmony_ci		return -EAGAIN; /* try again, but later... */
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	ret = pccard_validate_cis(s, &no_chains);
6348c2ecf20Sopenharmony_ci	if (ret || !no_chains) {
6358c2ecf20Sopenharmony_ci#if defined(CONFIG_MTD_PCMCIA_ANONYMOUS)
6368c2ecf20Sopenharmony_ci		/* Set up as an anonymous card. If we don't have anonymous
6378c2ecf20Sopenharmony_ci		   memory support then just error the card as there is no
6388c2ecf20Sopenharmony_ci		   point trying to second guess.
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci		   Note: some cards have just a device entry, it may be
6418c2ecf20Sopenharmony_ci		   worth extending support to cover these in future */
6428c2ecf20Sopenharmony_ci		if (ret == -EIO) {
6438c2ecf20Sopenharmony_ci			dev_info(&s->dev, "no CIS, assuming an anonymous memory card.\n");
6448c2ecf20Sopenharmony_ci			pcmcia_replace_cis(s, "\xFF", 1);
6458c2ecf20Sopenharmony_ci			no_chains = 1;
6468c2ecf20Sopenharmony_ci			ret = 0;
6478c2ecf20Sopenharmony_ci		} else
6488c2ecf20Sopenharmony_ci#endif
6498c2ecf20Sopenharmony_ci		{
6508c2ecf20Sopenharmony_ci			dev_dbg(&s->dev, "invalid CIS or invalid resources\n");
6518c2ecf20Sopenharmony_ci			return -ENODEV;
6528c2ecf20Sopenharmony_ci		}
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc))
6568c2ecf20Sopenharmony_ci		no_funcs = mfc.nfn;
6578c2ecf20Sopenharmony_ci	else
6588c2ecf20Sopenharmony_ci		no_funcs = 1;
6598c2ecf20Sopenharmony_ci	s->functions = no_funcs;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	for (i = 0; i < no_funcs; i++)
6628c2ecf20Sopenharmony_ci		pcmcia_device_add(s, i);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	return ret;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic int pcmcia_requery_callback(struct device *dev, void *_data)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
6718c2ecf20Sopenharmony_ci	if (!p_dev->dev.driver) {
6728c2ecf20Sopenharmony_ci		dev_dbg(dev, "update device information\n");
6738c2ecf20Sopenharmony_ci		pcmcia_device_query(p_dev);
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	return 0;
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cistatic void pcmcia_requery(struct pcmcia_socket *s)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	int has_pfc;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT))
6858c2ecf20Sopenharmony_ci		return;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	if (s->functions == 0) {
6888c2ecf20Sopenharmony_ci		pcmcia_card_add(s);
6898c2ecf20Sopenharmony_ci		return;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	/* some device information might have changed because of a CIS
6938c2ecf20Sopenharmony_ci	 * update or because we can finally read it correctly... so
6948c2ecf20Sopenharmony_ci	 * determine it again, overwriting old values if necessary. */
6958c2ecf20Sopenharmony_ci	bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery_callback);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* if the CIS changed, we need to check whether the number of
6988c2ecf20Sopenharmony_ci	 * functions changed. */
6998c2ecf20Sopenharmony_ci	if (s->fake_cis) {
7008c2ecf20Sopenharmony_ci		int old_funcs, new_funcs;
7018c2ecf20Sopenharmony_ci		cistpl_longlink_mfc_t mfc;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci		/* does this cis override add or remove functions? */
7048c2ecf20Sopenharmony_ci		old_funcs = s->functions;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci		if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC,
7078c2ecf20Sopenharmony_ci					&mfc))
7088c2ecf20Sopenharmony_ci			new_funcs = mfc.nfn;
7098c2ecf20Sopenharmony_ci		else
7108c2ecf20Sopenharmony_ci			new_funcs = 1;
7118c2ecf20Sopenharmony_ci		if (old_funcs != new_funcs) {
7128c2ecf20Sopenharmony_ci			/* we need to re-start */
7138c2ecf20Sopenharmony_ci			pcmcia_card_remove(s, NULL);
7148c2ecf20Sopenharmony_ci			s->functions = 0;
7158c2ecf20Sopenharmony_ci			pcmcia_card_add(s);
7168c2ecf20Sopenharmony_ci		}
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	/* If the PCMCIA device consists of two pseudo devices,
7208c2ecf20Sopenharmony_ci	 * call pcmcia_device_add() -- which will fail if both
7218c2ecf20Sopenharmony_ci	 * devices are already registered. */
7228c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
7238c2ecf20Sopenharmony_ci	has_pfc = s->pcmcia_pfc;
7248c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
7258c2ecf20Sopenharmony_ci	if (has_pfc)
7268c2ecf20Sopenharmony_ci		pcmcia_device_add(s, 0);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/* we re-scan all devices, not just the ones connected to this
7298c2ecf20Sopenharmony_ci	 * socket. This does not matter, though. */
7308c2ecf20Sopenharmony_ci	if (bus_rescan_devices(&pcmcia_bus_type))
7318c2ecf20Sopenharmony_ci		dev_warn(&s->dev, "rescanning the bus failed\n");
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci#ifdef CONFIG_PCMCIA_LOAD_CIS
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci/**
7388c2ecf20Sopenharmony_ci * pcmcia_load_firmware - load CIS from userspace if device-provided is broken
7398c2ecf20Sopenharmony_ci * @dev: the pcmcia device which needs a CIS override
7408c2ecf20Sopenharmony_ci * @filename: requested filename in /lib/firmware/
7418c2ecf20Sopenharmony_ci *
7428c2ecf20Sopenharmony_ci * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if
7438c2ecf20Sopenharmony_ci * the one provided by the card is broken. The firmware files reside in
7448c2ecf20Sopenharmony_ci * /lib/firmware/ in userspace.
7458c2ecf20Sopenharmony_ci */
7468c2ecf20Sopenharmony_cistatic int pcmcia_load_firmware(struct pcmcia_device *dev, char *filename)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = dev->socket;
7498c2ecf20Sopenharmony_ci	const struct firmware *fw;
7508c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
7518c2ecf20Sopenharmony_ci	cistpl_longlink_mfc_t mfc;
7528c2ecf20Sopenharmony_ci	int old_funcs, new_funcs = 1;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	if (!filename)
7558c2ecf20Sopenharmony_ci		return -EINVAL;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "trying to load CIS file %s\n", filename);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (request_firmware(&fw, filename, &dev->dev) == 0) {
7608c2ecf20Sopenharmony_ci		if (fw->size >= CISTPL_MAX_CIS_SIZE) {
7618c2ecf20Sopenharmony_ci			ret = -EINVAL;
7628c2ecf20Sopenharmony_ci			dev_err(&dev->dev, "pcmcia: CIS override is too big\n");
7638c2ecf20Sopenharmony_ci			goto release;
7648c2ecf20Sopenharmony_ci		}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci		if (!pcmcia_replace_cis(s, fw->data, fw->size))
7678c2ecf20Sopenharmony_ci			ret = 0;
7688c2ecf20Sopenharmony_ci		else {
7698c2ecf20Sopenharmony_ci			dev_err(&dev->dev, "pcmcia: CIS override failed\n");
7708c2ecf20Sopenharmony_ci			goto release;
7718c2ecf20Sopenharmony_ci		}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci		/* we need to re-start if the number of functions changed */
7748c2ecf20Sopenharmony_ci		old_funcs = s->functions;
7758c2ecf20Sopenharmony_ci		if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC,
7768c2ecf20Sopenharmony_ci					&mfc))
7778c2ecf20Sopenharmony_ci			new_funcs = mfc.nfn;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci		if (old_funcs != new_funcs)
7808c2ecf20Sopenharmony_ci			ret = -EBUSY;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		/* update information */
7838c2ecf20Sopenharmony_ci		pcmcia_device_query(dev);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		/* requery (as number of functions might have changed) */
7868c2ecf20Sopenharmony_ci		pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci release:
7898c2ecf20Sopenharmony_ci	release_firmware(fw);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	return ret;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci#else /* !CONFIG_PCMCIA_LOAD_CIS */
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_cistatic inline int pcmcia_load_firmware(struct pcmcia_device *dev,
7978c2ecf20Sopenharmony_ci				       char *filename)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	return -ENODEV;
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci#endif
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_cistatic inline int pcmcia_devmatch(struct pcmcia_device *dev,
8068c2ecf20Sopenharmony_ci				  const struct pcmcia_device_id *did)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID) {
8098c2ecf20Sopenharmony_ci		if ((!dev->has_manf_id) || (dev->manf_id != did->manf_id))
8108c2ecf20Sopenharmony_ci			return 0;
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID) {
8148c2ecf20Sopenharmony_ci		if ((!dev->has_card_id) || (dev->card_id != did->card_id))
8158c2ecf20Sopenharmony_ci			return 0;
8168c2ecf20Sopenharmony_ci	}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION) {
8198c2ecf20Sopenharmony_ci		if (dev->func != did->function)
8208c2ecf20Sopenharmony_ci			return 0;
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1) {
8248c2ecf20Sopenharmony_ci		if (!dev->prod_id[0])
8258c2ecf20Sopenharmony_ci			return 0;
8268c2ecf20Sopenharmony_ci		if (strcmp(did->prod_id[0], dev->prod_id[0]))
8278c2ecf20Sopenharmony_ci			return 0;
8288c2ecf20Sopenharmony_ci	}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2) {
8318c2ecf20Sopenharmony_ci		if (!dev->prod_id[1])
8328c2ecf20Sopenharmony_ci			return 0;
8338c2ecf20Sopenharmony_ci		if (strcmp(did->prod_id[1], dev->prod_id[1]))
8348c2ecf20Sopenharmony_ci			return 0;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3) {
8388c2ecf20Sopenharmony_ci		if (!dev->prod_id[2])
8398c2ecf20Sopenharmony_ci			return 0;
8408c2ecf20Sopenharmony_ci		if (strcmp(did->prod_id[2], dev->prod_id[2]))
8418c2ecf20Sopenharmony_ci			return 0;
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4) {
8458c2ecf20Sopenharmony_ci		if (!dev->prod_id[3])
8468c2ecf20Sopenharmony_ci			return 0;
8478c2ecf20Sopenharmony_ci		if (strcmp(did->prod_id[3], dev->prod_id[3]))
8488c2ecf20Sopenharmony_ci			return 0;
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) {
8528c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "this is a pseudo-multi-function device\n");
8538c2ecf20Sopenharmony_ci		mutex_lock(&dev->socket->ops_mutex);
8548c2ecf20Sopenharmony_ci		dev->socket->pcmcia_pfc = 1;
8558c2ecf20Sopenharmony_ci		mutex_unlock(&dev->socket->ops_mutex);
8568c2ecf20Sopenharmony_ci		if (dev->device_no != did->device_no)
8578c2ecf20Sopenharmony_ci			return 0;
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) {
8618c2ecf20Sopenharmony_ci		int ret;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci		if ((!dev->has_func_id) || (dev->func_id != did->func_id))
8648c2ecf20Sopenharmony_ci			return 0;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci		/* if this is a pseudo-multi-function device,
8678c2ecf20Sopenharmony_ci		 * we need explicit matches */
8688c2ecf20Sopenharmony_ci		if (dev->socket->pcmcia_pfc)
8698c2ecf20Sopenharmony_ci			return 0;
8708c2ecf20Sopenharmony_ci		if (dev->device_no)
8718c2ecf20Sopenharmony_ci			return 0;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci		/* also, FUNC_ID matching needs to be activated by userspace
8748c2ecf20Sopenharmony_ci		 * after it has re-checked that there is no possible module
8758c2ecf20Sopenharmony_ci		 * with a prod_id/manf_id/card_id match.
8768c2ecf20Sopenharmony_ci		 */
8778c2ecf20Sopenharmony_ci		mutex_lock(&dev->socket->ops_mutex);
8788c2ecf20Sopenharmony_ci		ret = dev->allow_func_id_match;
8798c2ecf20Sopenharmony_ci		mutex_unlock(&dev->socket->ops_mutex);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci		if (!ret) {
8828c2ecf20Sopenharmony_ci			dev_dbg(&dev->dev,
8838c2ecf20Sopenharmony_ci				"skipping FUNC_ID match until userspace ACK\n");
8848c2ecf20Sopenharmony_ci			return 0;
8858c2ecf20Sopenharmony_ci		}
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
8898c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "device needs a fake CIS\n");
8908c2ecf20Sopenharmony_ci		if (!dev->socket->fake_cis)
8918c2ecf20Sopenharmony_ci			if (pcmcia_load_firmware(dev, did->cisfile))
8928c2ecf20Sopenharmony_ci				return 0;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) {
8968c2ecf20Sopenharmony_ci		int i;
8978c2ecf20Sopenharmony_ci		for (i = 0; i < 4; i++)
8988c2ecf20Sopenharmony_ci			if (dev->prod_id[i])
8998c2ecf20Sopenharmony_ci				return 0;
9008c2ecf20Sopenharmony_ci		if (dev->has_manf_id || dev->has_card_id || dev->has_func_id)
9018c2ecf20Sopenharmony_ci			return 0;
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	return 1;
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic int pcmcia_bus_match(struct device *dev, struct device_driver *drv)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
9118c2ecf20Sopenharmony_ci	struct pcmcia_driver *p_drv = to_pcmcia_drv(drv);
9128c2ecf20Sopenharmony_ci	const struct pcmcia_device_id *did = p_drv->id_table;
9138c2ecf20Sopenharmony_ci	struct pcmcia_dynid *dynid;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	/* match dynamic devices first */
9168c2ecf20Sopenharmony_ci	mutex_lock(&p_drv->dynids.lock);
9178c2ecf20Sopenharmony_ci	list_for_each_entry(dynid, &p_drv->dynids.list, node) {
9188c2ecf20Sopenharmony_ci		dev_dbg(dev, "trying to match to %s\n", drv->name);
9198c2ecf20Sopenharmony_ci		if (pcmcia_devmatch(p_dev, &dynid->id)) {
9208c2ecf20Sopenharmony_ci			dev_dbg(dev, "matched to %s\n", drv->name);
9218c2ecf20Sopenharmony_ci			mutex_unlock(&p_drv->dynids.lock);
9228c2ecf20Sopenharmony_ci			return 1;
9238c2ecf20Sopenharmony_ci		}
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci	mutex_unlock(&p_drv->dynids.lock);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	while (did && did->match_flags) {
9288c2ecf20Sopenharmony_ci		dev_dbg(dev, "trying to match to %s\n", drv->name);
9298c2ecf20Sopenharmony_ci		if (pcmcia_devmatch(p_dev, did)) {
9308c2ecf20Sopenharmony_ci			dev_dbg(dev, "matched to %s\n", drv->name);
9318c2ecf20Sopenharmony_ci			return 1;
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci		did++;
9348c2ecf20Sopenharmony_ci	}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	return 0;
9378c2ecf20Sopenharmony_ci}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_cistatic int pcmcia_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
9408c2ecf20Sopenharmony_ci{
9418c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev;
9428c2ecf20Sopenharmony_ci	int i;
9438c2ecf20Sopenharmony_ci	u32 hash[4] = { 0, 0, 0, 0};
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	if (!dev)
9468c2ecf20Sopenharmony_ci		return -ENODEV;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	p_dev = to_pcmcia_dev(dev);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	/* calculate hashes */
9518c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
9528c2ecf20Sopenharmony_ci		if (!p_dev->prod_id[i])
9538c2ecf20Sopenharmony_ci			continue;
9548c2ecf20Sopenharmony_ci		hash[i] = crc32(0, p_dev->prod_id[i], strlen(p_dev->prod_id[i]));
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (add_uevent_var(env, "SOCKET_NO=%u", p_dev->socket->sock))
9588c2ecf20Sopenharmony_ci		return -ENOMEM;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	if (add_uevent_var(env, "DEVICE_NO=%02X", p_dev->device_no))
9618c2ecf20Sopenharmony_ci		return -ENOMEM;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	if (add_uevent_var(env, "MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X"
9648c2ecf20Sopenharmony_ci			   "pa%08Xpb%08Xpc%08Xpd%08X",
9658c2ecf20Sopenharmony_ci			   p_dev->has_manf_id ? p_dev->manf_id : 0,
9668c2ecf20Sopenharmony_ci			   p_dev->has_card_id ? p_dev->card_id : 0,
9678c2ecf20Sopenharmony_ci			   p_dev->has_func_id ? p_dev->func_id : 0,
9688c2ecf20Sopenharmony_ci			   p_dev->func,
9698c2ecf20Sopenharmony_ci			   p_dev->device_no,
9708c2ecf20Sopenharmony_ci			   hash[0],
9718c2ecf20Sopenharmony_ci			   hash[1],
9728c2ecf20Sopenharmony_ci			   hash[2],
9738c2ecf20Sopenharmony_ci			   hash[3]))
9748c2ecf20Sopenharmony_ci		return -ENOMEM;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	return 0;
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci/************************ runtime PM support ***************************/
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_cistatic int pcmcia_dev_suspend(struct device *dev);
9828c2ecf20Sopenharmony_cistatic int pcmcia_dev_resume(struct device *dev);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_cistatic int runtime_suspend(struct device *dev)
9858c2ecf20Sopenharmony_ci{
9868c2ecf20Sopenharmony_ci	int rc;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	device_lock(dev);
9898c2ecf20Sopenharmony_ci	rc = pcmcia_dev_suspend(dev);
9908c2ecf20Sopenharmony_ci	device_unlock(dev);
9918c2ecf20Sopenharmony_ci	return rc;
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic int runtime_resume(struct device *dev)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	int rc;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	device_lock(dev);
9998c2ecf20Sopenharmony_ci	rc = pcmcia_dev_resume(dev);
10008c2ecf20Sopenharmony_ci	device_unlock(dev);
10018c2ecf20Sopenharmony_ci	return rc;
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci/************************ per-device sysfs output ***************************/
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci#define pcmcia_device_attr(field, test, format)				\
10078c2ecf20Sopenharmony_cistatic ssize_t field##_show (struct device *dev, struct device_attribute *attr, char *buf)		\
10088c2ecf20Sopenharmony_ci{									\
10098c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);		\
10108c2ecf20Sopenharmony_ci	return p_dev->test ? sprintf(buf, format, p_dev->field) : -ENODEV; \
10118c2ecf20Sopenharmony_ci}									\
10128c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(field);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci#define pcmcia_device_stringattr(name, field)					\
10158c2ecf20Sopenharmony_cistatic ssize_t name##_show (struct device *dev, struct device_attribute *attr, char *buf)		\
10168c2ecf20Sopenharmony_ci{									\
10178c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);		\
10188c2ecf20Sopenharmony_ci	return p_dev->field ? sprintf(buf, "%s\n", p_dev->field) : -ENODEV; \
10198c2ecf20Sopenharmony_ci}									\
10208c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cipcmcia_device_attr(func_id, has_func_id, "0x%02x\n");
10238c2ecf20Sopenharmony_cipcmcia_device_attr(manf_id, has_manf_id, "0x%04x\n");
10248c2ecf20Sopenharmony_cipcmcia_device_attr(card_id, has_card_id, "0x%04x\n");
10258c2ecf20Sopenharmony_cipcmcia_device_stringattr(prod_id1, prod_id[0]);
10268c2ecf20Sopenharmony_cipcmcia_device_stringattr(prod_id2, prod_id[1]);
10278c2ecf20Sopenharmony_cipcmcia_device_stringattr(prod_id3, prod_id[2]);
10288c2ecf20Sopenharmony_cipcmcia_device_stringattr(prod_id4, prod_id[3]);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_cistatic ssize_t function_show(struct device *dev, struct device_attribute *attr,
10318c2ecf20Sopenharmony_ci			     char *buf)
10328c2ecf20Sopenharmony_ci{
10338c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
10348c2ecf20Sopenharmony_ci	return p_dev->socket ? sprintf(buf, "0x%02x\n", p_dev->func) : -ENODEV;
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(function);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_cistatic ssize_t resources_show(struct device *dev,
10398c2ecf20Sopenharmony_ci			      struct device_attribute *attr, char *buf)
10408c2ecf20Sopenharmony_ci{
10418c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
10428c2ecf20Sopenharmony_ci	char *str = buf;
10438c2ecf20Sopenharmony_ci	int i;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	for (i = 0; i < PCMCIA_NUM_RESOURCES; i++)
10468c2ecf20Sopenharmony_ci		str += sprintf(str, "%pr\n", p_dev->resource[i]);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	return str - buf;
10498c2ecf20Sopenharmony_ci}
10508c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(resources);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_cistatic ssize_t pm_state_show(struct device *dev, struct device_attribute *attr, char *buf)
10538c2ecf20Sopenharmony_ci{
10548c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	if (p_dev->suspended)
10578c2ecf20Sopenharmony_ci		return sprintf(buf, "off\n");
10588c2ecf20Sopenharmony_ci	else
10598c2ecf20Sopenharmony_ci		return sprintf(buf, "on\n");
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_cistatic ssize_t pm_state_store(struct device *dev, struct device_attribute *attr,
10638c2ecf20Sopenharmony_ci			      const char *buf, size_t count)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
10668c2ecf20Sopenharmony_ci	int ret = 0;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	if (!count)
10698c2ecf20Sopenharmony_ci		return -EINVAL;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	if ((!p_dev->suspended) && !strncmp(buf, "off", 3))
10728c2ecf20Sopenharmony_ci		ret = runtime_suspend(dev);
10738c2ecf20Sopenharmony_ci	else if (p_dev->suspended && !strncmp(buf, "on", 2))
10748c2ecf20Sopenharmony_ci		ret = runtime_resume(dev);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	return ret ? ret : count;
10778c2ecf20Sopenharmony_ci}
10788c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(pm_state);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
10818c2ecf20Sopenharmony_ci{
10828c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
10838c2ecf20Sopenharmony_ci	int i;
10848c2ecf20Sopenharmony_ci	u32 hash[4] = { 0, 0, 0, 0};
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	/* calculate hashes */
10878c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
10888c2ecf20Sopenharmony_ci		if (!p_dev->prod_id[i])
10898c2ecf20Sopenharmony_ci			continue;
10908c2ecf20Sopenharmony_ci		hash[i] = crc32(0, p_dev->prod_id[i],
10918c2ecf20Sopenharmony_ci				strlen(p_dev->prod_id[i]));
10928c2ecf20Sopenharmony_ci	}
10938c2ecf20Sopenharmony_ci	return sprintf(buf, "pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X"
10948c2ecf20Sopenharmony_ci				"pa%08Xpb%08Xpc%08Xpd%08X\n",
10958c2ecf20Sopenharmony_ci				p_dev->has_manf_id ? p_dev->manf_id : 0,
10968c2ecf20Sopenharmony_ci				p_dev->has_card_id ? p_dev->card_id : 0,
10978c2ecf20Sopenharmony_ci				p_dev->has_func_id ? p_dev->func_id : 0,
10988c2ecf20Sopenharmony_ci				p_dev->func, p_dev->device_no,
10998c2ecf20Sopenharmony_ci				hash[0], hash[1], hash[2], hash[3]);
11008c2ecf20Sopenharmony_ci}
11018c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_cistatic ssize_t allow_func_id_match_store(struct device *dev,
11048c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
11058c2ecf20Sopenharmony_ci{
11068c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	if (!count)
11098c2ecf20Sopenharmony_ci		return -EINVAL;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	mutex_lock(&p_dev->socket->ops_mutex);
11128c2ecf20Sopenharmony_ci	p_dev->allow_func_id_match = 1;
11138c2ecf20Sopenharmony_ci	mutex_unlock(&p_dev->socket->ops_mutex);
11148c2ecf20Sopenharmony_ci	pcmcia_parse_uevents(p_dev->socket, PCMCIA_UEVENT_REQUERY);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	return count;
11178c2ecf20Sopenharmony_ci}
11188c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(allow_func_id_match);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cistatic struct attribute *pcmcia_dev_attrs[] = {
11218c2ecf20Sopenharmony_ci	&dev_attr_resources.attr,
11228c2ecf20Sopenharmony_ci	&dev_attr_pm_state.attr,
11238c2ecf20Sopenharmony_ci	&dev_attr_function.attr,
11248c2ecf20Sopenharmony_ci	&dev_attr_func_id.attr,
11258c2ecf20Sopenharmony_ci	&dev_attr_manf_id.attr,
11268c2ecf20Sopenharmony_ci	&dev_attr_card_id.attr,
11278c2ecf20Sopenharmony_ci	&dev_attr_prod_id1.attr,
11288c2ecf20Sopenharmony_ci	&dev_attr_prod_id2.attr,
11298c2ecf20Sopenharmony_ci	&dev_attr_prod_id3.attr,
11308c2ecf20Sopenharmony_ci	&dev_attr_prod_id4.attr,
11318c2ecf20Sopenharmony_ci	&dev_attr_modalias.attr,
11328c2ecf20Sopenharmony_ci	&dev_attr_allow_func_id_match.attr,
11338c2ecf20Sopenharmony_ci	NULL,
11348c2ecf20Sopenharmony_ci};
11358c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(pcmcia_dev);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci/* PM support, also needed for reset */
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_cistatic int pcmcia_dev_suspend(struct device *dev)
11408c2ecf20Sopenharmony_ci{
11418c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
11428c2ecf20Sopenharmony_ci	struct pcmcia_driver *p_drv = NULL;
11438c2ecf20Sopenharmony_ci	int ret = 0;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	mutex_lock(&p_dev->socket->ops_mutex);
11468c2ecf20Sopenharmony_ci	if (p_dev->suspended) {
11478c2ecf20Sopenharmony_ci		mutex_unlock(&p_dev->socket->ops_mutex);
11488c2ecf20Sopenharmony_ci		return 0;
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci	p_dev->suspended = 1;
11518c2ecf20Sopenharmony_ci	mutex_unlock(&p_dev->socket->ops_mutex);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	dev_dbg(dev, "suspending\n");
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	if (dev->driver)
11568c2ecf20Sopenharmony_ci		p_drv = to_pcmcia_drv(dev->driver);
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	if (!p_drv)
11598c2ecf20Sopenharmony_ci		goto out;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	if (p_drv->suspend) {
11628c2ecf20Sopenharmony_ci		ret = p_drv->suspend(p_dev);
11638c2ecf20Sopenharmony_ci		if (ret) {
11648c2ecf20Sopenharmony_ci			dev_err(dev,
11658c2ecf20Sopenharmony_ci				"pcmcia: device %s (driver %s) did not want to go to sleep (%d)\n",
11668c2ecf20Sopenharmony_ci				p_dev->devname, p_drv->name, ret);
11678c2ecf20Sopenharmony_ci			mutex_lock(&p_dev->socket->ops_mutex);
11688c2ecf20Sopenharmony_ci			p_dev->suspended = 0;
11698c2ecf20Sopenharmony_ci			mutex_unlock(&p_dev->socket->ops_mutex);
11708c2ecf20Sopenharmony_ci			goto out;
11718c2ecf20Sopenharmony_ci		}
11728c2ecf20Sopenharmony_ci	}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	if (p_dev->device_no == p_dev->func) {
11758c2ecf20Sopenharmony_ci		dev_dbg(dev, "releasing configuration\n");
11768c2ecf20Sopenharmony_ci		pcmcia_release_configuration(p_dev);
11778c2ecf20Sopenharmony_ci	}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci out:
11808c2ecf20Sopenharmony_ci	return ret;
11818c2ecf20Sopenharmony_ci}
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_cistatic int pcmcia_dev_resume(struct device *dev)
11858c2ecf20Sopenharmony_ci{
11868c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
11878c2ecf20Sopenharmony_ci	struct pcmcia_driver *p_drv = NULL;
11888c2ecf20Sopenharmony_ci	int ret = 0;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	mutex_lock(&p_dev->socket->ops_mutex);
11918c2ecf20Sopenharmony_ci	if (!p_dev->suspended) {
11928c2ecf20Sopenharmony_ci		mutex_unlock(&p_dev->socket->ops_mutex);
11938c2ecf20Sopenharmony_ci		return 0;
11948c2ecf20Sopenharmony_ci	}
11958c2ecf20Sopenharmony_ci	p_dev->suspended = 0;
11968c2ecf20Sopenharmony_ci	mutex_unlock(&p_dev->socket->ops_mutex);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	dev_dbg(dev, "resuming\n");
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	if (dev->driver)
12018c2ecf20Sopenharmony_ci		p_drv = to_pcmcia_drv(dev->driver);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	if (!p_drv)
12048c2ecf20Sopenharmony_ci		goto out;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	if (p_dev->device_no == p_dev->func) {
12078c2ecf20Sopenharmony_ci		dev_dbg(dev, "requesting configuration\n");
12088c2ecf20Sopenharmony_ci		ret = pcmcia_enable_device(p_dev);
12098c2ecf20Sopenharmony_ci		if (ret)
12108c2ecf20Sopenharmony_ci			goto out;
12118c2ecf20Sopenharmony_ci	}
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	if (p_drv->resume)
12148c2ecf20Sopenharmony_ci		ret = p_drv->resume(p_dev);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci out:
12178c2ecf20Sopenharmony_ci	return ret;
12188c2ecf20Sopenharmony_ci}
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_cistatic int pcmcia_bus_suspend_callback(struct device *dev, void *_data)
12228c2ecf20Sopenharmony_ci{
12238c2ecf20Sopenharmony_ci	struct pcmcia_socket *skt = _data;
12248c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	if (p_dev->socket != skt || p_dev->suspended)
12278c2ecf20Sopenharmony_ci		return 0;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	return runtime_suspend(dev);
12308c2ecf20Sopenharmony_ci}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_cistatic int pcmcia_bus_resume_callback(struct device *dev, void *_data)
12338c2ecf20Sopenharmony_ci{
12348c2ecf20Sopenharmony_ci	struct pcmcia_socket *skt = _data;
12358c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	if (p_dev->socket != skt || !p_dev->suspended)
12388c2ecf20Sopenharmony_ci		return 0;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	runtime_resume(dev);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	return 0;
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic int pcmcia_bus_resume(struct pcmcia_socket *skt)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	dev_dbg(&skt->dev, "resuming socket %d\n", skt->sock);
12488c2ecf20Sopenharmony_ci	bus_for_each_dev(&pcmcia_bus_type, NULL, skt, pcmcia_bus_resume_callback);
12498c2ecf20Sopenharmony_ci	return 0;
12508c2ecf20Sopenharmony_ci}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_cistatic int pcmcia_bus_suspend(struct pcmcia_socket *skt)
12538c2ecf20Sopenharmony_ci{
12548c2ecf20Sopenharmony_ci	dev_dbg(&skt->dev, "suspending socket %d\n", skt->sock);
12558c2ecf20Sopenharmony_ci	if (bus_for_each_dev(&pcmcia_bus_type, NULL, skt,
12568c2ecf20Sopenharmony_ci			     pcmcia_bus_suspend_callback)) {
12578c2ecf20Sopenharmony_ci		pcmcia_bus_resume(skt);
12588c2ecf20Sopenharmony_ci		return -EIO;
12598c2ecf20Sopenharmony_ci	}
12608c2ecf20Sopenharmony_ci	return 0;
12618c2ecf20Sopenharmony_ci}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_cistatic int pcmcia_bus_remove(struct pcmcia_socket *skt)
12648c2ecf20Sopenharmony_ci{
12658c2ecf20Sopenharmony_ci	atomic_set(&skt->present, 0);
12668c2ecf20Sopenharmony_ci	pcmcia_card_remove(skt, NULL);
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	mutex_lock(&skt->ops_mutex);
12698c2ecf20Sopenharmony_ci	destroy_cis_cache(skt);
12708c2ecf20Sopenharmony_ci	pcmcia_cleanup_irq(skt);
12718c2ecf20Sopenharmony_ci	mutex_unlock(&skt->ops_mutex);
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	return 0;
12748c2ecf20Sopenharmony_ci}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_cistatic int pcmcia_bus_add(struct pcmcia_socket *skt)
12778c2ecf20Sopenharmony_ci{
12788c2ecf20Sopenharmony_ci	atomic_set(&skt->present, 1);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	mutex_lock(&skt->ops_mutex);
12818c2ecf20Sopenharmony_ci	skt->pcmcia_pfc = 0;
12828c2ecf20Sopenharmony_ci	destroy_cis_cache(skt); /* to be on the safe side... */
12838c2ecf20Sopenharmony_ci	mutex_unlock(&skt->ops_mutex);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	pcmcia_card_add(skt);
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	return 0;
12888c2ecf20Sopenharmony_ci}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_cistatic int pcmcia_bus_early_resume(struct pcmcia_socket *skt)
12918c2ecf20Sopenharmony_ci{
12928c2ecf20Sopenharmony_ci	if (!verify_cis_cache(skt))
12938c2ecf20Sopenharmony_ci		return 0;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	dev_dbg(&skt->dev, "cis mismatch - different card\n");
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	/* first, remove the card */
12988c2ecf20Sopenharmony_ci	pcmcia_bus_remove(skt);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	mutex_lock(&skt->ops_mutex);
13018c2ecf20Sopenharmony_ci	destroy_cis_cache(skt);
13028c2ecf20Sopenharmony_ci	kfree(skt->fake_cis);
13038c2ecf20Sopenharmony_ci	skt->fake_cis = NULL;
13048c2ecf20Sopenharmony_ci	skt->functions = 0;
13058c2ecf20Sopenharmony_ci	mutex_unlock(&skt->ops_mutex);
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	/* now, add the new card */
13088c2ecf20Sopenharmony_ci	pcmcia_bus_add(skt);
13098c2ecf20Sopenharmony_ci	return 0;
13108c2ecf20Sopenharmony_ci}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci/*
13148c2ecf20Sopenharmony_ci * NOTE: This is racy. There's no guarantee the card will still be
13158c2ecf20Sopenharmony_ci * physically present, even if the call to this function returns
13168c2ecf20Sopenharmony_ci * non-NULL. Furthermore, the device driver most likely is unbound
13178c2ecf20Sopenharmony_ci * almost immediately, so the timeframe where pcmcia_dev_present
13188c2ecf20Sopenharmony_ci * returns NULL is probably really really small.
13198c2ecf20Sopenharmony_ci */
13208c2ecf20Sopenharmony_cistruct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
13218c2ecf20Sopenharmony_ci{
13228c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev;
13238c2ecf20Sopenharmony_ci	struct pcmcia_device *ret = NULL;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	p_dev = pcmcia_get_dev(_p_dev);
13268c2ecf20Sopenharmony_ci	if (!p_dev)
13278c2ecf20Sopenharmony_ci		return NULL;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	if (atomic_read(&p_dev->socket->present) != 0)
13308c2ecf20Sopenharmony_ci		ret = p_dev;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	pcmcia_put_dev(p_dev);
13338c2ecf20Sopenharmony_ci	return ret;
13348c2ecf20Sopenharmony_ci}
13358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_dev_present);
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_cistatic struct pcmcia_callback pcmcia_bus_callback = {
13398c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
13408c2ecf20Sopenharmony_ci	.add = pcmcia_bus_add,
13418c2ecf20Sopenharmony_ci	.remove = pcmcia_bus_remove,
13428c2ecf20Sopenharmony_ci	.requery = pcmcia_requery,
13438c2ecf20Sopenharmony_ci	.validate = pccard_validate_cis,
13448c2ecf20Sopenharmony_ci	.suspend = pcmcia_bus_suspend,
13458c2ecf20Sopenharmony_ci	.early_resume = pcmcia_bus_early_resume,
13468c2ecf20Sopenharmony_ci	.resume = pcmcia_bus_resume,
13478c2ecf20Sopenharmony_ci};
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_cistatic int pcmcia_bus_add_socket(struct device *dev,
13508c2ecf20Sopenharmony_ci					   struct class_interface *class_intf)
13518c2ecf20Sopenharmony_ci{
13528c2ecf20Sopenharmony_ci	struct pcmcia_socket *socket = dev_get_drvdata(dev);
13538c2ecf20Sopenharmony_ci	int ret;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	socket = pcmcia_get_socket(socket);
13568c2ecf20Sopenharmony_ci	if (!socket) {
13578c2ecf20Sopenharmony_ci		dev_err(dev, "PCMCIA obtaining reference to socket failed\n");
13588c2ecf20Sopenharmony_ci		return -ENODEV;
13598c2ecf20Sopenharmony_ci	}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr);
13628c2ecf20Sopenharmony_ci	if (ret) {
13638c2ecf20Sopenharmony_ci		dev_err(dev, "PCMCIA registration failed\n");
13648c2ecf20Sopenharmony_ci		pcmcia_put_socket(socket);
13658c2ecf20Sopenharmony_ci		return ret;
13668c2ecf20Sopenharmony_ci	}
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&socket->devices_list);
13698c2ecf20Sopenharmony_ci	socket->pcmcia_pfc = 0;
13708c2ecf20Sopenharmony_ci	socket->device_count = 0;
13718c2ecf20Sopenharmony_ci	atomic_set(&socket->present, 0);
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	ret = pccard_register_pcmcia(socket, &pcmcia_bus_callback);
13748c2ecf20Sopenharmony_ci	if (ret) {
13758c2ecf20Sopenharmony_ci		dev_err(dev, "PCMCIA registration failed\n");
13768c2ecf20Sopenharmony_ci		pcmcia_put_socket(socket);
13778c2ecf20Sopenharmony_ci		return ret;
13788c2ecf20Sopenharmony_ci	}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	return 0;
13818c2ecf20Sopenharmony_ci}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic void pcmcia_bus_remove_socket(struct device *dev,
13848c2ecf20Sopenharmony_ci				     struct class_interface *class_intf)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	struct pcmcia_socket *socket = dev_get_drvdata(dev);
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	if (!socket)
13898c2ecf20Sopenharmony_ci		return;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	pccard_register_pcmcia(socket, NULL);
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	/* unregister any unbound devices */
13948c2ecf20Sopenharmony_ci	mutex_lock(&socket->skt_mutex);
13958c2ecf20Sopenharmony_ci	pcmcia_card_remove(socket, NULL);
13968c2ecf20Sopenharmony_ci	release_cis_mem(socket);
13978c2ecf20Sopenharmony_ci	mutex_unlock(&socket->skt_mutex);
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr);
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	pcmcia_put_socket(socket);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	return;
14048c2ecf20Sopenharmony_ci}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci/* the pcmcia_bus_interface is used to handle pcmcia socket devices */
14088c2ecf20Sopenharmony_cistatic struct class_interface pcmcia_bus_interface __refdata = {
14098c2ecf20Sopenharmony_ci	.class = &pcmcia_socket_class,
14108c2ecf20Sopenharmony_ci	.add_dev = &pcmcia_bus_add_socket,
14118c2ecf20Sopenharmony_ci	.remove_dev = &pcmcia_bus_remove_socket,
14128c2ecf20Sopenharmony_ci};
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_cistatic const struct dev_pm_ops pcmcia_bus_pm_ops = {
14158c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pcmcia_dev_suspend, pcmcia_dev_resume)
14168c2ecf20Sopenharmony_ci};
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_cistruct bus_type pcmcia_bus_type = {
14198c2ecf20Sopenharmony_ci	.name = "pcmcia",
14208c2ecf20Sopenharmony_ci	.uevent = pcmcia_bus_uevent,
14218c2ecf20Sopenharmony_ci	.match = pcmcia_bus_match,
14228c2ecf20Sopenharmony_ci	.dev_groups = pcmcia_dev_groups,
14238c2ecf20Sopenharmony_ci	.probe = pcmcia_device_probe,
14248c2ecf20Sopenharmony_ci	.remove = pcmcia_device_remove,
14258c2ecf20Sopenharmony_ci	.pm = &pcmcia_bus_pm_ops,
14268c2ecf20Sopenharmony_ci};
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_cistatic int __init init_pcmcia_bus(void)
14308c2ecf20Sopenharmony_ci{
14318c2ecf20Sopenharmony_ci	int ret;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	ret = bus_register(&pcmcia_bus_type);
14348c2ecf20Sopenharmony_ci	if (ret < 0) {
14358c2ecf20Sopenharmony_ci		printk(KERN_WARNING "pcmcia: bus_register error: %d\n", ret);
14368c2ecf20Sopenharmony_ci		return ret;
14378c2ecf20Sopenharmony_ci	}
14388c2ecf20Sopenharmony_ci	ret = class_interface_register(&pcmcia_bus_interface);
14398c2ecf20Sopenharmony_ci	if (ret < 0) {
14408c2ecf20Sopenharmony_ci		printk(KERN_WARNING
14418c2ecf20Sopenharmony_ci			"pcmcia: class_interface_register error: %d\n", ret);
14428c2ecf20Sopenharmony_ci		bus_unregister(&pcmcia_bus_type);
14438c2ecf20Sopenharmony_ci		return ret;
14448c2ecf20Sopenharmony_ci	}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	return 0;
14478c2ecf20Sopenharmony_ci}
14488c2ecf20Sopenharmony_cifs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that
14498c2ecf20Sopenharmony_ci			       * pcmcia_socket_class is already registered */
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_cistatic void __exit exit_pcmcia_bus(void)
14538c2ecf20Sopenharmony_ci{
14548c2ecf20Sopenharmony_ci	class_interface_unregister(&pcmcia_bus_interface);
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	bus_unregister(&pcmcia_bus_type);
14578c2ecf20Sopenharmony_ci}
14588c2ecf20Sopenharmony_cimodule_exit(exit_pcmcia_bus);
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ciMODULE_ALIAS("ds");
1462