aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips
Commit message (Expand)AuthorAge
* [MIPS] Use __ffs() instead of ffs() for waybit calculation.Atsushi Nemoto2006-04-18
* [MIPS] Fix Makefile bugs for MIPS32/MIPS64 R1 and R2.Ralf Baechle2006-04-18
* [MIPS] Handle IDE PIO cache aliases on SMP.Ralf Baechle2006-04-18
* [MIPS] Make mips_srs_init static.Ralf Baechle2006-04-18
* [MIPS] kgdb: Let gcc compute the array size itself.Ralf Baechle2006-04-18
* [MIPS] FPU affinity for MT ASE.Ralf Baechle2006-04-18
* [MIPS] MT: Improved multithreading support.Ralf Baechle2006-04-18
* [MIPS] kpsd and other AP/SP improvements.Ralf Baechle2006-04-18
* [MIPS] R2: Instruction hazard barrier.Ralf Baechle2006-04-18
* [MIPS] R2: Implement shadow register allocation without spinlock.Ralf Baechle2006-04-18
* [MIPS] Fix VR41xx build errors.Yoichi Yuasa2006-04-18
* [MIPS] Fix tx49_blast_icache32_page_indexed.Atsushi Nemoto2006-04-18
* [MIPS] Enable SCHED_NO_NO_OMIT_FRAME_POINTER for MIPS.Atsushi Nemoto2006-04-18
* [MIPS] Rewrite all the assembler interrupt handlers to C.Ralf Baechle2006-04-18
* [MIPS] Fix the crime against humanity that mipsIRQ.S is.Ralf Baechle2006-04-18
* [MIPS] Fixup damage done by 22a9835c350782a5c3257343713932af3ac92ee0.Ralf Baechle2006-04-18
* [MIPS] Replace redundant declarations of _end by <asm/sections.h>.Ralf Baechle2006-04-18
* [MIPS] JMR3927 build fixes for the RTC code.Ralf Baechle2006-04-18
* [MIPS] EV96100: ev96100_cpu_irq needs a struct pt_regs argument.Ralf Baechle2006-04-18
* [MIPS] EV96100: Fix over two year old typo in variable name.Ralf Baechle2006-04-18
* [MIPS] Ocelot 3: Fix build errors after the recent move of Marvell headers.Ralf Baechle2006-04-18
* [MIPS] ITE: Glue build.Ralf Baechle2006-04-18
* [MIPS] Jaguar: Fix build errors after the recent move of Marvell headers.Ralf Baechle2006-04-18
* [MIPS] ITE8172: Fix build error due to missmatching prototypes.Ralf Baechle2006-04-18
* [MIPS] Fix CONFIG_LIMITED_DMA build.Ralf Baechle2006-04-18
* [MIPS] PNX8550 build fix.Ralf Baechle2006-04-18
* [MIPS] Rewrite spurious_interrupt from assembler to C.Ralf Baechle2006-04-18
* [MIPS] Fix breakage due to the grand makefile crapectomy.Ralf Baechle2006-04-18
* [MIPS] Sort out duplicate exports.Ralf Baechle2006-04-18
* [MIPS] Wire up sync_file_range(2).Ralf Baechle2006-04-18
* [MIPS] Wire splice syscall.Ralf Baechle2006-04-18
* [MIPS] Fix vectored interrupt support in TLB exception handler generator.Ralf Baechle2006-04-18
* [MIPS] Some formatting fixes.Ralf Baechle2006-04-18
* [MIPS] Fixup printk in mips_srs_init.Ralf Baechle2006-04-18
* [MIPS] Remove redundant initialization of sr_allocated.Ralf Baechle2006-04-18
* [MIPS] Make set_vi_srs_handler static.Ralf Baechle2006-04-18
* [MIPS] Cleanup free_initmem the same way as i386 did.Ralf Baechle2006-04-18
* [MIPS] Added tb0287_defconfig back.Yoichi Yuasa2006-04-18
* [PATCH] No arch-specific strpbrk implementationsKyle McMartin2006-04-11
* [PATCH] Configurable NODES_SHIFTYasunori Goto2006-04-11
* Manual merge with Linus.Dmitry Torokhov2006-04-02
|\
| * [PATCH] unexport get_wchanAdrian Bunk2006-03-31
| * [PATCH] remove add_parent()'s parent argumentOleg Nesterov2006-03-28
| * Merge master.kernel.org:/home/rmk/linux-2.6-serialLinus Torvalds2006-03-28
| |\
| | * [SERIAL] Remove obsoleted au1x00_uart driverRalf Baechle2006-03-26
| * | [PATCH] RTC: Remove some duplicate BCD definitionsMatt Mackall2006-03-28
| * | [PATCH] RTC: Remove RTC UIP synchronization on MIPS-based DECMatt Mackall2006-03-28
| * | [PATCH] mips: fixed collision of rtc function nameYoichi Yuasa2006-03-27
| * | [PATCH] Notifier chain update: API changesAlan Stern2006-03-27
| * | [PATCH] unify PFN_* macrosDave Hansen2006-03-27
n461'>461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 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 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
/*
 *	linux/kernel/resource.c
 *
 * Copyright (C) 1999	Linus Torvalds
 * Copyright (C) 1999	Martin Mares <mj@ucw.cz>
 *
 * Arbitrary resource management.
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/io.h>


struct resource ioport_resource = {
	.name	= "PCI IO",
	.start	= 0x0000,
	.end	= IO_SPACE_LIMIT,
	.flags	= IORESOURCE_IO,
};

EXPORT_SYMBOL(ioport_resource);

struct resource iomem_resource = {
	.name	= "PCI mem",
	.start	= 0UL,
	.end	= ~0UL,
	.flags	= IORESOURCE_MEM,
};

EXPORT_SYMBOL(iomem_resource);

static DEFINE_RWLOCK(resource_lock);

#ifdef CONFIG_PROC_FS

enum { MAX_IORES_LEVEL = 5 };

static void *r_next(struct seq_file *m, void *v, loff_t *pos)
{
	struct resource *p = v;
	(*pos)++;
	if (p->child)
		return p->child;
	while (!p->sibling && p->parent)
		p = p->parent;
	return p->sibling;
}

static void *r_start(struct seq_file *m, loff_t *pos)
	__acquires(resource_lock)
{
	struct resource *p = m->private;
	loff_t l = 0;
	read_lock(&resource_lock);
	for (p = p->child; p && l < *pos; p = r_next(m, p, &l))
		;
	return p;
}

static void r_stop(struct seq_file *m, void *v)
	__releases(resource_lock)
{
	read_unlock(&resource_lock);
}

static int r_show(struct seq_file *m, void *v)
{
	struct resource *root = m->private;
	struct resource *r = v, *p;
	int width = root->end < 0x10000 ? 4 : 8;
	int depth;

	for (depth = 0, p = r; depth < MAX_IORES_LEVEL; depth++, p = p->parent)
		if (p->parent == root)
			break;
	seq_printf(m, "%*s%0*lx-%0*lx : %s\n",
			depth * 2, "",
			width, r->start,
			width, r->end,
			r->name ? r->name : "<BAD>");
	return 0;
}

static struct seq_operations resource_op = {
	.start	= r_start,
	.next	= r_next,
	.stop	= r_stop,
	.show	= r_show,
};

static int ioports_open(struct inode *inode, struct file *file)
{
	int res = seq_open(file, &resource_op);
	if (!res) {
		struct seq_file *m = file->private_data;
		m->private = &ioport_resource;
	}
	return res;
}

static int iomem_open(struct inode *inode, struct file *file)
{
	int res = seq_open(file, &resource_op);
	if (!res) {
		struct seq_file *m = file->private_data;
		m->private = &iomem_resource;
	}
	return res;
}

static struct file_operations proc_ioports_operations = {
	.open		= ioports_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
};

static struct file_operations proc_iomem_operations = {
	.open		= iomem_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
};

static int __init ioresources_init(void)
{
	struct proc_dir_entry *entry;

	entry = create_proc_entry("ioports", 0, NULL);
	if (entry)
		entry->proc_fops = &proc_ioports_operations;
	entry = create_proc_entry("iomem", 0, NULL);
	if (entry)
		entry->proc_fops = &proc_iomem_operations;
	return 0;
}
__initcall(ioresources_init);

#endif /* CONFIG_PROC_FS */

