Rework sysresource management. Instead of having each sysresource object

hold its own values, pass them up to the parent (acpi0) and merge/uniq them
on the way.  After the namespace evaluation, acpi will reserve these
resources and manage them via rman before bus_generic_probe() and
bus_generic_attach().  This is necessary because some systems specify
conflicting resources in separate sysresource objects.  It's also cleaner
in that the interface between sysresource and acpi is now merely the parent's
resource list.  This code handles the following cases:

1. Unique resource:  add it to the parent via bus_set_resource().
2. New wholly contained in old:  discard new.
3. New tail overlaps old head:  grow old head downward.
   AND/OR
4. New head overlaps old tail:  grow old tail upward.

Tested by:	Pawel Worach <sajd_at_telia.com>
Tested by:	Radek Kozlowski <radek_at_raadradd.com>
MFC after:	5 days
This commit is contained in:
njl 2004-08-23 16:28:42 +00:00
parent 0bb5f385ac
commit 485b8d8426
3 changed files with 144 additions and 127 deletions

View File

@ -83,9 +83,6 @@ struct mtx acpi_mutex;
/* Bitmap of device quirks. */
int acpi_quirks;
/* Local pools for managing system resources for ACPI child devices. */
struct rman acpi_rman_io, acpi_rman_mem;
static int acpi_modevent(struct module *mod, int event, void *junk);
static void acpi_identify(driver_t *driver, device_t parent);
static int acpi_probe(device_t dev);
@ -99,6 +96,9 @@ static int acpi_read_ivar(device_t dev, device_t child, int index,
static int acpi_write_ivar(device_t dev, device_t child, int index,
uintptr_t value);
static struct resource_list *acpi_get_rlist(device_t dev, device_t child);
static int acpi_sysres_alloc(device_t dev);
static struct resource_list_entry *acpi_sysres_find(device_t dev, int type,
u_long addr);
static struct resource *acpi_alloc_resource(device_t bus, device_t child,
int type, int *rid, u_long start, u_long end,
u_long count, u_int flags);
@ -189,6 +189,9 @@ MODULE_VERSION(acpi, 1);
ACPI_SERIAL_DECL(acpi, "ACPI root bus");
/* Local pools for managing system resources for ACPI child devices. */
static struct rman acpi_rman_io, acpi_rman_mem;
#define ACPI_MINIMUM_AWAKETIME 5
static const char* sleep_state_names[] = {
@ -745,6 +748,75 @@ acpi_get_rlist(device_t dev, device_t child)
return (&ad->ad_rl);
}
/*
* Pre-allocate/manage all memory and IO resources. Since rman can't handle
* duplicates, we merge any in the sysresource attach routine.
*/
static int
acpi_sysres_alloc(device_t dev)
{
struct resource *res;
struct resource_list *rl;
struct resource_list_entry *rle;
struct rman *rm;
rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
SLIST_FOREACH(rle, rl, link) {
if (rle->res != NULL) {
device_printf(dev, "duplicate resource for %lx\n", rle->start);
continue;
}
/* Only memory and IO resources are valid here. */
switch (rle->type) {
case SYS_RES_IOPORT:
rm = &acpi_rman_io;
break;
case SYS_RES_MEMORY:
rm = &acpi_rman_mem;
break;
default:
continue;
}
/* Pre-allocate resource and add to our rman pool. */
res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, rle->type,
&rle->rid, rle->start, rle->start + rle->count - 1, rle->count, 0);
if (res != NULL) {
rman_manage_region(rm, rman_get_start(res), rman_get_end(res));
rle->res = res;
} else
device_printf(dev, "reservation of %lx, %lx (%d) failed\n",
rle->start, rle->count, rle->type);
}
return (0);
}
/* Find if we manage a given resource. */
static struct resource_list_entry *
acpi_sysres_find(device_t dev, int type, u_long addr)
{
struct resource_list *rl;
struct resource_list_entry *rle;
ACPI_SERIAL_ASSERT(acpi);
/* We only consider IO and memory resources for our pool. */
rle = NULL;
if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY)
goto out;
rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
SLIST_FOREACH(rle, rl, link) {
if (type == rle->type && addr >= rle->start &&
addr < rle->start + rle->count)
break;
}
out:
return (rle);
}
static struct resource *
acpi_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
@ -774,7 +846,7 @@ acpi_alloc_resource(device_t bus, device_t child, int type, int *rid,
}
/* If we don't manage this address, pass the request up to the parent. */
rle = acpi_sysres_find(type, start);
rle = acpi_sysres_find(bus, type, start);
if (rle == NULL) {
res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid,
start, end, count, flags);
@ -844,7 +916,7 @@ acpi_release_resource(device_t bus, device_t child, int type, int rid,
* If we know about this address, deactivate it and release it to the
* local pool. If we don't, pass this request up to the parent.
*/
if (acpi_sysres_find(type, rman_get_start(r)) == NULL) {
if (acpi_sysres_find(bus, type, rman_get_start(r)) == NULL) {
if (rman_get_flags(r) & RF_ACTIVE) {
ret = bus_deactivate_resource(child, type, rid, r);
if (ret != 0)
@ -1134,6 +1206,9 @@ acpi_probe_children(device_t bus)
}
}
/* Pre-allocate resources for our rman from any sysresource devices. */
acpi_sysres_alloc(bus);
/* Create any static children by calling device identify methods. */
ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "device identify routines\n"));
bus_generic_probe(bus);
@ -1757,7 +1832,7 @@ acpi_SetSleepState(struct acpi_softc *sc, int state)
case ACPI_STATE_S2:
case ACPI_STATE_S3:
case ACPI_STATE_S4:
status = AcpiGetSleepTypeData((UINT8)state, &TypeA, &TypeB);
status = AcpiGetSleepTypeData(state, &TypeA, &TypeB);
if (status == AE_NOT_FOUND) {
device_printf(sc->acpi_dev,
"Sleep state S%d not supported by BIOS\n", state);
@ -1807,7 +1882,7 @@ acpi_SetSleepState(struct acpi_softc *sc, int state)
AcpiEnable();
} else {
ACPI_DISABLE_IRQS();
status = AcpiEnterSleepState((UINT8)state);
status = AcpiEnterSleepState(state);
if (ACPI_FAILURE(status)) {
device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n",
AcpiFormatException(status));

View File

@ -504,51 +504,6 @@ struct acpi_res_context {
void *ar_parent;
};
/*
* Add a resource to the device's resource list. We define our own function
* for this since bus_set_resource() doesn't handle duplicates of any kind.
*
* XXX This should be merged into resource_list_add() eventually.
*/
static int
acpi_reslist_add(device_t dev, int type, int rid, u_long start, u_long count)
{
struct resource_list_entry *rle;
struct resource_list *rl;
u_long end;
end = start + count - 1;
rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
/*
* Loop through all current resources to see if the new one overlaps
* any existing ones. If so, the old one always takes precedence and
* the new one is adjusted (or rejected). We check for three cases:
*
* 1. Tail of new resource overlaps head of old resource: truncate the
* new resource so it is contiguous with the start of the old.
* 2. New resource wholly contained within the old resource: error.
* 3. Head of new resource overlaps tail of old resource: truncate the
* new resource so it is contiguous, following the old.
*/
SLIST_FOREACH(rle, rl, link) {
if (rle->type == type) {
if (start < rle->start && end >= rle->start) {
count = rle->start - start;
break;
} else if (start >= rle->start && start <= rle->end) {
if (end > rle->end) {
start = rle->end + 1;
count = end - start + 1;
break;
} else
return (EEXIST);
}
}
}
return (bus_set_resource(dev, type, rid, start, count));
}
static void
acpi_res_set_init(device_t dev, void *arg, void **context)
{
@ -579,7 +534,7 @@ acpi_res_set_ioport(device_t dev, void *context, u_int32_t base,
if (cp == NULL)
return;
acpi_reslist_add(dev, SYS_RES_IOPORT, cp->ar_nio++, base, length);
bus_set_resource(dev, SYS_RES_IOPORT, cp->ar_nio++, base, length);
}
static void
@ -602,7 +557,7 @@ acpi_res_set_memory(device_t dev, void *context, u_int32_t base,
if (cp == NULL)
return;
acpi_reslist_add(dev, SYS_RES_MEMORY, cp->ar_nmem++, base, length);
bus_set_resource(dev, SYS_RES_MEMORY, cp->ar_nmem++, base, length);
}
static void
@ -629,7 +584,7 @@ acpi_res_set_irq(device_t dev, void *context, u_int32_t *irq, int count,
if (count != 1)
return;
acpi_reslist_add(dev, SYS_RES_IRQ, cp->ar_nirq++, *irq, 1);
bus_set_resource(dev, SYS_RES_IRQ, cp->ar_nirq++, *irq, 1);
}
static void
@ -644,7 +599,7 @@ acpi_res_set_drq(device_t dev, void *context, u_int32_t *drq, int count)
if (count != 1)
return;
acpi_reslist_add(dev, SYS_RES_DRQ, cp->ar_ndrq++, *drq, 1);
bus_set_resource(dev, SYS_RES_DRQ, cp->ar_ndrq++, *drq, 1);
}
static void
@ -670,11 +625,13 @@ acpi_res_set_end_dependant(device_t dev, void *context)
/*
* Resource-owning placeholders for IO and memory pseudo-devices.
*
* This code allocates system resource objects that will be owned by ACPI
* child devices. Really, the acpi parent device should have the resources
* but this would significantly affect the device probe code.
* This code allocates system resources that will be used by ACPI
* child devices. The acpi parent manages these resources through a
* private rman.
*/
static int acpi_sysres_rid = 100;
static int acpi_sysres_probe(device_t dev);
static int acpi_sysres_attach(device_t dev);
@ -714,79 +671,66 @@ acpi_sysres_probe(device_t dev)
static int
acpi_sysres_attach(device_t dev)
{
device_t gparent;
struct resource *res;
struct rman *rm;
struct resource_list_entry *rle;
struct resource_list *rl;
device_t bus;
struct resource_list_entry *bus_rle, *dev_rle;
struct resource_list *bus_rl, *dev_rl;
int done, type;
u_long start, end, count;
/*
* Pre-allocate/manage all memory and IO resources. We detect duplicates
* by setting rle->res to the resource we got from the parent. We can't
* ignore them since rman can't handle duplicates.
* Loop through all current resources to see if the new one overlaps
* any existing ones. If so, grow the old one up and/or down
* accordingly. Discard any that are wholly contained in the old. If
* the resource is unique, add it to the parent. It will later go into
* the rman pool.
*/
rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
SLIST_FOREACH(rle, rl, link) {
if (rle->res != NULL) {
device_printf(dev, "duplicate resource for %lx\n", rle->start);
bus = device_get_parent(dev);
dev_rl = BUS_GET_RESOURCE_LIST(bus, dev);
bus_rl = BUS_GET_RESOURCE_LIST(device_get_parent(bus), bus);
SLIST_FOREACH(dev_rle, dev_rl, link) {
if (dev_rle->type != SYS_RES_IOPORT && dev_rle->type != SYS_RES_MEMORY)
continue;
start = dev_rle->start;
end = dev_rle->end;
count = dev_rle->count;
type = dev_rle->type;
done = FALSE;
SLIST_FOREACH(bus_rle, bus_rl, link) {
if (bus_rle->type != type)
continue;
/* New resource wholly contained in old, discard. */
if (start >= bus_rle->start && end <= bus_rle->end)
break;
/* New tail overlaps old head, grow existing resource downward. */
if (start < bus_rle->start && end >= bus_rle->start) {
bus_rle->count += bus_rle->start - start;
bus_rle->start = start;
done = TRUE;
}
/* New head overlaps old tail, grow existing resource upward. */
if (start <= bus_rle->end && end > bus_rle->end) {
bus_rle->count += end - bus_rle->end;
bus_rle->end = end;
done = TRUE;
}
/* If we adjusted the old resource, we're finished. */
if (done)
break;
}
/* Only memory and IO resources are valid here. */
switch (rle->type) {
case SYS_RES_IOPORT:
rm = &acpi_rman_io;
break;
case SYS_RES_MEMORY:
rm = &acpi_rman_mem;
break;
default:
continue;
}
/* Pre-allocate resource and add to our rman pool. */
gparent = device_get_parent(device_get_parent(dev));
res = BUS_ALLOC_RESOURCE(gparent, dev, rle->type, &rle->rid,
rle->start, rle->start + rle->count - 1, rle->count, 0);
if (res != NULL) {
rman_manage_region(rm, rman_get_start(res), rman_get_end(res));
rle->res = res;
}
/* If we didn't merge with anything, add this resource. */
if (bus_rle == NULL)
bus_set_resource(bus, type, acpi_sysres_rid++, start, count);
}
/* After merging/moving resources to the parent, free the list. */
resource_list_free(dev_rl);
return (0);
}
/* XXX The resource list may require locking and refcounting. */
struct resource_list_entry *
acpi_sysres_find(int type, u_long addr)
{
device_t *devs;
int i, numdevs;
struct resource_list *rl;
struct resource_list_entry *rle;
/* We only consider IO and memory resources for our pool. */
rle = NULL;
if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY)
return (rle);
/* Find all the sysresource devices. */
if (devclass_get_devices(acpi_sysres_devclass, &devs, &numdevs) != 0)
return (rle);
/* Check each device for a resource that contains "addr". */
for (i = 0; i < numdevs && rle == NULL; i++) {
rl = BUS_GET_RESOURCE_LIST(device_get_parent(devs[i]), devs[i]);
if (rl == NULL)
continue;
SLIST_FOREACH(rle, rl, link) {
if (type == rle->type && addr >= rle->start &&
addr < rle->start + rle->count)
break;
}
}
free(devs, M_TEMP);
return (rle);
}

View File

@ -306,8 +306,6 @@ ACPI_STATUS acpi_lookup_irq_resource(device_t dev, int rid,
struct resource *res, ACPI_RESOURCE *acpi_res);
ACPI_STATUS acpi_parse_resources(device_t dev, ACPI_HANDLE handle,
struct acpi_parse_resource_set *set, void *arg);
extern struct rman acpi_rman_io, acpi_rman_mem;
struct resource_list_entry *acpi_sysres_find(int type, u_long addr);
/* ACPI event handling */
UINT32 acpi_event_power_button_sleep(void *context);