resource.c 12.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 * resource.c - Contains functions for registering and analyzing resource information
 *
4
 * based on isapnp.c resource management (c) Jaroslav Kysela <perex@perex.cz>
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * Copyright 2003 Adam Belay <ambx1@neo.rr.com>
 */

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/init.h>

#include <linux/pnp.h>
#include "base.h"

B
Bjorn Helgaas 已提交
22 23 24 25
static int pnp_reserve_irq[16] = {[0 ... 15] = -1 };	/* reserve (don't use) some IRQ */
static int pnp_reserve_dma[8] = {[0 ... 7] = -1 };	/* reserve (don't use) some DMA */
static int pnp_reserve_io[16] = {[0 ... 15] = -1 };	/* reserve (don't use) some I/O region */
static int pnp_reserve_mem[16] = {[0 ... 15] = -1 };	/* reserve (don't use) some memory region */
L
Linus Torvalds 已提交
26 27 28 29 30

/*
 * option registration
 */

B
Bjorn Helgaas 已提交
31
static struct pnp_option *pnp_build_option(int priority)
L
Linus Torvalds 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45
{
	struct pnp_option *option = pnp_alloc(sizeof(struct pnp_option));

	if (!option)
		return NULL;

	option->priority = priority & 0xff;
	/* make sure the priority is valid */
	if (option->priority > PNP_RES_PRIORITY_FUNCTIONAL)
		option->priority = PNP_RES_PRIORITY_INVALID;

	return option;
}

B
Bjorn Helgaas 已提交
46
struct pnp_option *pnp_register_independent_option(struct pnp_dev *dev)
L
Linus Torvalds 已提交
47 48
{
	struct pnp_option *option;
B
Bjorn Helgaas 已提交
49

L
Linus Torvalds 已提交
50 51 52 53
	option = pnp_build_option(PNP_RES_PRIORITY_PREFERRED);

	/* this should never happen but if it does we'll try to continue */
	if (dev->independent)
54
		dev_err(&dev->dev, "independent resource already registered\n");
L
Linus Torvalds 已提交
55
	dev->independent = option;
56 57

	dev_dbg(&dev->dev, "new independent option\n");
L
Linus Torvalds 已提交
58 59 60
	return option;
}

B
Bjorn Helgaas 已提交
61 62
struct pnp_option *pnp_register_dependent_option(struct pnp_dev *dev,
						 int priority)
L
Linus Torvalds 已提交
63 64
{
	struct pnp_option *option;
B
Bjorn Helgaas 已提交
65

L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74
	option = pnp_build_option(priority);

	if (dev->dependent) {
		struct pnp_option *parent = dev->dependent;
		while (parent->next)
			parent = parent->next;
		parent->next = option;
	} else
		dev->dependent = option;
75 76

	dev_dbg(&dev->dev, "new dependent option (priority %#x)\n", priority);
L
Linus Torvalds 已提交
77 78 79
	return option;
}

80 81
int pnp_register_irq_resource(struct pnp_dev *dev, struct pnp_option *option,
			      struct pnp_irq *data)
L
Linus Torvalds 已提交
82 83
{
	struct pnp_irq *ptr;
84 85 86
#ifdef DEBUG
	char buf[PNP_IRQ_NR];   /* hex-encoded, so this is overkill but safe */
#endif
B
Bjorn Helgaas 已提交
87

L
Linus Torvalds 已提交
88 89 90 91 92 93 94 95 96 97 98 99 100 101
	ptr = option->irq;
	while (ptr && ptr->next)
		ptr = ptr->next;
	if (ptr)
		ptr->next = data;
	else
		option->irq = data;

#ifdef CONFIG_PCI
	{
		int i;

		for (i = 0; i < 16; i++)
			if (test_bit(i, data->map))
D
David Shaohua Li 已提交
102
				pcibios_penalize_isa_irq(i, 0);
L
Linus Torvalds 已提交
103 104
	}
#endif
105 106 107 108 109 110

#ifdef DEBUG
	bitmap_scnprintf(buf, sizeof(buf), data->map, PNP_IRQ_NR);
	dev_dbg(&dev->dev, "  irq bitmask %s flags %#x\n", buf,
		data->flags);
#endif
L
Linus Torvalds 已提交
111 112 113
	return 0;
}

114 115
int pnp_register_dma_resource(struct pnp_dev *dev, struct pnp_option *option,
			      struct pnp_dma *data)