/* Return the conflict entry if you can't request it */
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
	unsigned long start = new->start;
	unsigned long end = new->end;
	struct resource *tmp, **p;

	if (end < start)
		return root;
	if (start < root->start)
		return root;
	if (end > root->end)
		return root;
	p = &root->child;
	for (;;) {
		tmp = *p;
		if (!tmp || tmp->start > end) {
			new->sibling = tmp;
			*p = new;
			new->parent = root;
			return NULL;
		}
		p = &tmp->sibling;
		if (tmp->end < start)
			continue;
		return tmp;
	}
}

static int __release_resource(struct resource *old)
{
	struct resource *tmp, **p;

	p = &old->parent->child;
	for (;;) {
		tmp = *p;
		if (!tmp)
			break;
		if (tmp == old) {
			*p = tmp->sibling;
			old->parent = NULL;
			return 0;
		}
		p = &tmp->sibling;
	}
	return -EINVAL;
}

int request_resource(struct resource *root, struct resource *new)
{
	struct resource *conflict;

	write_lock(&resource_lock);
	conflict = __request_resource(root, new);
	write_unlock(&resource_lock);
	return conflict ? -EBUSY : 0;
}

EXPORT_SYMBOL(request_resource);

struct resource *____request_resource(struct resource *root, struct resource *new)
{
	struct resource *conflict;

