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