xref: /openbmc/linux/drivers/pnp/card.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * card.c - contains functions for managing groups of PnP devices
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright 2002 Adam Belay <ambx1@neo.rr.com>
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds #include <linux/module.h>
938f6b38dSRafael J. Wysocki #include <linux/mutex.h>
10e436675fSBjorn Helgaas #include <linux/ctype.h>
111da177e4SLinus Torvalds #include <linux/slab.h>
121da177e4SLinus Torvalds #include <linux/pnp.h>
13e86b19ceSRene Herman #include <linux/dma-mapping.h>
141da177e4SLinus Torvalds #include "base.h"
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds LIST_HEAD(pnp_cards);
17b449f63cSAdrian Bunk static LIST_HEAD(pnp_card_drivers);
181da177e4SLinus Torvalds 
match_card(struct pnp_card_driver * drv,struct pnp_card * card)199dd78466SBjorn Helgaas static const struct pnp_card_device_id *match_card(struct pnp_card_driver *drv,
209dd78466SBjorn Helgaas 						   struct pnp_card *card)
211da177e4SLinus Torvalds {
221da177e4SLinus Torvalds 	const struct pnp_card_device_id *drv_id = drv->id_table;
2307d4e9afSBjorn Helgaas 
241da177e4SLinus Torvalds 	while (*drv_id->id) {
251da177e4SLinus Torvalds 		if (compare_pnp_id(card->id, drv_id->id)) {
261da177e4SLinus Torvalds 			int i = 0;
2707d4e9afSBjorn Helgaas 
281da177e4SLinus Torvalds 			for (;;) {
291da177e4SLinus Torvalds 				int found;
301da177e4SLinus Torvalds 				struct pnp_dev *dev;
3107d4e9afSBjorn Helgaas 
321e0aa9adSBjorn Helgaas 				if (i == PNP_MAX_DEVICES ||
331e0aa9adSBjorn Helgaas 				    !*drv_id->devs[i].id)
341da177e4SLinus Torvalds 					return drv_id;
351da177e4SLinus Torvalds 				found = 0;
361da177e4SLinus Torvalds 				card_for_each_dev(card, dev) {
371e0aa9adSBjorn Helgaas 					if (compare_pnp_id(dev->id,
381e0aa9adSBjorn Helgaas 						   drv_id->devs[i].id)) {
391da177e4SLinus Torvalds 						found = 1;
401da177e4SLinus Torvalds 						break;
411da177e4SLinus Torvalds 					}
421da177e4SLinus Torvalds 				}
431da177e4SLinus Torvalds 				if (!found)
441da177e4SLinus Torvalds 					break;
451da177e4SLinus Torvalds 				i++;
461da177e4SLinus Torvalds 			}
471da177e4SLinus Torvalds 		}
481da177e4SLinus Torvalds 		drv_id++;
491da177e4SLinus Torvalds 	}
501da177e4SLinus Torvalds 	return NULL;
511da177e4SLinus Torvalds }
521da177e4SLinus Torvalds 
card_remove(struct pnp_dev * dev)531da177e4SLinus Torvalds static void card_remove(struct pnp_dev *dev)
541da177e4SLinus Torvalds {
551da177e4SLinus Torvalds 	dev->card_link = NULL;
561da177e4SLinus Torvalds }
571da177e4SLinus Torvalds 
card_remove_first(struct pnp_dev * dev)581da177e4SLinus Torvalds static void card_remove_first(struct pnp_dev *dev)
591da177e4SLinus Torvalds {
601da177e4SLinus Torvalds 	struct pnp_card_driver *drv = to_pnp_card_driver(dev->driver);
6107d4e9afSBjorn Helgaas 
621da177e4SLinus Torvalds 	if (!dev->card || !drv)
631da177e4SLinus Torvalds 		return;
641da177e4SLinus Torvalds 	if (drv->remove)
651da177e4SLinus Torvalds 		drv->remove(dev->card_link);
661da177e4SLinus Torvalds 	drv->link.remove = &card_remove;
671da177e4SLinus Torvalds 	kfree(dev->card_link);
681da177e4SLinus Torvalds 	card_remove(dev);
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
card_probe(struct pnp_card * card,struct pnp_card_driver * drv)711da177e4SLinus Torvalds static int card_probe(struct pnp_card *card, struct pnp_card_driver *drv)
721da177e4SLinus Torvalds {
73a9adb8dbSJesper Juhl 	const struct pnp_card_device_id *id;
74a9adb8dbSJesper Juhl 	struct pnp_card_link *clink;
75a9adb8dbSJesper Juhl 	struct pnp_dev *dev;
76a9adb8dbSJesper Juhl 
77a9adb8dbSJesper Juhl 	if (!drv->probe)
78a9adb8dbSJesper Juhl 		return 0;
79a9adb8dbSJesper Juhl 	id = match_card(drv, card);
80a9adb8dbSJesper Juhl 	if (!id)
81a9adb8dbSJesper Juhl 		return 0;
82a9adb8dbSJesper Juhl 
83b15fc7c2SHeiner Kallweit 	clink = kzalloc(sizeof(*clink), GFP_KERNEL);
841da177e4SLinus Torvalds 	if (!clink)
851da177e4SLinus Torvalds 		return 0;
861da177e4SLinus Torvalds 	clink->card = card;
871da177e4SLinus Torvalds 	clink->driver = drv;
884c98cfefSTakashi Iwai 	clink->pm_state = PMSG_ON;
89a9adb8dbSJesper Juhl 
901da177e4SLinus Torvalds 	if (drv->probe(clink, id) >= 0)
911da177e4SLinus Torvalds 		return 1;
92a9adb8dbSJesper Juhl 
93a9adb8dbSJesper Juhl 	/* Recovery */
941da177e4SLinus Torvalds 	card_for_each_dev(card, dev) {
951da177e4SLinus Torvalds 		if (dev->card_link == clink)
961da177e4SLinus Torvalds 			pnp_release_card_device(dev);
971da177e4SLinus Torvalds 	}
981da177e4SLinus Torvalds 	kfree(clink);
991da177e4SLinus Torvalds 	return 0;
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds /**
1031da177e4SLinus Torvalds  * pnp_add_card_id - adds an EISA id to the specified card
1041da177e4SLinus Torvalds  * @id: pointer to a pnp_id structure
1051da177e4SLinus Torvalds  * @card: pointer to the desired card
1061da177e4SLinus Torvalds  */
pnp_add_card_id(struct pnp_card * card,char * id)10725cdcd00SAdrian Bunk static struct pnp_id *pnp_add_card_id(struct pnp_card *card, char *id)
1081da177e4SLinus Torvalds {
109e436675fSBjorn Helgaas 	struct pnp_id *dev_id, *ptr;
11007d4e9afSBjorn Helgaas 
111e436675fSBjorn Helgaas 	dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
112e436675fSBjorn Helgaas 	if (!dev_id)
113e436675fSBjorn Helgaas 		return NULL;
114e436675fSBjorn Helgaas 
115e436675fSBjorn Helgaas 	dev_id->id[0] = id[0];
116e436675fSBjorn Helgaas 	dev_id->id[1] = id[1];
117e436675fSBjorn Helgaas 	dev_id->id[2] = id[2];
118e436675fSBjorn Helgaas 	dev_id->id[3] = tolower(id[3]);
119e436675fSBjorn Helgaas 	dev_id->id[4] = tolower(id[4]);
120e436675fSBjorn Helgaas 	dev_id->id[5] = tolower(id[5]);
121e436675fSBjorn Helgaas 	dev_id->id[6] = tolower(id[6]);
122e436675fSBjorn Helgaas 	dev_id->id[7] = '\0';
123e436675fSBjorn Helgaas 
124e436675fSBjorn Helgaas 	dev_id->next = NULL;
1251da177e4SLinus Torvalds 	ptr = card->id;
1261da177e4SLinus Torvalds 	while (ptr && ptr->next)
1271da177e4SLinus Torvalds 		ptr = ptr->next;
1281da177e4SLinus Torvalds 	if (ptr)
129e436675fSBjorn Helgaas 		ptr->next = dev_id;
1301da177e4SLinus Torvalds 	else
131e436675fSBjorn Helgaas 		card->id = dev_id;
132e436675fSBjorn Helgaas 
133e436675fSBjorn Helgaas 	return dev_id;
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds 
pnp_free_card_ids(struct pnp_card * card)1361da177e4SLinus Torvalds static void pnp_free_card_ids(struct pnp_card *card)
1371da177e4SLinus Torvalds {
1381da177e4SLinus Torvalds 	struct pnp_id *id;
1391da177e4SLinus Torvalds 	struct pnp_id *next;
14007d4e9afSBjorn Helgaas 
1411da177e4SLinus Torvalds 	id = card->id;
1421da177e4SLinus Torvalds 	while (id) {
1431da177e4SLinus Torvalds 		next = id->next;
1441da177e4SLinus Torvalds 		kfree(id);
1451da177e4SLinus Torvalds 		id = next;
1461da177e4SLinus Torvalds 	}
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds 
pnp_release_card(struct device * dmdev)1491da177e4SLinus Torvalds static void pnp_release_card(struct device *dmdev)
1501da177e4SLinus Torvalds {
1511da177e4SLinus Torvalds 	struct pnp_card *card = to_pnp_card(dmdev);
15207d4e9afSBjorn Helgaas 
1531da177e4SLinus Torvalds 	pnp_free_card_ids(card);
1541da177e4SLinus Torvalds 	kfree(card);
1551da177e4SLinus Torvalds }
1561da177e4SLinus Torvalds 
pnp_alloc_card(struct pnp_protocol * protocol,int id,char * pnpid)1576bf2aab2SBjorn Helgaas struct pnp_card *pnp_alloc_card(struct pnp_protocol *protocol, int id, char *pnpid)
1586bf2aab2SBjorn Helgaas {
1596bf2aab2SBjorn Helgaas 	struct pnp_card *card;
1606bf2aab2SBjorn Helgaas 	struct pnp_id *dev_id;
1616bf2aab2SBjorn Helgaas 
1626bf2aab2SBjorn Helgaas 	card = kzalloc(sizeof(struct pnp_card), GFP_KERNEL);
1636bf2aab2SBjorn Helgaas 	if (!card)
1646bf2aab2SBjorn Helgaas 		return NULL;
1656bf2aab2SBjorn Helgaas 
1666bf2aab2SBjorn Helgaas 	card->protocol = protocol;
1676bf2aab2SBjorn Helgaas 	card->number = id;
1686bf2aab2SBjorn Helgaas 
1696bf2aab2SBjorn Helgaas 	card->dev.parent = &card->protocol->dev;
170c85e37c5SKay Sievers 	dev_set_name(&card->dev, "%02x:%02x", card->protocol->number, card->number);
1716bf2aab2SBjorn Helgaas 
1722f4f27d4SYang Hongyang 	card->dev.coherent_dma_mask = DMA_BIT_MASK(24);
173e86b19ceSRene Herman 	card->dev.dma_mask = &card->dev.coherent_dma_mask;
174e86b19ceSRene Herman 
1756bf2aab2SBjorn Helgaas 	dev_id = pnp_add_card_id(card, pnpid);
1766bf2aab2SBjorn Helgaas 	if (!dev_id) {
1776bf2aab2SBjorn Helgaas 		kfree(card);
1786bf2aab2SBjorn Helgaas 		return NULL;
1796bf2aab2SBjorn Helgaas 	}
1806bf2aab2SBjorn Helgaas 
1816bf2aab2SBjorn Helgaas 	return card;
1826bf2aab2SBjorn Helgaas }
1836bf2aab2SBjorn Helgaas 
name_show(struct device * dmdev,struct device_attribute * attr,char * buf)1843935787eSZhen Lei static ssize_t name_show(struct device *dmdev,
1859dd78466SBjorn Helgaas 			 struct device_attribute *attr, char *buf)
1861da177e4SLinus Torvalds {
1871da177e4SLinus Torvalds 	char *str = buf;
1881da177e4SLinus Torvalds 	struct pnp_card *card = to_pnp_card(dmdev);
18907d4e9afSBjorn Helgaas 
1901da177e4SLinus Torvalds 	str += sprintf(str, "%s\n", card->name);
1911da177e4SLinus Torvalds 	return (str - buf);
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds 
1943935787eSZhen Lei static DEVICE_ATTR_RO(name);
1951da177e4SLinus Torvalds 
card_id_show(struct device * dmdev,struct device_attribute * attr,char * buf)1963935787eSZhen Lei static ssize_t card_id_show(struct device *dmdev,
1979dd78466SBjorn Helgaas 			    struct device_attribute *attr, char *buf)
1981da177e4SLinus Torvalds {
1991da177e4SLinus Torvalds 	char *str = buf;
2001da177e4SLinus Torvalds 	struct pnp_card *card = to_pnp_card(dmdev);
2011da177e4SLinus Torvalds 	struct pnp_id *pos = card->id;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	while (pos) {
2041da177e4SLinus Torvalds 		str += sprintf(str, "%s\n", pos->id);
2051da177e4SLinus Torvalds 		pos = pos->next;
2061da177e4SLinus Torvalds 	}
2071da177e4SLinus Torvalds 	return (str - buf);
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
2103935787eSZhen Lei static DEVICE_ATTR_RO(card_id);
2111da177e4SLinus Torvalds 
pnp_interface_attach_card(struct pnp_card * card)2121da177e4SLinus Torvalds static int pnp_interface_attach_card(struct pnp_card *card)
2131da177e4SLinus Torvalds {
214bfc7ee20SJeff Garzik 	int rc = device_create_file(&card->dev, &dev_attr_name);
21507d4e9afSBjorn Helgaas 
2169dd78466SBjorn Helgaas 	if (rc)
2179dd78466SBjorn Helgaas 		return rc;
218bfc7ee20SJeff Garzik 
219bfc7ee20SJeff Garzik 	rc = device_create_file(&card->dev, &dev_attr_card_id);
2209dd78466SBjorn Helgaas 	if (rc)
2219dd78466SBjorn Helgaas 		goto err_name;
222bfc7ee20SJeff Garzik 
2231da177e4SLinus Torvalds 	return 0;
224bfc7ee20SJeff Garzik 
225bfc7ee20SJeff Garzik err_name:
226bfc7ee20SJeff Garzik 	device_remove_file(&card->dev, &dev_attr_name);
227bfc7ee20SJeff Garzik 	return rc;
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds /**
2311da177e4SLinus Torvalds  * pnp_add_card - adds a PnP card to the PnP Layer
2321da177e4SLinus Torvalds  * @card: pointer to the card to add
2331da177e4SLinus Torvalds  */
pnp_add_card(struct pnp_card * card)2341da177e4SLinus Torvalds int pnp_add_card(struct pnp_card *card)
2351da177e4SLinus Torvalds {
2361da177e4SLinus Torvalds 	int error;
2371da177e4SLinus Torvalds 	struct list_head *pos, *temp;
23807d4e9afSBjorn Helgaas 
2391da177e4SLinus Torvalds 	card->dev.bus = NULL;
2401da177e4SLinus Torvalds 	card->dev.release = &pnp_release_card;
2411da177e4SLinus Torvalds 	error = device_register(&card->dev);
2425bfc43a0SBjorn Helgaas 	if (error) {
243a05d0781SBjorn Helgaas 		dev_err(&card->dev, "could not register (err=%d)\n", error);
24475365d04SLevente Kurusa 		put_device(&card->dev);
2455bfc43a0SBjorn Helgaas 		return error;
2465bfc43a0SBjorn Helgaas 	}
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	pnp_interface_attach_card(card);
24938f6b38dSRafael J. Wysocki 	mutex_lock(&pnp_lock);
2501da177e4SLinus Torvalds 	list_add_tail(&card->global_list, &pnp_cards);
2511da177e4SLinus Torvalds 	list_add_tail(&card->protocol_list, &card->protocol->cards);
25238f6b38dSRafael J. Wysocki 	mutex_unlock(&pnp_lock);
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	/* we wait until now to add devices in order to ensure the drivers
2551da177e4SLinus Torvalds 	 * will be able to use all of the related devices on the card
2565bfc43a0SBjorn Helgaas 	 * without waiting an unreasonable length of time */
2571da177e4SLinus Torvalds 	list_for_each(pos, &card->devices) {
2581da177e4SLinus Torvalds 		struct pnp_dev *dev = card_to_pnp_dev(pos);
2591da177e4SLinus Torvalds 		__pnp_add_device(dev);
2601da177e4SLinus Torvalds 	}
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds 	/* match with card drivers */
2631da177e4SLinus Torvalds 	list_for_each_safe(pos, temp, &pnp_card_drivers) {
2649dd78466SBjorn Helgaas 		struct pnp_card_driver *drv =
2659dd78466SBjorn Helgaas 		    list_entry(pos, struct pnp_card_driver,
2669dd78466SBjorn Helgaas 			       global_list);
2671da177e4SLinus Torvalds 		card_probe(card, drv);
2681da177e4SLinus Torvalds 	}
2695bfc43a0SBjorn Helgaas 	return 0;
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds /**
2731da177e4SLinus Torvalds  * pnp_remove_card - removes a PnP card from the PnP Layer
2741da177e4SLinus Torvalds  * @card: pointer to the card to remove
2751da177e4SLinus Torvalds  */
pnp_remove_card(struct pnp_card * card)2761da177e4SLinus Torvalds void pnp_remove_card(struct pnp_card *card)
2771da177e4SLinus Torvalds {
2781da177e4SLinus Torvalds 	struct list_head *pos, *temp;
27907d4e9afSBjorn Helgaas 
2801da177e4SLinus Torvalds 	device_unregister(&card->dev);
28138f6b38dSRafael J. Wysocki 	mutex_lock(&pnp_lock);
2821da177e4SLinus Torvalds 	list_del(&card->global_list);
2831da177e4SLinus Torvalds 	list_del(&card->protocol_list);
28438f6b38dSRafael J. Wysocki 	mutex_unlock(&pnp_lock);
2851da177e4SLinus Torvalds 	list_for_each_safe(pos, temp, &card->devices) {
2861da177e4SLinus Torvalds 		struct pnp_dev *dev = card_to_pnp_dev(pos);
2871da177e4SLinus Torvalds 		pnp_remove_card_device(dev);
2881da177e4SLinus Torvalds 	}
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds /**
2921da177e4SLinus Torvalds  * pnp_add_card_device - adds a device to the specified card
2931da177e4SLinus Torvalds  * @card: pointer to the card to add to
2941da177e4SLinus Torvalds  * @dev: pointer to the device to add
2951da177e4SLinus Torvalds  */
pnp_add_card_device(struct pnp_card * card,struct pnp_dev * dev)2961da177e4SLinus Torvalds int pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev)
2971da177e4SLinus Torvalds {
2981da177e4SLinus Torvalds 	dev->dev.parent = &card->dev;
2991da177e4SLinus Torvalds 	dev->card_link = NULL;
300c85e37c5SKay Sievers 	dev_set_name(&dev->dev, "%02x:%02x.%02x",
3019dd78466SBjorn Helgaas 		     dev->protocol->number, card->number, dev->number);
30238f6b38dSRafael J. Wysocki 	mutex_lock(&pnp_lock);
3031da177e4SLinus Torvalds 	dev->card = card;
3041da177e4SLinus Torvalds 	list_add_tail(&dev->card_list, &card->devices);
30538f6b38dSRafael J. Wysocki 	mutex_unlock(&pnp_lock);
3061da177e4SLinus Torvalds 	return 0;
3071da177e4SLinus Torvalds }
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds /**
3101da177e4SLinus Torvalds  * pnp_remove_card_device- removes a device from the specified card
3111da177e4SLinus Torvalds  * @dev: pointer to the device to remove
3121da177e4SLinus Torvalds  */
pnp_remove_card_device(struct pnp_dev * dev)3131da177e4SLinus Torvalds void pnp_remove_card_device(struct pnp_dev *dev)
3141da177e4SLinus Torvalds {
31538f6b38dSRafael J. Wysocki 	mutex_lock(&pnp_lock);
3161da177e4SLinus Torvalds 	dev->card = NULL;
3171da177e4SLinus Torvalds 	list_del(&dev->card_list);
31838f6b38dSRafael J. Wysocki 	mutex_unlock(&pnp_lock);
3191da177e4SLinus Torvalds 	__pnp_remove_device(dev);
3201da177e4SLinus Torvalds }
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds /**
3231da177e4SLinus Torvalds  * pnp_request_card_device - Searches for a PnP device under the specified card
3243d41088fSMartin Waitz  * @clink: pointer to the card link, cannot be NULL
3251da177e4SLinus Torvalds  * @id: pointer to a PnP ID structure that explains the rules for finding the device
32625985edcSLucas De Marchi  * @from: Starting place to search from. If NULL it will start from the beginning.
3271da177e4SLinus Torvalds  */
pnp_request_card_device(struct pnp_card_link * clink,const char * id,struct pnp_dev * from)3289dd78466SBjorn Helgaas struct pnp_dev *pnp_request_card_device(struct pnp_card_link *clink,
3299dd78466SBjorn Helgaas 					const char *id, struct pnp_dev *from)
3301da177e4SLinus Torvalds {
3311da177e4SLinus Torvalds 	struct list_head *pos;
3321da177e4SLinus Torvalds 	struct pnp_dev *dev;
3331da177e4SLinus Torvalds 	struct pnp_card_driver *drv;
3341da177e4SLinus Torvalds 	struct pnp_card *card;
33507d4e9afSBjorn Helgaas 
3361da177e4SLinus Torvalds 	if (!clink || !id)
3375bfc43a0SBjorn Helgaas 		return NULL;
3385bfc43a0SBjorn Helgaas 
3391da177e4SLinus Torvalds 	card = clink->card;
3401da177e4SLinus Torvalds 	drv = clink->driver;
3411da177e4SLinus Torvalds 	if (!from) {
3421da177e4SLinus Torvalds 		pos = card->devices.next;
3431da177e4SLinus Torvalds 	} else {
3441da177e4SLinus Torvalds 		if (from->card != card)
3455bfc43a0SBjorn Helgaas 			return NULL;
3461da177e4SLinus Torvalds 		pos = from->card_list.next;
3471da177e4SLinus Torvalds 	}
3481da177e4SLinus Torvalds 	while (pos != &card->devices) {
3491da177e4SLinus Torvalds 		dev = card_to_pnp_dev(pos);
3501da177e4SLinus Torvalds 		if ((!dev->card_link) && compare_pnp_id(dev->id, id))
3511da177e4SLinus Torvalds 			goto found;
3521da177e4SLinus Torvalds 		pos = pos->next;
3531da177e4SLinus Torvalds 	}
3541da177e4SLinus Torvalds 
3551da177e4SLinus Torvalds 	return NULL;
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds found:
3581da177e4SLinus Torvalds 	dev->card_link = clink;
3591da177e4SLinus Torvalds 	dev->dev.driver = &drv->link.driver;
360bfc7ee20SJeff Garzik 	if (pnp_bus_type.probe(&dev->dev))
361bfc7ee20SJeff Garzik 		goto err_out;
362bfc7ee20SJeff Garzik 	if (device_bind_driver(&dev->dev))
363bfc7ee20SJeff Garzik 		goto err_out;
364bfc7ee20SJeff Garzik 
365bfc7ee20SJeff Garzik 	return dev;
366bfc7ee20SJeff Garzik 
367bfc7ee20SJeff Garzik err_out:
3681da177e4SLinus Torvalds 	dev->dev.driver = NULL;
36930d5b64bSJaroslav Kysela 	dev->card_link = NULL;
3701da177e4SLinus Torvalds 	return NULL;
3711da177e4SLinus Torvalds }
372*d02908adSJinchao Wang EXPORT_SYMBOL(pnp_request_card_device);
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds /**
3751da177e4SLinus Torvalds  * pnp_release_card_device - call this when the driver no longer needs the device
37625985edcSLucas De Marchi  * @dev: pointer to the PnP device structure
3771da177e4SLinus Torvalds  */
pnp_release_card_device(struct pnp_dev * dev)3781da177e4SLinus Torvalds void pnp_release_card_device(struct pnp_dev *dev)
3791da177e4SLinus Torvalds {
3801da177e4SLinus Torvalds 	struct pnp_card_driver *drv = dev->card_link->driver;
38107d4e9afSBjorn Helgaas 
3821da177e4SLinus Torvalds 	drv->link.remove = &card_remove;
3831da177e4SLinus Torvalds 	device_release_driver(&dev->dev);
3841da177e4SLinus Torvalds 	drv->link.remove = &card_remove_first;
3851da177e4SLinus Torvalds }
386*d02908adSJinchao Wang EXPORT_SYMBOL(pnp_release_card_device);
3871da177e4SLinus Torvalds 
3884c98cfefSTakashi Iwai /*
3894c98cfefSTakashi Iwai  * suspend/resume callbacks
3904c98cfefSTakashi Iwai  */
card_suspend(struct pnp_dev * dev,pm_message_t state)3914c98cfefSTakashi Iwai static int card_suspend(struct pnp_dev *dev, pm_message_t state)
3924c98cfefSTakashi Iwai {
3934c98cfefSTakashi Iwai 	struct pnp_card_link *link = dev->card_link;
39407d4e9afSBjorn Helgaas 
3954c98cfefSTakashi Iwai 	if (link->pm_state.event == state.event)
3964c98cfefSTakashi Iwai 		return 0;
3974c98cfefSTakashi Iwai 	link->pm_state = state;
3984c98cfefSTakashi Iwai 	return link->driver->suspend(link, state);
3994c98cfefSTakashi Iwai }
4004c98cfefSTakashi Iwai 
card_resume(struct pnp_dev * dev)4014c98cfefSTakashi Iwai static int card_resume(struct pnp_dev *dev)
4024c98cfefSTakashi Iwai {
4034c98cfefSTakashi Iwai 	struct pnp_card_link *link = dev->card_link;
40407d4e9afSBjorn Helgaas 
4054c98cfefSTakashi Iwai 	if (link->pm_state.event == PM_EVENT_ON)
4064c98cfefSTakashi Iwai 		return 0;
4074c98cfefSTakashi Iwai 	link->pm_state = PMSG_ON;
4084c98cfefSTakashi Iwai 	link->driver->resume(link);
4094c98cfefSTakashi Iwai 	return 0;
4104c98cfefSTakashi Iwai }
4114c98cfefSTakashi Iwai 
4121da177e4SLinus Torvalds /**
4131da177e4SLinus Torvalds  * pnp_register_card_driver - registers a PnP card driver with the PnP Layer
4141da177e4SLinus Torvalds  * @drv: pointer to the driver to register
4151da177e4SLinus Torvalds  */
pnp_register_card_driver(struct pnp_card_driver * drv)4161da177e4SLinus Torvalds int pnp_register_card_driver(struct pnp_card_driver *drv)
4171da177e4SLinus Torvalds {
418982c6094SBjorn Helgaas 	int error;
4191da177e4SLinus Torvalds 	struct list_head *pos, *temp;
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 	drv->link.name = drv->name;
4221da177e4SLinus Torvalds 	drv->link.id_table = NULL;	/* this will disable auto matching */
4231da177e4SLinus Torvalds 	drv->link.flags = drv->flags;
4241da177e4SLinus Torvalds 	drv->link.probe = NULL;
4251da177e4SLinus Torvalds 	drv->link.remove = &card_remove_first;
4264c98cfefSTakashi Iwai 	drv->link.suspend = drv->suspend ? card_suspend : NULL;
4274c98cfefSTakashi Iwai 	drv->link.resume = drv->resume ? card_resume : NULL;
4281da177e4SLinus Torvalds 
429982c6094SBjorn Helgaas 	error = pnp_register_driver(&drv->link);
430982c6094SBjorn Helgaas 	if (error < 0)
431982c6094SBjorn Helgaas 		return error;
432d1d051b2SAdam Belay 
43338f6b38dSRafael J. Wysocki 	mutex_lock(&pnp_lock);
4341da177e4SLinus Torvalds 	list_add_tail(&drv->global_list, &pnp_card_drivers);
43538f6b38dSRafael J. Wysocki 	mutex_unlock(&pnp_lock);
436d1d051b2SAdam Belay 
4371da177e4SLinus Torvalds 	list_for_each_safe(pos, temp, &pnp_cards) {
4389dd78466SBjorn Helgaas 		struct pnp_card *card =
4399dd78466SBjorn Helgaas 		    list_entry(pos, struct pnp_card, global_list);
440982c6094SBjorn Helgaas 		card_probe(card, drv);
4411da177e4SLinus Torvalds 	}
442982c6094SBjorn Helgaas 	return 0;
4431da177e4SLinus Torvalds }
444*d02908adSJinchao Wang EXPORT_SYMBOL(pnp_register_card_driver);
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds /**
4471da177e4SLinus Torvalds  * pnp_unregister_card_driver - unregisters a PnP card driver from the PnP Layer
4481da177e4SLinus Torvalds  * @drv: pointer to the driver to unregister
4491da177e4SLinus Torvalds  */
pnp_unregister_card_driver(struct pnp_card_driver * drv)4501da177e4SLinus Torvalds void pnp_unregister_card_driver(struct pnp_card_driver *drv)
4511da177e4SLinus Torvalds {
45238f6b38dSRafael J. Wysocki 	mutex_lock(&pnp_lock);
4531da177e4SLinus Torvalds 	list_del(&drv->global_list);
45438f6b38dSRafael J. Wysocki 	mutex_unlock(&pnp_lock);
4551da177e4SLinus Torvalds 	pnp_unregister_driver(&drv->link);
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds EXPORT_SYMBOL(pnp_unregister_card_driver);
458