brintos

brintos / linux-shallow public Read only

0
0
Text · 9.8 KiB · 1765d6e Raw
430 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * manager.c - Resource Management, Conflict Resolution, Activation and Disabling of Devices4 *5 * based on isapnp.c resource management (c) Jaroslav Kysela <perex@perex.cz>6 * Copyright 2003 Adam Belay <ambx1@neo.rr.com>7 * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.8 *	Bjorn Helgaas <bjorn.helgaas@hp.com>9 */10 11#include <linux/errno.h>12#include <linux/module.h>13#include <linux/init.h>14#include <linux/kernel.h>15#include <linux/pnp.h>16#include <linux/bitmap.h>17#include <linux/mutex.h>18#include "base.h"19 20DEFINE_MUTEX(pnp_res_mutex);21 22static struct resource *pnp_find_resource(struct pnp_dev *dev,23					  unsigned char rule,24					  unsigned long type,25					  unsigned int bar)26{27	struct resource *res = pnp_get_resource(dev, type, bar);28 29	/* when the resource already exists, set its resource bits from rule */30	if (res) {31		res->flags &= ~IORESOURCE_BITS;32		res->flags |= rule & IORESOURCE_BITS;33	}34 35	return res;36}37 38static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx)39{40	struct resource *res, local_res;41 42	res = pnp_find_resource(dev, rule->flags, IORESOURCE_IO, idx);43	if (res) {44		pnp_dbg(&dev->dev, "  io %d already set to %#llx-%#llx "45			"flags %#lx\n", idx, (unsigned long long) res->start,46			(unsigned long long) res->end, res->flags);47		return 0;48	}49 50	res = &local_res;51	res->flags = rule->flags | IORESOURCE_AUTO;52	res->start = 0;53	res->end = 0;54 55	if (!rule->size) {56		res->flags |= IORESOURCE_DISABLED;57		pnp_dbg(&dev->dev, "  io %d disabled\n", idx);58		goto __add;59	}60 61	res->start = rule->min;62	res->end = res->start + rule->size - 1;63 64	while (!pnp_check_port(dev, res)) {65		res->start += rule->align;66		res->end = res->start + rule->size - 1;67		if (res->start > rule->max || !rule->align) {68			pnp_dbg(&dev->dev, "  couldn't assign io %d "69				"(min %#llx max %#llx)\n", idx,70				(unsigned long long) rule->min,71				(unsigned long long) rule->max);72			return -EBUSY;73		}74	}75 76__add:77	pnp_add_io_resource(dev, res->start, res->end, res->flags);78	return 0;79}80 81static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)82{83	struct resource *res, local_res;84 85	res = pnp_find_resource(dev, rule->flags, IORESOURCE_MEM, idx);86	if (res) {87		pnp_dbg(&dev->dev, "  mem %d already set to %#llx-%#llx "88			"flags %#lx\n", idx, (unsigned long long) res->start,89			(unsigned long long) res->end, res->flags);90		return 0;91	}92 93	res = &local_res;94	res->flags = rule->flags | IORESOURCE_AUTO;95	res->start = 0;96	res->end = 0;97 98	/* ??? rule->flags restricted to 8 bits, all tests bogus ??? */99	if (!(rule->flags & IORESOURCE_MEM_WRITEABLE))100		res->flags |= IORESOURCE_READONLY;101	if (rule->flags & IORESOURCE_MEM_RANGELENGTH)102		res->flags |= IORESOURCE_RANGELENGTH;103	if (rule->flags & IORESOURCE_MEM_SHADOWABLE)104		res->flags |= IORESOURCE_SHADOWABLE;105 106	if (!rule->size) {107		res->flags |= IORESOURCE_DISABLED;108		pnp_dbg(&dev->dev, "  mem %d disabled\n", idx);109		goto __add;110	}111 112	res->start = rule->min;113	res->end = res->start + rule->size - 1;114 115	while (!pnp_check_mem(dev, res)) {116		res->start += rule->align;117		res->end = res->start + rule->size - 1;118		if (res->start > rule->max || !rule->align) {119			pnp_dbg(&dev->dev, "  couldn't assign mem %d "120				"(min %#llx max %#llx)\n", idx,121				(unsigned long long) rule->min,122				(unsigned long long) rule->max);123			return -EBUSY;124		}125	}126 127__add:128	pnp_add_mem_resource(dev, res->start, res->end, res->flags);129	return 0;130}131 132static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx)133{134	struct resource *res, local_res;135	int i;136 137	/* IRQ priority: this table is good for i386 */138	static unsigned short xtab[16] = {139		5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2140	};141 142	res = pnp_find_resource(dev, rule->flags, IORESOURCE_IRQ, idx);143	if (res) {144		pnp_dbg(&dev->dev, "  irq %d already set to %d flags %#lx\n",145			idx, (int) res->start, res->flags);146		return 0;147	}148 149	res = &local_res;150	res->flags = rule->flags | IORESOURCE_AUTO;151	res->start = -1;152	res->end = -1;153 154	if (bitmap_empty(rule->map.bits, PNP_IRQ_NR)) {155		res->flags |= IORESOURCE_DISABLED;156		pnp_dbg(&dev->dev, "  irq %d disabled\n", idx);157		goto __add;158	}159 160	/* TBD: need check for >16 IRQ */161	res->start = find_next_bit(rule->map.bits, PNP_IRQ_NR, 16);162	if (res->start < PNP_IRQ_NR) {163		res->end = res->start;164		goto __add;165	}166	for (i = 0; i < 16; i++) {167		if (test_bit(xtab[i], rule->map.bits)) {168			res->start = res->end = xtab[i];169			if (pnp_check_irq(dev, res))170				goto __add;171		}172	}173 174	if (rule->flags & IORESOURCE_IRQ_OPTIONAL) {175		res->start = -1;176		res->end = -1;177		res->flags |= IORESOURCE_DISABLED;178		pnp_dbg(&dev->dev, "  irq %d disabled (optional)\n", idx);179		goto __add;180	}181 182	pnp_dbg(&dev->dev, "  couldn't assign irq %d\n", idx);183	return -EBUSY;184 185__add:186	pnp_add_irq_resource(dev, res->start, res->flags);187	return 0;188}189 190#ifdef CONFIG_ISA_DMA_API191static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)192{193	struct resource *res, local_res;194	int i;195 196	/* DMA priority: this table is good for i386 */197	static unsigned short xtab[8] = {198		1, 3, 5, 6, 7, 0, 2, 4199	};200 201	res = pnp_find_resource(dev, rule->flags, IORESOURCE_DMA, idx);202	if (res) {203		pnp_dbg(&dev->dev, "  dma %d already set to %d flags %#lx\n",204			idx, (int) res->start, res->flags);205		return 0;206	}207 208	res = &local_res;209	res->flags = rule->flags | IORESOURCE_AUTO;210	res->start = -1;211	res->end = -1;212 213	if (!rule->map) {214		res->flags |= IORESOURCE_DISABLED;215		pnp_dbg(&dev->dev, "  dma %d disabled\n", idx);216		goto __add;217	}218 219	for (i = 0; i < 8; i++) {220		if (rule->map & (1 << xtab[i])) {221			res->start = res->end = xtab[i];222			if (pnp_check_dma(dev, res))223				goto __add;224		}225	}226 227	pnp_dbg(&dev->dev, "  couldn't assign dma %d\n", idx);228	return -EBUSY;229 230__add:231	pnp_add_dma_resource(dev, res->start, res->flags);232	return 0;233}234#endif /* CONFIG_ISA_DMA_API */235 236void pnp_init_resources(struct pnp_dev *dev)237{238	pnp_free_resources(dev);239}240 241static void pnp_clean_resource_table(struct pnp_dev *dev)242{243	struct pnp_resource *pnp_res, *tmp;244 245	list_for_each_entry_safe(pnp_res, tmp, &dev->resources, list) {246		if (pnp_res->res.flags & IORESOURCE_AUTO)247			pnp_free_resource(pnp_res);248	}249}250 251/**252 * pnp_assign_resources - assigns resources to the device based on the specified dependent number253 * @dev: pointer to the desired device254 * @set: the dependent function number255 */256static int pnp_assign_resources(struct pnp_dev *dev, int set)257{258	struct pnp_option *option;259	int nport = 0, nmem = 0, nirq = 0;260	int ndma __maybe_unused = 0;261	int ret = 0;262 263	pnp_dbg(&dev->dev, "pnp_assign_resources, try dependent set %d\n", set);264	mutex_lock(&pnp_res_mutex);265	pnp_clean_resource_table(dev);266 267	list_for_each_entry(option, &dev->options, list) {268		if (pnp_option_is_dependent(option) &&269		    pnp_option_set(option) != set)270				continue;271 272		switch (option->type) {273		case IORESOURCE_IO:274			ret = pnp_assign_port(dev, &option->u.port, nport++);275			break;276		case IORESOURCE_MEM:277			ret = pnp_assign_mem(dev, &option->u.mem, nmem++);278			break;279		case IORESOURCE_IRQ:280			ret = pnp_assign_irq(dev, &option->u.irq, nirq++);281			break;282#ifdef CONFIG_ISA_DMA_API283		case IORESOURCE_DMA:284			ret = pnp_assign_dma(dev, &option->u.dma, ndma++);285			break;286#endif287		default:288			ret = -EINVAL;289			break;290		}291		if (ret < 0)292			break;293	}294 295	mutex_unlock(&pnp_res_mutex);296	if (ret < 0) {297		pnp_dbg(&dev->dev, "pnp_assign_resources failed (%d)\n", ret);298		pnp_clean_resource_table(dev);299	} else300		dbg_pnp_show_resources(dev, "pnp_assign_resources succeeded");301	return ret;302}303 304/**305 * pnp_auto_config_dev - automatically assigns resources to a device306 * @dev: pointer to the desired device307 */308int pnp_auto_config_dev(struct pnp_dev *dev)309{310	int i, ret;311 312	if (!pnp_can_configure(dev)) {313		pnp_dbg(&dev->dev, "configuration not supported\n");314		return -ENODEV;315	}316 317	ret = pnp_assign_resources(dev, 0);318	if (ret == 0)319		return 0;320 321	for (i = 1; i < dev->num_dependent_sets; i++) {322		ret = pnp_assign_resources(dev, i);323		if (ret == 0)324			return 0;325	}326 327	dev_err(&dev->dev, "unable to assign resources\n");328	return ret;329}330 331/**332 * pnp_start_dev - low-level start of the PnP device333 * @dev: pointer to the desired device334 *335 * assumes that resources have already been allocated336 */337int pnp_start_dev(struct pnp_dev *dev)338{339	if (!pnp_can_write(dev)) {340		pnp_dbg(&dev->dev, "activation not supported\n");341		return -EINVAL;342	}343 344	dbg_pnp_show_resources(dev, "pnp_start_dev");345	if (dev->protocol->set(dev) < 0) {346		dev_err(&dev->dev, "activation failed\n");347		return -EIO;348	}349 350	dev_info(&dev->dev, "activated\n");351	return 0;352}353EXPORT_SYMBOL(pnp_start_dev);354 355/**356 * pnp_stop_dev - low-level disable of the PnP device357 * @dev: pointer to the desired device358 *359 * does not free resources360 */361int pnp_stop_dev(struct pnp_dev *dev)362{363	if (!pnp_can_disable(dev)) {364		pnp_dbg(&dev->dev, "disabling not supported\n");365		return -EINVAL;366	}367	if (dev->protocol->disable(dev) < 0) {368		dev_err(&dev->dev, "disable failed\n");369		return -EIO;370	}371 372	dev_info(&dev->dev, "disabled\n");373	return 0;374}375EXPORT_SYMBOL(pnp_stop_dev);376 377/**378 * pnp_activate_dev - activates a PnP device for use379 * @dev: pointer to the desired device380 *381 * does not validate or set resources so be careful.382 */383int pnp_activate_dev(struct pnp_dev *dev)384{385	int error;386 387	if (dev->active)388		return 0;389 390	/* ensure resources are allocated */391	if (pnp_auto_config_dev(dev))392		return -EBUSY;393 394	error = pnp_start_dev(dev);395	if (error)396		return error;397 398	dev->active = 1;399	return 0;400}401EXPORT_SYMBOL(pnp_activate_dev);402 403/**404 * pnp_disable_dev - disables device405 * @dev: pointer to the desired device406 *407 * inform the correct pnp protocol so that resources can be used by other devices408 */409int pnp_disable_dev(struct pnp_dev *dev)410{411	int error;412 413	if (!dev->active)414		return 0;415 416	error = pnp_stop_dev(dev);417	if (error)418		return error;419 420	dev->active = 0;421 422	/* release the resources so that other devices can use them */423	mutex_lock(&pnp_res_mutex);424	pnp_clean_resource_table(dev);425	mutex_unlock(&pnp_res_mutex);426 427	return 0;428}429EXPORT_SYMBOL(pnp_disable_dev);430