From de0b7a6307961b2bbc4df279437d8e2ec11393e3 Mon Sep 17 00:00:00 2001
From: Poul-Henning Kamp <phk@FreeBSD.org>
Date: Mon, 5 Jul 2004 20:56:06 +0000
Subject: [PATCH] Allocate the DMA channel shareable and only aquire it while
 the device is open.  This allows certain old and rather special dual floppy
 controllers to work on both channels, as long as you only have one open at a
 time.

---
 sys/dev/fdc/fdc.c | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c
index 78839273f5d8..70e9835ec1fa 100644
--- a/sys/dev/fdc/fdc.c
+++ b/sys/dev/fdc/fdc.c
@@ -132,6 +132,7 @@ struct fdc_data
 {
 	int	fdcu;		/* our unit number */
 	int	dmachan;
+	int	dmacnt;
 	int	flags;
 #define FDC_ATTACHED	0x01
 #define FDC_STAT_VALID	0x08
@@ -770,7 +771,8 @@ fdc_alloc_resources(struct fdc_data *fdc)
 	}
 
 	fdc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
-					      &fdc->rid_irq, RF_ACTIVE | RF_SHAREABLE);
+					      &fdc->rid_irq,
+					      RF_ACTIVE | RF_SHAREABLE);
 	if (fdc->res_irq == 0) {
 		device_printf(dev, "cannot reserve interrupt line\n");
 		return ENXIO;
@@ -778,8 +780,7 @@ fdc_alloc_resources(struct fdc_data *fdc)
 
 	if ((fdc->flags & FDC_NODMA) == 0) {
 		fdc->res_drq = bus_alloc_resource_any(dev, SYS_RES_DRQ,
-						      &fdc->rid_drq,
-						      RF_ACTIVE);
+		    &fdc->rid_drq, RF_ACTIVE | RF_SHAREABLE);
 		if (fdc->res_drq == 0) {
 			device_printf(dev, "cannot reserve DMA request line\n");
 			fdc->flags |= FDC_NODMA;
@@ -964,9 +965,6 @@ fdc_detach(device_t dev)
 	/* reset controller, turn motor off */
 	fdout_wr(fdc, 0);
 
-	if ((fdc->flags & FDC_NODMA) == 0)
-		isa_dma_release(fdc->dmachan);
-
 	if ((fdc->flags & FDC_ATTACHED) == 0) {
 		device_printf(dev, "already unloaded\n");
 		return (0);
@@ -1030,15 +1028,6 @@ fdc_attach(device_t dev)
 	fdc->fdcu = device_get_unit(dev);
 	fdc->flags |= FDC_ATTACHED | FDC_NEEDS_RESET;
 
-	if ((fdc->flags & FDC_NODMA) == 0) {
-		/*
-		 * Acquire the DMA channel forever, the driver will do
-		 * the rest
-		 * XXX should integrate with rman
-		 */
-		isa_dma_acquire(fdc->dmachan);
-		isa_dmainit(fdc->dmachan, MAX_SEC_SIZE);
-	}
 	fdc->state = DEVIDLE;
 
 	/* reset controller, turn motor off, clear fdout mirror reg */
@@ -1574,6 +1563,14 @@ fdopen(struct cdev *dev, int flags, int mode, struct thread *td)
 			return (rv);
 	}
 	fd->flags |= FD_OPEN;
+
+	if ((fdc->flags & FDC_NODMA) == 0) {
+		if (fdc->dmacnt++ == 0) {
+			isa_dma_acquire(fdc->dmachan);
+			isa_dmainit(fdc->dmachan, MAX_SEC_SIZE);
+		}
+	}
+
 	/*
 	 * Clearing the DMA overrun counter at open time is a bit messy.
 	 * Since we're only managing one counter per controller, opening
@@ -1594,11 +1591,17 @@ static int
 fdclose(struct cdev *dev, int flags, int mode, struct thread *td)
 {
 	struct fd_data *fd;
+ 	fdc_p	fdc;
 
 	fd = dev->si_drv1;
+	fdc = fd->fdc;
 	fd->flags &= ~(FD_OPEN | FD_NONBLOCK);
 	fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR);
 
+	if ((fdc->flags & FDC_NODMA) == 0)
+		if (--fdc->dmacnt == 0)
+			isa_dma_release(fdc->dmachan);
+
 	return (0);
 }