xref: /freebsd/sys/dev/fdc/fdc_isa.c (revision c98323078dede7579020518ec84cdcb478e5c142)
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/module.h>
37 #include <sys/rman.h>
38 #include <sys/systm.h>
39 
40 #include <machine/bus.h>
41 
42 #include <dev/fdc/fdcvar.h>
43 #include <dev/fdc/fdcreg.h>
44 
45 #include <isa/isavar.h>
46 #include <isa/isareg.h>
47 
48 static int fdc_isa_probe(device_t);
49 static void fdctl_wr_isa(fdc_p, u_int8_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 static void
58 fdctl_wr_isa(fdc_p fdc, u_int8_t v)
59 {
60 	bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v);
61 }
62 
63 int
64 fdc_isa_alloc_resources(device_t dev, struct fdc_data *fdc)
65 {
66 	int ispnp, nports;
67 
68 	ispnp = (fdc->flags & FDC_ISPNP) != 0;
69 	fdc->fdc_dev = dev;
70 	fdc->rid_ioport = 0;
71 	fdc->rid_irq = 0;
72 	fdc->rid_drq = 0;
73 	fdc->rid_ctl = 1;
74 
75 	/*
76 	 * On standard ISA, we don't just use an 8 port range
77 	 * (e.g. 0x3f0-0x3f7) since that covers an IDE control
78 	 * register at 0x3f6.
79 	 *
80 	 * Isn't PC hardware wonderful.
81 	 */
82 	nports = ispnp ? 1 : 6;
83 
84 	/*
85 	 * Some ACPI BIOSen have _CRS objects for the floppy device that
86 	 * split the I/O port resource into several resources.  We detect
87 	 * this case by checking if there are more than 2 IOPORT resources.
88 	 * If so, we use the resource with the smallest start address as
89 	 * the port RID and the largest start address as the control RID.
90 	 */
91 	if (bus_get_resource_count(dev, SYS_RES_IOPORT, 2) != 0) {
92 		u_long min_start, max_start, tmp;
93 		int i;
94 
95 		/* Find the min/max start addresses and their RIDs. */
96 		max_start = 0ul;
97 		min_start = ~0ul;
98 		for (i = 0; bus_get_resource_count(dev, SYS_RES_IOPORT, i) > 0;
99 		    i++) {
100 			tmp = bus_get_resource_start(dev, SYS_RES_IOPORT, i);
101 			KASSERT(tmp != 0, ("bogus resource"));
102 			if (tmp < min_start) {
103 				min_start = tmp;
104 				fdc->rid_ioport = i;
105 			}
106 			if (tmp > max_start) {
107 				max_start = tmp;
108 				fdc->rid_ctl = i;
109 			}
110 		}
111 		if (min_start + 7 != max_start) {
112 			device_printf(dev, "I/O to control range incorrect\n");
113 			return (ENXIO);
114 		}
115 	}
116 
117 	fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
118 	    &fdc->rid_ioport, 0ul, ~0ul, nports, RF_ACTIVE);
119 	if (fdc->res_ioport == 0) {
120 		device_printf(dev, "cannot reserve I/O port range (%d ports)\n",
121 			      nports);
122 		return ENXIO;
123 	}
124 	fdc->portt = rman_get_bustag(fdc->res_ioport);
125 	fdc->porth = rman_get_bushandle(fdc->res_ioport);
126 
127 	/*
128 	 * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7
129 	 * and some at 0x3f0-0x3f5,0x3f7. We detect the former
130 	 * by checking the size and adjust the port address
131 	 * accordingly.
132 	 */
133 	if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4)
134 		fdc->port_off = -2;
135 
136 	/*
137 	 * Register the control port range as rid 1 if it
138 	 * isn't there already. Most PnP BIOSen will have
139 	 * already done this but non-PnP configurations don't.
140 	 *
141 	 * And some (!!) report 0x3f2-0x3f5 and completely
142 	 * leave out the control register!  It seems that some
143 	 * non-antique controller chips have a different
144 	 * method of programming the transfer speed which
145 	 * doesn't require the control register, but it's
146 	 * mighty bogus as the chip still responds to the
147 	 * address for the control register.
148 	 */
149 	if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) {
150 		u_long ctlstart;
151 		/* Find the control port, usually 0x3f7 */
152 		ctlstart = rman_get_start(fdc->res_ioport) + fdc->port_off + 7;
153 		bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1);
154 	}
155 
156 	/*
157 	 * Now (finally!) allocate the control port.
158 	 */
159 	fdc->res_ctl = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
160 	    &fdc->rid_ctl, RF_ACTIVE);
161 	if (fdc->res_ctl == 0) {
162 		device_printf(dev,
163 		    "cannot reserve control I/O port range (control port)\n");
164 		return ENXIO;
165 	}
166 	fdc->ctlt = rman_get_bustag(fdc->res_ctl);
167 	fdc->ctlh = rman_get_bushandle(fdc->res_ctl);
168 
169 	fdc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fdc->rid_irq,
170 					      RF_ACTIVE | RF_SHAREABLE);
171 	if (fdc->res_irq == 0) {
172 		device_printf(dev, "cannot reserve interrupt line\n");
173 		return ENXIO;
174 	}
175 
176 	if ((fdc->flags & FDC_NODMA) == 0) {
177 		fdc->res_drq = bus_alloc_resource_any(dev, SYS_RES_DRQ,
178 		    &fdc->rid_drq, RF_ACTIVE | RF_SHAREABLE);
179 		if (fdc->res_drq == 0) {
180 			device_printf(dev, "cannot reserve DMA request line\n");
181 			fdc->flags |= FDC_NODMA;
182 		} else
183 			fdc->dmachan = rman_get_start(fdc->res_drq);
184 	}
185 
186 	return 0;
187 }
188 
189 static int
190 fdc_isa_probe(device_t dev)
191 {
192 	int	error, ic_type;
193 	struct	fdc_data *fdc;
194 
195 	fdc = device_get_softc(dev);
196 	fdc->fdc_dev = dev;
197 	fdc->fdctl_wr = fdctl_wr_isa;
198 
199 	/* Check pnp ids */
200 	error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids);
201 	if (error == ENXIO)
202 		return (ENXIO);
203 
204 	/* Attempt to allocate our resources for the duration of the probe */
205 	error = fdc_isa_alloc_resources(dev, fdc);
206 	if (error)
207 		goto out;
208 
209 	/* Check that the controller is working. */
210 	error = fdc_initial_reset(fdc);
211 	if (error)
212 		goto out;
213 
214 	/* Try to determine a more specific device type. */
215 	if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) {
216 		switch (ic_type & 0xff) {
217 		case 0x80:
218 			device_set_desc(dev, "NEC 765 or clone");
219 			break;
220 		case 0x81:	/* not mentioned in any hardware doc */
221 		case 0x90:
222 			device_set_desc(dev,
223 		"Enhanced floppy controller (i82077, NE72065 or clone)");
224 			break;
225 		default:
226 			device_set_desc(dev, "Generic floppy controller");
227 			break;
228 		}
229 	}
230 
231 out:
232 	fdc_release_resources(fdc);
233 	return (error);
234 }
235 
236 static int
237 fdc_isa_attach(device_t dev)
238 {
239 	int	ic_type;
240 	struct	fdc_data *fdc;
241 	int error;
242 
243 	fdc = device_get_softc(dev);
244 	fdc->fdctl_wr = fdctl_wr_isa;
245 	error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids);
246 	if (error == 0)
247 		fdc->flags |= FDC_ISPNP;
248 	if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) {
249 		switch (ic_type & 0xff) {
250 		case 0x80:
251 			fdc->fdct = FDC_NE765;
252 			break;
253 		case 0x81:	/* not mentioned in any hardware doc */
254 		case 0x90:
255 			fdc->fdct = FDC_ENHANCED;
256 			break;
257 		default:
258 			fdc->fdct = FDC_UNKNOWN;
259 			break;
260 		}
261 	}
262 
263 	error = fdc_isa_alloc_resources(dev, fdc);
264 	if (error)
265 		goto out;
266 	error = fdc_attach(dev);
267 	if (error)
268 		goto out;
269 	error = fdc_hints_probe(dev);
270 	if (error)
271 		goto out;
272 out:
273 	if (error)
274 		fdc_release_resources(fdc);
275 	return (error);
276 }
277 
278 static device_method_t fdc_methods[] = {
279 	/* Device interface */
280 	DEVMETHOD(device_probe,		fdc_isa_probe),
281 	DEVMETHOD(device_attach,	fdc_isa_attach),
282 	DEVMETHOD(device_detach,	fdc_detach),
283 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
284 	DEVMETHOD(device_suspend,	bus_generic_suspend),
285 	DEVMETHOD(device_resume,	bus_generic_resume),
286 
287 	/* Bus interface */
288 	DEVMETHOD(bus_print_child,	fdc_print_child),
289 	DEVMETHOD(bus_read_ivar,	fdc_read_ivar),
290 	DEVMETHOD(bus_write_ivar,       fdc_write_ivar),
291 	/* Our children never use any other bus interface methods. */
292 
293 	{ 0, 0 }
294 };
295 
296 static driver_t fdc_driver = {
297 	"fdc",
298 	fdc_methods,
299 	sizeof(struct fdc_data)
300 };
301 
302 DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0);
303