L
Linus Torvalds 已提交
116 117
{
	struct pnp_dma *ptr;
B
Bjorn Helgaas 已提交
118

L
Linus Torvalds 已提交
119 120 121 122 123 124 125 126
	ptr = option->dma;
	while (ptr && ptr->next)
		ptr = ptr->next;
	if (ptr)
		ptr->next = data;
	else
		option->dma = data;

127 128
	dev_dbg(&dev->dev, "  dma bitmask %#x flags %#x\n", data->map,
		data->flags);
L
Linus Torvalds 已提交
129 130 131
	return 0;
}

132 133
int pnp_register_port_resource(struct pnp_dev *dev, struct pnp_option *option,
			       struct pnp_port *data)
L
Linus Torvalds 已提交
134 135
{
	struct pnp_port *ptr;
B
Bjorn Helgaas 已提交
136

L
Linus Torvalds 已提交
137 138 139 140 141 142 143 144
	ptr = option->port;
	while (ptr && ptr->next)
		ptr = ptr->next;
	if (ptr)
		ptr->next = data;
	else
		option->port = data;

145 146 147
	dev_dbg(&dev->dev, "  io  "
		"min %#x max %#x align %d size %d flags %#x\n",
		data->min, data->max, data->align, data->size, data->flags);
L
Linus Torvalds 已提交
148 149 150
	return 0;
}

151 152
int pnp_register_mem_resource(struct pnp_dev *dev, struct pnp_option *option,
			      struct pnp_mem *data)
L
Linus Torvalds 已提交
153 154
{
	struct pnp_mem *ptr;
B
Bjorn Helgaas 已提交
155

L
Linus Torvalds 已提交
156 157 158 159 160 161 162
	ptr = option->mem;
	while (ptr && ptr->next)
		ptr = ptr->next;
	if (ptr)
		ptr->next = data;
	else
		option->mem = data;
163 164 165 166

	dev_dbg(&dev->dev, "  mem "
		"min %#x max %#x align %d size %d flags %#x\n",
		data->min, data->max, data->align, data->size, data->flags);
L
Linus Torvalds 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
	return 0;
}

static void pnp_free_port(struct pnp_port *port)
{
	struct pnp_port *next;

	while (port) {
		next = port->next;
		kfree(port);
		port = next;
	}
}

static void pnp_free_irq(struct pnp_irq *irq)
{
	struct pnp_irq *next;

	while (irq) {
		next = irq->next;
		kfree(irq);
		irq = next;
	}
}

static void pnp_free_dma(struct pnp_dma *dma)
{
	struct pnp_dma *next;

	while (dma) {
		next = dma->next;
		kfree(dma);
		dma = next;
	}
}

static void pnp_free_mem(struct pnp_mem *mem)
{
	struct pnp_mem *next;

	while (mem) {
		next = mem->next;
		kfree(mem);
		mem = next;
	}
}

void pnp_free_option(struct pnp_option *option)
{
	struct pnp_option *next;

	while (option) {
		next = option->next;
		pnp_free_port(option->port);
		pnp_free_irq(option->irq);
		pnp_free_dma(option->dma);
		pnp_free_mem(option->mem);
		kfree(option);
		option = next;
	}
}

/*
 * resource validity checking
 */

#define length(start, end) (*(end) - *(start) + 1)

/* Two ranges conflict if one doesn't end before the other starts */
#define ranged_conflict(starta, enda, startb, endb) \
	!((*(enda) < *(startb)) || (*(endb) < *(starta)))

#define cannot_compare(flags) \
((flags) & (IORESOURCE_UNSET | IORESOURCE_DISABLED))