	write_lock(&resource_lock);
	conflict = __request_resource(root, new);
	write_unlock(&resource_lock);
	return conflict;
}

EXPORT_SYMBOL(____request_resource);

int release_resource(struct resource *old)
{
	int retval;

	write_lock(&resource_lock);
	retval = __release_resource(old);
	write_unlock(&resource_lock);
	return retval;
}

EXPORT_SYMBOL(release_resource);

/*
 * Find empty slot in the resource tree given range and alignment.
 */
static int find_resource(struct resource *root, struct resource *new,
			 unsigned long size,
			 unsigned long min, unsigned long max,
			 unsigned long align,
			 void (*alignf)(void *, struct resource *,
					unsigned long, unsigned long),
			 void *alignf_data)
{
	struct resource *this = root->child;

	new->start = root->start;
	/*
	 * Skip past an allocated resource that starts at 0, since the assignment
	 * of this->start - 1 to new->end below would cause an underflow.
	 */
	if (this && this->start == 0) {
		new->start = this->end + 1;
		this = this->sibling;
	}
	for(;;) {
		if (this)
			new->end = this->start - 1;
		else
			new->end = root->end;
		if (new->start < min)
			new->start = min;
		if (new->end > max)
			new->end = max;
		new->start = (new->start + align - 1) & ~(align - 1);
		if (alignf)
			alignf(alignf_data, new, size, align);
		if (new->start < new->end && new->end - new->start >= size - 1) {
			new->end = new->start + size - 1;
			return 0;
		}
		if (!this)
			break;
		new->start = this->end + 1;
		this = this->sibling;
	}
	return -EBUSY;
}

/*
 * Allocate empty slot in the resource tree given range and alignment.
 */
int allocate_resource(struct resource *root, struct resource *new,
		      unsigned long size,
		      unsigned long min, unsigned long max,
		      unsigned long align,
		      void (*alignf)(void *, struct resource *,
				     unsigned long, unsigned long),
		      void *alignf_data)
{
	int err;

	write_lock(&resource_lock);
	err = find_resource(root, new, size, min, max, align, alignf, alignf_data);
	if (err >= 0 && __request_resource(root, new))
		err = -EBUSY;
	write_unlock(&resource_lock);
	return err;
}

EXPORT_SYMBOL(allocate_resource);

/**
 * insert_resource - Inserts a resource in the resource tree
 * @parent: parent of the new resource
 * @new: new resource to insert
 *
 * Returns 0 on success, -EBUSY if the resource can't be inserted.
 *
 * This function is equivalent of request_resource when no conflict
 * happens. If a conflict happens, and the conflicting resources
 * entirely fit within the range of the new resource, then the new
 * resource is inserted and the conflicting resources become childs of
 * the new resource.  Otherwise the new resource becomes the child of
 * the conflicting resource
 */
int insert_resource(struct resource *parent, struct resource *new)
{
	int result;
	struct resource *first, *next;

	write_lock(&resource_lock);
 begin:
 	result = 0;
	first = __request_resource(parent, new);
	if (!first)
		goto out;

	result = -EBUSY;
	if (first == parent)
		goto out;

	/* Resource fully contained by the clashing resource? Recurse into it */
	if (first->start <= new->start && first->end >= new->end) {
		parent = first;
		goto begin;
	}

	for (next = first; ; next = next->sibling) {
		/* Partial overlap? Bad, and unfixable */
		if (next->start < new->start || next->end > new->end)
			goto out;
		if (!next->sibling)
			break;
		if (next->sibling->start > new->end)
			break;
	}

	result = 0;

	new->parent = parent;
	new->sibling = next->sibling;
	new->child = first;

	next->sibling = NULL;
	for (next = first; next; next = next->sibling)
		next->parent = new;

	if (parent->child == first) {
		parent->child = new;
	} else {
		next = parent->child;
		while (next->sibling != first)
			next = next->sibling;
		next->sibling = new;
	}

 out:
	write_unlock(&resource_lock);
	return result;
}

