DragonFly BSD
DragonFly bugs List (threaded) for 2004-10
[Date Prev][Date Next]  [Thread Prev][Thread Next]  [Date Index][Thread Index]

Re: floppy drives and ACPI


From: Matthew Dillon <dillon@xxxxxxxxxxxxxxxxxxxx>
Date: Tue, 12 Oct 2004 18:41:13 -0700 (PDT)

    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]