summaryrefslogtreecommitdiffstats
path: root/sys/isa/isa_common.c
diff options
context:
space:
mode:
authordfr <dfr@FreeBSD.org>1999-09-01 20:53:43 +0000
committerdfr <dfr@FreeBSD.org>1999-09-01 20:53:43 +0000
commitf351c4f3dc1d761f44335d85fee57eb133919756 (patch)
treef5d858916a67aca8a86dac46559341c270c6d087 /sys/isa/isa_common.c
parentc93ae1166c126cfb59736d68f475ad5948708da7 (diff)
downloadFreeBSD-src-f351c4f3dc1d761f44335d85fee57eb133919756.zip
FreeBSD-src-f351c4f3dc1d761f44335d85fee57eb133919756.tar.gz
This represents essentially a complete rewrite of the ISA PnP code. The
new system is integrated with the ISA bus code more cleanly and allows the future addition of more enumerators such as PnPBIOS and ACPI. This commit also enables the new pcm driver since it is somewhat tied to the new PnP code.
Diffstat (limited to 'sys/isa/isa_common.c')
-rw-r--r--sys/isa/isa_common.c554
1 files changed, 535 insertions, 19 deletions
diff --git a/sys/isa/isa_common.c b/sys/isa/isa_common.c
index c16b663..8d597fe 100644
--- a/sys/isa/isa_common.c
+++ b/sys/isa/isa_common.c
@@ -99,13 +99,391 @@ static int
isa_attach(device_t dev)
{
/*
- * Arrange for bus_generic_attach(dev) to be called later.
+ * Arrange for isa_probe_children(dev) to be called later. XXX
*/
isa_bus_device = dev;
return 0;
}
/*
+ * Find a working set of memory regions for a child using the ranges
+ * in *config and return the regions in *result. Returns non-zero if
+ * a set of ranges was found.
+ */
+static int
+isa_find_memory(device_t child,
+ struct isa_config *config,
+ struct isa_config *result)
+{
+ device_t dev = device_get_parent(child);
+ int success, i;
+ struct resource *res[ISA_NMEM];
+
+ /*
+ * First clear out any existing resource definitions.
+ */
+ for (i = 0; i < ISA_NMEM; i++) {
+ ISA_DELETE_RESOURCE(dev, child, SYS_RES_MEMORY, i);
+ res[i] = NULL;
+ }
+
+ success = 1;
+ result->ic_nmem = config->ic_nmem;
+ for (i = 0; i < config->ic_nmem; i++) {
+ u_int32_t start, end, size, align;
+ for (start = config->ic_mem[i].ir_start,
+ end = config->ic_mem[i].ir_end,
+ size = config->ic_mem[i].ir_size,
+ align = config->ic_mem[i].ir_align;
+ start + size - 1 <= end;
+ start += align) {
+ ISA_SET_RESOURCE(dev, child, SYS_RES_MEMORY, i,
+ start, size);
+ res[i] = bus_alloc_resource(child,
+ SYS_RES_MEMORY, &i,
+ 0, ~0, 1, RF_ACTIVE);
+ if (res[i]) {
+ result->ic_mem[i].ir_start = start;
+ result->ic_mem[i].ir_end = start + size - 1;
+ result->ic_mem[i].ir_size = size;
+ result->ic_mem[i].ir_align = align;
+ break;
+ }
+ }
+
+ /*
+ * If we didn't find a place for memory range i, then
+ * give up now.
+ */
+ if (!res[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ for (i = 0; i < ISA_NMEM; i++) {
+ if (res[i])
+ bus_release_resource(child, SYS_RES_MEMORY,
+ i, res[i]);
+ }
+
+ return success;
+}
+
+/*
+ * Find a working set of port regions for a child using the ranges
+ * in *config and return the regions in *result. Returns non-zero if
+ * a set of ranges was found.
+ */
+static int
+isa_find_port(device_t child,
+ struct isa_config *config,
+ struct isa_config *result)
+{
+ device_t dev = device_get_parent(child);
+ int success, i;
+ struct resource *res[ISA_NPORT];
+
+ /*
+ * First clear out any existing resource definitions.
+ */
+ for (i = 0; i < ISA_NPORT; i++) {
+ ISA_DELETE_RESOURCE(dev, child, SYS_RES_IOPORT, i);
+ res[i] = NULL;
+ }
+
+ success = 1;
+ result->ic_nport = config->ic_nport;
+ for (i = 0; i < config->ic_nport; i++) {
+ u_int32_t start, end, size, align;
+ for (start = config->ic_port[i].ir_start,
+ end = config->ic_port[i].ir_end,
+ size = config->ic_port[i].ir_size,
+ align = config->ic_port[i].ir_align;
+ start + size - 1 <= end;
+ start += align) {
+ ISA_SET_RESOURCE(dev, child, SYS_RES_IOPORT, i,
+ start, size);
+ res[i] = bus_alloc_resource(child,
+ SYS_RES_IOPORT, &i,
+ 0, ~0, 1, RF_ACTIVE);
+ if (res[i]) {
+ result->ic_port[i].ir_start = start;
+ result->ic_port[i].ir_end = start + size - 1;
+ result->ic_port[i].ir_size = size;
+ result->ic_port[i].ir_align = align;
+ break;
+ }
+ }
+
+ /*
+ * If we didn't find a place for port range i, then
+ * give up now.
+ */
+ if (!res[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ for (i = 0; i < ISA_NPORT; i++) {
+ if (res[i])
+ bus_release_resource(child, SYS_RES_IOPORT,
+ i, res[i]);
+ }
+
+ return success;
+}
+
+/*
+ * Return the index of the first bit in the mask (or -1 if mask is empty.
+ */
+static int
+find_first_bit(u_int32_t mask)
+{
+ return ffs(mask) - 1;
+}
+
+/*
+ * Return the index of the next bit in the mask, or -1 if there are no more.
+ */
+static int
+find_next_bit(u_int32_t mask, int bit)
+{
+ bit++;
+ while (bit < 32 && !(mask & (1 << bit)))
+ bit++;
+ if (bit != 32)
+ return bit;
+ return -1;
+}
+
+/*
+ * Find a working set of irqs for a child using the masks in *config
+ * and return the regions in *result. Returns non-zero if a set of
+ * irqs was found.
+ */
+static int
+isa_find_irq(device_t child,
+ struct isa_config *config,
+ struct isa_config *result)
+{
+ device_t dev = device_get_parent(child);
+ int success, i;
+ struct resource *res[ISA_NIRQ];
+
+ /*
+ * First clear out any existing resource definitions.
+ */
+ for (i = 0; i < ISA_NIRQ; i++) {
+ ISA_DELETE_RESOURCE(dev, child, SYS_RES_IRQ, i);
+ res[i] = NULL;
+ }
+
+ success = 1;
+ result->ic_nirq = config->ic_nirq;
+ for (i = 0; i < config->ic_nirq; i++) {
+ u_int32_t mask = config->ic_irqmask[i];
+ int irq;
+ for (irq = find_first_bit(mask);
+ irq != -1;
+ irq = find_next_bit(mask, irq)) {
+ ISA_SET_RESOURCE(dev, child, SYS_RES_IRQ, i,
+ irq, 1);
+ res[i] = bus_alloc_resource(child,
+ SYS_RES_IRQ, &i,
+ 0, ~0, 1, RF_ACTIVE);
+ if (res[i]) {
+ result->ic_irqmask[i] = (1 << irq);
+ break;
+ }
+ }
+
+ /*
+ * If we didn't find a place for irq range i, then
+ * give up now.
+ */
+ if (!res[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ for (i = 0; i < ISA_NIRQ; i++) {
+ if (res[i])
+ bus_release_resource(child, SYS_RES_IRQ,
+ i, res[i]);
+ }
+
+ return success;
+}
+
+/*
+ * Find a working set of drqs for a child using the masks in *config
+ * and return the regions in *result. Returns non-zero if a set of
+ * drqs was found.
+ */
+static int
+isa_find_drq(device_t child,
+ struct isa_config *config,
+ struct isa_config *result)
+{
+ device_t dev = device_get_parent(child);
+ int success, i;
+ struct resource *res[ISA_NDRQ];
+
+ /*
+ * First clear out any existing resource definitions.
+ */
+ for (i = 0; i < ISA_NDRQ; i++) {
+ ISA_DELETE_RESOURCE(dev, child, SYS_RES_DRQ, i);
+ res[i] = NULL;
+ }
+
+ success = 1;
+ result->ic_ndrq = config->ic_ndrq;
+ for (i = 0; i < config->ic_ndrq; i++) {
+ u_int32_t mask = config->ic_drqmask[i];
+ int drq;
+ for (drq = find_first_bit(mask);
+ drq != -1;
+ drq = find_next_bit(mask, drq)) {
+ ISA_SET_RESOURCE(dev, child, SYS_RES_DRQ, i,
+ drq, 1);
+ res[i] = bus_alloc_resource(child,
+ SYS_RES_DRQ, &i,
+ 0, ~0, 1, RF_ACTIVE);
+ if (res[i]) {
+ result->ic_drqmask[i] = (1 << drq);
+ break;
+ }
+ }
+
+ /*
+ * If we didn't find a place for drq range i, then
+ * give up now.
+ */
+ if (!res[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ for (i = 0; i < ISA_NDRQ; i++) {
+ if (res[i])
+ bus_release_resource(child, SYS_RES_DRQ,
+ i, res[i]);
+ }
+
+ return success;
+}
+
+/*
+ * Attempt to find a working set of resources for a device. Return
+ * non-zero if a working configuration is found.
+ */
+static int
+isa_assign_resources(device_t child)
+{
+ struct isa_device *idev = DEVTOISA(child);
+ struct isa_config_entry *ice;
+ struct isa_config config;
+
+ bzero(&config, sizeof config);
+ TAILQ_FOREACH(ice, &idev->id_configs, ice_link) {
+ if (!isa_find_memory(child, &ice->ice_config, &config))
+ continue;
+ if (!isa_find_port(child, &ice->ice_config, &config))
+ continue;
+ if (!isa_find_irq(child, &ice->ice_config, &config))
+ continue;
+ if (!isa_find_drq(child, &ice->ice_config, &config))
+ continue;
+
+ /*
+ * A working configuration was found enable the device
+ * with this configuration.
+ */
+ if (idev->id_config_cb) {
+ idev->id_config_cb(idev->id_config_arg,
+ &config, 1);
+ return 1;
+ }
+ }
+
+ /*
+ * Disable the device.
+ */
+ bzero(&config, sizeof config);
+ if (idev->id_config_cb)
+ idev->id_config_cb(idev->id_config_arg, &config, 0);
+ device_disable(child);
+
+ return 0;
+}
+
+/*
+ * Called after other devices have initialised to probe for isa devices.
+ */
+void
+isa_probe_children(device_t dev)
+{
+ device_t *children;
+ int nchildren, i;
+
+ if (device_get_children(dev, &children, &nchildren))
+ return;
+
+ /*
+ * First probe all non-pnp devices so that they claim their
+ * resources first.
+ */
+ for (i = 0; i < nchildren; i++) {
+ device_t child = children[i];
+ struct isa_device *idev = DEVTOISA(child);
+
+ if (TAILQ_FIRST(&idev->id_configs))
+ continue;
+
+ device_probe_and_attach(child);
+ }
+
+ /*
+ * Next assign resource to pnp devices and probe them.
+ */
+ for (i = 0; i < nchildren; i++) {
+ device_t child = children[i];
+ struct isa_device* idev = DEVTOISA(child);
+
+ if (!TAILQ_FIRST(&idev->id_configs))
+ continue;
+
+ if (isa_assign_resources(child)) {
+ struct resource_list_entry *rle;
+
+ device_probe_and_attach(child);
+
+ /*
+ * Claim any unallocated resources to keep other
+ * devices from using them.
+ */
+ SLIST_FOREACH(rle, &idev->id_resources, link) {
+ if (!rle->res) {
+ int rid = rle->rid;
+ resource_list_alloc(dev, child,
+ rle->type,
+ &rid,
+ 0, ~0, 1,
+ RF_ACTIVE);
+ }
+ }
+ }
+ }
+
+ free(children, M_TEMP);
+}
+
+/*
* Add a new child with default ivars.
*/
static device_t
@@ -120,6 +498,7 @@ isa_add_child(device_t dev, int order, const char *name, int unit)
resource_list_init(&idev->id_resources);
idev->id_flags = 0;
+ TAILQ_INIT(&idev->id_configs);
return device_add_child_ordered(dev, order, name, unit, idev);
}
@@ -128,26 +507,27 @@ static void
isa_print_resources(struct resource_list *rl, const char *name, int type,
const char *format)
{
- struct resource_list_entry *rle0 = resource_list_find(rl, type, 0);
- struct resource_list_entry *rle1 = resource_list_find(rl, type, 1);
-
- if (rle0 || rle1) {
- printf(" %s ", name);
- if (rle0) {
- printf(format, rle0->start);
- if (rle0->count > 1) {
- printf("-");
- printf(format, rle0->start + rle0->count - 1);
- }
- }
- if (rle1) {
- if (rle0)
+ struct resource_list_entry *rle;
+ int printed;
+ int i;
+
+ printed = 0;
+ for (i = 0; i < 16; i++) {
+ rle = resource_list_find(rl, type, i);
+ if (rle) {
+ if (printed == 0)
+ printf(" %s ", name);
+ else if (printed > 0)
printf(",");
- printf(format, rle1->start);
- if (rle1->count > 1) {
+ printed++;
+ printf(format, rle->start);
+ if (rle->count > 1) {
printf("-");
- printf(format, rle1->start + rle1->count - 1);
+ printf(format, rle->start + rle->count - 1);
}
+ } else if (i > 3) {
+ /* check the first few regardless */
+ break;
}
}
}
@@ -355,6 +735,25 @@ isa_write_ivar(device_t bus, device_t dev,
return (0);
}
+/*
+ * Free any resources which the driver missed or which we were holding for
+ * it (see isa_probe_children).
+ */
+static void
+isa_child_detached(device_t dev, device_t child)
+{
+ struct isa_device* idev = DEVTOISA(child);
+ struct resource_list_entry *rle;
+
+ SLIST_FOREACH(rle, &idev->id_resources, link) {
+ if (rle->res)
+ resource_list_release(dev, child,
+ rle->type,
+ rle->rid,
+ rle->res);
+ }
+}
+
static int
isa_set_resource(device_t dev, device_t child, int type, int rid,
u_long start, u_long count)
@@ -365,7 +764,15 @@ isa_set_resource(device_t dev, device_t child, int type, int rid,
if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY
&& type != SYS_RES_IRQ && type != SYS_RES_DRQ)
return EINVAL;
- if (rid < 0 || rid > 1)
+ if (rid < 0)
+ return EINVAL;
+ if (type == SYS_RES_IOPORT && rid >= ISA_NPORT)
+ return EINVAL;
+ if (type == SYS_RES_MEMORY && rid >= ISA_NMEM)
+ return EINVAL;
+ if (type == SYS_RES_IRQ && rid >= ISA_NIRQ)
+ return EINVAL;
+ if (type == SYS_RES_DRQ && rid >= ISA_NDRQ)
return EINVAL;
resource_list_add(rl, type, rid, start, start + count - 1, count);
@@ -399,6 +806,65 @@ isa_delete_resource(device_t dev, device_t child, int type, int rid)
resource_list_delete(rl, type, rid);
}
+static int
+isa_add_config(device_t dev, device_t child,
+ int priority, struct isa_config *config)
+{
+ struct isa_device* idev = DEVTOISA(child);
+ struct isa_config_entry *newice, *ice;
+
+ newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT);
+ if (!newice)
+ return ENOMEM;
+
+ newice->ice_priority = priority;
+ newice->ice_config = *config;
+
+ TAILQ_FOREACH(ice, &idev->id_configs, ice_link) {
+ if (ice->ice_priority > priority)
+ break;
+ }
+ if (ice)
+ TAILQ_INSERT_BEFORE(ice, newice, ice_link);
+ else
+ TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link);
+
+ return 0;
+}
+
+static void
+isa_set_config_callback(device_t dev, device_t child,
+ isa_config_cb *fn, void *arg)
+{
+ struct isa_device* idev = DEVTOISA(child);
+
+ idev->id_config_cb = fn;
+ idev->id_config_arg = arg;
+}
+
+static int
+isa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids)
+{
+ struct isa_device* idev = DEVTOISA(child);
+
+ if (!idev->id_vendorid)
+ return ENOENT;
+
+ while (ids->ip_id) {
+ /*
+ * Really ought to support >1 compat id per device.
+ */
+ if (idev->id_logicalid == ids->ip_id
+ || idev->id_compatid == ids->ip_id) {
+ device_set_desc(child, ids->ip_desc);
+ return 0;
+ }
+ ids++;
+ }
+
+ return ENXIO;
+}
+
static device_method_t isa_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, isa_probe),
@@ -413,6 +879,7 @@ static device_method_t isa_methods[] = {
DEVMETHOD(bus_print_child, isa_print_child),
DEVMETHOD(bus_read_ivar, isa_read_ivar),
DEVMETHOD(bus_write_ivar, isa_write_ivar),
+ DEVMETHOD(bus_child_detached, isa_child_detached),
DEVMETHOD(bus_alloc_resource, isa_alloc_resource),
DEVMETHOD(bus_release_resource, isa_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
@@ -424,6 +891,9 @@ static device_method_t isa_methods[] = {
DEVMETHOD(isa_set_resource, isa_set_resource),
DEVMETHOD(isa_get_resource, isa_get_resource),
DEVMETHOD(isa_delete_resource, isa_delete_resource),
+ DEVMETHOD(isa_add_config, isa_add_config),
+ DEVMETHOD(isa_set_config_callback, isa_set_config_callback),
+ DEVMETHOD(isa_pnp_probe, isa_pnp_probe),
{ 0, 0 }
};
@@ -441,3 +911,49 @@ DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0);
#ifdef __i386__
DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0);
#endif
+
+/*
+ * A fallback driver for reporting un-matched pnp devices.
+ */
+
+static int
+unknown_probe(device_t dev)
+{
+ /*
+ * Only match pnp devices.
+ */
+ if (isa_get_vendorid(dev) != 0)
+ return -100;
+ return ENXIO;
+}
+
+static int
+unknown_attach(device_t dev)
+{
+ return 0;
+}
+
+static int
+unknown_detach(device_t dev)
+{
+ return 0;
+}
+
+static device_method_t unknown_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, unknown_probe),
+ DEVMETHOD(device_attach, unknown_attach),
+ DEVMETHOD(device_detach, unknown_detach),
+
+ { 0, 0 }
+};
+
+static driver_t unknown_driver = {
+ "unknown",
+ unknown_methods,
+ 1, /* no softc */
+};
+
+static devclass_t unknown_devclass;
+
+DRIVER_MODULE(unknown, isa, unknown_driver, unknown_devclass, 0, 0);
OpenPOWER on IntegriCloud