B
Bjorn Helgaas 已提交
242
int pnp_check_port(struct pnp_dev *dev, int idx)
L
Linus Torvalds 已提交
243
{
244
	int i;
L
Linus Torvalds 已提交
245
	struct pnp_dev *tdev;
246
	struct resource *res, *tres;
247
	resource_size_t *port, *end, *tport, *tend;
B
Bjorn Helgaas 已提交
248

249 250 251
	res = &dev->res.port_resource[idx];
	port = &res->start;
	end = &res->end;
L
Linus Torvalds 已提交
252 253

	/* if the resource doesn't exist, don't complain about it */
254
	if (cannot_compare(res->flags))
L
Linus Torvalds 已提交
255 256 257 258
		return 1;

	/* check if the resource is already in use, skip if the
	 * device is active because it itself may be in use */
B
Bjorn Helgaas 已提交
259 260
	if (!dev->active) {
		if (__check_region(&ioport_resource, *port, length(port, end)))
L
Linus Torvalds 已提交
261 262 263 264
			return 0;
	}

	/* check if the resource is reserved */
265 266 267
	for (i = 0; i < 8; i++) {
		int rport = pnp_reserve_io[i << 1];
		int rend = pnp_reserve_io[(i << 1) + 1] + rport - 1;
B
Bjorn Helgaas 已提交
268
		if (ranged_conflict(port, end, &rport, &rend))
L
Linus Torvalds 已提交
269 270 271 272
			return 0;
	}

	/* check for internal conflicts */
273
	for (i = 0; i < PNP_MAX_PORT; i++) {
274
		tres = &dev->res.port_resource[i];
275
		if (tres != res && tres->flags & IORESOURCE_IO) {
276 277
			tport = &tres->start;
			tend = &tres->end;
B
Bjorn Helgaas 已提交
278
			if (ranged_conflict(port, end, tport, tend))
L
Linus Torvalds 已提交
279 280 281 282 283 284 285 286
				return 0;
		}
	}

	/* check for conflicts with other pnp devices */
	pnp_for_each_dev(tdev) {
		if (tdev == dev)
			continue;
287
		for (i = 0; i < PNP_MAX_PORT; i++) {
288 289 290
			tres = &tdev->res.port_resource[i];
			if (tres->flags & IORESOURCE_IO) {
				if (cannot_compare(tres->flags))
L
Linus Torvalds 已提交
291
					continue;
292 293
				tport = &tres->start;
				tend = &tres->end;
B
Bjorn Helgaas 已提交
294
				if (ranged_conflict(port, end, tport, tend))
L
Linus Torvalds 已提交
295 296 297 298 299 300 301 302
					return 0;
			}
		}
	}

	return 1;
}

B
Bjorn Helgaas 已提交
303
int pnp_check_mem(struct pnp_dev *dev, int idx)
L
Linus Torvalds 已提交
304
{
305
	int i;
L
Linus Torvalds 已提交
306
	struct pnp_dev *tdev;
307
	struct resource *res, *tres;
308
	resource_size_t *addr, *end, *taddr, *tend;
B
Bjorn Helgaas 已提交
309

310 311 312
	res = &dev->res.mem_resource[idx];
	addr = &res->start;
	end = &res->end;
L
Linus Torvalds 已提交
313 314

	/* if the resource doesn't exist, don't complain about it */
315
	if (cannot_compare(res->flags))
L
Linus Torvalds 已提交
316 317 318 319
		return 1;

	/* check if the resource is already in use, skip if the
	 * device is active because it itself may be in use */
B
Bjorn Helgaas 已提交
320 321
	if (!dev->active) {
		if (check_mem_region(*addr, length(addr, end)))
L
Linus Torvalds 已提交
322 323 324 325
			return 0;
	}

	/* check if the resource is reserved */
326 327 328
	for (i = 0; i < 8; i++) {
		int raddr = pnp_reserve_mem[i << 1];
		int rend = pnp_reserve_mem[(i << 1) + 1] + raddr - 1;
B
Bjorn Helgaas 已提交
329
		if (ranged_conflict(addr, end, &raddr, &rend))
L
Linus Torvalds 已提交
330 331 332 333
			return 0;
	}

	/* check for internal conflicts */
334
	for (i = 0; i < PNP_MAX_MEM; i++) {
335
		tres = &dev->res.mem_resource[i];
336
		if (tres != res && tres->flags & IORESOURCE_MEM) {
337 338
			taddr = &tres->start;
			tend = &tres->end;
B
Bjorn Helgaas 已提交
339
			if (ranged_conflict(addr, end, taddr, tend))
L
Linus Torvalds 已提交
340 341 342 343 344 345 346 347
				return 0;
		}
	}

	/* check for conflicts with other pnp devices */
	pnp_for_each_dev(tdev) {
		if (tdev == dev)
			continue;
348
		for (i = 0; i < PNP_MAX_MEM; i++) {
349 350 351
			tres = &tdev->res.mem_resource[i];
			if (tres->flags & IORESOURCE_MEM) {
				if (cannot_compare(tres->flags))
L
Linus Torvalds 已提交
352
					continue;
353 354
				taddr = &tres->start;
				tend = &tres->end;
B
Bjorn Helgaas 已提交
355
				if (ranged_conflict(addr, end, taddr, tend))
L
Linus Torvalds 已提交
356 357 358 359 360 361 362 363
					return 0;
			}
		}
	}

	return 1;
}

