/*
|
/*
|
* linux/kernel/resource.c
|
* linux/kernel/resource.c
|
*
|
*
|
* Copyright (C) 1999 Linus Torvalds
|
* Copyright (C) 1999 Linus Torvalds
|
* Copyright (C) 1999 Martin Mares <mj@ucw.cz>
|
* Copyright (C) 1999 Martin Mares <mj@ucw.cz>
|
*
|
*
|
* Arbitrary resource management.
|
* Arbitrary resource management.
|
*/
|
*/
|
|
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
#include <linux/init.h>
|
#include <linux/init.h>
|
#include <linux/slab.h>
|
#include <linux/slab.h>
|
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
#include <asm/io.h>
|
#include <asm/io.h>
|
|
|
struct resource ioport_resource = { "PCI IO", 0x0000, IO_SPACE_LIMIT, IORESOURCE_IO };
|
struct resource ioport_resource = { "PCI IO", 0x0000, IO_SPACE_LIMIT, IORESOURCE_IO };
|
struct resource iomem_resource = { "PCI mem", 0x00000000, 0xffffffff, IORESOURCE_MEM };
|
struct resource iomem_resource = { "PCI mem", 0x00000000, 0xffffffff, IORESOURCE_MEM };
|
|
|
static rwlock_t resource_lock = RW_LOCK_UNLOCKED;
|
static rwlock_t resource_lock = RW_LOCK_UNLOCKED;
|
|
|
enum { MAX_IORES_LEVEL = 5 };
|
enum { MAX_IORES_LEVEL = 5 };
|
|
|
static void *r_next(struct seq_file *m, void *v, loff_t *pos)
|
static void *r_next(struct seq_file *m, void *v, loff_t *pos)
|
{
|
{
|
struct resource *p = v;
|
struct resource *p = v;
|
(*pos)++;
|
(*pos)++;
|
if (p->child)
|
if (p->child)
|
return p->child;
|
return p->child;
|
while (!p->sibling && p->parent)
|
while (!p->sibling && p->parent)
|
p = p->parent;
|
p = p->parent;
|
return p->sibling;
|
return p->sibling;
|
}
|
}
|
|
|
static void *r_start(struct seq_file *m, loff_t *pos)
|
static void *r_start(struct seq_file *m, loff_t *pos)
|
{
|
{
|
struct resource *p = m->private;
|
struct resource *p = m->private;
|
loff_t l = 0;
|
loff_t l = 0;
|
read_lock(&resource_lock);
|
read_lock(&resource_lock);
|
for (p = p->child; p && l < *pos; p = r_next(m, p, &l))
|
for (p = p->child; p && l < *pos; p = r_next(m, p, &l))
|
;
|
;
|
return p;
|
return p;
|
}
|
}
|
|
|
static void r_stop(struct seq_file *m, void *v)
|
static void r_stop(struct seq_file *m, void *v)
|
{
|
{
|
read_unlock(&resource_lock);
|
read_unlock(&resource_lock);
|
}
|
}
|
|
|
static int r_show(struct seq_file *m, void *v)
|
static int r_show(struct seq_file *m, void *v)
|
{
|
{
|
struct resource *root = m->private;
|
struct resource *root = m->private;
|
struct resource *r = v, *p;
|
struct resource *r = v, *p;
|
int width = root->end < 0x10000 ? 4 : 8;
|
int width = root->end < 0x10000 ? 4 : 8;
|
int depth;
|
int depth;
|
|
|
for (depth = 0, p = r; depth < MAX_IORES_LEVEL; depth++, p = p->parent)
|
for (depth = 0, p = r; depth < MAX_IORES_LEVEL; depth++, p = p->parent)
|
if (p->parent == root)
|
if (p->parent == root)
|
break;
|
break;
|
seq_printf(m, "%*s%0*lx-%0*lx : %s\n",
|
seq_printf(m, "%*s%0*lx-%0*lx : %s\n",
|
depth * 2, "",
|
depth * 2, "",
|
width, r->start,
|
width, r->start,
|
width, r->end,
|
width, r->end,
|
r->name ? r->name : "<BAD>");
|
r->name ? r->name : "<BAD>");
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static struct seq_operations resource_op = {
|
static struct seq_operations resource_op = {
|
.start = r_start,
|
.start = r_start,
|
.next = r_next,
|
.next = r_next,
|
.stop = r_stop,
|
.stop = r_stop,
|
.show = r_show,
|
.show = r_show,
|
};
|
};
|
|
|
static int ioports_open(struct inode *inode, struct file *file)
|
static int ioports_open(struct inode *inode, struct file *file)
|
{
|
{
|
int res = seq_open(file, &resource_op);
|
int res = seq_open(file, &resource_op);
|
if (!res) {
|
if (!res) {
|
struct seq_file *m = file->private_data;
|
struct seq_file *m = file->private_data;
|
m->private = &ioport_resource;
|
m->private = &ioport_resource;
|
}
|
}
|
return res;
|
return res;
|
}
|
}
|
|
|
static int iomem_open(struct inode *inode, struct file *file)
|
static int iomem_open(struct inode *inode, struct file *file)
|
{
|
{
|
int res = seq_open(file, &resource_op);
|
int res = seq_open(file, &resource_op);
|
if (!res) {
|
if (!res) {
|
struct seq_file *m = file->private_data;
|
struct seq_file *m = file->private_data;
|
m->private = &iomem_resource;
|
m->private = &iomem_resource;
|
}
|
}
|
return res;
|
return res;
|
}
|
}
|
|
|
struct file_operations proc_ioports_operations = {
|
struct file_operations proc_ioports_operations = {
|
.open = ioports_open,
|
.open = ioports_open,
|
.read = seq_read,
|
.read = seq_read,
|
.llseek = seq_lseek,
|
.llseek = seq_lseek,
|
.release = seq_release,
|
.release = seq_release,
|
};
|
};
|
|
|
struct file_operations proc_iomem_operations = {
|
struct file_operations proc_iomem_operations = {
|
.open = iomem_open,
|
.open = iomem_open,
|
.read = seq_read,
|
.read = seq_read,
|
.llseek = seq_lseek,
|
.llseek = seq_lseek,
|
.release = seq_release,
|
.release = seq_release,
|
};
|
};
|
|
|
/* Return the conflict entry if you can't request it */
|
/* Return the conflict entry if you can't request it */
|
static struct resource * __request_resource(struct resource *root, struct resource *new)
|
static struct resource * __request_resource(struct resource *root, struct resource *new)
|
{
|
{
|
unsigned long start = new->start;
|
unsigned long start = new->start;
|
unsigned long end = new->end;
|
unsigned long end = new->end;
|
struct resource *tmp, **p;
|
struct resource *tmp, **p;
|
|
|
if (end < start)
|
if (end < start)
|
return root;
|
return root;
|
if (start < root->start)
|
if (start < root->start)
|
return root;
|
return root;
|
if (end > root->end)
|
if (end > root->end)
|
return root;
|
return root;
|
p = &root->child;
|
p = &root->child;
|
for (;;) {
|
for (;;) {
|
tmp = *p;
|
tmp = *p;
|
if (!tmp || tmp->start > end) {
|
if (!tmp || tmp->start > end) {
|
new->sibling = tmp;
|
new->sibling = tmp;
|
*p = new;
|
*p = new;
|
new->parent = root;
|
new->parent = root;
|
return NULL;
|
return NULL;
|
}
|
}
|
p = &tmp->sibling;
|
p = &tmp->sibling;
|
if (tmp->end < start)
|
if (tmp->end < start)
|
continue;
|
continue;
|
return tmp;
|
return tmp;
|
}
|
}
|
}
|
}
|
|
|
static int __release_resource(struct resource *old)
|
static int __release_resource(struct resource *old)
|
{
|
{
|
struct resource *tmp, **p;
|
struct resource *tmp, **p;
|
|
|
p = &old->parent->child;
|
p = &old->parent->child;
|
for (;;) {
|
for (;;) {
|
tmp = *p;
|
tmp = *p;
|
if (!tmp)
|
if (!tmp)
|
break;
|
break;
|
if (tmp == old) {
|
if (tmp == old) {
|
*p = tmp->sibling;
|
*p = tmp->sibling;
|
old->parent = NULL;
|
old->parent = NULL;
|
return 0;
|
return 0;
|
}
|
}
|
p = &tmp->sibling;
|
p = &tmp->sibling;
|
}
|
}
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
|
|
int request_resource(struct resource *root, struct resource *new)
|
int request_resource(struct resource *root, struct resource *new)
|
{
|
{
|
struct resource *conflict;
|
struct resource *conflict;
|
|
|
write_lock(&resource_lock);
|
write_lock(&resource_lock);
|
conflict = __request_resource(root, new);
|
conflict = __request_resource(root, new);
|
write_unlock(&resource_lock);
|
write_unlock(&resource_lock);
|
return conflict ? -EBUSY : 0;
|
return conflict ? -EBUSY : 0;
|
}
|
}
|
|
|
int release_resource(struct resource *old)
|
int release_resource(struct resource *old)
|
{
|
{
|
int retval;
|
int retval;
|
|
|
write_lock(&resource_lock);
|
write_lock(&resource_lock);
|
retval = __release_resource(old);
|
retval = __release_resource(old);
|
write_unlock(&resource_lock);
|
write_unlock(&resource_lock);
|
return retval;
|
return retval;
|
}
|
}
|
|
|
int check_resource(struct resource *root, unsigned long start, unsigned long len)
|
int check_resource(struct resource *root, unsigned long start, unsigned long len)
|
{
|
{
|
struct resource *conflict, tmp;
|
struct resource *conflict, tmp;
|
|
|
tmp.start = start;
|
tmp.start = start;
|
tmp.end = start + len - 1;
|
tmp.end = start + len - 1;
|
write_lock(&resource_lock);
|
write_lock(&resource_lock);
|
conflict = __request_resource(root, &tmp);
|
conflict = __request_resource(root, &tmp);
|
if (!conflict)
|
if (!conflict)
|
__release_resource(&tmp);
|
__release_resource(&tmp);
|
write_unlock(&resource_lock);
|
write_unlock(&resource_lock);
|
return conflict ? -EBUSY : 0;
|
return conflict ? -EBUSY : 0;
|
}
|
}
|
|
|
/*
|
/*
|
* Find empty slot in the resource tree given range and alignment.
|
* Find empty slot in the resource tree given range and alignment.
|
*/
|
*/
|
static int find_resource(struct resource *root, struct resource *new,
|
static int find_resource(struct resource *root, struct resource *new,
|
unsigned long size,
|
unsigned long size,
|
unsigned long min, unsigned long max,
|
unsigned long min, unsigned long max,
|
unsigned long align,
|
unsigned long align,
|
void (*alignf)(void *, struct resource *,
|
void (*alignf)(void *, struct resource *,
|
unsigned long, unsigned long),
|
unsigned long, unsigned long),
|
void *alignf_data)
|
void *alignf_data)
|
{
|
{
|
struct resource *this = root->child;
|
struct resource *this = root->child;
|
|
|
new->start = root->start;
|
new->start = root->start;
|
for(;;) {
|
for(;;) {
|
if (this)
|
if (this)
|
new->end = this->start;
|
new->end = this->start;
|
else
|
else
|
new->end = root->end;
|
new->end = root->end;
|
if (new->start < min)
|
if (new->start < min)
|
new->start = min;
|
new->start = min;
|
if (new->end > max)
|
if (new->end > max)
|
new->end = max;
|
new->end = max;
|
new->start = (new->start + align - 1) & ~(align - 1);
|
new->start = (new->start + align - 1) & ~(align - 1);
|
if (alignf)
|
if (alignf)
|
alignf(alignf_data, new, size, align);
|
alignf(alignf_data, new, size, align);
|
if (new->start < new->end && new->end - new->start + 1 >= size) {
|
if (new->start < new->end && new->end - new->start + 1 >= size) {
|
new->end = new->start + size - 1;
|
new->end = new->start + size - 1;
|
return 0;
|
return 0;
|
}
|
}
|
if (!this)
|
if (!this)
|
break;
|
break;
|
new->start = this->end + 1;
|
new->start = this->end + 1;
|
this = this->sibling;
|
this = this->sibling;
|
}
|
}
|
return -EBUSY;
|
return -EBUSY;
|
}
|
}
|
|
|
/*
|
/*
|
* Allocate empty slot in the resource tree given range and alignment.
|
* Allocate empty slot in the resource tree given range and alignment.
|
*/
|
*/
|
int allocate_resource(struct resource *root, struct resource *new,
|
int allocate_resource(struct resource *root, struct resource *new,
|
unsigned long size,
|
unsigned long size,
|
unsigned long min, unsigned long max,
|
unsigned long min, unsigned long max,
|
unsigned long align,
|
unsigned long align,
|
void (*alignf)(void *, struct resource *,
|
void (*alignf)(void *, struct resource *,
|
unsigned long, unsigned long),
|
unsigned long, unsigned long),
|
void *alignf_data)
|
void *alignf_data)
|
{
|
{
|
int err;
|
int err;
|
|
|
write_lock(&resource_lock);
|
write_lock(&resource_lock);
|
err = find_resource(root, new, size, min, max, align, alignf, alignf_data);
|
err = find_resource(root, new, size, min, max, align, alignf, alignf_data);
|
if (err >= 0 && __request_resource(root, new))
|
if (err >= 0 && __request_resource(root, new))
|
err = -EBUSY;
|
err = -EBUSY;
|
write_unlock(&resource_lock);
|
write_unlock(&resource_lock);
|
return err;
|
return err;
|
}
|
}
|
|
|
/*
|
/*
|
* This is compatibility stuff for IO resources.
|
* This is compatibility stuff for IO resources.
|
*
|
*
|
* Note how this, unlike the above, knows about
|
* Note how this, unlike the above, knows about
|
* the IO flag meanings (busy etc).
|
* the IO flag meanings (busy etc).
|
*
|
*
|
* Request-region creates a new busy region.
|
* Request-region creates a new busy region.
|
*
|
*
|
* Check-region returns non-zero if the area is already busy
|
* Check-region returns non-zero if the area is already busy
|
*
|
*
|
* Release-region releases a matching busy region.
|
* 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 * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name)
|
{
|
{
|
struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
|
struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
|
|
|
if (res) {
|
if (res) {
|
memset(res, 0, sizeof(*res));
|
memset(res, 0, sizeof(*res));
|
res->name = name;
|
res->name = name;
|
res->start = start;
|
res->start = start;
|
res->end = start + n - 1;
|
res->end = start + n - 1;
|
res->flags = IORESOURCE_BUSY;
|
res->flags = IORESOURCE_BUSY;
|
|
|
write_lock(&resource_lock);
|
write_lock(&resource_lock);
|
|
|
for (;;) {
|
for (;;) {
|
struct resource *conflict;
|
struct resource *conflict;
|
|
|
conflict = __request_resource(parent, res);
|
conflict = __request_resource(parent, res);
|
if (!conflict)
|
if (!conflict)
|
break;
|
break;
|
if (conflict != parent) {
|
if (conflict != parent) {
|
parent = conflict;
|
parent = conflict;
|
if (!(conflict->flags & IORESOURCE_BUSY))
|
if (!(conflict->flags & IORESOURCE_BUSY))
|
continue;
|
continue;
|
}
|
}
|
|
|
/* Uhhuh, that didn't work out.. */
|
/* Uhhuh, that didn't work out.. */
|
kfree(res);
|
kfree(res);
|
res = NULL;
|
res = NULL;
|
break;
|
break;
|
}
|
}
|
write_unlock(&resource_lock);
|
write_unlock(&resource_lock);
|
}
|
}
|
return res;
|
return res;
|
}
|
}
|
|
|
int __check_region(struct resource *parent, unsigned long start, unsigned long n)
|
int __check_region(struct resource *parent, unsigned long start, unsigned long n)
|
{
|
{
|
struct resource * res;
|
struct resource * res;
|
|
|
res = __request_region(parent, start, n, "check-region");
|
res = __request_region(parent, start, n, "check-region");
|
if (!res)
|
if (!res)
|
return -EBUSY;
|
return -EBUSY;
|
|
|
release_resource(res);
|
release_resource(res);
|
kfree(res);
|
kfree(res);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
void __release_region(struct resource *parent, unsigned long start, unsigned long n)
|
void __release_region(struct resource *parent, unsigned long start, unsigned long n)
|
{
|
{
|
struct resource **p;
|
struct resource **p;
|
unsigned long end;
|
unsigned long end;
|
|
|
p = &parent->child;
|
p = &parent->child;
|
end = start + n - 1;
|
end = start + n - 1;
|
|
|
for (;;) {
|
for (;;) {
|
struct resource *res = *p;
|
struct resource *res = *p;
|
|
|
if (!res)
|
if (!res)
|
break;
|
break;
|
if (res->start <= start && res->end >= end) {
|
if (res->start <= start && res->end >= end) {
|
if (!(res->flags & IORESOURCE_BUSY)) {
|
if (!(res->flags & IORESOURCE_BUSY)) {
|
p = &res->child;
|
p = &res->child;
|
continue;
|
continue;
|
}
|
}
|
if (res->start != start || res->end != end)
|
if (res->start != start || res->end != end)
|
break;
|
break;
|
*p = res->sibling;
|
*p = res->sibling;
|
kfree(res);
|
kfree(res);
|
return;
|
return;
|
}
|
}
|
p = &res->sibling;
|
p = &res->sibling;
|
}
|
}
|
printk("Trying to free nonexistent resource <%08lx-%08lx>\n", start, end);
|
printk("Trying to free nonexistent resource <%08lx-%08lx>\n", start, end);
|
}
|
}
|
|
|
/*
|
/*
|
* Called from init/main.c to reserve IO ports.
|
* Called from init/main.c to reserve IO ports.
|
*/
|
*/
|
#define MAXRESERVE 4
|
#define MAXRESERVE 4
|
static int __init reserve_setup(char *str)
|
static int __init reserve_setup(char *str)
|
{
|
{
|
static int reserved = 0;
|
static int reserved = 0;
|
static struct resource reserve[MAXRESERVE];
|
static struct resource reserve[MAXRESERVE];
|
|
|
for (;;) {
|
for (;;) {
|
int io_start, io_num;
|
int io_start, io_num;
|
int x = reserved;
|
int x = reserved;
|
|
|
if (get_option (&str, &io_start) != 2)
|
if (get_option (&str, &io_start) != 2)
|
break;
|
break;
|
if (get_option (&str, &io_num) == 0)
|
if (get_option (&str, &io_num) == 0)
|
break;
|
break;
|
if (x < MAXRESERVE) {
|
if (x < MAXRESERVE) {
|
struct resource *res = reserve + x;
|
struct resource *res = reserve + x;
|
res->name = "reserved";
|
res->name = "reserved";
|
res->start = io_start;
|
res->start = io_start;
|
res->end = io_start + io_num - 1;
|
res->end = io_start + io_num - 1;
|
res->flags = IORESOURCE_BUSY;
|
res->flags = IORESOURCE_BUSY;
|
res->child = NULL;
|
res->child = NULL;
|
if (request_resource(res->start >= 0x10000 ? &iomem_resource : &ioport_resource, res) == 0)
|
if (request_resource(res->start >= 0x10000 ? &iomem_resource : &ioport_resource, res) == 0)
|
reserved = x+1;
|
reserved = x+1;
|
}
|
}
|
}
|
}
|
return 1;
|
return 1;
|
}
|
}
|
|
|
__setup("reserve=", reserve_setup);
|
__setup("reserve=", reserve_setup);
|
|
|