xref: /freebsd/sys/dev/fdc/fdc_isa.c (revision 7500723246255e62f83ecdf05d9c8877b2d12cd5)
1 /*-
2  * Copyright (c) 2004 M. Warner Losh.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions, and the following disclaimer,
10  *    without modification, immediately at the beginning of the file.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/bio.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/module.h>
38 #include <sys/mutex.h>
39 #include <sys/rman.h>
40 #include <sys/systm.h>
41 
42 #include <machine/bus.h>
43 
44 #include <dev/fdc/fdcvar.h>
45 
46 #include <isa/isavar.h>
47 #include <isa/isareg.h>
48 
49 static int fdc_isa_probe(device_t);
50 
51 static struct isa_pnp_id fdc_ids[] = {
52 	{0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */
53 	{0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */
54 	{0}
55 };
56 
57 int
58 fdc_isa_alloc_resources(device_t dev, struct fdc_data *fdc)
59 {
60 	int ispnp, nports;
61 
62 	ispnp = (fdc->flags & FDC_ISPNP) != 0;
63 	fdc->fdc_dev = dev;
64 	fdc->rid_ioport = 0;
65 	fdc->rid_irq = 0;
66 	fdc->rid_drq = 0;
67 	fdc->rid_ctl = 1;
68 
69 	/*
70 	 * On standard ISA, we don't just use an 8 port range
71 	 * (e.g. 0x3f0-0x3f7) since that covers an IDE control
72 	 * register at 0x3f6.
73 	 *
74 	 * Isn't PC hardware wonderful.
75 	 */
76 	nports = ispnp ? 1 : 6;
77 
78 	/*
79 	 * Some ACPI BIOSen have _CRS objects for the floppy device that
80 	 * split the I/O port resource into several resources.  We detect
81 	 * this case by checking if there are more than 2 IOPORT resources.
82 	 * If so, we use the resource with the smallest start address as
83 	 * the port RID and the largest start address as the control RID.
84 	 */
85 	if (bus_get_resource_count(dev, SYS_RES_IOPORT, 2) != 0) {
86 		u_long min_start, max_start, tmp;
87 		int i;
88 
89 		/* Find the min/max start addresses and their RIDs. */
90 		max_start = 0ul;
91 		min_start = ~0ul;
92 		for (i = 0; bus_get_resource_count(dev, SYS_RES_IOPORT, i) > 0;
93 		    i++) {
94 			tmp = bus_get_resource_start(dev, SYS_RES_IOPORT, i);
95 			KASSERT(tmp != 0, ("bogus resource"));
96 			if (tmp < min_start) {
97 				min_start = tmp;
98 				fdc->rid_ioport = i;
99 			}
100 			if (tmp > max_start) {
101 				max_start = tmp;
102 				fdc->rid_ctl = i;
103 			}
104 		}
105 	}
106 
107 	fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
108 	    &fdc->rid_ioport, 0ul, ~0ul, nports, RF_ACTIVE);
109 	if (fdc->res_ioport == 0) {
110 		device_printf(dev, "cannot reserve I/O port range (%d ports)\n",
111 			      nports);
112 		return ENXIO;
113 	}
114 	fdc->portt = rman_get_bustag(fdc->res_ioport);
115 	fdc->porth = rman_get_bushandle(fdc->res_ioport);
116 
117 	/*
118 	 * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7
119 	 * and some at 0x3f0-0x3f5,0x3f7. We detect the former
120 	 * by checking the size and adjust the port address
121 	 * accordingly.
122 	 */
123 	if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4)
124 		fdc->port_off = -2;
125 
126 	/*
127 	 * Register the control port range as rid 1 if it
128 	 * isn't there already. Most PnP BIOSen will have
129 	 * already done this but non-PnP configurations don't.
130 	 *
131 	 * And some (!!) report 0x3f2-0x3f5 and completely
132 	 * leave out the control register!  It seems that some
133 	 * non-antique controller chips have a different
134 	 * method of programming the transfer speed which
135 	 * doesn't require the control register, but it's
136 	 * mighty bogus as the chip still responds to the
137 	 * address for the control register.
138 	 */
139 	if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) {
140 		u_long ctlstart;
141 		/* Find the control port, usually 0x3f7 */
142 		ctlstart = rman_get_start(fdc->res_ioport) + fdc->port_off + 7;
143 		bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1);
144 	}
145 
146 	/*
147 	 * Now (finally!) allocate the control port.
148 	 */
149 	fdc->res_ctl = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
150 	    &fdc->rid_ctl, RF_ACTIVE);
151 	if (fdc->res_ctl == 0) {
152 		device_printf(dev,
153 		    "cannot reserve control I/O port range (control port)\n");
154 		return ENXIO;
155 	}
156 	fdc->ctlt = rman_get_bustag(fdc->res_ctl);
157 	fdc->ctlh = rman_get_bushandle(fdc->res_ctl);
158 	fdc->ctl_off = 0;
159 
160 	fdc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fdc->rid_irq,
161 					      RF_ACTIVE | RF_SHAREABLE);
162 	if (fdc->res_irq == 0) {
163 		device_printf(dev, "cannot reserve interrupt line\n");
164 		return ENXIO;
165 	}
166 
167 	if ((fdc->flags & FDC_NODMA) == 0) {
168 		fdc->res_drq = bus_alloc_resource_any(dev, SYS_RES_DRQ,
169 		    &fdc->rid_drq, RF_ACTIVE | RF_SHAREABLE);
170 		if (fdc->res_drq == 0) {
171 			device_printf(dev, "cannot reserve DMA request line\n");
172 			fdc->flags |= FDC_NODMA;
173 		} else
174 			fdc->dmachan = rman_get_start(fdc->res_drq);
175 	}
176 
177 	return 0;
178 }
179 
180 static int
181 fdc_isa_probe(device_t dev)
182 {
183 	int	error;
184 	struct	fdc_data *fdc;
185 
186 	fdc = device_get_softc(dev);
187 	fdc->fdc_dev = dev;
188 
189 	/* Check pnp ids */
190 	error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids);
191 	if (error == ENXIO)
192 		return (ENXIO);
193 
194 	/* Attempt to allocate our resources for the duration of the probe */
195 	error = fdc_isa_alloc_resources(dev, fdc);
196 	if (error == 0)
197 		error = fdc_initial_reset(dev, fdc);
198 
199 	fdc_release_resources(fdc);
200 	return (error);
201 }
202 
203 static int
204 fdc_isa_attach(device_t dev)
205 {
206 	struct	fdc_data *fdc;
207 	int error;
208 
209 	fdc = device_get_softc(dev);
210 	error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids);
211 	if (error == 0)
212 		fdc->flags |= FDC_ISPNP;
213 
214 	if (error == 0)
215 		error = fdc_isa_alloc_resources(dev, fdc);
216 	if (error == 0)
217 		error = fdc_attach(dev);
218 	if (error == 0)
219 		error = fdc_hints_probe(dev);
220 	if (error)
221 		fdc_release_resources(fdc);
222 	return (error);
223 }
224 
225 static device_method_t fdc_methods[] = {
226 	/* Device interface */
227 	DEVMETHOD(device_probe,		fdc_isa_probe),
228 	DEVMETHOD(device_attach,	fdc_isa_attach),
229 	DEVMETHOD(device_detach,	fdc_detach),
230 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
231 	DEVMETHOD(device_suspend,	bus_generic_suspend),
232 	DEVMETHOD(device_resume,	bus_generic_resume),
233 
234 	/* Bus interface */
235 	DEVMETHOD(bus_print_child,	fdc_print_child),
236 	DEVMETHOD(bus_read_ivar,	fdc_read_ivar),
237 	DEVMETHOD(bus_write_ivar,       fdc_write_ivar),
238 	/* Our children never use any other bus interface methods. */
239 
240 	{ 0, 0 }
241 };
242 
243 static driver_t fdc_driver = {
244 	"fdc",
245 	fdc_methods,
246 	sizeof(struct fdc_data)
247 };
248 
249 DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0);
250