364
static irqreturn_t pnp_test_handler(int irq, void *dev_id)
L
Linus Torvalds 已提交
365 366 367 368
{
	return IRQ_HANDLED;
}

B
Bjorn Helgaas 已提交
369
int pnp_check_irq(struct pnp_dev *dev, int idx)
L
Linus Torvalds 已提交
370
{
371
	int i;
L
Linus Torvalds 已提交
372
	struct pnp_dev *tdev;
373 374 375 376 377
	struct resource *res, *tres;
	resource_size_t *irq;

	res = &dev->res.irq_resource[idx];
	irq = &res->start;
L
Linus Torvalds 已提交
378 379

	/* if the resource doesn't exist, don't complain about it */
380
	if (cannot_compare(res->flags))
L
Linus Torvalds 已提交
381 382 383 384 385 386 387
		return 1;

	/* check if the resource is valid */
	if (*irq < 0 || *irq > 15)
		return 0;

	/* check if the resource is reserved */
388 389
	for (i = 0; i < 16; i++) {
		if (pnp_reserve_irq[i] == *irq)
L
Linus Torvalds 已提交
390 391 392 393
			return 0;
	}

	/* check for internal conflicts */
394
	for (i = 0; i < PNP_MAX_IRQ; i++) {
395
		tres = &dev->res.irq_resource[i];
396
		if (tres != res && tres->flags & IORESOURCE_IRQ) {
397
			if (tres->start == *irq)
L
Linus Torvalds 已提交
398 399 400 401 402 403 404 405 406
				return 0;
		}
	}

#ifdef CONFIG_PCI
	/* check if the resource is being used by a pci device */
	{
		struct pci_dev *pci = NULL;
		for_each_pci_dev(pci) {
407 408
			if (pci->irq == *irq) {
				pci_dev_put(pci);
L
Linus Torvalds 已提交
409
				return 0;
410
			}
L
Linus Torvalds 已提交
411 412 413 414 415 416
		}
	}
#endif

	/* check if the resource is already in use, skip if the
	 * device is active because it itself may be in use */
B
Bjorn Helgaas 已提交
417
	if (!dev->active) {
418
		if (request_irq(*irq, pnp_test_handler,
B
Bjorn Helgaas 已提交
419
				IRQF_DISABLED | IRQF_PROBE_SHARED, "pnp", NULL))
L
Linus Torvalds 已提交
420 421 422 423 424 425 426 427
			return 0;
		free_irq(*irq, NULL);
	}

	/* check for conflicts with other pnp devices */
	pnp_for_each_dev(tdev) {
		if (tdev == dev)
			continue;
428
		for (i = 0; i < PNP_MAX_IRQ; i++) {
429 430 431
			tres = &tdev->res.irq_resource[i];
			if (tres->flags & IORESOURCE_IRQ) {
				if (cannot_compare(tres->flags))
L
Linus Torvalds 已提交
432
					continue;
433
				if (tres->start == *irq)
L
Linus Torvalds 已提交
434 435 436 437 438 439 440 441
					return 0;
			}
		}
	}

	return 1;
}

