2011-12-01 20:44:02 +00:00
|
|
|
/*
|
|
|
|
* resource.c - barebox resource management
|
|
|
|
*
|
|
|
|
* Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
|
|
|
|
*
|
|
|
|
* See file CREDITS for list of people who contributed to this
|
|
|
|
* project.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2
|
|
|
|
* as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*/
|
|
|
|
#include <common.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <init.h>
|
|
|
|
#include <linux/ioport.h>
|
2014-07-31 06:33:33 +00:00
|
|
|
#include <linux/err.h>
|
2014-04-07 10:01:20 +00:00
|
|
|
#include <asm/io.h>
|
2011-12-01 20:44:02 +00:00
|
|
|
|
|
|
|
static int init_resource(struct resource *res, const char *name)
|
|
|
|
{
|
|
|
|
INIT_LIST_HEAD(&res->children);
|
|
|
|
res->parent = NULL;
|
|
|
|
res->name = xstrdup(name);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* request a region.
|
|
|
|
* This will succedd when the requested region is completely inside
|
|
|
|
* the parent resource and does not conflict with any of the child
|
|
|
|
* resources.
|
|
|
|
*/
|
2014-04-07 10:01:20 +00:00
|
|
|
struct resource *__request_region(struct resource *parent,
|
2011-12-01 20:44:02 +00:00
|
|
|
const char *name, resource_size_t start,
|
2012-05-24 06:52:22 +00:00
|
|
|
resource_size_t end)
|
2011-12-01 20:44:02 +00:00
|
|
|
{
|
|
|
|
struct resource *r, *new;
|
|
|
|
|
2012-05-24 06:52:22 +00:00
|
|
|
if (end < start) {
|
2013-01-26 11:40:48 +00:00
|
|
|
debug("%s: request region 0x%08llx:0x%08llx: end < start\n",
|
|
|
|
__func__,
|
|
|
|
(unsigned long long)start,
|
|
|
|
(unsigned long long)end);
|
2014-07-31 06:33:33 +00:00
|
|
|
return ERR_PTR(-EINVAL);
|
2012-05-24 06:52:22 +00:00
|
|
|
}
|
|
|
|
|
2011-12-01 20:44:02 +00:00
|
|
|
/* outside parent resource? */
|
2012-05-24 06:52:22 +00:00
|
|
|
if (start < parent->start || end > parent->end) {
|
2013-01-26 11:40:48 +00:00
|
|
|
debug("%s: 0x%08llx:0x%08llx outside parent resource 0x%08llx:0x%08llx\n",
|
|
|
|
__func__,
|
|
|
|
(unsigned long long)start,
|
|
|
|
(unsigned long long)end,
|
|
|
|
(unsigned long long)parent->start,
|
|
|
|
(unsigned long long)parent->end);
|
2014-07-31 06:33:33 +00:00
|
|
|
return ERR_PTR(-EINVAL);
|
2011-12-01 20:44:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We keep the list of child resources ordered which helps
|
|
|
|
* us searching for conflicts here.
|
|
|
|
*/
|
|
|
|
list_for_each_entry(r, &parent->children, sibling) {
|
2012-05-24 06:52:22 +00:00
|
|
|
if (end < r->start)
|
2011-12-01 20:44:02 +00:00
|
|
|
goto ok;
|
2012-05-24 06:52:22 +00:00
|
|
|
if (start > r->end)
|
2011-12-01 20:44:02 +00:00
|
|
|
continue;
|
2014-07-22 19:57:51 +00:00
|
|
|
debug("%s: 0x%08llx:0x%08llx conflicts with 0x%08llx:0x%08llx\n",
|
2013-01-26 11:40:48 +00:00
|
|
|
__func__,
|
|
|
|
(unsigned long long)start,
|
|
|
|
(unsigned long long)end,
|
|
|
|
(unsigned long long)r->start,
|
|
|
|
(unsigned long long)r->end);
|
2014-07-31 06:33:33 +00:00
|
|
|
return ERR_PTR(-EBUSY);
|
2011-12-01 20:44:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ok:
|
2013-01-26 11:40:48 +00:00
|
|
|
debug("%s ok: 0x%08llx:0x%08llx\n", __func__,
|
|
|
|
(unsigned long long)start,
|
|
|
|
(unsigned long long)end);
|
2011-12-01 20:44:02 +00:00
|
|
|
|
|
|
|
new = xzalloc(sizeof(*new));
|
|
|
|
init_resource(new, name);
|
|
|
|
new->start = start;
|
2012-05-24 06:52:22 +00:00
|
|
|
new->end = end;
|
2011-12-01 20:44:02 +00:00
|
|
|
new->parent = parent;
|
|
|
|
list_add_tail(&new->sibling, &r->sibling);
|
|
|
|
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-04-07 10:01:20 +00:00
|
|
|
* release a region previously requested with request_*_region
|
2011-12-01 20:44:02 +00:00
|
|
|
*/
|
|
|
|
int release_region(struct resource *res)
|
|
|
|
{
|
|
|
|
if (!list_empty(&res->children))
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
list_del(&res->sibling);
|
|
|
|
free((char *)res->name);
|
|
|
|
free(res);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-07 10:01:20 +00:00
|
|
|
/* The root resource for the whole memory-mapped io space */
|
2011-12-01 20:44:02 +00:00
|
|
|
struct resource iomem_resource = {
|
|
|
|
.start = 0,
|
2012-05-24 06:52:22 +00:00
|
|
|
.end = 0xffffffff,
|
2012-10-06 17:49:35 +00:00
|
|
|
.name = "iomem",
|
|
|
|
.children = LIST_HEAD_INIT(iomem_resource.children),
|
2011-12-01 20:44:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2014-04-07 10:01:20 +00:00
|
|
|
* request a region inside the io space (memory)
|
2011-12-01 20:44:02 +00:00
|
|
|
*/
|
|
|
|
struct resource *request_iomem_region(const char *name,
|
2012-05-24 06:52:22 +00:00
|
|
|
resource_size_t start, resource_size_t end)
|
2011-12-01 20:44:02 +00:00
|
|
|
{
|
2014-07-31 06:38:08 +00:00
|
|
|
return __request_region(&iomem_resource, name, start, end);
|
2014-04-07 10:01:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The root resource for the whole io-mapped io space */
|
|
|
|
struct resource ioport_resource = {
|
|
|
|
.start = 0,
|
|
|
|
.end = IO_SPACE_LIMIT,
|
|
|
|
.name = "ioport",
|
|
|
|
.children = LIST_HEAD_INIT(ioport_resource.children),
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* request a region inside the io space (i/o port)
|
|
|
|
*/
|
|
|
|
struct resource *request_ioport_region(const char *name,
|
|
|
|
resource_size_t start, resource_size_t end)
|
|
|
|
{
|
2014-07-31 06:33:33 +00:00
|
|
|
struct resource *res;
|
|
|
|
|
|
|
|
res = __request_region(&ioport_resource, name, start, end);
|
|
|
|
if (IS_ERR(res))
|
2014-09-12 07:22:40 +00:00
|
|
|
return ERR_CAST(res);
|
2014-07-31 06:33:33 +00:00
|
|
|
|
|
|
|
return res;
|
2011-12-01 20:44:02 +00:00
|
|
|
}
|