EXPORT_SYMBOL(insert_resource);

/*
 * Given an existing resource, change its start and size to match the
 * arguments.  Returns -EBUSY if it can't fit.  Existing children of
 * the resource are assumed to be immutable.
 */
int adjust_resource(struct resource *res, unsigned long start, unsigned long size)
{
	struct resource *tmp, *parent = res->parent;
	unsigned long end = start + size - 1;
	int result = -EBUSY;

	write_lock(&resource_lock);

	if ((start < parent->start) || (end > parent->end))
		goto out;

	for (tmp = res->child; tmp; tmp = tmp->sibling) {
		if ((tmp->start < start) || (tmp->end > end))
			goto out;
	}

	if (res->sibling && (res->sibling->start <= end))
		goto out;

	tmp = parent->child;
	if (tmp != res) {
		while (tmp->sibling != res)
			tmp = tmp->sibling;
		if (start <= tmp->end)
			goto out;
	}

	res->start = start;
	res->end = end;
	result = 0;

 out:
	write_unlock(&resource_lock);
	return result;
}

EXPORT_SYMBOL(adjust_resource);

/*
 * This is compatibility stuff for IO resources.
 *
 * Note how this, unlike the above, knows about
 * the IO flag meanings (busy etc).
 *
 * Request-region creates a new busy region.
 *
 * Check-region returns non-zero if the area is already busy
 *
 * Release-region releases a matching busy region.
 */
struct resource * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name)
{
	struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);

	if (res) {
		memset(res, 0, sizeof(*res));
		res->name = name;
		res->start = start;
		res->end = start + n - 1;
		res->flags = IORESOURCE_BUSY;

		write_lock(&resource_lock);

		for (;;) {
			struct resource *conflict;

			conflict = __request_resource(parent, res);
			if (!conflict)
				break;
			if (conflict != parent) {
				parent = conflict;
				if (!(conflict->flags & IORESOURCE_BUSY))
					continue;
			}

			/* Uhhuh, that didn't work out.. */
			kfree(res);
			res = NULL;
			break;
		}
		write_unlock(&resource_lock);
	}
	return res;
}

EXPORT_SYMBOL(__request_region);

int __deprecated __check_region(struct resource *parent, unsigned long start, unsigned long n)
{
	struct resource * res;

	res = __request_region(parent, start, n, "check-region");
	if (!res)
		return -EBUSY;

	release_resource(res);
	kfree(res);
	return 0;
}

EXPORT_SYMBOL(__check_region);

void __release_region(struct resource *parent, unsigned long start, unsigned long n)
{
	struct resource **p;
	unsigned long end;

	p = &parent->child;
	end = start + n - 1;

	write_lock(&resource_lock);

	for (;;) {
		struct resource *res = *p;

		if (!res)
			break;
		if (res->start <= start && res->end >= end) {
			if (!(res->flags & IORESOURCE_BUSY)) {
				p = &res->child;
				continue;
			}
			if (res->start != start || res->end != end)
				break;
			*p = res->sibling;
			write_unlock(&resource_lock);
			kfree(res);
			return;
		}
		p = &res->sibling;
	}

	write_unlock(&resource_lock);

	printk(KERN_WARNING "Trying to free nonexistent resource <%08lx-%08lx>\n", start, end);
}

EXPORT_SYMBOL(__release_region);

/*
 * Called from init/main.c to reserve IO ports.
 */
#define MAXRESERVE 4
static int __init reserve_setup(char *str)
{
	static int reserved;
	static struct resource reserve[MAXRESERVE];

	for (;;) {
		int io_start, io_num;
		int x = reserved;

		if (get_option (&str, &io_start) != 2)
			break;
		if (get_option (&str, &io_num)   == 0)
			break;
		if (x < MAXRESERVE) {
			struct resource *res = reserve + x;
			res->name = "reserved";
			res->start = io_start;
			res->end = io_start + io_num - 1;
			res->flags = IORESOURCE_BUSY;
			res->child = NULL;
			if (request_resource(res->start >= 0x10000 ? &iomem_resource : &ioport_resource, res) == 0)
				reserved = x+1;
		}
	}
	return 1;
}

__setup("reserve=", reserve_setup);