B
Bjorn Helgaas 已提交
442
int pnp_check_dma(struct pnp_dev *dev, int idx)
L
Linus Torvalds 已提交
443 444
{
#ifndef CONFIG_IA64
445
	int i;
L
Linus Torvalds 已提交
446
	struct pnp_dev *tdev;
447 448 449 450 451
	struct resource *res, *tres;
	resource_size_t *dma;

	res = &dev->res.dma_resource[idx];
	dma = &res->start;
L
Linus Torvalds 已提交
452 453

	/* if the resource doesn't exist, don't complain about it */
454
	if (cannot_compare(res->flags))
L
Linus Torvalds 已提交
455 456 457 458 459 460 461
		return 1;

	/* check if the resource is valid */
	if (*dma < 0 || *dma == 4 || *dma > 7)
		return 0;

	/* check if the resource is reserved */
462 463
	for (i = 0; i < 8; i++) {
		if (pnp_reserve_dma[i] == *dma)
L
Linus Torvalds 已提交
464 465 466 467
			return 0;
	}

	/* check for internal conflicts */
468
	for (i = 0; i < PNP_MAX_DMA; i++) {
469
		tres = &dev->res.dma_resource[i];
470
		if (tres != res && tres->flags & IORESOURCE_DMA) {
471
			if (tres->start == *dma)
L
Linus Torvalds 已提交
472 473 474 475 476 477
				return 0;
		}
	}

	/* check if the resource is already in use, skip if the
	 * device is active because it itself may be in use */
B
Bjorn Helgaas 已提交
478
	if (!dev->active) {
L
Linus Torvalds 已提交
479 480 481 482 483 484 485 486 487
		if (request_dma(*dma, "pnp"))
			return 0;
		free_dma(*dma);
	}

	/* check for conflicts with other pnp devices */
	pnp_for_each_dev(tdev) {
		if (tdev == dev)
			continue;
488
		for (i = 0; i < PNP_MAX_DMA; i++) {
489 490 491
			tres = &tdev->res.dma_resource[i];
			if (tres->flags & IORESOURCE_DMA) {
				if (cannot_compare(tres->flags))
L
Linus Torvalds 已提交
492
					continue;
493
				if (tres->start == *dma)
L
Linus Torvalds 已提交
494 495 496 497 498 499 500
					return 0;
			}
		}
	}

	return 1;
#else
B
Bjorn Helgaas 已提交
501
	/* IA64 does not have legacy DMA */
L
Linus Torvalds 已提交
502 503 504 505
	return 0;
#endif
}

506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
struct resource *pnp_get_resource(struct pnp_dev *dev,
				  unsigned int type, unsigned int num)
{
	struct pnp_resource_table *res = &dev->res;

	switch (type) {
	case IORESOURCE_IO:
		if (num >= PNP_MAX_PORT)
			return NULL;
		return &res->port_resource[num];
	case IORESOURCE_MEM:
		if (num >= PNP_MAX_MEM)
			return NULL;
		return &res->mem_resource[num];
	case IORESOURCE_IRQ:
		if (num >= PNP_MAX_IRQ)
			return NULL;
		return &res->irq_resource[num];
	case IORESOURCE_DMA:
		if (num >= PNP_MAX_DMA)
			return NULL;
		return &res->dma_resource[num];
	}
	return NULL;
}
EXPORT_SYMBOL(pnp_get_resource);

L
Linus Torvalds 已提交
533 534 535 536 537 538
/* format is: pnp_reserve_irq=irq1[,irq2] .... */
static int __init pnp_setup_reserve_irq(char *str)
{
	int i;

	for (i = 0; i < 16; i++)
B
Bjorn Helgaas 已提交
539
		if (get_option(&str, &pnp_reserve_irq[i]) != 2)
L
Linus Torvalds 已提交
540 541 542 543 544 545 546 547 548 549 550 551
			break;
	return 1;
}

__setup("pnp_reserve_irq=", pnp_setup_reserve_irq);

/* format is: pnp_reserve_dma=dma1[,dma2] .... */
static int __init pnp_setup_reserve_dma(char *str)
{
	int i;

	for (i = 0; i < 8; i++)
B
Bjorn Helgaas 已提交
552
		if (get_option(&str, &pnp_reserve_dma[i]) != 2)
L
Linus Torvalds 已提交
553 554 555 556 557 558 559 560 561 562 563 564
			break;
	return 1;
}

__setup("pnp_reserve_dma=", pnp_setup_reserve_dma);

/* format is: pnp_reserve_io=io1,size1[,io2,size2] .... */
static int __init pnp_setup_reserve_io(char *str)
{
	int i;

	for (i = 0; i < 16; i++)
B
Bjorn Helgaas 已提交
565
		if (get_option(&str, &pnp_reserve_io[i]) != 2)
L
Linus Torvalds 已提交
566 567 568 569 570 571 572 573 574 575 576 577
			break;
	return 1;
}

__setup("pnp_reserve_io=", pnp_setup_reserve_io);

/* format is: pnp_reserve_mem=mem1,size1[,mem2,size2] .... */
static int __init pnp_setup_reserve_mem(char *str)
{
	int i;

	for (i = 0; i < 16; i++)
B
Bjorn Helgaas 已提交
578
		if (get_option(&str, &pnp_reserve_mem[i]) != 2)
L
Linus Torvalds 已提交
579 580 581 582 583
			break;
	return 1;
}

__setup("pnp_reserve_mem=", pnp_setup_reserve_mem);