xref: /kernel/linux/linux-5.10/drivers/pnp/card.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * card.c - contains functions for managing groups of PnP devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2002 Adam Belay <ambx1@neo.rr.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/mutex.h>
108c2ecf20Sopenharmony_ci#include <linux/ctype.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/pnp.h>
138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
148c2ecf20Sopenharmony_ci#include "base.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciLIST_HEAD(pnp_cards);
178c2ecf20Sopenharmony_cistatic LIST_HEAD(pnp_card_drivers);
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic const struct pnp_card_device_id *match_card(struct pnp_card_driver *drv,
208c2ecf20Sopenharmony_ci						   struct pnp_card *card)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	const struct pnp_card_device_id *drv_id = drv->id_table;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	while (*drv_id->id) {
258c2ecf20Sopenharmony_ci		if (compare_pnp_id(card->id, drv_id->id)) {
268c2ecf20Sopenharmony_ci			int i = 0;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci			for (;;) {
298c2ecf20Sopenharmony_ci				int found;
308c2ecf20Sopenharmony_ci				struct pnp_dev *dev;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci				if (i == PNP_MAX_DEVICES ||
338c2ecf20Sopenharmony_ci				    !*drv_id->devs[i].id)
348c2ecf20Sopenharmony_ci					return drv_id;
358c2ecf20Sopenharmony_ci				found = 0;
368c2ecf20Sopenharmony_ci				card_for_each_dev(card, dev) {
378c2ecf20Sopenharmony_ci					if (compare_pnp_id(dev->id,
388c2ecf20Sopenharmony_ci						   drv_id->devs[i].id)) {
398c2ecf20Sopenharmony_ci						found = 1;
408c2ecf20Sopenharmony_ci						break;
418c2ecf20Sopenharmony_ci					}
428c2ecf20Sopenharmony_ci				}
438c2ecf20Sopenharmony_ci				if (!found)
448c2ecf20Sopenharmony_ci					break;
458c2ecf20Sopenharmony_ci				i++;
468c2ecf20Sopenharmony_ci			}
478c2ecf20Sopenharmony_ci		}
488c2ecf20Sopenharmony_ci		drv_id++;
498c2ecf20Sopenharmony_ci	}
508c2ecf20Sopenharmony_ci	return NULL;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void card_remove(struct pnp_dev *dev)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	dev->card_link = NULL;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void card_remove_first(struct pnp_dev *dev)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct pnp_card_driver *drv = to_pnp_card_driver(dev->driver);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!dev->card || !drv)
638c2ecf20Sopenharmony_ci		return;
648c2ecf20Sopenharmony_ci	if (drv->remove)
658c2ecf20Sopenharmony_ci		drv->remove(dev->card_link);
668c2ecf20Sopenharmony_ci	drv->link.remove = &card_remove;
678c2ecf20Sopenharmony_ci	kfree(dev->card_link);
688c2ecf20Sopenharmony_ci	card_remove(dev);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int card_probe(struct pnp_card *card, struct pnp_card_driver *drv)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	const struct pnp_card_device_id *id;
748c2ecf20Sopenharmony_ci	struct pnp_card_link *clink;
758c2ecf20Sopenharmony_ci	struct pnp_dev *dev;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (!drv->probe)
788c2ecf20Sopenharmony_ci		return 0;
798c2ecf20Sopenharmony_ci	id = match_card(drv, card);
808c2ecf20Sopenharmony_ci	if (!id)
818c2ecf20Sopenharmony_ci		return 0;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	clink = pnp_alloc(sizeof(*clink));
848c2ecf20Sopenharmony_ci	if (!clink)
858c2ecf20Sopenharmony_ci		return 0;
868c2ecf20Sopenharmony_ci	clink->card = card;
878c2ecf20Sopenharmony_ci	clink->driver = drv;
888c2ecf20Sopenharmony_ci	clink->pm_state = PMSG_ON;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (drv->probe(clink, id) >= 0)
918c2ecf20Sopenharmony_ci		return 1;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* Recovery */
948c2ecf20Sopenharmony_ci	card_for_each_dev(card, dev) {
958c2ecf20Sopenharmony_ci		if (dev->card_link == clink)
968c2ecf20Sopenharmony_ci			pnp_release_card_device(dev);
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci	kfree(clink);
998c2ecf20Sopenharmony_ci	return 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/**
1038c2ecf20Sopenharmony_ci * pnp_add_card_id - adds an EISA id to the specified card
1048c2ecf20Sopenharmony_ci * @id: pointer to a pnp_id structure
1058c2ecf20Sopenharmony_ci * @card: pointer to the desired card
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_cistatic struct pnp_id *pnp_add_card_id(struct pnp_card *card, char *id)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct pnp_id *dev_id, *ptr;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
1128c2ecf20Sopenharmony_ci	if (!dev_id)
1138c2ecf20Sopenharmony_ci		return NULL;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	dev_id->id[0] = id[0];
1168c2ecf20Sopenharmony_ci	dev_id->id[1] = id[1];
1178c2ecf20Sopenharmony_ci	dev_id->id[2] = id[2];
1188c2ecf20Sopenharmony_ci	dev_id->id[3] = tolower(id[3]);
1198c2ecf20Sopenharmony_ci	dev_id->id[4] = tolower(id[4]);
1208c2ecf20Sopenharmony_ci	dev_id->id[5] = tolower(id[5]);
1218c2ecf20Sopenharmony_ci	dev_id->id[6] = tolower(id[6]);
1228c2ecf20Sopenharmony_ci	dev_id->id[7] = '\0';
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	dev_id->next = NULL;
1258c2ecf20Sopenharmony_ci	ptr = card->id;
1268c2ecf20Sopenharmony_ci	while (ptr && ptr->next)
1278c2ecf20Sopenharmony_ci		ptr = ptr->next;
1288c2ecf20Sopenharmony_ci	if (ptr)
1298c2ecf20Sopenharmony_ci		ptr->next = dev_id;
1308c2ecf20Sopenharmony_ci	else
1318c2ecf20Sopenharmony_ci		card->id = dev_id;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return dev_id;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic void pnp_free_card_ids(struct pnp_card *card)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct pnp_id *id;
1398c2ecf20Sopenharmony_ci	struct pnp_id *next;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	id = card->id;
1428c2ecf20Sopenharmony_ci	while (id) {
1438c2ecf20Sopenharmony_ci		next = id->next;
1448c2ecf20Sopenharmony_ci		kfree(id);
1458c2ecf20Sopenharmony_ci		id = next;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void pnp_release_card(struct device *dmdev)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct pnp_card *card = to_pnp_card(dmdev);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	pnp_free_card_ids(card);
1548c2ecf20Sopenharmony_ci	kfree(card);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistruct pnp_card *pnp_alloc_card(struct pnp_protocol *protocol, int id, char *pnpid)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct pnp_card *card;
1608c2ecf20Sopenharmony_ci	struct pnp_id *dev_id;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	card = kzalloc(sizeof(struct pnp_card), GFP_KERNEL);
1638c2ecf20Sopenharmony_ci	if (!card)
1648c2ecf20Sopenharmony_ci		return NULL;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	card->protocol = protocol;
1678c2ecf20Sopenharmony_ci	card->number = id;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	card->dev.parent = &card->protocol->dev;
1708c2ecf20Sopenharmony_ci	dev_set_name(&card->dev, "%02x:%02x", card->protocol->number, card->number);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	card->dev.coherent_dma_mask = DMA_BIT_MASK(24);
1738c2ecf20Sopenharmony_ci	card->dev.dma_mask = &card->dev.coherent_dma_mask;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	dev_id = pnp_add_card_id(card, pnpid);
1768c2ecf20Sopenharmony_ci	if (!dev_id) {
1778c2ecf20Sopenharmony_ci		kfree(card);
1788c2ecf20Sopenharmony_ci		return NULL;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return card;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic ssize_t pnp_show_card_name(struct device *dmdev,
1858c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	char *str = buf;
1888c2ecf20Sopenharmony_ci	struct pnp_card *card = to_pnp_card(dmdev);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	str += sprintf(str, "%s\n", card->name);
1918c2ecf20Sopenharmony_ci	return (str - buf);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, S_IRUGO, pnp_show_card_name, NULL);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic ssize_t pnp_show_card_ids(struct device *dmdev,
1978c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	char *str = buf;
2008c2ecf20Sopenharmony_ci	struct pnp_card *card = to_pnp_card(dmdev);
2018c2ecf20Sopenharmony_ci	struct pnp_id *pos = card->id;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	while (pos) {
2048c2ecf20Sopenharmony_ci		str += sprintf(str, "%s\n", pos->id);
2058c2ecf20Sopenharmony_ci		pos = pos->next;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci	return (str - buf);
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic DEVICE_ATTR(card_id, S_IRUGO, pnp_show_card_ids, NULL);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int pnp_interface_attach_card(struct pnp_card *card)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	int rc = device_create_file(&card->dev, &dev_attr_name);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (rc)
2178c2ecf20Sopenharmony_ci		return rc;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	rc = device_create_file(&card->dev, &dev_attr_card_id);
2208c2ecf20Sopenharmony_ci	if (rc)
2218c2ecf20Sopenharmony_ci		goto err_name;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return 0;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cierr_name:
2268c2ecf20Sopenharmony_ci	device_remove_file(&card->dev, &dev_attr_name);
2278c2ecf20Sopenharmony_ci	return rc;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/**
2318c2ecf20Sopenharmony_ci * pnp_add_card - adds a PnP card to the PnP Layer
2328c2ecf20Sopenharmony_ci * @card: pointer to the card to add
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_ciint pnp_add_card(struct pnp_card *card)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	int error;
2378c2ecf20Sopenharmony_ci	struct list_head *pos, *temp;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	card->dev.bus = NULL;
2408c2ecf20Sopenharmony_ci	card->dev.release = &pnp_release_card;
2418c2ecf20Sopenharmony_ci	error = device_register(&card->dev);
2428c2ecf20Sopenharmony_ci	if (error) {
2438c2ecf20Sopenharmony_ci		dev_err(&card->dev, "could not register (err=%d)\n", error);
2448c2ecf20Sopenharmony_ci		put_device(&card->dev);
2458c2ecf20Sopenharmony_ci		return error;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	pnp_interface_attach_card(card);
2498c2ecf20Sopenharmony_ci	mutex_lock(&pnp_lock);
2508c2ecf20Sopenharmony_ci	list_add_tail(&card->global_list, &pnp_cards);
2518c2ecf20Sopenharmony_ci	list_add_tail(&card->protocol_list, &card->protocol->cards);
2528c2ecf20Sopenharmony_ci	mutex_unlock(&pnp_lock);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* we wait until now to add devices in order to ensure the drivers
2558c2ecf20Sopenharmony_ci	 * will be able to use all of the related devices on the card
2568c2ecf20Sopenharmony_ci	 * without waiting an unreasonable length of time */
2578c2ecf20Sopenharmony_ci	list_for_each(pos, &card->devices) {
2588c2ecf20Sopenharmony_ci		struct pnp_dev *dev = card_to_pnp_dev(pos);
2598c2ecf20Sopenharmony_ci		__pnp_add_device(dev);
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* match with card drivers */
2638c2ecf20Sopenharmony_ci	list_for_each_safe(pos, temp, &pnp_card_drivers) {
2648c2ecf20Sopenharmony_ci		struct pnp_card_driver *drv =
2658c2ecf20Sopenharmony_ci		    list_entry(pos, struct pnp_card_driver,
2668c2ecf20Sopenharmony_ci			       global_list);
2678c2ecf20Sopenharmony_ci		card_probe(card, drv);
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci	return 0;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/**
2738c2ecf20Sopenharmony_ci * pnp_remove_card - removes a PnP card from the PnP Layer
2748c2ecf20Sopenharmony_ci * @card: pointer to the card to remove
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_civoid pnp_remove_card(struct pnp_card *card)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct list_head *pos, *temp;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	device_unregister(&card->dev);
2818c2ecf20Sopenharmony_ci	mutex_lock(&pnp_lock);
2828c2ecf20Sopenharmony_ci	list_del(&card->global_list);
2838c2ecf20Sopenharmony_ci	list_del(&card->protocol_list);
2848c2ecf20Sopenharmony_ci	mutex_unlock(&pnp_lock);
2858c2ecf20Sopenharmony_ci	list_for_each_safe(pos, temp, &card->devices) {
2868c2ecf20Sopenharmony_ci		struct pnp_dev *dev = card_to_pnp_dev(pos);
2878c2ecf20Sopenharmony_ci		pnp_remove_card_device(dev);
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci/**
2928c2ecf20Sopenharmony_ci * pnp_add_card_device - adds a device to the specified card
2938c2ecf20Sopenharmony_ci * @card: pointer to the card to add to
2948c2ecf20Sopenharmony_ci * @dev: pointer to the device to add
2958c2ecf20Sopenharmony_ci */
2968c2ecf20Sopenharmony_ciint pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	dev->dev.parent = &card->dev;
2998c2ecf20Sopenharmony_ci	dev->card_link = NULL;
3008c2ecf20Sopenharmony_ci	dev_set_name(&dev->dev, "%02x:%02x.%02x",
3018c2ecf20Sopenharmony_ci		     dev->protocol->number, card->number, dev->number);
3028c2ecf20Sopenharmony_ci	mutex_lock(&pnp_lock);
3038c2ecf20Sopenharmony_ci	dev->card = card;
3048c2ecf20Sopenharmony_ci	list_add_tail(&dev->card_list, &card->devices);
3058c2ecf20Sopenharmony_ci	mutex_unlock(&pnp_lock);
3068c2ecf20Sopenharmony_ci	return 0;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci/**
3108c2ecf20Sopenharmony_ci * pnp_remove_card_device- removes a device from the specified card
3118c2ecf20Sopenharmony_ci * @dev: pointer to the device to remove
3128c2ecf20Sopenharmony_ci */
3138c2ecf20Sopenharmony_civoid pnp_remove_card_device(struct pnp_dev *dev)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	mutex_lock(&pnp_lock);
3168c2ecf20Sopenharmony_ci	dev->card = NULL;
3178c2ecf20Sopenharmony_ci	list_del(&dev->card_list);
3188c2ecf20Sopenharmony_ci	mutex_unlock(&pnp_lock);
3198c2ecf20Sopenharmony_ci	__pnp_remove_device(dev);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci/**
3238c2ecf20Sopenharmony_ci * pnp_request_card_device - Searches for a PnP device under the specified card
3248c2ecf20Sopenharmony_ci * @clink: pointer to the card link, cannot be NULL
3258c2ecf20Sopenharmony_ci * @id: pointer to a PnP ID structure that explains the rules for finding the device
3268c2ecf20Sopenharmony_ci * @from: Starting place to search from. If NULL it will start from the beginning.
3278c2ecf20Sopenharmony_ci */
3288c2ecf20Sopenharmony_cistruct pnp_dev *pnp_request_card_device(struct pnp_card_link *clink,
3298c2ecf20Sopenharmony_ci					const char *id, struct pnp_dev *from)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	struct list_head *pos;
3328c2ecf20Sopenharmony_ci	struct pnp_dev *dev;
3338c2ecf20Sopenharmony_ci	struct pnp_card_driver *drv;
3348c2ecf20Sopenharmony_ci	struct pnp_card *card;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (!clink || !id)
3378c2ecf20Sopenharmony_ci		return NULL;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	card = clink->card;
3408c2ecf20Sopenharmony_ci	drv = clink->driver;
3418c2ecf20Sopenharmony_ci	if (!from) {
3428c2ecf20Sopenharmony_ci		pos = card->devices.next;
3438c2ecf20Sopenharmony_ci	} else {
3448c2ecf20Sopenharmony_ci		if (from->card != card)
3458c2ecf20Sopenharmony_ci			return NULL;
3468c2ecf20Sopenharmony_ci		pos = from->card_list.next;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci	while (pos != &card->devices) {
3498c2ecf20Sopenharmony_ci		dev = card_to_pnp_dev(pos);
3508c2ecf20Sopenharmony_ci		if ((!dev->card_link) && compare_pnp_id(dev->id, id))
3518c2ecf20Sopenharmony_ci			goto found;
3528c2ecf20Sopenharmony_ci		pos = pos->next;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	return NULL;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cifound:
3588c2ecf20Sopenharmony_ci	dev->card_link = clink;
3598c2ecf20Sopenharmony_ci	dev->dev.driver = &drv->link.driver;
3608c2ecf20Sopenharmony_ci	if (pnp_bus_type.probe(&dev->dev))
3618c2ecf20Sopenharmony_ci		goto err_out;
3628c2ecf20Sopenharmony_ci	if (device_bind_driver(&dev->dev))
3638c2ecf20Sopenharmony_ci		goto err_out;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return dev;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cierr_out:
3688c2ecf20Sopenharmony_ci	dev->dev.driver = NULL;
3698c2ecf20Sopenharmony_ci	dev->card_link = NULL;
3708c2ecf20Sopenharmony_ci	return NULL;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/**
3748c2ecf20Sopenharmony_ci * pnp_release_card_device - call this when the driver no longer needs the device
3758c2ecf20Sopenharmony_ci * @dev: pointer to the PnP device structure
3768c2ecf20Sopenharmony_ci */
3778c2ecf20Sopenharmony_civoid pnp_release_card_device(struct pnp_dev *dev)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct pnp_card_driver *drv = dev->card_link->driver;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	drv->link.remove = &card_remove;
3828c2ecf20Sopenharmony_ci	device_release_driver(&dev->dev);
3838c2ecf20Sopenharmony_ci	drv->link.remove = &card_remove_first;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/*
3878c2ecf20Sopenharmony_ci * suspend/resume callbacks
3888c2ecf20Sopenharmony_ci */
3898c2ecf20Sopenharmony_cistatic int card_suspend(struct pnp_dev *dev, pm_message_t state)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct pnp_card_link *link = dev->card_link;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (link->pm_state.event == state.event)
3948c2ecf20Sopenharmony_ci		return 0;
3958c2ecf20Sopenharmony_ci	link->pm_state = state;
3968c2ecf20Sopenharmony_ci	return link->driver->suspend(link, state);
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic int card_resume(struct pnp_dev *dev)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct pnp_card_link *link = dev->card_link;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if (link->pm_state.event == PM_EVENT_ON)
4048c2ecf20Sopenharmony_ci		return 0;
4058c2ecf20Sopenharmony_ci	link->pm_state = PMSG_ON;
4068c2ecf20Sopenharmony_ci	link->driver->resume(link);
4078c2ecf20Sopenharmony_ci	return 0;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci/**
4118c2ecf20Sopenharmony_ci * pnp_register_card_driver - registers a PnP card driver with the PnP Layer
4128c2ecf20Sopenharmony_ci * @drv: pointer to the driver to register
4138c2ecf20Sopenharmony_ci */
4148c2ecf20Sopenharmony_ciint pnp_register_card_driver(struct pnp_card_driver *drv)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	int error;
4178c2ecf20Sopenharmony_ci	struct list_head *pos, *temp;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	drv->link.name = drv->name;
4208c2ecf20Sopenharmony_ci	drv->link.id_table = NULL;	/* this will disable auto matching */
4218c2ecf20Sopenharmony_ci	drv->link.flags = drv->flags;
4228c2ecf20Sopenharmony_ci	drv->link.probe = NULL;
4238c2ecf20Sopenharmony_ci	drv->link.remove = &card_remove_first;
4248c2ecf20Sopenharmony_ci	drv->link.suspend = drv->suspend ? card_suspend : NULL;
4258c2ecf20Sopenharmony_ci	drv->link.resume = drv->resume ? card_resume : NULL;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	error = pnp_register_driver(&drv->link);
4288c2ecf20Sopenharmony_ci	if (error < 0)
4298c2ecf20Sopenharmony_ci		return error;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	mutex_lock(&pnp_lock);
4328c2ecf20Sopenharmony_ci	list_add_tail(&drv->global_list, &pnp_card_drivers);
4338c2ecf20Sopenharmony_ci	mutex_unlock(&pnp_lock);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	list_for_each_safe(pos, temp, &pnp_cards) {
4368c2ecf20Sopenharmony_ci		struct pnp_card *card =
4378c2ecf20Sopenharmony_ci		    list_entry(pos, struct pnp_card, global_list);
4388c2ecf20Sopenharmony_ci		card_probe(card, drv);
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci	return 0;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci/**
4448c2ecf20Sopenharmony_ci * pnp_unregister_card_driver - unregisters a PnP card driver from the PnP Layer
4458c2ecf20Sopenharmony_ci * @drv: pointer to the driver to unregister
4468c2ecf20Sopenharmony_ci */
4478c2ecf20Sopenharmony_civoid pnp_unregister_card_driver(struct pnp_card_driver *drv)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	mutex_lock(&pnp_lock);
4508c2ecf20Sopenharmony_ci	list_del(&drv->global_list);
4518c2ecf20Sopenharmony_ci	mutex_unlock(&pnp_lock);
4528c2ecf20Sopenharmony_ci	pnp_unregister_driver(&drv->link);
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnp_request_card_device);
4568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnp_release_card_device);
4578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnp_register_card_driver);
4588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnp_unregister_card_driver);
459