DragonFly bugs List (threaded) for 2004-10
[
Date Prev][
Date Next]
[
Thread Prev][
Thread Next]
[
Date Index][
Thread Index]
Re: floppy drives and ACPI
Ok, people whos floppies aren't recognized, please try this patch.
I did not adopt the FreeBSD work, it was such a huge hack that there's
no point even trying to decipher the mess. Instead I rolled a routine
to allocate all available resources of a particular type and return
information about what it did, and then check that against reasonable
values.
-Matt
Index: dev/disk/fd/fd.c
===================================================================
RCS file: /cvs/src/sys/dev/disk/fd/fd.c,v
retrieving revision 1.19
diff -u -r1.19 fd.c
--- dev/disk/fd/fd.c 19 Sep 2004 00:36:37 -0000 1.19
+++ dev/disk/fd/fd.c 13 Oct 2004 01:24:50 -0000
@@ -313,7 +313,7 @@
static void
fdctl_wr_isa(fdc_p fdc, u_int8_t v)
{
- bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v);
+ bus_space_write_1(fdc->portt, fdc->porth, 7 + fdc->port_off, v);
}
#if 0
@@ -537,84 +537,107 @@
{
device_t dev;
int ispnp, ispcmcia;
+ u_long range_start;
+ u_long range_size;
+ u_long actual_size;
+ int error;
dev = fdc->fdc_dev;
ispnp = (fdc->flags & FDC_ISPNP) != 0;
ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0;
- fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0;
- fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0;
+ fdc->rid_irq = 0;
+ fdc->rid_drq = 0;
+ fdc->res_nioports = 0;
+ fdc->res_irq = NULL;
+ fdc->res_drq = NULL;
+ callout_init(&fdc->pseudointr_ch);
/*
* On standard ISA, we don't just use an 8 port range
* (e.g. 0x3f0-0x3f7) since that covers an IDE control
- * register at 0x3f6.
- *
- * Isn't PC hardware wonderful.
+ * register at 0x3f6. Isn't PC hardware wonderful?
*
* The Y-E Data PCMCIA FDC doesn't have this problem, it
* uses the register with offset 6 for pseudo-DMA, and the
* one with offset 7 as control register.
+ *
+ * Even worse, many BIOS makers improperly specify the ACPI
+ * resource lists, sometimes starting at 0x3f2, sometimes only
+ * listing a contiguous range of 2 bytes for the first (rid 0)
+ * resource, and so forth. Known combinations:
+ *
+ * 1: 0x3f0-0x3f5 # very rare
+ * 2: 0x3f0 # hints -> 0x3f0-0x3f5,0x3f7
+ * 3: 0x3f0-0x3f5,0x3f7 # Most common
+ * 4: 0x3f2-0x3f5,0x3f7 # Second most common
+ * 5: 0x3f2-0x3f5 # implies 0x3f7 too.
+ * 6: 0x3f2-0x3f3,0x3f4-0x3f5,0x3f7 # becoming common
+ * 7: 0x3f2-0x3f3,0x3f4-0x3f5 # rare
+ * 8: 0x3f0-0x3f1,0x3f2-0x3f3,0x3f4-0x3f5,0x3f7
+ * 9: 0x3f0-0x3f3,0x3f4-0x3f5,0x3f7
+ *
+ * To deal with this mess we use bus_alloc_all_resources() which
+ * will allocate all necessary resources within the area mask and
+ * return the overall start and size, and the resource associated with
+ * the lowest address found.
+ *
+ * For floppy contorllers we just assume that
*/
- fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
- &fdc->rid_ioport, 0ul, ~0ul,
- ispcmcia ? 8 : (ispnp ? 1 : 6),
- RF_ACTIVE);
- if (fdc->res_ioport == 0) {
- device_printf(dev, "cannot reserve I/O port range\n");
+ error = bus_alloc_all_resources(dev, SYS_RES_IOPORT, RF_ACTIVE,
+ fdc->res_ioports, FDC_MAX_IORESOURCES,
+ &fdc->res_nioports, 7,
+ &range_start, &range_size,
+ &actual_size, &fdc->res_ioport);
+ printf("floppy: nioresources %d range %04lx-%04lx (%ld)\n",
+ fdc->res_nioports, range_start, range_start + range_size,
+ actual_size);
+
+ if (error) {
+ device_printf(dev, "Has no conforming I/O port range\n");
return ENXIO;
}
- fdc->portt = rman_get_bustag(fdc->res_ioport);
- fdc->porth = rman_get_bushandle(fdc->res_ioport);
-
- if (!ispcmcia) {
- /*
- * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7
- * and some at 0x3f0-0x3f5,0x3f7. We detect the former
- * by checking the size and adjust the port address
- * accordingly.
- */
- if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4)
- fdc->port_off = -2;
+ /*
+ * Check the masked start port offset for the 0x3f2 case.
+ */
+ switch(range_start & 7) {
+ case 0:
+ fdc->port_off = 0;
+ break;
+ case 2:
/*
- * Register the control port range as rid 1 if it
- * isn't there already. Most PnP BIOSen will have
- * already done this but non-PnP configurations don't.
- *
- * And some (!!) report 0x3f2-0x3f5 and completely
- * leave out the control register! It seems that some
- * non-antique controller chips have a different
- * method of programming the transfer speed which
- * doesn't require the control register, but it's
- * mighty bogus as the chip still responds to the
- * address for the control register.
+ * BIOS resource list bug, started at 0x3f2 instead of 0x3f0.
*/
- if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) {
- u_long ctlstart;
-
- /* Find the control port, usually 0x3f7 */
- ctlstart = rman_get_start(fdc->res_ioport) +
- fdc->port_off + 7;
-
- bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1);
- }
+ device_printf(dev, "Warning: corrected illegal port range\n");
+ fdc->port_off = -2;
+ actual_size += 2; /* fake it */
+ break;
+ default:
+ device_printf(dev, "Illegal I/O port range\n");
+ return (ENXIO);
+ }
- /*
- * Now (finally!) allocate the control port.
- */
- fdc->rid_ctl = 1;
- fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT,
- &fdc->rid_ctl,
- 0ul, ~0ul, 1, RF_ACTIVE);
- if (fdc->res_ctl == 0) {
- device_printf(dev,
- "cannot reserve control I/O port range\n");
- return ENXIO;
- }
- fdc->ctlt = rman_get_bustag(fdc->res_ctl);
- fdc->ctlh = rman_get_bushandle(fdc->res_ctl);
+ /*
+ * The actual size ought to be 0, 7, or 8. It will be 0 if generated
+ * from an ISA hint, 7 normally (the range doesn't include 0x3f6 which
+ * is an IDE control register), or 8, which is usually a PCMCIA
+ * card that doesn't have an IDE control register in the middle of
+ * the floppy controller.
+ *
+ * Degenerate ISA hints will have a size of 1
+ */
+ printf("actual size: %d\n", actual_size);
+ if (actual_size != 0 && actual_size < 7) {
+ device_printf(dev, "Illegal I/O port range\n");
+ return (ENXIO);
}
+ fdc->portt = rman_get_bustag(fdc->res_ioport);
+ fdc->porth = rman_get_bushandle(fdc->res_ioport);
+
+ /*
+ * Move on to other resources
+ */
fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ,
&fdc->rid_irq, 0ul, ~0ul, 1,
RF_ACTIVE);
@@ -642,6 +665,7 @@
{
device_t dev;
+ callout_stop(&fdc->pseudointr_ch);
dev = fdc->fdc_dev;
if (fdc->res_irq != 0) {
bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
@@ -649,17 +673,14 @@
bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
fdc->res_irq);
}
- if (fdc->res_ctl != 0) {
- bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
- fdc->res_ctl);
- bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
- fdc->res_ctl);
- }
- if (fdc->res_ioport != 0) {
- bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
- fdc->res_ioport);
- bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
- fdc->res_ioport);
+
+ if (fdc->res_nioports) {
+ bus_deactivate_all_resources(dev, SYS_RES_IOPORT,
+ fdc->res_ioports, fdc->res_nioports);
+ bus_release_all_resources(dev, SYS_RES_IOPORT,
+ fdc->res_ioports, &fdc->res_nioports);
+ fdc->res_nioports = 0;
+ fdc->res_ioport = NULL;
}
if (fdc->res_drq != 0) {
bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
Index: dev/disk/fd/fdc.h
===================================================================
RCS file: /cvs/src/sys/dev/disk/fd/fdc.h,v
retrieving revision 1.4
diff -u -r1.4 fdc.h
--- dev/disk/fd/fdc.h 19 Sep 2004 00:36:37 -0000 1.4
+++ dev/disk/fd/fdc.h 13 Oct 2004 01:05:51 -0000
@@ -75,8 +75,15 @@
struct resource *res_irq, *res_drq;
int rid_ioport, rid_irq, rid_drq;
#else
- struct resource *res_ioport, *res_ctl, *res_irq, *res_drq;
- int rid_ioport, rid_ctl, rid_irq, rid_drq;
+#define FDC_MAX_IORESOURCES 8
+ struct resource_info res_ioports[FDC_MAX_IORESOURCES];
+ struct resource *res_ioport; /* lowest port from ioports[]*/
+ struct resource *res_irq;
+ struct resource *res_drq;
+ int res_nioports;
+ int rid_iostart;
+ int rid_irq;
+ int rid_drq;
#endif
int port_off;
bus_space_tag_t portt;
@@ -86,9 +93,6 @@
bus_space_handle_t sc_fdsioh;
bus_space_tag_t sc_fdemsiot;
bus_space_handle_t sc_fdemsioh;
-#else
- bus_space_tag_t ctlt;
- bus_space_handle_t ctlh;
#endif
void *fdc_intr;
struct device *fdc_dev;
Index: kern/subr_bus.c
===================================================================
RCS file: /cvs/src/sys/kern/subr_bus.c,v
retrieving revision 1.21
diff -u -r1.21 subr_bus.c
--- kern/subr_bus.c 8 Jul 2004 12:43:32 -0000 1.21
+++ kern/subr_bus.c 13 Oct 2004 01:16:22 -0000
@@ -2094,6 +2094,127 @@
count, flags));
}
+/*
+ * This routine allocates all resources which fall within the area mask.
+ * The first address retrieved determines the I/O area. Resources are
+ * stuffed in the provided array (note that array entry 0 is left NULL)
+ *
+ * This is a real mess because the bus_*() infrastructure is a mess.
+ */
+int
+bus_alloc_all_resources(device_t dev, int type, u_int flags,
+ struct resource_info *res_ary, int maxcount,
+ int *count, u_long area_mask,
+ u_long *range_start, u_long *range_size,
+ u_long *actual_size, struct resource **res_lo)
+{
+ struct resource *scan_res;
+ u_long scan_start;
+ u_long scan_size;
+ u_long range_area;
+ int error;
+ int rid;
+ int i;
+
+ *res_lo = NULL;
+ *range_start = 0;
+ *range_size = 0;
+ *actual_size = 0;
+ *count = 0;
+
+ if (maxcount == 0)
+ return (EINVAL);
+
+ *range_start = 0;
+ *range_size = 0;
+ range_area = 0;
+ error = 0;
+
+ for (rid = 0; *count < maxcount; ++*count, ++rid) {
+ scan_res = bus_alloc_resource(dev, type, &rid, 0ul, ~0ul,
+ 0, flags);
+ if (scan_res == NULL) {
+ if (*count == 0)
+ error = ENXIO;
+ break;
+ }
+ if (rid < 0 || rid >= maxcount) {
+ device_printf(dev, "bus_alloc_all_resources(): rid %d is out of range, cannot continue\n", rid);
+ bus_release_resource(dev, type, rid, scan_res);
+ error = ENXIO;
+ break;
+ }
+ scan_start = rman_get_start(scan_res);
+ scan_size = rman_get_size(scan_res);
+
+ /*
+ * Initialize our initial range start in the first loop.
+ */
+ if (*range_size == 0) {
+ *range_start = scan_start;
+ range_area = scan_start & ~area_mask;
+ }
+
+ /*
+ * If we are beyond the requested area mask, stop.
+ */
+ if (range_area != (scan_start & ~area_mask)) {
+ bus_release_resource(dev, type, rid, scan_res);
+ break;
+ }
+
+ /*
+ * Update the resource array
+ */
+ res_ary[*count].ri_res = scan_res;
+ res_ary[*count].ri_rid = rid;
+
+ /*
+ * Clip the resource to the requested area mask and update
+ * return parameters.
+ */
+ if (range_area != ((scan_start + scan_size) & ~area_mask)) {
+ scan_size = ((scan_start + area_mask) & ~area_mask) -
+ scan_start;
+ }
+ if (*range_start > scan_start) {
+ *range_size += *range_start - scan_start;
+ *range_start = scan_start;
+ }
+ if (*range_start + *range_size < scan_start + scan_size) {
+ *range_size += (scan_start + scan_size) -
+ (*range_start + *range_size);
+ }
+ if (scan_start <= *range_start)
+ *res_lo = scan_res;
+ *actual_size += scan_size;
+ }
+
+ if (error) {
+ for (i = *count - 1; i >= 0; --i)
+ bus_release_resource(dev, type, res_ary[i].ri_rid,
+ res_ary[i].ri_res);
+ *count = 0;
+ *res_lo = NULL;
+ }
+ return (error);
+}
+
+void
+bus_release_all_resources(device_t dev, int type,
+ struct resource_info *res_ary, int *count)
+{
+ int i;
+
+ for (i = *count - 1; i >= 0; --i) {
+ bus_release_resource(dev, type,
+ res_ary[i].ri_rid, res_ary[i].ri_res);
+ res_ary[i].ri_rid = 0;
+ res_ary[i].ri_res = NULL;
+ }
+ *count = 0;
+}
+
int
bus_activate_resource(device_t dev, int type, int rid, struct resource *r)
{
@@ -2111,6 +2232,24 @@
}
int
+bus_deactivate_all_resources(device_t dev, int type,
+ struct resource_info *res_ary, int count)
+{
+ int cumulative_error;
+ int error;
+
+ cumulative_error = 0;
+ for (--count; count >= 0; --count) {
+ error = bus_deactivate_resource(dev, type,
+ res_ary[count].ri_rid, res_ary[count].ri_res);
+ if (error)
+ cumulative_error = error;
+ }
+ return(cumulative_error);
+}
+
+
+int
bus_release_resource(device_t dev, int type, int rid, struct resource *r)
{
if (dev->parent == 0)
Index: sys/bus.h
===================================================================
RCS file: /cvs/src/sys/sys/bus.h,v
retrieving revision 1.12
diff -u -r1.12 bus.h
--- sys/bus.h 5 May 2004 16:57:11 -0000 1.12
+++ sys/bus.h 13 Oct 2004 01:15:34 -0000
@@ -96,6 +96,7 @@
* for their child devices.
*/
struct resource;
+struct resource_info;
struct resource_list_entry {
SLIST_ENTRY(resource_list_entry) link;
@@ -236,10 +237,19 @@
struct resource *bus_alloc_resource(device_t dev, int type, int *rid,
u_long start, u_long end, u_long count,
u_int flags);
+int bus_alloc_all_resources(device_t dev, int type, u_int flags,
+ struct resource_info *res_ary, int maxcount,
+ int *count, u_long area_mask,
+ u_long *range_start, u_long *range_size,
+ u_long *actual_size, struct resource **res_lo);
+void bus_release_all_resources(device_t dev, int type,
+ struct resource_info *res_ary, int *count);
int bus_activate_resource(device_t dev, int type, int rid,
struct resource *r);
int bus_deactivate_resource(device_t dev, int type, int rid,
struct resource *r);
+int bus_deactivate_all_resources(device_t dev, int type,
+ struct resource_info *res_ary, int count);
int bus_release_resource(device_t dev, int type, int rid,
struct resource *r);
int bus_setup_intr(device_t dev, struct resource *r, int flags,
Index: sys/rman.h
===================================================================
RCS file: /cvs/src/sys/sys/rman.h,v
retrieving revision 1.7
diff -u -r1.7 rman.h
--- sys/rman.h 1 Mar 2004 06:33:19 -0000 1.7
+++ sys/rman.h 13 Oct 2004 01:05:14 -0000
@@ -61,6 +61,11 @@
struct rman *r_rm; /* resource manager from whence this came */
};
+struct resource_info {
+ struct resource *ri_res;
+ int ri_rid;
+};
+
#define RF_ALLOCATED 0x0001 /* resource has been reserved */
#define RF_ACTIVE 0x0002 /* resource allocation has been activated */
#define RF_SHAREABLE 0x0004 /* resource permits contemporaneous sharing */
[
Date Prev][
Date Next]
[
Thread Prev][
Thread Next]
[
Date Index][
Thread Index]