xref: /freebsd/sys/dev/fdc/fdc.c (revision 5ceae6b826d141fa4a3be92c8e9e0178ca4d6763)
1752d4735SNate Lawson /*-
25b81b6b3SRodney W. Grimes  * Copyright (c) 1990 The Regents of the University of California.
35b81b6b3SRodney W. Grimes  * All rights reserved.
45b81b6b3SRodney W. Grimes  *
55b81b6b3SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
65b81b6b3SRodney W. Grimes  * Don Ahn.
75b81b6b3SRodney W. Grimes  *
869acd21dSWarner Losh  * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu)
969acd21dSWarner Losh  * aided by the Linux floppy driver modifications from David Bateman
1069acd21dSWarner Losh  * (dbateman@eng.uts.edu.au).
1169acd21dSWarner Losh  *
12dc16046fSJoerg Wunsch  * Copyright (c) 1993, 1994 by
133a2f7427SDavid Greenman  *  jc@irbs.UUCP (John Capo)
143a2f7427SDavid Greenman  *  vak@zebub.msk.su (Serge Vakulenko)
153a2f7427SDavid Greenman  *  ache@astral.msk.su (Andrew A. Chernov)
16dc16046fSJoerg Wunsch  *
17dc16046fSJoerg Wunsch  * Copyright (c) 1993, 1994, 1995 by
183a2f7427SDavid Greenman  *  joerg_wunsch@uriah.sax.de (Joerg Wunsch)
19dc5df763SJoerg Wunsch  *  dufault@hda.com (Peter Dufault)
203a2f7427SDavid Greenman  *
212995d110SJoerg Wunsch  * Copyright (c) 2001 Joerg Wunsch,
2206740f7bSJoerg Wunsch  *  joerg_wunsch@uriah.heep.sax.de (Joerg Wunsch)
232995d110SJoerg Wunsch  *
245b81b6b3SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
255b81b6b3SRodney W. Grimes  * modification, are permitted provided that the following conditions
265b81b6b3SRodney W. Grimes  * are met:
275b81b6b3SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
285b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
295b81b6b3SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
305b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
315b81b6b3SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
3264860614SJoerg Wunsch  * 4. Neither the name of the University nor the names of its contributors
3364860614SJoerg Wunsch  *    may be used to endorse or promote products derived from this software
3464860614SJoerg Wunsch  *    without specific prior written permission.
355b81b6b3SRodney W. Grimes  *
365b81b6b3SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
375b81b6b3SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
385b81b6b3SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
395b81b6b3SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
405b81b6b3SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
415b81b6b3SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
425b81b6b3SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
435b81b6b3SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
445b81b6b3SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
455b81b6b3SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
465b81b6b3SRodney W. Grimes  * SUCH DAMAGE.
475b81b6b3SRodney W. Grimes  *
48dc4ff321SRodney W. Grimes  *	from:	@(#)fd.c	7.4 (Berkeley) 5/25/91
495b81b6b3SRodney W. Grimes  */
505b81b6b3SRodney W. Grimes 
518c9bbf48SDavid E. O'Brien #include <sys/cdefs.h>
528c9bbf48SDavid E. O'Brien __FBSDID("$FreeBSD$");
538c9bbf48SDavid E. O'Brien 
54d2fb4892SJoerg Wunsch #include "opt_fdc.h"
555b81b6b3SRodney W. Grimes 
56b99f0a4aSAndrew Moore #include <sys/param.h>
579626b608SPoul-Henning Kamp #include <sys/bio.h>
586182fdbdSPeter Wemm #include <sys/bus.h>
59b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h>
60f90c382cSPoul-Henning Kamp #include <sys/disk.h>
616182fdbdSPeter Wemm #include <sys/fcntl.h>
62e774b251SJoerg Wunsch #include <sys/fdcio.h>
631a6bed68SJoerg Wunsch #include <sys/filio.h>
64fb919e4dSMark Murray #include <sys/kernel.h>
65fb919e4dSMark Murray #include <sys/lock.h>
66b99f0a4aSAndrew Moore #include <sys/malloc.h>
676182fdbdSPeter Wemm #include <sys/module.h>
68fb919e4dSMark Murray #include <sys/mutex.h>
693a2f7427SDavid Greenman #include <sys/proc.h>
706182fdbdSPeter Wemm #include <sys/rman.h>
716d6fa4fdSWarner Losh #include <sys/systm.h>
726182fdbdSPeter Wemm 
73c3ae4c40SWarner Losh #include <machine/bus.h>
746182fdbdSPeter Wemm #include <machine/clock.h>
75dc5df763SJoerg Wunsch #include <machine/stdarg.h>
766182fdbdSPeter Wemm 
776182fdbdSPeter Wemm #include <isa/isavar.h>
78a97c75b7SDoug Rabson #include <isa/isareg.h>
79734e3cc5SWarner Losh #include <dev/fdc/fdcreg.h>
806d6fa4fdSWarner Losh #include <dev/fdc/fdcvar.h>
81a97c75b7SDoug Rabson #include <isa/rtc.h>
826182fdbdSPeter Wemm 
83419f39ceSPoul-Henning Kamp #define FDBIO_FORMAT	BIO_CMD2
84d306122dSPoul-Henning Kamp 
851a6bed68SJoerg Wunsch /* configuration flags for fdc */
86e34c71eaSJoerg Wunsch #define FDC_NO_FIFO	(1 << 2)	/* do not enable FIFO  */
870722d6abSJoerg Wunsch 
883fef646eSJoerg Wunsch /*
893fef646eSJoerg Wunsch  * Stop retrying after this many DMA overruns.  Since each retry takes
9083edbfa5SJoerg Wunsch  * one revolution, with 300 rpm., 25 retries take approximately 5
913fef646eSJoerg Wunsch  * seconds which the read attempt will block in case the DMA overrun
923fef646eSJoerg Wunsch  * is persistent.
933fef646eSJoerg Wunsch  */
943fef646eSJoerg Wunsch #define FDC_DMAOV_MAX	25
95dc5df763SJoerg Wunsch 
961a6bed68SJoerg Wunsch /*
97cb38bb6cSJoerg Wunsch  * Timeout value for the PIO loops to wait until the FDC main status
98cb38bb6cSJoerg Wunsch  * register matches our expectations (request for master, direction
99cb38bb6cSJoerg Wunsch  * bit).  This is supposed to be a number of microseconds, although
100cb38bb6cSJoerg Wunsch  * timing might actually not be very accurate.
101cb38bb6cSJoerg Wunsch  *
102cb38bb6cSJoerg Wunsch  * Timeouts of 100 msec are believed to be required for some broken
103cb38bb6cSJoerg Wunsch  * (old) hardware.
104cb38bb6cSJoerg Wunsch  */
105cb38bb6cSJoerg Wunsch #define	FDSTS_TIMEOUT	100000
106cb38bb6cSJoerg Wunsch 
107cb38bb6cSJoerg Wunsch /*
1081a6bed68SJoerg Wunsch  * Number of subdevices that can be used for different density types.
1091a6bed68SJoerg Wunsch  */
1101a6bed68SJoerg Wunsch #define NUMDENS		16
111b99f0a4aSAndrew Moore 
112419f39ceSPoul-Henning Kamp #define FDBIO_RDSECTID	BIO_CMD1
1137ca0641bSAndrey A. Chernov 
1141a6bed68SJoerg Wunsch /*
1151a6bed68SJoerg Wunsch  * List of native drive densities.  Order must match enum fd_drivetype
1161a6bed68SJoerg Wunsch  * in <sys/fdcio.h>.  Upon attaching the drive, each of the
1171a6bed68SJoerg Wunsch  * programmable subdevices is initialized with the native density
1181a6bed68SJoerg Wunsch  * definition.
1191a6bed68SJoerg Wunsch  */
1201a6bed68SJoerg Wunsch static struct fd_type fd_native_types[] =
1215b81b6b3SRodney W. Grimes {
1221a6bed68SJoerg Wunsch { 0 },				/* FDT_NONE */
1231a6bed68SJoerg Wunsch {  9,2,0xFF,0x2A,40, 720,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* FDT_360K */
1241a6bed68SJoerg Wunsch { 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* FDT_12M  */
1251a6bed68SJoerg Wunsch {  9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* FDT_720K */
1261a6bed68SJoerg Wunsch { 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* FDT_144M */
1271a6bed68SJoerg Wunsch #if 0				/* we currently don't handle 2.88 MB */
1281a6bed68SJoerg Wunsch { 36,2,0xFF,0x1B,80,5760,FDC_1MBPS,  2,0x4C,1,1,FL_MFM|FL_PERPND } /*FDT_288M*/
1291a6bed68SJoerg Wunsch #else
1301a6bed68SJoerg Wunsch { 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* FDT_144M */
1311a6bed68SJoerg Wunsch #endif
1325b81b6b3SRodney W. Grimes };
1335b81b6b3SRodney W. Grimes 
1341a6bed68SJoerg Wunsch /*
1351a6bed68SJoerg Wunsch  * 360 KB 5.25" and 720 KB 3.5" drives don't have automatic density
1361a6bed68SJoerg Wunsch  * selection, they just start out with their native density (or lose).
1371a6bed68SJoerg Wunsch  * So 1.2 MB 5.25", 1.44 MB 3.5", and 2.88 MB 3.5" drives have their
1381a6bed68SJoerg Wunsch  * respective lists of densities to search for.
1391a6bed68SJoerg Wunsch  */
1401a6bed68SJoerg Wunsch static struct fd_type fd_searchlist_12m[] = {
1411a6bed68SJoerg Wunsch { 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
1421a6bed68SJoerg Wunsch {  9,2,0xFF,0x23,40, 720,FDC_300KBPS,2,0x50,1,0,FL_MFM|FL_2STEP }, /* 360K */
1431a6bed68SJoerg Wunsch {  9,2,0xFF,0x20,80,1440,FDC_300KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
1441a6bed68SJoerg Wunsch };
1451a6bed68SJoerg Wunsch 
1461a6bed68SJoerg Wunsch static struct fd_type fd_searchlist_144m[] = {
1471a6bed68SJoerg Wunsch { 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
1481a6bed68SJoerg Wunsch {  9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
1491a6bed68SJoerg Wunsch };
1501a6bed68SJoerg Wunsch 
1511a6bed68SJoerg Wunsch /* We search for 1.44M first since this is the most common case. */
1521a6bed68SJoerg Wunsch static struct fd_type fd_searchlist_288m[] = {
1531a6bed68SJoerg Wunsch { 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
1541a6bed68SJoerg Wunsch #if 0
1551a6bed68SJoerg Wunsch { 36,2,0xFF,0x1B,80,5760,FDC_1MBPS,  2,0x4C,1,1,FL_MFM|FL_PERPND } /* 2.88M */
1561a6bed68SJoerg Wunsch #endif
1571a6bed68SJoerg Wunsch {  9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
1581a6bed68SJoerg Wunsch };
159dc16046fSJoerg Wunsch 
160f8ce7dd5SJoerg Wunsch #define MAX_SEC_SIZE	(128 << 3)
16164860614SJoerg Wunsch #define MAX_CYLINDER	85	/* some people really stress their drives
16264860614SJoerg Wunsch 				 * up to cyl 82 */
163250300ebSJoerg Wunsch #define MAX_HEAD	1
164f8ce7dd5SJoerg Wunsch 
1656d6fa4fdSWarner Losh devclass_t fdc_devclass;
1665b81b6b3SRodney W. Grimes 
167246ed35dSJoerg Wunsch /*
168246ed35dSJoerg Wunsch  * Per drive structure (softc).
169246ed35dSJoerg Wunsch  */
1706182fdbdSPeter Wemm struct fd_data {
171b99f0a4aSAndrew Moore 	struct	fdc_data *fdc;	/* pointer to controller structure */
1725b81b6b3SRodney W. Grimes 	int	fdsu;		/* this units number on this controller */
1731a6bed68SJoerg Wunsch 	enum	fd_drivetype type; /* drive type */
1741a6bed68SJoerg Wunsch 	struct	fd_type *ft;	/* pointer to current type descriptor */
1751a6bed68SJoerg Wunsch 	struct	fd_type fts[NUMDENS]; /* type descriptors */
1765b81b6b3SRodney W. Grimes 	int	flags;
1775b81b6b3SRodney W. Grimes #define	FD_OPEN		0x01	/* it's open		*/
1781a6bed68SJoerg Wunsch #define	FD_NONBLOCK	0x02	/* O_NONBLOCK set	*/
1791a6bed68SJoerg Wunsch #define	FD_ACTIVE	0x04	/* it's active		*/
1801a6bed68SJoerg Wunsch #define	FD_MOTOR	0x08	/* motor should be on	*/
1811a6bed68SJoerg Wunsch #define	FD_MOTOR_WAIT	0x10	/* motor coming up	*/
1821a6bed68SJoerg Wunsch #define	FD_UA		0x20	/* force unit attention */
1835b81b6b3SRodney W. Grimes 	int	skip;
1845b81b6b3SRodney W. Grimes 	int	hddrv;
185dc5df763SJoerg Wunsch #define FD_NO_TRACK -2
1865b81b6b3SRodney W. Grimes 	int	track;		/* where we think the head is */
1871a6bed68SJoerg Wunsch 	int	options;	/* user configurable options, see fdcio.h */
18802a19910SJustin T. Gibbs 	struct	callout_handle toffhandle;
18902a19910SJustin T. Gibbs 	struct	callout_handle tohandle;
19080980460SPoul-Henning Kamp 	struct	devstat *device_stats;
19189c9c53dSPoul-Henning Kamp 	struct cdev *masterdev;
1926182fdbdSPeter Wemm 	device_t dev;
1936182fdbdSPeter Wemm 	fdu_t	fdu;
1946182fdbdSPeter Wemm };
19537286586SPeter Wemm 
19637286586SPeter Wemm struct fdc_ivars {
19737286586SPeter Wemm 	int	fdunit;
198752d4735SNate Lawson 	int	fdtype;
19937286586SPeter Wemm };
200752d4735SNate Lawson 
2016182fdbdSPeter Wemm static devclass_t fd_devclass;
2025b81b6b3SRodney W. Grimes 
2031a6bed68SJoerg Wunsch /* configuration flags for fd */
2041a6bed68SJoerg Wunsch #define FD_TYPEMASK	0x0f	/* drive type, matches enum
2051a6bed68SJoerg Wunsch 				 * fd_drivetype; on i386 machines, if
2061a6bed68SJoerg Wunsch 				 * given as 0, use RTC type for fd0
2071a6bed68SJoerg Wunsch 				 * and fd1 */
2081a6bed68SJoerg Wunsch #define FD_DTYPE(flags)	((flags) & FD_TYPEMASK)
2091a6bed68SJoerg Wunsch #define FD_NO_CHLINE	0x10	/* drive does not support changeline
2101a6bed68SJoerg Wunsch 				 * aka. unit attention */
2111a6bed68SJoerg Wunsch #define FD_NO_PROBE	0x20	/* don't probe drive (seek test), just
2121a6bed68SJoerg Wunsch 				 * assume it is there */
2131a6bed68SJoerg Wunsch 
214246ed35dSJoerg Wunsch /*
215246ed35dSJoerg Wunsch  * Throughout this file the following conventions will be used:
216246ed35dSJoerg Wunsch  *
217246ed35dSJoerg Wunsch  * fd is a pointer to the fd_data struct for the drive in question
218246ed35dSJoerg Wunsch  * fdc is a pointer to the fdc_data struct for the controller
219246ed35dSJoerg Wunsch  * fdu is the floppy drive unit number
220246ed35dSJoerg Wunsch  * fdcu is the floppy controller unit number
221246ed35dSJoerg Wunsch  * fdsu is the floppy drive unit number on that controller. (sub-unit)
222246ed35dSJoerg Wunsch  */
223b99f0a4aSAndrew Moore 
224246ed35dSJoerg Wunsch /*
225246ed35dSJoerg Wunsch  * Function declarations, same (chaotic) order as they appear in the
226246ed35dSJoerg Wunsch  * file.  Re-ordering is too late now, it would only obfuscate the
227246ed35dSJoerg Wunsch  * diffs against old and offspring versions (like the PC98 one).
228246ed35dSJoerg Wunsch  *
229246ed35dSJoerg Wunsch  * Anyone adding functions here, please keep this sequence the same
230246ed35dSJoerg Wunsch  * as below -- makes locating a particular function in the body much
231246ed35dSJoerg Wunsch  * easier.
232246ed35dSJoerg Wunsch  */
233246ed35dSJoerg Wunsch static u_int8_t fdsts_rd(fdc_p);
234246ed35dSJoerg Wunsch static void fddata_wr(fdc_p, u_int8_t);
235246ed35dSJoerg Wunsch static u_int8_t fddata_rd(fdc_p);
236246ed35dSJoerg Wunsch #if 0
237246ed35dSJoerg Wunsch static u_int8_t fdin_rd(fdc_p);
238246ed35dSJoerg Wunsch #endif
239246ed35dSJoerg Wunsch static int fdc_err(struct fdc_data *, const char *);
240246ed35dSJoerg Wunsch static int enable_fifo(fdc_p fdc);
241246ed35dSJoerg Wunsch static int fd_sense_drive_status(fdc_p, int *);
242246ed35dSJoerg Wunsch static int fd_sense_int(fdc_p, int *, int *);
243246ed35dSJoerg Wunsch static int fd_read_status(fdc_p);
244246ed35dSJoerg Wunsch static int fd_probe(device_t);
245246ed35dSJoerg Wunsch static int fd_attach(device_t);
246246ed35dSJoerg Wunsch static int fd_detach(device_t);
2476182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int);
2483a2f7427SDavid Greenman #  define TURNON 1
2493a2f7427SDavid Greenman #  define TURNOFF 0
2503a2f7427SDavid Greenman static timeout_t fd_turnoff;
2513a2f7427SDavid Greenman static timeout_t fd_motor_on;
2526182fdbdSPeter Wemm static void fd_turnon(struct fd_data *);
2533a2f7427SDavid Greenman static void fdc_reset(fdc_p);
2546182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *);
25580909a7dSJoerg Wunsch static int out_fdc(struct fdc_data *, int);
256246ed35dSJoerg Wunsch /*
257f967c4f9SPoul-Henning Kamp  * The open function is named fdopen() to avoid confusion with fdopen()
258246ed35dSJoerg Wunsch  * in fd(4).  The difference is now only meaningful for debuggers.
259246ed35dSJoerg Wunsch  */
260f967c4f9SPoul-Henning Kamp static	d_open_t	fdopen;
261246ed35dSJoerg Wunsch static	d_close_t	fdclose;
262246ed35dSJoerg Wunsch static	d_strategy_t	fdstrategy;
2636182fdbdSPeter Wemm static void fdstart(struct fdc_data *);
2645c1a1eaeSBruce Evans static timeout_t fd_iotimeout;
2653a2f7427SDavid Greenman static timeout_t fd_pseudointr;
266246ed35dSJoerg Wunsch static driver_intr_t fdc_intr;
267246ed35dSJoerg Wunsch static int fdcpio(fdc_p, long, caddr_t, u_int);
26889c9c53dSPoul-Henning Kamp static int fdautoselect(struct cdev *);
2696182fdbdSPeter Wemm static int fdstate(struct fdc_data *);
2706182fdbdSPeter Wemm static int retrier(struct fdc_data *);
271246ed35dSJoerg Wunsch static void fdbiodone(struct bio *);
27289c9c53dSPoul-Henning Kamp static int fdmisccmd(struct cdev *, u_int, void *);
273246ed35dSJoerg Wunsch static	d_ioctl_t	fdioctl;
274d66c374fSTor Egge 
275d66c374fSTor Egge static int fifo_threshold = 8;	/* XXX: should be accessible via sysctl */
276d66c374fSTor Egge 
277d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
2783a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */
279cba2a7c6SBruce Evans static int volatile fd_debug = 0;
2800e17a5bcSJoerg Wunsch #define TRACE0(arg) do { if (fd_debug) printf(arg); } while (0)
2810e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { if (fd_debug) printf(arg1, arg2); } while (0)
282d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */
2830e17a5bcSJoerg Wunsch #define TRACE0(arg) do { } while (0)
2840e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { } while (0)
285d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */
2865b81b6b3SRodney W. Grimes 
287246ed35dSJoerg Wunsch /*
288246ed35dSJoerg Wunsch  * Bus space handling (access to low-level IO).
289246ed35dSJoerg Wunsch  */
2906d6fa4fdSWarner Losh void
291427ccf00SDoug Rabson fdout_wr(fdc_p fdc, u_int8_t v)
292427ccf00SDoug Rabson {
293427ccf00SDoug Rabson 	bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v);
294427ccf00SDoug Rabson }
295427ccf00SDoug Rabson 
296427ccf00SDoug Rabson static u_int8_t
297427ccf00SDoug Rabson fdsts_rd(fdc_p fdc)
298427ccf00SDoug Rabson {
299427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off);
300427ccf00SDoug Rabson }
301427ccf00SDoug Rabson 
302427ccf00SDoug Rabson static void
303427ccf00SDoug Rabson fddata_wr(fdc_p fdc, u_int8_t v)
304427ccf00SDoug Rabson {
305427ccf00SDoug Rabson 	bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v);
306427ccf00SDoug Rabson }
307427ccf00SDoug Rabson 
308427ccf00SDoug Rabson static u_int8_t
309427ccf00SDoug Rabson fddata_rd(fdc_p fdc)
310427ccf00SDoug Rabson {
311427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off);
312427ccf00SDoug Rabson }
313427ccf00SDoug Rabson 
314427ccf00SDoug Rabson static u_int8_t
315427ccf00SDoug Rabson fdin_rd(fdc_p fdc)
316427ccf00SDoug Rabson {
317427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDIN);
318427ccf00SDoug Rabson }
319427ccf00SDoug Rabson 
3204e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = {
321dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
322f967c4f9SPoul-Henning Kamp 	.d_open =	fdopen,
3237ac40f5fSPoul-Henning Kamp 	.d_close =	fdclose,
3247ac40f5fSPoul-Henning Kamp 	.d_read =	physread,
3257ac40f5fSPoul-Henning Kamp 	.d_write =	physwrite,
3267ac40f5fSPoul-Henning Kamp 	.d_ioctl =	fdioctl,
3277ac40f5fSPoul-Henning Kamp 	.d_strategy =	fdstrategy,
3287ac40f5fSPoul-Henning Kamp 	.d_name =	"fd",
329dc08ffecSPoul-Henning Kamp 	.d_flags =	D_DISK | D_NEEDGIANT,
3304e2f199eSPoul-Henning Kamp };
3314e2f199eSPoul-Henning Kamp 
332246ed35dSJoerg Wunsch /*
333246ed35dSJoerg Wunsch  * Auxiliary functions.  Well, some only.  Others are scattered
334246ed35dSJoerg Wunsch  * throughout the entire file.
335246ed35dSJoerg Wunsch  */
336dc5df763SJoerg Wunsch static int
3376182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s)
338dc5df763SJoerg Wunsch {
3396182fdbdSPeter Wemm 	fdc->fdc_errs++;
34016b04b6aSJoerg Wunsch 	if (s) {
341b6e5f28eSPeter Wemm 		if (fdc->fdc_errs < FDC_ERRMAX)
342b6e5f28eSPeter Wemm 			device_printf(fdc->fdc_dev, "%s", s);
343b6e5f28eSPeter Wemm 		else if (fdc->fdc_errs == FDC_ERRMAX)
344b6e5f28eSPeter Wemm 			device_printf(fdc->fdc_dev, "too many errors, not "
345b6e5f28eSPeter Wemm 						    "logging any more\n");
34616b04b6aSJoerg Wunsch 	}
347dc5df763SJoerg Wunsch 
348dc5df763SJoerg Wunsch 	return FD_FAILED;
349dc5df763SJoerg Wunsch }
350dc5df763SJoerg Wunsch 
351dc5df763SJoerg Wunsch /*
352dc5df763SJoerg Wunsch  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
353dc5df763SJoerg Wunsch  * Unit number,
354dc5df763SJoerg Wunsch  * # of output bytes, output bytes as ints ...,
355dc5df763SJoerg Wunsch  * # of input bytes, input bytes as ints ...
356dc5df763SJoerg Wunsch  */
3576d6fa4fdSWarner Losh int
3586182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...)
359dc5df763SJoerg Wunsch {
360dc5df763SJoerg Wunsch 	u_char cmd;
361dc5df763SJoerg Wunsch 	int n_in;
362dc5df763SJoerg Wunsch 	int n;
363dc5df763SJoerg Wunsch 	va_list ap;
364dc5df763SJoerg Wunsch 
365dc5df763SJoerg Wunsch 	va_start(ap, n_out);
366dc5df763SJoerg Wunsch 	cmd = (u_char)(va_arg(ap, int));
367dc5df763SJoerg Wunsch 	va_end(ap);
368dc5df763SJoerg Wunsch 	va_start(ap, n_out);
369dc5df763SJoerg Wunsch 	for (n = 0; n < n_out; n++)
370dc5df763SJoerg Wunsch 	{
3716182fdbdSPeter Wemm 		if (out_fdc(fdc, va_arg(ap, int)) < 0)
372dc5df763SJoerg Wunsch 		{
373dc5df763SJoerg Wunsch 			char msg[50];
3742127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
375dc5df763SJoerg Wunsch 				"cmd %x failed at out byte %d of %d\n",
376dc5df763SJoerg Wunsch 				cmd, n + 1, n_out);
3776182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
378dc5df763SJoerg Wunsch 		}
379dc5df763SJoerg Wunsch 	}
380dc5df763SJoerg Wunsch 	n_in = va_arg(ap, int);
381dc5df763SJoerg Wunsch 	for (n = 0; n < n_in; n++)
382dc5df763SJoerg Wunsch 	{
383dc5df763SJoerg Wunsch 		int *ptr = va_arg(ap, int *);
3846182fdbdSPeter Wemm 		if (fd_in(fdc, ptr) < 0)
385dc5df763SJoerg Wunsch 		{
386dc5df763SJoerg Wunsch 			char msg[50];
3872127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
388dc5df763SJoerg Wunsch 				"cmd %02x failed at in byte %d of %d\n",
389dc5df763SJoerg Wunsch 				cmd, n + 1, n_in);
3906182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
391dc5df763SJoerg Wunsch 		}
392dc5df763SJoerg Wunsch 	}
393dc5df763SJoerg Wunsch 
394dc5df763SJoerg Wunsch 	return 0;
395dc5df763SJoerg Wunsch }
396dc5df763SJoerg Wunsch 
3976f4e0bebSPoul-Henning Kamp static int
398d66c374fSTor Egge enable_fifo(fdc_p fdc)
399d66c374fSTor Egge {
400d66c374fSTor Egge 	int i, j;
401d66c374fSTor Egge 
402d66c374fSTor Egge 	if ((fdc->flags & FDC_HAS_FIFO) == 0) {
403d66c374fSTor Egge 
404d66c374fSTor Egge 		/*
405d66c374fSTor Egge 		 * Cannot use fd_cmd the normal way here, since
406d66c374fSTor Egge 		 * this might be an invalid command. Thus we send the
407d66c374fSTor Egge 		 * first byte, and check for an early turn of data directon.
408d66c374fSTor Egge 		 */
409d66c374fSTor Egge 
4106182fdbdSPeter Wemm 		if (out_fdc(fdc, I8207X_CONFIGURE) < 0)
4116182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
412d66c374fSTor Egge 
413d66c374fSTor Egge 		/* If command is invalid, return */
414d0900d6bSJoerg Wunsch 		j = FDSTS_TIMEOUT;
415427ccf00SDoug Rabson 		while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM))
416d0900d6bSJoerg Wunsch 		       != NE7_RQM && j-- > 0) {
417d66c374fSTor Egge 			if (i == (NE7_DIO | NE7_RQM)) {
418d66c374fSTor Egge 				fdc_reset(fdc);
419d66c374fSTor Egge 				return FD_FAILED;
420d66c374fSTor Egge 			}
421d0900d6bSJoerg Wunsch 			DELAY(1);
422d0900d6bSJoerg Wunsch 		}
423d66c374fSTor Egge 		if (j<0 ||
4246182fdbdSPeter Wemm 		    fd_cmd(fdc, 3,
425d66c374fSTor Egge 			   0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) {
426d66c374fSTor Egge 			fdc_reset(fdc);
4276182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
428d66c374fSTor Egge 		}
429d66c374fSTor Egge 		fdc->flags |= FDC_HAS_FIFO;
430d66c374fSTor Egge 		return 0;
431d66c374fSTor Egge 	}
4326182fdbdSPeter Wemm 	if (fd_cmd(fdc, 4,
433d66c374fSTor Egge 		   I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0)
4346182fdbdSPeter Wemm 		return fdc_err(fdc, "Re-enable FIFO failed\n");
435d66c374fSTor Egge 	return 0;
436d66c374fSTor Egge }
437d66c374fSTor Egge 
438d66c374fSTor Egge static int
439dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p)
440dc5df763SJoerg Wunsch {
441dc5df763SJoerg Wunsch 	int st3;
442dc5df763SJoerg Wunsch 
4436182fdbdSPeter Wemm 	if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
444dc5df763SJoerg Wunsch 	{
4456182fdbdSPeter Wemm 		return fdc_err(fdc, "Sense Drive Status failed\n");
446dc5df763SJoerg Wunsch 	}
447dc5df763SJoerg Wunsch 	if (st3p)
448dc5df763SJoerg Wunsch 		*st3p = st3;
449dc5df763SJoerg Wunsch 
450dc5df763SJoerg Wunsch 	return 0;
451dc5df763SJoerg Wunsch }
452dc5df763SJoerg Wunsch 
4536f4e0bebSPoul-Henning Kamp static int
454dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
455dc5df763SJoerg Wunsch {
4566182fdbdSPeter Wemm 	int cyl, st0, ret;
457dc5df763SJoerg Wunsch 
4586182fdbdSPeter Wemm 	ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0);
4596182fdbdSPeter Wemm 	if (ret) {
4606182fdbdSPeter Wemm 		(void)fdc_err(fdc,
461dc5df763SJoerg Wunsch 			      "sense intr err reading stat reg 0\n");
462dc5df763SJoerg Wunsch 		return ret;
463dc5df763SJoerg Wunsch 	}
464dc5df763SJoerg Wunsch 
465dc5df763SJoerg Wunsch 	if (st0p)
466dc5df763SJoerg Wunsch 		*st0p = st0;
467dc5df763SJoerg Wunsch 
4686182fdbdSPeter Wemm 	if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) {
469dc5df763SJoerg Wunsch 		/*
470dc5df763SJoerg Wunsch 		 * There doesn't seem to have been an interrupt.
471dc5df763SJoerg Wunsch 		 */
472dc5df763SJoerg Wunsch 		return FD_NOT_VALID;
473dc5df763SJoerg Wunsch 	}
474dc5df763SJoerg Wunsch 
4756182fdbdSPeter Wemm 	if (fd_in(fdc, &cyl) < 0) {
4766182fdbdSPeter Wemm 		return fdc_err(fdc, "can't get cyl num\n");
477dc5df763SJoerg Wunsch 	}
478dc5df763SJoerg Wunsch 
479dc5df763SJoerg Wunsch 	if (cylp)
480dc5df763SJoerg Wunsch 		*cylp = cyl;
481dc5df763SJoerg Wunsch 
482dc5df763SJoerg Wunsch 	return 0;
483dc5df763SJoerg Wunsch }
484dc5df763SJoerg Wunsch 
485dc5df763SJoerg Wunsch 
4866f4e0bebSPoul-Henning Kamp static int
48764860614SJoerg Wunsch fd_read_status(fdc_p fdc)
488dc5df763SJoerg Wunsch {
489dc5df763SJoerg Wunsch 	int i, ret;
490b5e8ce9fSBruce Evans 
491246ed35dSJoerg Wunsch 	for (i = ret = 0; i < 7; i++) {
492b5e8ce9fSBruce Evans 		/*
49364860614SJoerg Wunsch 		 * XXX types are poorly chosen.  Only bytes can be read
494a838d83dSBruce Evans 		 * from the hardware, but fdc->status[] wants u_ints and
495b5e8ce9fSBruce Evans 		 * fd_in() gives ints.
496b5e8ce9fSBruce Evans 		 */
497b5e8ce9fSBruce Evans 		int status;
498b5e8ce9fSBruce Evans 
4996182fdbdSPeter Wemm 		ret = fd_in(fdc, &status);
500b5e8ce9fSBruce Evans 		fdc->status[i] = status;
501b5e8ce9fSBruce Evans 		if (ret != 0)
502dc5df763SJoerg Wunsch 			break;
503dc5df763SJoerg Wunsch 	}
504dc5df763SJoerg Wunsch 
505dc5df763SJoerg Wunsch 	if (ret == 0)
506dc5df763SJoerg Wunsch 		fdc->flags |= FDC_STAT_VALID;
507dc5df763SJoerg Wunsch 	else
508dc5df763SJoerg Wunsch 		fdc->flags &= ~FDC_STAT_VALID;
509dc5df763SJoerg Wunsch 
510dc5df763SJoerg Wunsch 	return ret;
511dc5df763SJoerg Wunsch }
512dc5df763SJoerg Wunsch 
5136d6fa4fdSWarner Losh void
51437286586SPeter Wemm fdc_release_resources(struct fdc_data *fdc)
51537286586SPeter Wemm {
51637286586SPeter Wemm 	device_t dev;
51737286586SPeter Wemm 
51837286586SPeter Wemm 	dev = fdc->fdc_dev;
519a6e4d8c4SNate Lawson 	if (fdc->fdc_intr) {
520a6e4d8c4SNate Lawson 		BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq,
521a6e4d8c4SNate Lawson 		    fdc->fdc_intr);
522a6e4d8c4SNate Lawson 		fdc->fdc_intr = NULL;
523a6e4d8c4SNate Lawson 	}
52437286586SPeter Wemm 	if (fdc->res_irq != 0) {
52537286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
52637286586SPeter Wemm 					fdc->res_irq);
52737286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
52837286586SPeter Wemm 				     fdc->res_irq);
529a6e4d8c4SNate Lawson 		fdc->res_irq = NULL;
53037286586SPeter Wemm 	}
53137286586SPeter Wemm 	if (fdc->res_ctl != 0) {
53237286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
53337286586SPeter Wemm 					fdc->res_ctl);
53437286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
53537286586SPeter Wemm 				     fdc->res_ctl);
536a6e4d8c4SNate Lawson 		fdc->res_ctl = NULL;
53737286586SPeter Wemm 	}
53837286586SPeter Wemm 	if (fdc->res_ioport != 0) {
53937286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
54037286586SPeter Wemm 					fdc->res_ioport);
54137286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
54237286586SPeter Wemm 				     fdc->res_ioport);
543a6e4d8c4SNate Lawson 		fdc->res_ioport = NULL;
54437286586SPeter Wemm 	}
54537286586SPeter Wemm 	if (fdc->res_drq != 0) {
54637286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
54737286586SPeter Wemm 					fdc->res_drq);
54837286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
54937286586SPeter Wemm 				     fdc->res_drq);
5505ceae6b8SYoshihiro Takahashi 		fdc->res_drq = NULL;
55137286586SPeter Wemm 	}
55237286586SPeter Wemm }
55337286586SPeter Wemm 
554246ed35dSJoerg Wunsch /*
555246ed35dSJoerg Wunsch  * Configuration/initialization stuff, per controller.
556246ed35dSJoerg Wunsch  */
55737286586SPeter Wemm 
5586d6fa4fdSWarner Losh int
5593aae7b16SBruce Evans fdc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
56037286586SPeter Wemm {
56137286586SPeter Wemm 	struct fdc_ivars *ivars = device_get_ivars(child);
56237286586SPeter Wemm 
56337286586SPeter Wemm 	switch (which) {
56437286586SPeter Wemm 	case FDC_IVAR_FDUNIT:
56537286586SPeter Wemm 		*result = ivars->fdunit;
56637286586SPeter Wemm 		break;
567752d4735SNate Lawson 	case FDC_IVAR_FDTYPE:
568752d4735SNate Lawson 		*result = ivars->fdtype;
569752d4735SNate Lawson 		break;
57037286586SPeter Wemm 	default:
571752d4735SNate Lawson 		return (ENOENT);
57237286586SPeter Wemm 	}
573752d4735SNate Lawson 	return (0);
574752d4735SNate Lawson }
575752d4735SNate Lawson 
576752d4735SNate Lawson int
577752d4735SNate Lawson fdc_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
578752d4735SNate Lawson {
579752d4735SNate Lawson 	struct fdc_ivars *ivars = device_get_ivars(child);
580752d4735SNate Lawson 
581752d4735SNate Lawson 	switch (which) {
582752d4735SNate Lawson 	case FDC_IVAR_FDUNIT:
583752d4735SNate Lawson 		ivars->fdunit = value;
584752d4735SNate Lawson 		break;
585752d4735SNate Lawson 	case FDC_IVAR_FDTYPE:
586752d4735SNate Lawson 		ivars->fdtype = value;
587752d4735SNate Lawson 		break;
588752d4735SNate Lawson 	default:
589752d4735SNate Lawson 		return (ENOENT);
590752d4735SNate Lawson 	}
591752d4735SNate Lawson 	return (0);
592752d4735SNate Lawson }
593752d4735SNate Lawson 
594752d4735SNate Lawson int
595752d4735SNate Lawson fdc_initial_reset(struct fdc_data *fdc)
596752d4735SNate Lawson {
597752d4735SNate Lawson 	/* First, reset the floppy controller. */
598752d4735SNate Lawson 	fdout_wr(fdc, 0);
599752d4735SNate Lawson 	DELAY(100);
600752d4735SNate Lawson 	fdout_wr(fdc, FDO_FRST);
601752d4735SNate Lawson 
602752d4735SNate Lawson 	/* Then, see if it can handle a command. */
603752d4735SNate Lawson 	if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240),
604752d4735SNate Lawson 	    NE7_SPEC_2(2, 0), 0))
605752d4735SNate Lawson 		return (ENXIO);
606752d4735SNate Lawson 	return (0);
60737286586SPeter Wemm }
60837286586SPeter Wemm 
6096d6fa4fdSWarner Losh int
610e219897aSJoerg Wunsch fdc_detach(device_t dev)
6115f830ea2SJoerg Wunsch {
6125f830ea2SJoerg Wunsch 	struct	fdc_data *fdc;
6135f830ea2SJoerg Wunsch 	int	error;
6145f830ea2SJoerg Wunsch 
6155f830ea2SJoerg Wunsch 	fdc = device_get_softc(dev);
6165f830ea2SJoerg Wunsch 
6175f830ea2SJoerg Wunsch 	/* have our children detached first */
6185f830ea2SJoerg Wunsch 	if ((error = bus_generic_detach(dev)))
6195f830ea2SJoerg Wunsch 		return (error);
6205f830ea2SJoerg Wunsch 
621e219897aSJoerg Wunsch 	/* reset controller, turn motor off */
622e219897aSJoerg Wunsch 	fdout_wr(fdc, 0);
623e219897aSJoerg Wunsch 
6245f830ea2SJoerg Wunsch 	fdc_release_resources(fdc);
6255f830ea2SJoerg Wunsch 	return (0);
6265f830ea2SJoerg Wunsch }
6275f830ea2SJoerg Wunsch 
6285b81b6b3SRodney W. Grimes /*
62937286586SPeter Wemm  * Add a child device to the fdc controller.  It will then be probed etc.
6305b81b6b3SRodney W. Grimes  */
631a6e4d8c4SNate Lawson device_t
63237286586SPeter Wemm fdc_add_child(device_t dev, const char *name, int unit)
6335b81b6b3SRodney W. Grimes {
634a6e4d8c4SNate Lawson 	int flags;
63537286586SPeter Wemm 	struct fdc_ivars *ivar;
6366182fdbdSPeter Wemm 	device_t child;
63792200632SGarrett Wollman 
6387cc0979fSDavid Malone 	ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT | M_ZERO);
63937286586SPeter Wemm 	if (ivar == NULL)
640a6e4d8c4SNate Lawson 		return (NULL);
641fe0d4089SMatthew N. Dodd 	child = device_add_child(dev, name, unit);
64242117b6cSPoul-Henning Kamp 	if (child == NULL) {
64342117b6cSPoul-Henning Kamp 		free(ivar, M_DEVBUF);
644a6e4d8c4SNate Lawson 		return (NULL);
64542117b6cSPoul-Henning Kamp 	}
64637286586SPeter Wemm 	device_set_ivars(child, ivar);
647a6e4d8c4SNate Lawson 	ivar->fdunit = unit;
648a6e4d8c4SNate Lawson 	ivar->fdtype = FDT_NONE;
6491a6bed68SJoerg Wunsch 	if (resource_int_value(name, unit, "flags", &flags) == 0)
6501a6bed68SJoerg Wunsch 		device_set_flags(child, flags);
6518a9bc9c0SJohn Baldwin 	if (resource_disabled(name, unit))
6526182fdbdSPeter Wemm 		device_disable(child);
653a6e4d8c4SNate Lawson 	return (child);
6546182fdbdSPeter Wemm }
6556182fdbdSPeter Wemm 
6566d6fa4fdSWarner Losh int
6576182fdbdSPeter Wemm fdc_attach(device_t dev)
6586182fdbdSPeter Wemm {
65937286586SPeter Wemm 	struct	fdc_data *fdc;
660a6e4d8c4SNate Lawson 	int	error;
661427ccf00SDoug Rabson 
66237286586SPeter Wemm 	fdc = device_get_softc(dev);
663b0c2b925SWarner Losh 	fdc->fdc_dev = dev;
66437286586SPeter Wemm 	error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq,
6653c36743eSMark Murray 			       INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc,
6663c36743eSMark Murray 			       &fdc->fdc_intr);
66737286586SPeter Wemm 	if (error) {
66837286586SPeter Wemm 		device_printf(dev, "cannot setup interrupt\n");
66937286586SPeter Wemm 		return error;
67037286586SPeter Wemm 	}
67137286586SPeter Wemm 	fdc->fdcu = device_get_unit(dev);
672a6e4d8c4SNate Lawson 	fdc->flags |= FDC_NEEDS_RESET;
6736182fdbdSPeter Wemm 
6745b81b6b3SRodney W. Grimes 	fdc->state = DEVIDLE;
6756182fdbdSPeter Wemm 
6763a2f7427SDavid Greenman 	/* reset controller, turn motor off, clear fdout mirror reg */
67764860614SJoerg Wunsch 	fdout_wr(fdc, fdc->fdout = 0);
6788177437dSPoul-Henning Kamp 	bioq_init(&fdc->head);
6795b81b6b3SRodney W. Grimes 
680a6e4d8c4SNate Lawson 	return (0);
681a6e4d8c4SNate Lawson }
682a6e4d8c4SNate Lawson 
683a6e4d8c4SNate Lawson int
684a6e4d8c4SNate Lawson fdc_hints_probe(device_t dev)
685a6e4d8c4SNate Lawson {
686a6e4d8c4SNate Lawson 	const char *name, *dname;
687a6e4d8c4SNate Lawson 	int i, error, dunit;
688a6e4d8c4SNate Lawson 
6896182fdbdSPeter Wemm 	/*
69037286586SPeter Wemm 	 * Probe and attach any children.  We should probably detect
69137286586SPeter Wemm 	 * devices from the BIOS unless overridden.
6926182fdbdSPeter Wemm 	 */
693ada54f9eSPeter Wemm 	name = device_get_nameunit(dev);
6942398f0cdSPeter Wemm 	i = 0;
695a6e4d8c4SNate Lawson 	while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) {
696a6e4d8c4SNate Lawson 		resource_int_value(dname, dunit, "drive", &dunit);
6972398f0cdSPeter Wemm 		fdc_add_child(dev, dname, dunit);
698a6e4d8c4SNate Lawson 	}
69937286586SPeter Wemm 
70060444853SJoerg Wunsch 	if ((error = bus_generic_attach(dev)) != 0)
70160444853SJoerg Wunsch 		return (error);
70260444853SJoerg Wunsch 	return (0);
7036182fdbdSPeter Wemm }
7046182fdbdSPeter Wemm 
7056d6fa4fdSWarner Losh int
7066182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child)
7076182fdbdSPeter Wemm {
7081a6bed68SJoerg Wunsch 	int retval = 0, flags;
70915317dd8SMatthew N. Dodd 
71015317dd8SMatthew N. Dodd 	retval += bus_print_child_header(me, child);
7111a6bed68SJoerg Wunsch 	retval += printf(" on %s drive %d", device_get_nameunit(me),
71237286586SPeter Wemm 	       fdc_get_fdunit(child));
7131a6bed68SJoerg Wunsch 	if ((flags = device_get_flags(me)) != 0)
7141a6bed68SJoerg Wunsch 		retval += printf(" flags %#x", flags);
7151a6bed68SJoerg Wunsch 	retval += printf("\n");
71615317dd8SMatthew N. Dodd 
71715317dd8SMatthew N. Dodd 	return (retval);
7186182fdbdSPeter Wemm }
7196182fdbdSPeter Wemm 
72016e68fc6SPeter Wemm /*
721246ed35dSJoerg Wunsch  * Configuration/initialization, per drive.
72216e68fc6SPeter Wemm  */
7236182fdbdSPeter Wemm static int
7246182fdbdSPeter Wemm fd_probe(device_t dev)
7256182fdbdSPeter Wemm {
7266182fdbdSPeter Wemm 	int	i;
7271a6bed68SJoerg Wunsch 	u_int	st0, st3;
7286182fdbdSPeter Wemm 	struct	fd_data *fd;
7296182fdbdSPeter Wemm 	struct	fdc_data *fdc;
7306182fdbdSPeter Wemm 	fdsu_t	fdsu;
731752d4735SNate Lawson 	int	flags, type;
7326182fdbdSPeter Wemm 
733752d4735SNate Lawson 	fdsu = fdc_get_fdunit(dev);
7346182fdbdSPeter Wemm 	fd = device_get_softc(dev);
7356182fdbdSPeter Wemm 	fdc = device_get_softc(device_get_parent(dev));
7361a6bed68SJoerg Wunsch 	flags = device_get_flags(dev);
7376182fdbdSPeter Wemm 
7386182fdbdSPeter Wemm 	fd->dev = dev;
7396182fdbdSPeter Wemm 	fd->fdc = fdc;
7406182fdbdSPeter Wemm 	fd->fdsu = fdsu;
7416182fdbdSPeter Wemm 	fd->fdu = device_get_unit(dev);
7426182fdbdSPeter Wemm 
743752d4735SNate Lawson 	/* Auto-probe if fdinfo is present, but always allow override. */
744a6e4d8c4SNate Lawson 	type = FD_DTYPE(flags);
745752d4735SNate Lawson 	if (type == FDT_NONE && (type = fdc_get_fdtype(dev)) != FDT_NONE) {
746752d4735SNate Lawson 		fd->type = type;
747752d4735SNate Lawson 		goto done;
748752d4735SNate Lawson 	} else {
749752d4735SNate Lawson 		/* make sure fdautoselect() will be called */
750752d4735SNate Lawson 		fd->flags = FD_UA;
751752d4735SNate Lawson 		fd->type = type;
752752d4735SNate Lawson 	}
753752d4735SNate Lawson 
754038d1bbdSJoerg Wunsch /*
755038d1bbdSJoerg Wunsch  * XXX I think using __i386__ is wrong here since we actually want to probe
756038d1bbdSJoerg Wunsch  * for the machine type, not the CPU type (so non-PC arch's like the PC98 will
757038d1bbdSJoerg Wunsch  * fail the probe).  However, for whatever reason, testing for _MACHINE_ARCH
758038d1bbdSJoerg Wunsch  * == i386 breaks the test on FreeBSD/Alpha.
759038d1bbdSJoerg Wunsch  */
76004209354SPeter Wemm #if defined(__i386__) || defined(__amd64__)
7611a6bed68SJoerg Wunsch 	if (fd->type == FDT_NONE && (fd->fdu == 0 || fd->fdu == 1)) {
7621a6bed68SJoerg Wunsch 		/* Look up what the BIOS thinks we have. */
7631a6bed68SJoerg Wunsch 		if (fd->fdu == 0) {
7645f830ea2SJoerg Wunsch 			if ((fdc->flags & FDC_ISPCMCIA))
7651a6bed68SJoerg Wunsch 				/*
7661a6bed68SJoerg Wunsch 				 * Somewhat special.  No need to force the
7671a6bed68SJoerg Wunsch 				 * user to set device flags, since the Y-E
7681a6bed68SJoerg Wunsch 				 * Data PCMCIA floppy is always a 1.44 MB
7691a6bed68SJoerg Wunsch 				 * device.
7701a6bed68SJoerg Wunsch 				 */
7711a6bed68SJoerg Wunsch 				fd->type = FDT_144M;
7720722d6abSJoerg Wunsch 			else
7731a6bed68SJoerg Wunsch 				fd->type = (rtcin(RTC_FDISKETTE) & 0xf0) >> 4;
7741a6bed68SJoerg Wunsch 		} else {
7751a6bed68SJoerg Wunsch 			fd->type = rtcin(RTC_FDISKETTE) & 0x0f;
7766b7bd95bSJoerg Wunsch 		}
7771a6bed68SJoerg Wunsch 		if (fd->type == FDT_288M_1)
7781a6bed68SJoerg Wunsch 			fd->type = FDT_288M;
7791a6bed68SJoerg Wunsch 	}
78004209354SPeter Wemm #endif /* __i386__ || __amd64__ */
7816182fdbdSPeter Wemm 	/* is there a unit? */
7821a6bed68SJoerg Wunsch 	if (fd->type == FDT_NONE)
7836182fdbdSPeter Wemm 		return (ENXIO);
7846182fdbdSPeter Wemm 
7856182fdbdSPeter Wemm 	/* select it */
7866182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNON);
787fb35bd37SJoerg Wunsch 	fdc_reset(fdc);		/* XXX reset, then unreset, etc. */
7886182fdbdSPeter Wemm 	DELAY(1000000);	/* 1 sec */
7896182fdbdSPeter Wemm 
7901a6bed68SJoerg Wunsch 	if ((flags & FD_NO_PROBE) == 0) {
791dc5df763SJoerg Wunsch 		/* If we're at track 0 first seek inwards. */
7921a6bed68SJoerg Wunsch 		if ((fd_sense_drive_status(fdc, &st3) == 0) &&
7931a6bed68SJoerg Wunsch 		    (st3 & NE7_ST3_T0)) {
794dc5df763SJoerg Wunsch 			/* Seek some steps... */
7956182fdbdSPeter Wemm 			if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
796dc5df763SJoerg Wunsch 				/* ...wait a moment... */
797dc5df763SJoerg Wunsch 				DELAY(300000);
798dc5df763SJoerg Wunsch 				/* make ctrlr happy: */
7996182fdbdSPeter Wemm 				fd_sense_int(fdc, 0, 0);
800dc5df763SJoerg Wunsch 			}
801dc5df763SJoerg Wunsch 		}
802dc5df763SJoerg Wunsch 
8036b7bd95bSJoerg Wunsch 		for (i = 0; i < 2; i++) {
8046b7bd95bSJoerg Wunsch 			/*
8056b7bd95bSJoerg Wunsch 			 * we must recalibrate twice, just in case the
8061a6bed68SJoerg Wunsch 			 * heads have been beyond cylinder 76, since
8071a6bed68SJoerg Wunsch 			 * most FDCs still barf when attempting to
8081a6bed68SJoerg Wunsch 			 * recalibrate more than 77 steps
8096b7bd95bSJoerg Wunsch 			 */
810dc5df763SJoerg Wunsch 			/* go back to 0: */
8116182fdbdSPeter Wemm 			if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
8126b7bd95bSJoerg Wunsch 				/* a second being enough for full stroke seek*/
8136b7bd95bSJoerg Wunsch 				DELAY(i == 0 ? 1000000 : 300000);
8145b81b6b3SRodney W. Grimes 
8156b7bd95bSJoerg Wunsch 				/* anything responding? */
816dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, 0) == 0 &&
817dc5df763SJoerg Wunsch 				    (st0 & NE7_ST0_EC) == 0)
8186b7bd95bSJoerg Wunsch 					break; /* already probed succesfully */
8196b7bd95bSJoerg Wunsch 			}
820dc5df763SJoerg Wunsch 		}
8211a6bed68SJoerg Wunsch 	}
8226b7bd95bSJoerg Wunsch 
8236182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNOFF);
8243a2f7427SDavid Greenman 
8251a6bed68SJoerg Wunsch 	if ((flags & FD_NO_PROBE) == 0 &&
8261a6bed68SJoerg Wunsch 	    (st0 & NE7_ST0_EC) != 0) /* no track 0 -> no drive present */
8276182fdbdSPeter Wemm 		return (ENXIO);
8285b81b6b3SRodney W. Grimes 
829752d4735SNate Lawson done:
830a6e4d8c4SNate Lawson 	/* This doesn't work before the first reset. */
831752d4735SNate Lawson 	if ((fdc->flags & FDC_HAS_FIFO) == 0 &&
832752d4735SNate Lawson 	    fdc->fdct == FDC_ENHANCED &&
833752d4735SNate Lawson 	    (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 &&
834752d4735SNate Lawson 	    enable_fifo(fdc) == 0) {
835752d4735SNate Lawson 		device_printf(device_get_parent(dev),
836752d4735SNate Lawson 		    "FIFO enabled, %d bytes threshold\n", fifo_threshold);
837752d4735SNate Lawson 	}
838752d4735SNate Lawson 
8391a6bed68SJoerg Wunsch 	switch (fd->type) {
8401a6bed68SJoerg Wunsch 	case FDT_12M:
8411a6bed68SJoerg Wunsch 		device_set_desc(dev, "1200-KB 5.25\" drive");
8421a6bed68SJoerg Wunsch 		break;
8431a6bed68SJoerg Wunsch 	case FDT_144M:
8441a6bed68SJoerg Wunsch 		device_set_desc(dev, "1440-KB 3.5\" drive");
8451a6bed68SJoerg Wunsch 		break;
8461a6bed68SJoerg Wunsch 	case FDT_288M:
8471a6bed68SJoerg Wunsch 		device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)");
8481a6bed68SJoerg Wunsch 		break;
8491a6bed68SJoerg Wunsch 	case FDT_360K:
8501a6bed68SJoerg Wunsch 		device_set_desc(dev, "360-KB 5.25\" drive");
8511a6bed68SJoerg Wunsch 		break;
8521a6bed68SJoerg Wunsch 	case FDT_720K:
8531a6bed68SJoerg Wunsch 		device_set_desc(dev, "720-KB 3.5\" drive");
8541a6bed68SJoerg Wunsch 		break;
8551a6bed68SJoerg Wunsch 	default:
8561a6bed68SJoerg Wunsch 		return (ENXIO);
8571a6bed68SJoerg Wunsch 	}
858dc5df763SJoerg Wunsch 	fd->track = FD_NO_TRACK;
859b99f0a4aSAndrew Moore 	fd->fdc = fdc;
860b99f0a4aSAndrew Moore 	fd->fdsu = fdsu;
8613a2f7427SDavid Greenman 	fd->options = 0;
86202a19910SJustin T. Gibbs 	callout_handle_init(&fd->toffhandle);
86302a19910SJustin T. Gibbs 	callout_handle_init(&fd->tohandle);
8645b81b6b3SRodney W. Grimes 
8651a6bed68SJoerg Wunsch 	/* initialize densities for subdevices */
8661a6bed68SJoerg Wunsch 	for (i = 0; i < NUMDENS; i++)
8671a6bed68SJoerg Wunsch 		memcpy(fd->fts + i, fd_native_types + fd->type,
8681a6bed68SJoerg Wunsch 		       sizeof(struct fd_type));
8696182fdbdSPeter Wemm 	return (0);
8706182fdbdSPeter Wemm }
8716182fdbdSPeter Wemm 
8726182fdbdSPeter Wemm static int
8736182fdbdSPeter Wemm fd_attach(device_t dev)
8746182fdbdSPeter Wemm {
8756182fdbdSPeter Wemm 	struct	fd_data *fd;
8766182fdbdSPeter Wemm 
87764860614SJoerg Wunsch 	fd = device_get_softc(dev);
878503799eaSPoul-Henning Kamp 	fd->masterdev = make_dev(&fd_cdevsw, fd->fdu,
8793f54a085SPoul-Henning Kamp 				 UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu);
880a2f19df9SPoul-Henning Kamp 	fd->masterdev->si_drv1 = fd;
88180980460SPoul-Henning Kamp 	fd->device_stats = devstat_new_entry(device_get_name(dev),
882f8ce7dd5SJoerg Wunsch 			  device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS,
8832a888f93SKenneth D. Merry 			  DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER,
8842a888f93SKenneth D. Merry 			  DEVSTAT_PRIORITY_FD);
8856182fdbdSPeter Wemm 	return (0);
8865b81b6b3SRodney W. Grimes }
8875b81b6b3SRodney W. Grimes 
8885f830ea2SJoerg Wunsch static int
8895f830ea2SJoerg Wunsch fd_detach(device_t dev)
8905f830ea2SJoerg Wunsch {
8915f830ea2SJoerg Wunsch 	struct	fd_data *fd;
8925f830ea2SJoerg Wunsch 
8935f830ea2SJoerg Wunsch 	fd = device_get_softc(dev);
89460444853SJoerg Wunsch 	untimeout(fd_turnoff, fd, fd->toffhandle);
89580980460SPoul-Henning Kamp 	devstat_remove_entry(fd->device_stats);
896e219897aSJoerg Wunsch 	destroy_dev(fd->masterdev);
8975f830ea2SJoerg Wunsch 
8985f830ea2SJoerg Wunsch 	return (0);
8995f830ea2SJoerg Wunsch }
9005f830ea2SJoerg Wunsch 
90116e68fc6SPeter Wemm static device_method_t fd_methods[] = {
90216e68fc6SPeter Wemm 	/* Device interface */
90316e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fd_probe),
90416e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fd_attach),
9055f830ea2SJoerg Wunsch 	DEVMETHOD(device_detach,	fd_detach),
90616e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
90716e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend), /* XXX */
90816e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume), /* XXX */
90916e68fc6SPeter Wemm 
91016e68fc6SPeter Wemm 	{ 0, 0 }
91116e68fc6SPeter Wemm };
91216e68fc6SPeter Wemm 
91316e68fc6SPeter Wemm static driver_t fd_driver = {
91416e68fc6SPeter Wemm 	"fd",
91516e68fc6SPeter Wemm 	fd_methods,
91616e68fc6SPeter Wemm 	sizeof(struct fd_data)
91716e68fc6SPeter Wemm };
91816e68fc6SPeter Wemm 
919475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0);
92016e68fc6SPeter Wemm 
921246ed35dSJoerg Wunsch /*
922246ed35dSJoerg Wunsch  * More auxiliary functions.
923246ed35dSJoerg Wunsch  */
924246ed35dSJoerg Wunsch /*
925246ed35dSJoerg Wunsch  * Motor control stuff.
926246ed35dSJoerg Wunsch  * Remember to not deselect the drive we're working on.
927246ed35dSJoerg Wunsch  */
9283a2f7427SDavid Greenman static void
9296182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon)
9305b81b6b3SRodney W. Grimes {
931fb35bd37SJoerg Wunsch 	int fdout;
9323a2f7427SDavid Greenman 
933fb35bd37SJoerg Wunsch 	fdout = fdc->fdout;
9343a2f7427SDavid Greenman 	if (turnon) {
9353a2f7427SDavid Greenman 		fdout &= ~FDO_FDSEL;
936fb35bd37SJoerg Wunsch 		fdout |= (FDO_MOEN0 << fdsu) | FDO_FDMAEN | FDO_FRST | fdsu;
9373a2f7427SDavid Greenman 	} else
9383a2f7427SDavid Greenman 		fdout &= ~(FDO_MOEN0 << fdsu);
9396182fdbdSPeter Wemm 	fdc->fdout = fdout;
940fb35bd37SJoerg Wunsch 	fdout_wr(fdc, fdout);
9413a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdout);
9423a2f7427SDavid Greenman }
9433a2f7427SDavid Greenman 
944381fe1aaSGarrett Wollman static void
9456182fdbdSPeter Wemm fd_turnoff(void *xfd)
9465b81b6b3SRodney W. Grimes {
947f5f7ba03SJordan K. Hubbard 	int	s;
9486182fdbdSPeter Wemm 	fd_p fd = xfd;
949dc16046fSJoerg Wunsch 
9506182fdbdSPeter Wemm 	TRACE1("[fd%d: turnoff]", fd->fdu);
9518335c1b8SBruce Evans 
9525f830ea2SJoerg Wunsch 	s = splbio();
9538335c1b8SBruce Evans 	/*
9548335c1b8SBruce Evans 	 * Don't turn off the motor yet if the drive is active.
9555f830ea2SJoerg Wunsch 	 *
9565f830ea2SJoerg Wunsch 	 * If we got here, this could only mean we missed an interrupt.
9575f830ea2SJoerg Wunsch 	 * This can e. g. happen on the Y-E Date PCMCIA floppy controller
9585f830ea2SJoerg Wunsch 	 * after a controller reset.  Just schedule a pseudo-interrupt
9595f830ea2SJoerg Wunsch 	 * so the state machine gets re-entered.
9608335c1b8SBruce Evans 	 */
9616182fdbdSPeter Wemm 	if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) {
9625f830ea2SJoerg Wunsch 		fdc_intr(fd->fdc);
9635f830ea2SJoerg Wunsch 		splx(s);
9648335c1b8SBruce Evans 		return;
9658335c1b8SBruce Evans 	}
9668335c1b8SBruce Evans 
9675b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR;
9686182fdbdSPeter Wemm 	set_motor(fd->fdc, fd->fdsu, TURNOFF);
969f5f7ba03SJordan K. Hubbard 	splx(s);
9705b81b6b3SRodney W. Grimes }
9715b81b6b3SRodney W. Grimes 
9723a2f7427SDavid Greenman static void
9736182fdbdSPeter Wemm fd_motor_on(void *xfd)
9745b81b6b3SRodney W. Grimes {
975f5f7ba03SJordan K. Hubbard 	int	s;
9766182fdbdSPeter Wemm 	fd_p fd = xfd;
977f5f7ba03SJordan K. Hubbard 
978f5f7ba03SJordan K. Hubbard 	s = splbio();
9795b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR_WAIT;
9805b81b6b3SRodney W. Grimes 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
9815b81b6b3SRodney W. Grimes 	{
9826182fdbdSPeter Wemm 		fdc_intr(fd->fdc);
9835b81b6b3SRodney W. Grimes 	}
984f5f7ba03SJordan K. Hubbard 	splx(s);
9855b81b6b3SRodney W. Grimes }
9865b81b6b3SRodney W. Grimes 
9873a2f7427SDavid Greenman static void
9886182fdbdSPeter Wemm fd_turnon(fd_p fd)
9895b81b6b3SRodney W. Grimes {
9905b81b6b3SRodney W. Grimes 	if(!(fd->flags & FD_MOTOR))
9915b81b6b3SRodney W. Grimes 	{
9923a2f7427SDavid Greenman 		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
9936182fdbdSPeter Wemm 		set_motor(fd->fdc, fd->fdsu, TURNON);
9946182fdbdSPeter Wemm 		timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */
9955b81b6b3SRodney W. Grimes 	}
9965b81b6b3SRodney W. Grimes }
9975b81b6b3SRodney W. Grimes 
998381fe1aaSGarrett Wollman static void
999dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc)
10005b81b6b3SRodney W. Grimes {
10013a2f7427SDavid Greenman 	/* Try a reset, keep motor on */
1002427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
10033a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
10043a2f7427SDavid Greenman 	DELAY(100);
10053a2f7427SDavid Greenman 	/* enable FDC, but defer interrupts a moment */
1006427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN);
10073a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
10083a2f7427SDavid Greenman 	DELAY(100);
1009427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout);
10103a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout);
10113a2f7427SDavid Greenman 
101264860614SJoerg Wunsch 	/* XXX after a reset, silently believe the FDC will accept commands */
10136182fdbdSPeter Wemm 	(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1014dc5df763SJoerg Wunsch 		     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1015dc5df763SJoerg Wunsch 		     0);
1016d66c374fSTor Egge 	if (fdc->flags & FDC_HAS_FIFO)
1017d66c374fSTor Egge 		(void) enable_fifo(fdc);
10185b81b6b3SRodney W. Grimes }
10195b81b6b3SRodney W. Grimes 
1020246ed35dSJoerg Wunsch /*
1021246ed35dSJoerg Wunsch  * FDC IO functions, take care of the main status register, timeout
1022246ed35dSJoerg Wunsch  * in case the desired status bits are never set.
1023a2642c4dSJoerg Wunsch  *
1024a2642c4dSJoerg Wunsch  * These PIO loops initially start out with short delays between
1025a2642c4dSJoerg Wunsch  * each iteration in the expectation that the required condition
1026a2642c4dSJoerg Wunsch  * is usually met quickly, so it can be handled immediately.  After
1027a2642c4dSJoerg Wunsch  * about 1 ms, stepping is increased to achieve a better timing
1028a2642c4dSJoerg Wunsch  * accuracy in the calls to DELAY().
1029246ed35dSJoerg Wunsch  */
1030b5e8ce9fSBruce Evans static int
10316182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr)
1032dc5df763SJoerg Wunsch {
1033a2642c4dSJoerg Wunsch 	int i, j, step;
1034a2642c4dSJoerg Wunsch 
1035a2642c4dSJoerg Wunsch 	for (j = 0, step = 1;
1036a2642c4dSJoerg Wunsch 	    (i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) &&
1037a2642c4dSJoerg Wunsch 	    j < FDSTS_TIMEOUT;
1038a2642c4dSJoerg Wunsch 	    j += step) {
1039dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
10408a0ba818SJoerg Wunsch 			return (fdc_err(fdc, "ready for output in input\n"));
1041a2642c4dSJoerg Wunsch 		if (j == 1000)
1042a2642c4dSJoerg Wunsch 			step = 1000;
1043a2642c4dSJoerg Wunsch 		DELAY(step);
1044d0900d6bSJoerg Wunsch 	}
1045a2642c4dSJoerg Wunsch 	if (j >= FDSTS_TIMEOUT)
10468a0ba818SJoerg Wunsch 		return (fdc_err(fdc, bootverbose? "input ready timeout\n": 0));
1047d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
1048427ccf00SDoug Rabson 	i = fddata_rd(fdc);
1049dc5df763SJoerg Wunsch 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
1050dc5df763SJoerg Wunsch 	*ptr = i;
10518a0ba818SJoerg Wunsch 	return (0);
1052d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
1053427ccf00SDoug Rabson 	i = fddata_rd(fdc);
1054dc5df763SJoerg Wunsch 	if (ptr)
1055dc5df763SJoerg Wunsch 		*ptr = i;
10568a0ba818SJoerg Wunsch 	return (0);
1057d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
1058dc5df763SJoerg Wunsch }
1059dc5df763SJoerg Wunsch 
106037c84183SPoul-Henning Kamp static int
10616182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x)
10625b81b6b3SRodney W. Grimes {
1063a2642c4dSJoerg Wunsch 	int i, j, step;
10645b81b6b3SRodney W. Grimes 
1065a2642c4dSJoerg Wunsch 	for (j = 0, step = 1;
1066a2642c4dSJoerg Wunsch 	    (i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != NE7_RQM &&
1067a2642c4dSJoerg Wunsch 	    j < FDSTS_TIMEOUT;
1068a2642c4dSJoerg Wunsch 	    j += step) {
1069a2642c4dSJoerg Wunsch 		if (i == (NE7_DIO|NE7_RQM))
1070a2642c4dSJoerg Wunsch 			return (fdc_err(fdc, "ready for input in output\n"));
1071a2642c4dSJoerg Wunsch 		if (j == 1000)
1072a2642c4dSJoerg Wunsch 			step = 1000;
1073a2642c4dSJoerg Wunsch 		DELAY(step);
1074a2642c4dSJoerg Wunsch 	}
1075a2642c4dSJoerg Wunsch 	if (j >= FDSTS_TIMEOUT)
10768a0ba818SJoerg Wunsch 		return (fdc_err(fdc, bootverbose? "output ready timeout\n": 0));
10773b3837dbSRodney W. Grimes 
10783b3837dbSRodney W. Grimes 	/* Send the command and return */
1079427ccf00SDoug Rabson 	fddata_wr(fdc, x);
10803a2f7427SDavid Greenman 	TRACE1("[0x%x->FDDATA]", x);
10815b81b6b3SRodney W. Grimes 	return (0);
10825b81b6b3SRodney W. Grimes }
10835b81b6b3SRodney W. Grimes 
1084246ed35dSJoerg Wunsch /*
1085246ed35dSJoerg Wunsch  * Block device driver interface functions (interspersed with even more
1086246ed35dSJoerg Wunsch  * auxiliary functions).
1087246ed35dSJoerg Wunsch  */
108837c84183SPoul-Henning Kamp static int
108989c9c53dSPoul-Henning Kamp fdopen(struct cdev *dev, int flags, int mode, struct thread *td)
10905b81b6b3SRodney W. Grimes {
10916182fdbdSPeter Wemm 	fd_p	fd;
1092b99f0a4aSAndrew Moore 	fdc_p	fdc;
10931a6bed68SJoerg Wunsch  	int rv, unitattn, dflags;
10945b81b6b3SRodney W. Grimes 
1095a2f19df9SPoul-Henning Kamp 	fd = dev->si_drv1;
1096a2f19df9SPoul-Henning Kamp 	if (fd == NULL)
1097b99f0a4aSAndrew Moore 		return (ENXIO);
10986182fdbdSPeter Wemm 	fdc = fd->fdc;
10991a6bed68SJoerg Wunsch 	if ((fdc == NULL) || (fd->type == FDT_NONE))
1100b99f0a4aSAndrew Moore 		return (ENXIO);
11011a6bed68SJoerg Wunsch 	dflags = device_get_flags(fd->dev);
11023e425b96SJulian Elischer 	/*
11031a6bed68SJoerg Wunsch 	 * This is a bit bogus.  It's still possible that e. g. a
11041a6bed68SJoerg Wunsch 	 * descriptor gets inherited to a child, but then it's at
11051a6bed68SJoerg Wunsch 	 * least for the same subdevice.  By checking FD_OPEN here, we
11061a6bed68SJoerg Wunsch 	 * can ensure that a device isn't attempted to be opened with
11071a6bed68SJoerg Wunsch 	 * different densities at the same time where the second open
11081a6bed68SJoerg Wunsch 	 * could clobber the settings from the first one.
11093e425b96SJulian Elischer 	 */
11101a6bed68SJoerg Wunsch 	if (fd->flags & FD_OPEN)
11111a6bed68SJoerg Wunsch 		return (EBUSY);
11121a6bed68SJoerg Wunsch 
11131a6bed68SJoerg Wunsch 	if (flags & FNONBLOCK) {
11141a6bed68SJoerg Wunsch 		/*
11151a6bed68SJoerg Wunsch 		 * Unfortunately, physio(9) discards its ioflag
11161a6bed68SJoerg Wunsch 		 * argument, thus preventing us from seeing the
11171a6bed68SJoerg Wunsch 		 * IO_NDELAY bit.  So we need to keep track
11181a6bed68SJoerg Wunsch 		 * ourselves.
11191a6bed68SJoerg Wunsch 		 */
11201a6bed68SJoerg Wunsch 		fd->flags |= FD_NONBLOCK;
11211a6bed68SJoerg Wunsch 		fd->ft = 0;
11221a6bed68SJoerg Wunsch 	} else {
11231a6bed68SJoerg Wunsch 		/*
11241a6bed68SJoerg Wunsch 		 * Figure out a unit attention condition.
11251a6bed68SJoerg Wunsch 		 *
11261a6bed68SJoerg Wunsch 		 * If UA has been forced, proceed.
11271a6bed68SJoerg Wunsch 		 *
112862cc1e05SYaroslav Tykhiy 		 * If the drive has no changeline support,
112962cc1e05SYaroslav Tykhiy 		 * or if the drive parameters have been lost
113062cc1e05SYaroslav Tykhiy 		 * due to previous non-blocking access,
113162cc1e05SYaroslav Tykhiy 		 * assume a forced UA condition.
113262cc1e05SYaroslav Tykhiy 		 *
11331a6bed68SJoerg Wunsch 		 * If motor is off, turn it on for a moment
11341a6bed68SJoerg Wunsch 		 * and select our drive, in order to read the
11351a6bed68SJoerg Wunsch 		 * UA hardware signal.
11361a6bed68SJoerg Wunsch 		 *
11371a6bed68SJoerg Wunsch 		 * If motor is on, and our drive is currently
11381a6bed68SJoerg Wunsch 		 * selected, just read the hardware bit.
11391a6bed68SJoerg Wunsch 		 *
11401a6bed68SJoerg Wunsch 		 * If motor is on, but active for another
11411a6bed68SJoerg Wunsch 		 * drive on that controller, we are lost.  We
11421a6bed68SJoerg Wunsch 		 * cannot risk to deselect the other drive, so
11431a6bed68SJoerg Wunsch 		 * we just assume a forced UA condition to be
11441a6bed68SJoerg Wunsch 		 * on the safe side.
11451a6bed68SJoerg Wunsch 		 */
11461a6bed68SJoerg Wunsch 		unitattn = 0;
11471a6bed68SJoerg Wunsch 		if ((dflags & FD_NO_CHLINE) != 0 ||
114862cc1e05SYaroslav Tykhiy 		    (fd->flags & FD_UA) != 0 ||
114962cc1e05SYaroslav Tykhiy 		    fd->ft == 0) {
11501a6bed68SJoerg Wunsch 			unitattn = 1;
11511a6bed68SJoerg Wunsch 			fd->flags &= ~FD_UA;
11521a6bed68SJoerg Wunsch 		} else if (fdc->fdout & (FDO_MOEN0 | FDO_MOEN1 |
11531a6bed68SJoerg Wunsch 					 FDO_MOEN2 | FDO_MOEN3)) {
11541a6bed68SJoerg Wunsch 			if ((fdc->fdout & FDO_FDSEL) == fd->fdsu)
11551a6bed68SJoerg Wunsch 				unitattn = fdin_rd(fdc) & FDI_DCHG;
11561a6bed68SJoerg Wunsch 			else
11571a6bed68SJoerg Wunsch 				unitattn = 1;
11581a6bed68SJoerg Wunsch 		} else {
11591a6bed68SJoerg Wunsch 			set_motor(fdc, fd->fdsu, TURNON);
11601a6bed68SJoerg Wunsch 			unitattn = fdin_rd(fdc) & FDI_DCHG;
11611a6bed68SJoerg Wunsch 			set_motor(fdc, fd->fdsu, TURNOFF);
1162b39c878eSAndrey A. Chernov 		}
11631a6bed68SJoerg Wunsch 		if (unitattn && (rv = fdautoselect(dev)) != 0)
11641a6bed68SJoerg Wunsch 			return (rv);
11657ca0641bSAndrey A. Chernov 	}
11666182fdbdSPeter Wemm 	fd->flags |= FD_OPEN;
1167de0b7a63SPoul-Henning Kamp 
1168de0b7a63SPoul-Henning Kamp 	if ((fdc->flags & FDC_NODMA) == 0) {
1169de0b7a63SPoul-Henning Kamp 		if (fdc->dmacnt++ == 0) {
1170de0b7a63SPoul-Henning Kamp 			isa_dma_acquire(fdc->dmachan);
1171de0b7a63SPoul-Henning Kamp 			isa_dmainit(fdc->dmachan, MAX_SEC_SIZE);
1172de0b7a63SPoul-Henning Kamp 		}
1173de0b7a63SPoul-Henning Kamp 	}
1174de0b7a63SPoul-Henning Kamp 
11753fef646eSJoerg Wunsch 	/*
11763fef646eSJoerg Wunsch 	 * Clearing the DMA overrun counter at open time is a bit messy.
11773fef646eSJoerg Wunsch 	 * Since we're only managing one counter per controller, opening
11783fef646eSJoerg Wunsch 	 * the second drive could mess it up.  Anyway, if the DMA overrun
11793fef646eSJoerg Wunsch 	 * condition is really persistent, it will eventually time out
11803fef646eSJoerg Wunsch 	 * still.  OTOH, clearing it here will ensure we'll at least start
11813fef646eSJoerg Wunsch 	 * trying again after a previous (maybe even long ago) failure.
11823fef646eSJoerg Wunsch 	 * Also, this is merely a stop-gap measure only that should not
11833fef646eSJoerg Wunsch 	 * happen during normal operation, so we can tolerate it to be a
11843fef646eSJoerg Wunsch 	 * bit sloppy about this.
11853fef646eSJoerg Wunsch 	 */
11863fef646eSJoerg Wunsch 	fdc->dma_overruns = 0;
11875f830ea2SJoerg Wunsch 
11885b81b6b3SRodney W. Grimes 	return 0;
11895b81b6b3SRodney W. Grimes }
11905b81b6b3SRodney W. Grimes 
119137c84183SPoul-Henning Kamp static int
119289c9c53dSPoul-Henning Kamp fdclose(struct cdev *dev, int flags, int mode, struct thread *td)
11935b81b6b3SRodney W. Grimes {
11946182fdbdSPeter Wemm 	struct fd_data *fd;
1195de0b7a63SPoul-Henning Kamp  	fdc_p	fdc;
1196b99f0a4aSAndrew Moore 
1197a2f19df9SPoul-Henning Kamp 	fd = dev->si_drv1;
1198de0b7a63SPoul-Henning Kamp 	fdc = fd->fdc;
11991a6bed68SJoerg Wunsch 	fd->flags &= ~(FD_OPEN | FD_NONBLOCK);
12002995d110SJoerg Wunsch 	fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR);
1201dc16046fSJoerg Wunsch 
1202de0b7a63SPoul-Henning Kamp 	if ((fdc->flags & FDC_NODMA) == 0)
1203de0b7a63SPoul-Henning Kamp 		if (--fdc->dmacnt == 0)
1204de0b7a63SPoul-Henning Kamp 			isa_dma_release(fdc->dmachan);
1205de0b7a63SPoul-Henning Kamp 
12065b81b6b3SRodney W. Grimes 	return (0);
12075b81b6b3SRodney W. Grimes }
12085b81b6b3SRodney W. Grimes 
120937c84183SPoul-Henning Kamp static void
12108177437dSPoul-Henning Kamp fdstrategy(struct bio *bp)
12113a2f7427SDavid Greenman {
1212fb35bd37SJoerg Wunsch 	long blknum, nblocks;
12133a2f7427SDavid Greenman  	int	s;
12143a2f7427SDavid Greenman  	fdu_t	fdu;
12153a2f7427SDavid Greenman  	fdc_p	fdc;
12163a2f7427SDavid Greenman  	fd_p	fd;
12173a2f7427SDavid Greenman 	size_t	fdblk;
12183a2f7427SDavid Greenman 
1219a2f19df9SPoul-Henning Kamp 	fd = bp->bio_dev->si_drv1;
1220503799eaSPoul-Henning Kamp  	fdu = fd->fdu;
12213a2f7427SDavid Greenman 	fdc = fd->fdc;
122283efe35aSYaroslav Tykhiy 	bp->bio_resid = bp->bio_bcount;
12231a6bed68SJoerg Wunsch 	if (fd->type == FDT_NONE || fd->ft == 0) {
1224f79981acSYaroslav Tykhiy 		if (fd->type != FDT_NONE && (fd->flags & FD_NONBLOCK))
1225f79981acSYaroslav Tykhiy 			bp->bio_error = EAGAIN;
1226f79981acSYaroslav Tykhiy 		else
12278177437dSPoul-Henning Kamp 			bp->bio_error = ENXIO;
12288177437dSPoul-Henning Kamp 		bp->bio_flags |= BIO_ERROR;
12293b178206SWarner Losh 		goto bad;
123064860614SJoerg Wunsch 	}
1231d3628763SRodney W. Grimes 	fdblk = 128 << (fd->ft->secsize);
1232419f39ceSPoul-Henning Kamp 	if (bp->bio_cmd != FDBIO_FORMAT && bp->bio_cmd != FDBIO_RDSECTID) {
12331a6bed68SJoerg Wunsch 		if (fd->flags & FD_NONBLOCK) {
12341a6bed68SJoerg Wunsch 			bp->bio_error = EAGAIN;
12351a6bed68SJoerg Wunsch 			bp->bio_flags |= BIO_ERROR;
12361a6bed68SJoerg Wunsch 			goto bad;
12371a6bed68SJoerg Wunsch 		}
1238b52b7f46SPoul-Henning Kamp 		if (bp->bio_offset < 0) {
1239dc5df763SJoerg Wunsch 			printf(
1240b52b7f46SPoul-Henning Kamp 		"fd%d: fdstrat: bad request offset = %ju, bcount = %ld\n",
1241b52b7f46SPoul-Henning Kamp 			       fdu, (intmax_t)bp->bio_offset, bp->bio_bcount);
12428177437dSPoul-Henning Kamp 			bp->bio_error = EINVAL;
12438177437dSPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
12443a2f7427SDavid Greenman 			goto bad;
12453a2f7427SDavid Greenman 		}
12468177437dSPoul-Henning Kamp 		if ((bp->bio_bcount % fdblk) != 0) {
12478177437dSPoul-Henning Kamp 			bp->bio_error = EINVAL;
12488177437dSPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
12493a2f7427SDavid Greenman 			goto bad;
12503a2f7427SDavid Greenman 		}
12513a2f7427SDavid Greenman 	}
12523a2f7427SDavid Greenman 
12533a2f7427SDavid Greenman 	/*
12543a2f7427SDavid Greenman 	 * Set up block calculations.
12553a2f7427SDavid Greenman 	 */
1256b52b7f46SPoul-Henning Kamp 	if (bp->bio_offset >= ((off_t)128 << fd->ft->secsize) * fd->ft->size) {
12578177437dSPoul-Henning Kamp 		bp->bio_error = EINVAL;
12588177437dSPoul-Henning Kamp 		bp->bio_flags |= BIO_ERROR;
12593a2f7427SDavid Greenman 		goto bad;
12603a2f7427SDavid Greenman 	}
1261b52b7f46SPoul-Henning Kamp 	blknum = bp->bio_offset / fdblk;
1262bb6382faSJoerg Wunsch  	nblocks = fd->ft->size;
1263fb35bd37SJoerg Wunsch 	if (blknum + bp->bio_bcount / fdblk > nblocks) {
1264fb35bd37SJoerg Wunsch 		if (blknum >= nblocks) {
126583efe35aSYaroslav Tykhiy 			if (bp->bio_cmd != BIO_READ) {
1266fb35bd37SJoerg Wunsch 				bp->bio_error = ENOSPC;
12678177437dSPoul-Henning Kamp 				bp->bio_flags |= BIO_ERROR;
1268bb6382faSJoerg Wunsch 			}
1269fb35bd37SJoerg Wunsch 			goto bad;	/* not always bad, but EOF */
1270fb35bd37SJoerg Wunsch 		}
1271fb35bd37SJoerg Wunsch 		bp->bio_bcount = (nblocks - blknum) * fdblk;
1272bb6382faSJoerg Wunsch 	}
12739a5e3ddbSJoerg Wunsch  	bp->bio_pblkno = blknum;
12743a2f7427SDavid Greenman 	s = splbio();
1275891619a6SPoul-Henning Kamp 	bioq_disksort(&fdc->head, bp);
12766182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */
12771ecc485cSPoul-Henning Kamp 	devstat_start_transaction_bio(fd->device_stats, bp);
12785f830ea2SJoerg Wunsch 	device_busy(fd->dev);
12796182fdbdSPeter Wemm 	fdstart(fdc);
12803a2f7427SDavid Greenman 	splx(s);
12813a2f7427SDavid Greenman 	return;
12823a2f7427SDavid Greenman 
12833a2f7427SDavid Greenman bad:
12843a2f7427SDavid Greenman 	biodone(bp);
12853a2f7427SDavid Greenman }
12863a2f7427SDavid Greenman 
1287246ed35dSJoerg Wunsch /*
1288246ed35dSJoerg Wunsch  * fdstart
1289246ed35dSJoerg Wunsch  *
1290246ed35dSJoerg Wunsch  * We have just queued something.  If the controller is not busy
1291246ed35dSJoerg Wunsch  * then simulate the case where it has just finished a command
1292246ed35dSJoerg Wunsch  * So that it (the interrupt routine) looks on the queue for more
1293246ed35dSJoerg Wunsch  * work to do and picks up what we just added.
1294246ed35dSJoerg Wunsch  *
1295246ed35dSJoerg Wunsch  * If the controller is already busy, we need do nothing, as it
1296246ed35dSJoerg Wunsch  * will pick up our work when the present work completes.
1297246ed35dSJoerg Wunsch  */
1298381fe1aaSGarrett Wollman static void
12996182fdbdSPeter Wemm fdstart(struct fdc_data *fdc)
13005b81b6b3SRodney W. Grimes {
13015b81b6b3SRodney W. Grimes 	int s;
13025b81b6b3SRodney W. Grimes 
13035b81b6b3SRodney W. Grimes 	s = splbio();
13046182fdbdSPeter Wemm 	if(fdc->state == DEVIDLE)
13055b81b6b3SRodney W. Grimes 	{
13066182fdbdSPeter Wemm 		fdc_intr(fdc);
13075b81b6b3SRodney W. Grimes 	}
13085b81b6b3SRodney W. Grimes 	splx(s);
13095b81b6b3SRodney W. Grimes }
13105b81b6b3SRodney W. Grimes 
1311381fe1aaSGarrett Wollman static void
13126182fdbdSPeter Wemm fd_iotimeout(void *xfdc)
13135b81b6b3SRodney W. Grimes {
13145c1a1eaeSBruce Evans  	fdc_p fdc;
1315f5f7ba03SJordan K. Hubbard 	int s;
13165b81b6b3SRodney W. Grimes 
13176182fdbdSPeter Wemm 	fdc = xfdc;
13185c1a1eaeSBruce Evans 	TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
13195b81b6b3SRodney W. Grimes 
13203a2f7427SDavid Greenman 	/*
13213a2f7427SDavid Greenman 	 * Due to IBM's brain-dead design, the FDC has a faked ready
13223a2f7427SDavid Greenman 	 * signal, hardwired to ready == true. Thus, any command
13233a2f7427SDavid Greenman 	 * issued if there's no diskette in the drive will _never_
13243a2f7427SDavid Greenman 	 * complete, and must be aborted by resetting the FDC.
13253a2f7427SDavid Greenman 	 * Many thanks, Big Blue!
13265c1a1eaeSBruce Evans 	 * The FDC must not be reset directly, since that would
13275c1a1eaeSBruce Evans 	 * interfere with the state machine.  Instead, pretend that
13285c1a1eaeSBruce Evans 	 * the command completed but was invalid.  The state machine
13295c1a1eaeSBruce Evans 	 * will reset the FDC and retry once.
13303a2f7427SDavid Greenman 	 */
13313a2f7427SDavid Greenman 	s = splbio();
13325c1a1eaeSBruce Evans 	fdc->status[0] = NE7_ST0_IC_IV;
13335c1a1eaeSBruce Evans 	fdc->flags &= ~FDC_STAT_VALID;
13345c1a1eaeSBruce Evans 	fdc->state = IOTIMEDOUT;
13356182fdbdSPeter Wemm 	fdc_intr(fdc);
1336f5f7ba03SJordan K. Hubbard 	splx(s);
13375b81b6b3SRodney W. Grimes }
13385b81b6b3SRodney W. Grimes 
1339246ed35dSJoerg Wunsch /* Just ensure it has the right spl. */
1340381fe1aaSGarrett Wollman static void
13416182fdbdSPeter Wemm fd_pseudointr(void *xfdc)
13425b81b6b3SRodney W. Grimes {
13435b81b6b3SRodney W. Grimes 	int	s;
13443a2f7427SDavid Greenman 
13455b81b6b3SRodney W. Grimes 	s = splbio();
13466182fdbdSPeter Wemm 	fdc_intr(xfdc);
13475b81b6b3SRodney W. Grimes 	splx(s);
13485b81b6b3SRodney W. Grimes }
13495b81b6b3SRodney W. Grimes 
1350246ed35dSJoerg Wunsch /*
1351246ed35dSJoerg Wunsch  * fdc_intr
1352246ed35dSJoerg Wunsch  *
1353246ed35dSJoerg Wunsch  * Keep calling the state machine until it returns a 0.
1354246ed35dSJoerg Wunsch  * Always called at splbio.
1355246ed35dSJoerg Wunsch  */
1356fe310de8SBruce Evans static void
13576182fdbdSPeter Wemm fdc_intr(void *xfdc)
13585b81b6b3SRodney W. Grimes {
13596182fdbdSPeter Wemm 	fdc_p fdc = xfdc;
13606182fdbdSPeter Wemm 	while(fdstate(fdc))
1361381fe1aaSGarrett Wollman 		;
13625b81b6b3SRodney W. Grimes }
13635b81b6b3SRodney W. Grimes 
136469acd21dSWarner Losh /*
1365246ed35dSJoerg Wunsch  * Magic pseudo-DMA initialization for YE FDC. Sets count and
1366246ed35dSJoerg Wunsch  * direction.
136769acd21dSWarner Losh  */
13683b178206SWarner Losh #define SET_BCDR(fdc,wr,cnt,port) \
13693b178206SWarner Losh 	bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port,	 \
13703b178206SWarner Losh 	    ((cnt)-1) & 0xff);						 \
13713b178206SWarner Losh 	bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \
13723b178206SWarner Losh 	    ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f)));
137369acd21dSWarner Losh 
137469acd21dSWarner Losh /*
1375246ed35dSJoerg Wunsch  * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy.
137669acd21dSWarner Losh  */
13774c34deeeSJoerg Wunsch static int
13784c34deeeSJoerg Wunsch fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count)
137969acd21dSWarner Losh {
138069acd21dSWarner Losh 	u_char *cptr = (u_char *)addr;
138169acd21dSWarner Losh 
138221144e3bSPoul-Henning Kamp 	if (flags == BIO_READ) {
138369acd21dSWarner Losh 		if (fdc->state != PIOREAD) {
138469acd21dSWarner Losh 			fdc->state = PIOREAD;
138569acd21dSWarner Losh 			return(0);
138664860614SJoerg Wunsch 		}
13873b178206SWarner Losh 		SET_BCDR(fdc, 0, count, 0);
13883b178206SWarner Losh 		bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off +
13893b178206SWarner Losh 		    FDC_YE_DATAPORT, cptr, count);
139069acd21dSWarner Losh 	} else {
13913b178206SWarner Losh 		bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off +
13923b178206SWarner Losh 		    FDC_YE_DATAPORT, cptr, count);
13933b178206SWarner Losh 		SET_BCDR(fdc, 0, count, 0);
139464860614SJoerg Wunsch 	}
139569acd21dSWarner Losh 	return(1);
139669acd21dSWarner Losh }
139769acd21dSWarner Losh 
1398246ed35dSJoerg Wunsch /*
13991a6bed68SJoerg Wunsch  * Try figuring out the density of the media present in our device.
14001a6bed68SJoerg Wunsch  */
14011a6bed68SJoerg Wunsch static int
140289c9c53dSPoul-Henning Kamp fdautoselect(struct cdev *dev)
14031a6bed68SJoerg Wunsch {
14041a6bed68SJoerg Wunsch  	fd_p fd;
14051a6bed68SJoerg Wunsch 	struct fd_type *fdtp;
14061a6bed68SJoerg Wunsch 	struct fdc_readid id;
14071a6bed68SJoerg Wunsch 	int i, n, oopts, rv;
14081a6bed68SJoerg Wunsch 
1409a2f19df9SPoul-Henning Kamp 	fd = dev->si_drv1;
14101a6bed68SJoerg Wunsch 
14111a6bed68SJoerg Wunsch 	switch (fd->type) {
14121a6bed68SJoerg Wunsch 	default:
14131a6bed68SJoerg Wunsch 		return (ENXIO);
14141a6bed68SJoerg Wunsch 
14151a6bed68SJoerg Wunsch 	case FDT_360K:
14161a6bed68SJoerg Wunsch 	case FDT_720K:
14171a6bed68SJoerg Wunsch 		/* no autoselection on those drives */
14181a6bed68SJoerg Wunsch 		fd->ft = fd_native_types + fd->type;
14191a6bed68SJoerg Wunsch 		return (0);
14201a6bed68SJoerg Wunsch 
14211a6bed68SJoerg Wunsch 	case FDT_12M:
14221a6bed68SJoerg Wunsch 		fdtp = fd_searchlist_12m;
14231a6bed68SJoerg Wunsch 		n = sizeof fd_searchlist_12m / sizeof(struct fd_type);
14241a6bed68SJoerg Wunsch 		break;
14251a6bed68SJoerg Wunsch 
14261a6bed68SJoerg Wunsch 	case FDT_144M:
14271a6bed68SJoerg Wunsch 		fdtp = fd_searchlist_144m;
14281a6bed68SJoerg Wunsch 		n = sizeof fd_searchlist_144m / sizeof(struct fd_type);
14291a6bed68SJoerg Wunsch 		break;
14301a6bed68SJoerg Wunsch 
14311a6bed68SJoerg Wunsch 	case FDT_288M:
14321a6bed68SJoerg Wunsch 		fdtp = fd_searchlist_288m;
14331a6bed68SJoerg Wunsch 		n = sizeof fd_searchlist_288m / sizeof(struct fd_type);
14341a6bed68SJoerg Wunsch 		break;
14351a6bed68SJoerg Wunsch 	}
14361a6bed68SJoerg Wunsch 
14371a6bed68SJoerg Wunsch 	/*
14381a6bed68SJoerg Wunsch 	 * Try reading sector ID fields, first at cylinder 0, head 0,
14391a6bed68SJoerg Wunsch 	 * then at cylinder 2, head N.  We don't probe cylinder 1,
14401a6bed68SJoerg Wunsch 	 * since for 5.25in DD media in a HD drive, there are no data
14411a6bed68SJoerg Wunsch 	 * to read (2 step pulses per media cylinder required).  For
14421a6bed68SJoerg Wunsch 	 * two-sided media, the second probe always goes to head 1, so
14431a6bed68SJoerg Wunsch 	 * we can tell them apart from single-sided media.  As a
14441a6bed68SJoerg Wunsch 	 * side-effect this means that single-sided media should be
14451a6bed68SJoerg Wunsch 	 * mentioned in the search list after two-sided media of an
14461a6bed68SJoerg Wunsch 	 * otherwise identical density.  Media with a different number
14471a6bed68SJoerg Wunsch 	 * of sectors per track but otherwise identical parameters
14481a6bed68SJoerg Wunsch 	 * cannot be distinguished at all.
14491a6bed68SJoerg Wunsch 	 *
14501a6bed68SJoerg Wunsch 	 * If we successfully read an ID field on both cylinders where
14511a6bed68SJoerg Wunsch 	 * the recorded values match our expectation, we are done.
14521a6bed68SJoerg Wunsch 	 * Otherwise, we try the next density entry from the table.
14531a6bed68SJoerg Wunsch 	 *
14541a6bed68SJoerg Wunsch 	 * Stepping to cylinder 2 has the side-effect of clearing the
14551a6bed68SJoerg Wunsch 	 * unit attention bit.
14561a6bed68SJoerg Wunsch 	 */
14571a6bed68SJoerg Wunsch 	oopts = fd->options;
14581a6bed68SJoerg Wunsch 	fd->options |= FDOPT_NOERRLOG | FDOPT_NORETRY;
14591a6bed68SJoerg Wunsch 	for (i = 0; i < n; i++, fdtp++) {
14601a6bed68SJoerg Wunsch 		fd->ft = fdtp;
14611a6bed68SJoerg Wunsch 
14621a6bed68SJoerg Wunsch 		id.cyl = id.head = 0;
1463419f39ceSPoul-Henning Kamp 		rv = fdmisccmd(dev, FDBIO_RDSECTID, &id);
14641a6bed68SJoerg Wunsch 		if (rv != 0)
14651a6bed68SJoerg Wunsch 			continue;
14661a6bed68SJoerg Wunsch 		if (id.cyl != 0 || id.head != 0 ||
14671a6bed68SJoerg Wunsch 		    id.secshift != fdtp->secsize)
14681a6bed68SJoerg Wunsch 			continue;
14691a6bed68SJoerg Wunsch 		id.cyl = 2;
14701a6bed68SJoerg Wunsch 		id.head = fd->ft->heads - 1;
1471419f39ceSPoul-Henning Kamp 		rv = fdmisccmd(dev, FDBIO_RDSECTID, &id);
14721a6bed68SJoerg Wunsch 		if (id.cyl != 2 || id.head != fdtp->heads - 1 ||
14731a6bed68SJoerg Wunsch 		    id.secshift != fdtp->secsize)
14741a6bed68SJoerg Wunsch 			continue;
14751a6bed68SJoerg Wunsch 		if (rv == 0)
14761a6bed68SJoerg Wunsch 			break;
14771a6bed68SJoerg Wunsch 	}
14781a6bed68SJoerg Wunsch 
14791a6bed68SJoerg Wunsch 	fd->options = oopts;
14801a6bed68SJoerg Wunsch 	if (i == n) {
14815613959dSJoerg Wunsch 		if (bootverbose)
14821a6bed68SJoerg Wunsch 			device_printf(fd->dev, "autoselection failed\n");
14831a6bed68SJoerg Wunsch 		fd->ft = 0;
14841a6bed68SJoerg Wunsch 		return (EIO);
14851a6bed68SJoerg Wunsch 	} else {
14865613959dSJoerg Wunsch 		if (bootverbose)
14871a6bed68SJoerg Wunsch 			device_printf(fd->dev, "autoselected %d KB medium\n",
14881a6bed68SJoerg Wunsch 				      fd->ft->size / 2);
14891a6bed68SJoerg Wunsch 		return (0);
14901a6bed68SJoerg Wunsch 	}
14911a6bed68SJoerg Wunsch }
14921a6bed68SJoerg Wunsch 
14931a6bed68SJoerg Wunsch 
14941a6bed68SJoerg Wunsch /*
1495246ed35dSJoerg Wunsch  * The controller state machine.
1496246ed35dSJoerg Wunsch  *
1497246ed35dSJoerg Wunsch  * If it returns a non zero value, it should be called again immediately.
1498246ed35dSJoerg Wunsch  */
14993a2f7427SDavid Greenman static int
15006182fdbdSPeter Wemm fdstate(fdc_p fdc)
15015b81b6b3SRodney W. Grimes {
150264860614SJoerg Wunsch 	struct fdc_readid *idp;
1503fb35bd37SJoerg Wunsch 	int read, format, rdsectid, cylinder, head, i, sec = 0, sectrac;
15041a6bed68SJoerg Wunsch 	int st0, cyl, st3, idf, ne7cmd, mfm, steptrac;
1505fb35bd37SJoerg Wunsch 	unsigned long blknum;
15065b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc->fdu;
15075b81b6b3SRodney W. Grimes 	fd_p fd;
15088177437dSPoul-Henning Kamp 	register struct bio *bp;
1509b39c878eSAndrey A. Chernov 	struct fd_formb *finfo = NULL;
15103a2f7427SDavid Greenman 	size_t fdblk;
15115b81b6b3SRodney W. Grimes 
1512e93e63cbSBruce Evans 	bp = fdc->bp;
1513e93e63cbSBruce Evans 	if (bp == NULL) {
15148177437dSPoul-Henning Kamp 		bp = bioq_first(&fdc->head);
1515e93e63cbSBruce Evans 		if (bp != NULL) {
15168177437dSPoul-Henning Kamp 			bioq_remove(&fdc->head, bp);
1517e93e63cbSBruce Evans 			fdc->bp = bp;
1518e93e63cbSBruce Evans 		}
1519e93e63cbSBruce Evans 	}
1520e93e63cbSBruce Evans 	if (bp == NULL) {
1521246ed35dSJoerg Wunsch 		/*
1522246ed35dSJoerg Wunsch 		 * Nothing left for this controller to do,
1523246ed35dSJoerg Wunsch 		 * force into the IDLE state.
1524246ed35dSJoerg Wunsch 		 */
15255b81b6b3SRodney W. Grimes 		fdc->state = DEVIDLE;
15266182fdbdSPeter Wemm 		if (fdc->fd) {
1527b6e5f28eSPeter Wemm 			device_printf(fdc->fdc_dev,
1528b6e5f28eSPeter Wemm 			    "unexpected valid fd pointer\n");
15295b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
15305b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
15315b81b6b3SRodney W. Grimes 		}
15326182fdbdSPeter Wemm 		TRACE1("[fdc%d IDLE]", fdc->fdcu);
15335b81b6b3SRodney W. Grimes  		return (0);
15345b81b6b3SRodney W. Grimes 	}
1535a2f19df9SPoul-Henning Kamp 	fd = bp->bio_dev->si_drv1;
1536503799eaSPoul-Henning Kamp 	fdu = fd->fdu;
15373a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
1538b6e5f28eSPeter Wemm 	if (fdc->fd && (fd != fdc->fd))
1539b6e5f28eSPeter Wemm 		device_printf(fd->dev, "confused fd pointers\n");
15408177437dSPoul-Henning Kamp 	read = bp->bio_cmd == BIO_READ;
15411a6bed68SJoerg Wunsch 	mfm = (fd->ft->flags & FL_MFM)? NE7CMD_MFM: 0;
15421a6bed68SJoerg Wunsch 	steptrac = (fd->ft->flags & FL_2STEP)? 2: 1;
154356a23089SPoul-Henning Kamp 	if (read)
154456a23089SPoul-Henning Kamp 		idf = ISADMA_READ;
154556a23089SPoul-Henning Kamp 	else
154656a23089SPoul-Henning Kamp 		idf = ISADMA_WRITE;
1547419f39ceSPoul-Henning Kamp 	format = bp->bio_cmd == FDBIO_FORMAT;
1548419f39ceSPoul-Henning Kamp 	rdsectid = bp->bio_cmd == FDBIO_RDSECTID;
1549fb35bd37SJoerg Wunsch 	if (format)
15508177437dSPoul-Henning Kamp 		finfo = (struct fd_formb *)bp->bio_data;
15515b81b6b3SRodney W. Grimes 	TRACE1("fd%d", fdu);
15525b81b6b3SRodney W. Grimes 	TRACE1("[%s]", fdstates[fdc->state]);
15535b81b6b3SRodney W. Grimes 	TRACE1("(0x%x)", fd->flags);
15546182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle);
15556182fdbdSPeter Wemm 	fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
15565b81b6b3SRodney W. Grimes 	switch (fdc->state)
15575b81b6b3SRodney W. Grimes 	{
15585b81b6b3SRodney W. Grimes 	case DEVIDLE:
15595b81b6b3SRodney W. Grimes 	case FINDWORK:	/* we have found new work */
15605b81b6b3SRodney W. Grimes 		fdc->retry = 0;
15615b81b6b3SRodney W. Grimes 		fd->skip = 0;
15625b81b6b3SRodney W. Grimes 		fdc->fd = fd;
15635b81b6b3SRodney W. Grimes 		fdc->fdu = fdu;
15645f830ea2SJoerg Wunsch 		fdc->fdctl_wr(fdc, fd->ft->trans);
15653a2f7427SDavid Greenman 		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
1566246ed35dSJoerg Wunsch 		/*
1567246ed35dSJoerg Wunsch 		 * If the next drive has a motor startup pending, then
1568246ed35dSJoerg Wunsch 		 * it will start up in its own good time.
1569246ed35dSJoerg Wunsch 		 */
15706182fdbdSPeter Wemm 		if(fd->flags & FD_MOTOR_WAIT) {
15715b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
1572246ed35dSJoerg Wunsch 			return (0); /* will return later */
15735b81b6b3SRodney W. Grimes 		}
1574246ed35dSJoerg Wunsch 		/*
1575246ed35dSJoerg Wunsch 		 * Maybe if it's not starting, it SHOULD be starting.
1576246ed35dSJoerg Wunsch 		 */
15775b81b6b3SRodney W. Grimes 		if (!(fd->flags & FD_MOTOR))
15785b81b6b3SRodney W. Grimes 		{
15795b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
15806182fdbdSPeter Wemm 			fd_turnon(fd);
1581246ed35dSJoerg Wunsch 			return (0); /* will return later */
15825b81b6b3SRodney W. Grimes 		}
15835b81b6b3SRodney W. Grimes 		else	/* at least make sure we are selected */
15845b81b6b3SRodney W. Grimes 		{
15856182fdbdSPeter Wemm 			set_motor(fdc, fd->fdsu, TURNON);
15865b81b6b3SRodney W. Grimes 		}
15875c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
15885c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
15895c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
15905c1a1eaeSBruce Evans 		} else
15915e235068SJordan K. Hubbard 			fdc->state = DOSEEK;
1592246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
1593fb35bd37SJoerg Wunsch 
15945b81b6b3SRodney W. Grimes 	case DOSEEK:
1595fb35bd37SJoerg Wunsch 		blknum = bp->bio_pblkno + fd->skip / fdblk;
1596fb35bd37SJoerg Wunsch 		cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1597fb35bd37SJoerg Wunsch 		if (cylinder == fd->track)
15985b81b6b3SRodney W. Grimes 		{
15995b81b6b3SRodney W. Grimes 			fdc->state = SEEKCOMPLETE;
1600246ed35dSJoerg Wunsch 			return (1); /* will return immediately */
16015b81b6b3SRodney W. Grimes 		}
16026182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK,
16031a6bed68SJoerg Wunsch 			   fd->fdsu, cylinder * steptrac, 0))
1604dc8603e3SJoerg Wunsch 		{
1605dc8603e3SJoerg Wunsch 			/*
1606246ed35dSJoerg Wunsch 			 * Seek command not accepted, looks like
1607dc8603e3SJoerg Wunsch 			 * the FDC went off to the Saints...
1608dc8603e3SJoerg Wunsch 			 */
1609dc8603e3SJoerg Wunsch 			fdc->retry = 6;	/* try a reset */
16106182fdbdSPeter Wemm 			return(retrier(fdc));
1611dc8603e3SJoerg Wunsch 		}
1612dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
16135b81b6b3SRodney W. Grimes 		fdc->state = SEEKWAIT;
16145b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
1615fb35bd37SJoerg Wunsch 
16165b81b6b3SRodney W. Grimes 	case SEEKWAIT:
16175b81b6b3SRodney W. Grimes 		/* allow heads to settle */
16186182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 16);
16195b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
16205b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
1621fb35bd37SJoerg Wunsch 
1622246ed35dSJoerg Wunsch 	case SEEKCOMPLETE : /* seek done, start DMA */
1623fb35bd37SJoerg Wunsch 		blknum = bp->bio_pblkno + fd->skip / fdblk;
1624fb35bd37SJoerg Wunsch 		cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1625fb35bd37SJoerg Wunsch 
1626246ed35dSJoerg Wunsch 		/* Make sure seek really happened. */
16276182fdbdSPeter Wemm 		if(fd->track == FD_NO_TRACK) {
16281a6bed68SJoerg Wunsch 			int descyl = cylinder * steptrac;
16293a2f7427SDavid Greenman 			do {
16303a2f7427SDavid Greenman 				/*
1631dc5df763SJoerg Wunsch 				 * This might be a "ready changed" interrupt,
1632dc5df763SJoerg Wunsch 				 * which cannot really happen since the
1633dc5df763SJoerg Wunsch 				 * RDY pin is hardwired to + 5 volts.  This
1634dc5df763SJoerg Wunsch 				 * generally indicates a "bouncing" intr
1635dc5df763SJoerg Wunsch 				 * line, so do one of the following:
1636dc5df763SJoerg Wunsch 				 *
1637dc5df763SJoerg Wunsch 				 * When running on an enhanced FDC that is
1638dc5df763SJoerg Wunsch 				 * known to not go stuck after responding
1639dc5df763SJoerg Wunsch 				 * with INVALID, fetch all interrupt states
1640dc5df763SJoerg Wunsch 				 * until seeing either an INVALID or a
1641dc5df763SJoerg Wunsch 				 * real interrupt condition.
1642dc5df763SJoerg Wunsch 				 *
1643dc5df763SJoerg Wunsch 				 * When running on a dumb old NE765, give
1644dc5df763SJoerg Wunsch 				 * up immediately.  The controller will
1645dc5df763SJoerg Wunsch 				 * provide up to four dummy RC interrupt
1646dc5df763SJoerg Wunsch 				 * conditions right after reset (for the
1647dc5df763SJoerg Wunsch 				 * corresponding four drives), so this is
1648dc5df763SJoerg Wunsch 				 * our only chance to get notice that it
1649dc5df763SJoerg Wunsch 				 * was not the FDC that caused the interrupt.
16503a2f7427SDavid Greenman 				 */
1651dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, &cyl)
1652dc5df763SJoerg Wunsch 				    == FD_NOT_VALID)
1653246ed35dSJoerg Wunsch 					return (0); /* will return later */
1654dc5df763SJoerg Wunsch 				if(fdc->fdct == FDC_NE765
1655dc5df763SJoerg Wunsch 				   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1656246ed35dSJoerg Wunsch 					return (0); /* hope for a real intr */
16573a2f7427SDavid Greenman 			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1658dc5df763SJoerg Wunsch 
16596182fdbdSPeter Wemm 			if (0 == descyl) {
1660dc5df763SJoerg Wunsch 				int failed = 0;
16613a2f7427SDavid Greenman 				/*
16623a2f7427SDavid Greenman 				 * seek to cyl 0 requested; make sure we are
16633a2f7427SDavid Greenman 				 * really there
16643a2f7427SDavid Greenman 				 */
1665dc5df763SJoerg Wunsch 				if (fd_sense_drive_status(fdc, &st3))
1666dc5df763SJoerg Wunsch 					failed = 1;
16673a2f7427SDavid Greenman 				if ((st3 & NE7_ST3_T0) == 0) {
16683a2f7427SDavid Greenman 					printf(
16693a2f7427SDavid Greenman 		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
16703a2f7427SDavid Greenman 					       fdu, st3, NE7_ST3BITS);
1671dc5df763SJoerg Wunsch 					failed = 1;
1672dc5df763SJoerg Wunsch 				}
1673dc5df763SJoerg Wunsch 
16746182fdbdSPeter Wemm 				if (failed) {
16753a2f7427SDavid Greenman 					if(fdc->retry < 3)
16763a2f7427SDavid Greenman 						fdc->retry = 3;
16776182fdbdSPeter Wemm 					return (retrier(fdc));
16783a2f7427SDavid Greenman 				}
16793a2f7427SDavid Greenman 			}
1680dc5df763SJoerg Wunsch 
16816182fdbdSPeter Wemm 			if (cyl != descyl) {
16823a2f7427SDavid Greenman 				printf(
16833a2f7427SDavid Greenman 		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
16842d9d0204SRodney W. Grimes 				       fdu, descyl, cyl, st0);
1685e5d7d243SBruce Evans 				if (fdc->retry < 3)
1686e5d7d243SBruce Evans 					fdc->retry = 3;
16876182fdbdSPeter Wemm 				return (retrier(fdc));
16885b81b6b3SRodney W. Grimes 			}
16895b81b6b3SRodney W. Grimes 		}
16905b81b6b3SRodney W. Grimes 
1691fb35bd37SJoerg Wunsch 		fd->track = cylinder;
1692fb35bd37SJoerg Wunsch 		if (format)
1693fb35bd37SJoerg Wunsch 			fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1694fb35bd37SJoerg Wunsch 			    - (char *)finfo;
1695250300ebSJoerg Wunsch 		if (!rdsectid && !(fdc->flags & FDC_NODMA))
16968177437dSPoul-Henning Kamp 			isa_dmastart(idf, bp->bio_data+fd->skip,
16978177437dSPoul-Henning Kamp 				format ? bp->bio_bcount : fdblk, fdc->dmachan);
1698fb35bd37SJoerg Wunsch 		blknum = bp->bio_pblkno + fd->skip / fdblk;
16995b81b6b3SRodney W. Grimes 		sectrac = fd->ft->sectrac;
17005b81b6b3SRodney W. Grimes 		sec = blknum %  (sectrac * fd->ft->heads);
17015b81b6b3SRodney W. Grimes 		head = sec / sectrac;
17025b81b6b3SRodney W. Grimes 		sec = sec % sectrac + 1;
17031a6bed68SJoerg Wunsch 		if (head != 0 && fd->ft->offset_side2 != 0)
17041a6bed68SJoerg Wunsch 			sec += fd->ft->offset_side2;
17053a2f7427SDavid Greenman 		fd->hddrv = ((head&1)<<2)+fdu;
17063a2f7427SDavid Greenman 
1707250300ebSJoerg Wunsch 		if(format || !(read || rdsectid))
17083a2f7427SDavid Greenman 		{
17093a2f7427SDavid Greenman 			/* make sure the drive is writable */
1710dc5df763SJoerg Wunsch 			if(fd_sense_drive_status(fdc, &st3) != 0)
1711dc8603e3SJoerg Wunsch 			{
1712dc8603e3SJoerg Wunsch 				/* stuck controller? */
17135f830ea2SJoerg Wunsch 				if (!(fdc->flags & FDC_NODMA))
171456a23089SPoul-Henning Kamp 					isa_dmadone(idf,
17158177437dSPoul-Henning Kamp 						    bp->bio_data + fd->skip,
17168177437dSPoul-Henning Kamp 						    format ? bp->bio_bcount : fdblk,
17175c1a1eaeSBruce Evans 						    fdc->dmachan);
1718dc8603e3SJoerg Wunsch 				fdc->retry = 6;	/* reset the beast */
17196182fdbdSPeter Wemm 				return (retrier(fdc));
1720dc8603e3SJoerg Wunsch 			}
17213a2f7427SDavid Greenman 			if(st3 & NE7_ST3_WP)
17223a2f7427SDavid Greenman 			{
17233a2f7427SDavid Greenman 				/*
17243a2f7427SDavid Greenman 				 * XXX YES! this is ugly.
17253a2f7427SDavid Greenman 				 * in order to force the current operation
17263a2f7427SDavid Greenman 				 * to fail, we will have to fake an FDC
17273a2f7427SDavid Greenman 				 * error - all error handling is done
17283a2f7427SDavid Greenman 				 * by the retrier()
17293a2f7427SDavid Greenman 				 */
17303a2f7427SDavid Greenman 				fdc->status[0] = NE7_ST0_IC_AT;
17313a2f7427SDavid Greenman 				fdc->status[1] = NE7_ST1_NW;
17323a2f7427SDavid Greenman 				fdc->status[2] = 0;
17333a2f7427SDavid Greenman 				fdc->status[3] = fd->track;
17343a2f7427SDavid Greenman 				fdc->status[4] = head;
17353a2f7427SDavid Greenman 				fdc->status[5] = sec;
17363a2f7427SDavid Greenman 				fdc->retry = 8;	/* break out immediately */
17373a2f7427SDavid Greenman 				fdc->state = IOTIMEDOUT; /* not really... */
1738246ed35dSJoerg Wunsch 				return (1); /* will return immediately */
17393a2f7427SDavid Greenman 			}
17403a2f7427SDavid Greenman 		}
17415b81b6b3SRodney W. Grimes 
17426182fdbdSPeter Wemm 		if (format) {
17431a6bed68SJoerg Wunsch 			ne7cmd = NE7CMD_FORMAT | mfm;
17445f830ea2SJoerg Wunsch 			if (fdc->flags & FDC_NODMA) {
17455f830ea2SJoerg Wunsch 				/*
17465f830ea2SJoerg Wunsch 				 * This seems to be necessary for
17475f830ea2SJoerg Wunsch 				 * whatever obscure reason; if we omit
17485f830ea2SJoerg Wunsch 				 * it, we end up filling the sector ID
17495f830ea2SJoerg Wunsch 				 * fields of the newly formatted track
17505f830ea2SJoerg Wunsch 				 * entirely with garbage, causing
17515f830ea2SJoerg Wunsch 				 * `wrong cylinder' errors all over
17525f830ea2SJoerg Wunsch 				 * the place when trying to read them
17535f830ea2SJoerg Wunsch 				 * back.
17545f830ea2SJoerg Wunsch 				 *
17555f830ea2SJoerg Wunsch 				 * Umpf.
17565f830ea2SJoerg Wunsch 				 */
17578177437dSPoul-Henning Kamp 				SET_BCDR(fdc, 1, bp->bio_bcount, 0);
17585f830ea2SJoerg Wunsch 
17598177437dSPoul-Henning Kamp 				(void)fdcpio(fdc,bp->bio_cmd,
17608177437dSPoul-Henning Kamp 					bp->bio_data+fd->skip,
17618177437dSPoul-Henning Kamp 					bp->bio_bcount);
17625f830ea2SJoerg Wunsch 
17635f830ea2SJoerg Wunsch 			}
1764b39c878eSAndrey A. Chernov 			/* formatting */
17651a6bed68SJoerg Wunsch 			if(fd_cmd(fdc, 6,  ne7cmd, head << 2 | fdu,
1766dc5df763SJoerg Wunsch 				  finfo->fd_formb_secshift,
1767dc5df763SJoerg Wunsch 				  finfo->fd_formb_nsecs,
1768dc5df763SJoerg Wunsch 				  finfo->fd_formb_gaplen,
17696182fdbdSPeter Wemm 				  finfo->fd_formb_fillbyte, 0)) {
1770dc8603e3SJoerg Wunsch 				/* controller fell over */
17715f830ea2SJoerg Wunsch 				if (!(fdc->flags & FDC_NODMA))
177256a23089SPoul-Henning Kamp 					isa_dmadone(idf,
17738177437dSPoul-Henning Kamp 						    bp->bio_data + fd->skip,
17748177437dSPoul-Henning Kamp 						    format ? bp->bio_bcount : fdblk,
17755c1a1eaeSBruce Evans 						    fdc->dmachan);
1776dc8603e3SJoerg Wunsch 				fdc->retry = 6;
17776182fdbdSPeter Wemm 				return (retrier(fdc));
1778dc8603e3SJoerg Wunsch 			}
1779250300ebSJoerg Wunsch 		} else if (rdsectid) {
17801a6bed68SJoerg Wunsch 			ne7cmd = NE7CMD_READID | mfm;
17811a6bed68SJoerg Wunsch 			if (fd_cmd(fdc, 2, ne7cmd, head << 2 | fdu, 0)) {
1782250300ebSJoerg Wunsch 				/* controller jamming */
1783250300ebSJoerg Wunsch 				fdc->retry = 6;
1784250300ebSJoerg Wunsch 				return (retrier(fdc));
1785250300ebSJoerg Wunsch 			}
17866182fdbdSPeter Wemm 		} else {
1787250300ebSJoerg Wunsch 			/* read or write operation */
17881a6bed68SJoerg Wunsch 			ne7cmd = (read ? NE7CMD_READ | NE7CMD_SK : NE7CMD_WRITE) | mfm;
17893b178206SWarner Losh 			if (fdc->flags & FDC_NODMA) {
179069acd21dSWarner Losh 				/*
1791246ed35dSJoerg Wunsch 				 * This seems to be necessary even when
1792246ed35dSJoerg Wunsch 				 * reading data.
179369acd21dSWarner Losh 				 */
17943b178206SWarner Losh 				SET_BCDR(fdc, 1, fdblk, 0);
179569acd21dSWarner Losh 
179669acd21dSWarner Losh 				/*
1797246ed35dSJoerg Wunsch 				 * Perform the write pseudo-DMA before
1798246ed35dSJoerg Wunsch 				 * the WRITE command is sent.
179969acd21dSWarner Losh 				 */
180069acd21dSWarner Losh 				if (!read)
18018177437dSPoul-Henning Kamp 					(void)fdcpio(fdc,bp->bio_cmd,
18028177437dSPoul-Henning Kamp 					    bp->bio_data+fd->skip,
180369acd21dSWarner Losh 					    fdblk);
180469acd21dSWarner Losh 			}
18056182fdbdSPeter Wemm 			if (fd_cmd(fdc, 9,
18061a6bed68SJoerg Wunsch 				   ne7cmd,
1807dc5df763SJoerg Wunsch 				   head << 2 | fdu,  /* head & unit */
1808dc5df763SJoerg Wunsch 				   fd->track,        /* track */
1809dc5df763SJoerg Wunsch 				   head,
1810dc5df763SJoerg Wunsch 				   sec,              /* sector + 1 */
1811dc5df763SJoerg Wunsch 				   fd->ft->secsize,  /* sector size */
1812dc5df763SJoerg Wunsch 				   sectrac,          /* sectors/track */
1813dc5df763SJoerg Wunsch 				   fd->ft->gap,      /* gap size */
1814dc5df763SJoerg Wunsch 				   fd->ft->datalen,  /* data length */
18156182fdbdSPeter Wemm 				   0)) {
1816dc8603e3SJoerg Wunsch 				/* the beast is sleeping again */
18175f830ea2SJoerg Wunsch 				if (!(fdc->flags & FDC_NODMA))
181856a23089SPoul-Henning Kamp 					isa_dmadone(idf,
18198177437dSPoul-Henning Kamp 						    bp->bio_data + fd->skip,
18208177437dSPoul-Henning Kamp 						    format ? bp->bio_bcount : fdblk,
18215c1a1eaeSBruce Evans 						    fdc->dmachan);
1822dc8603e3SJoerg Wunsch 				fdc->retry = 6;
18236182fdbdSPeter Wemm 				return (retrier(fdc));
18245b81b6b3SRodney W. Grimes 			}
1825b39c878eSAndrey A. Chernov 		}
1826250300ebSJoerg Wunsch 		if (!rdsectid && (fdc->flags & FDC_NODMA))
182769acd21dSWarner Losh 			/*
1828246ed35dSJoerg Wunsch 			 * If this is a read, then simply await interrupt
1829246ed35dSJoerg Wunsch 			 * before performing PIO.
183069acd21dSWarner Losh 			 */
18318177437dSPoul-Henning Kamp 			if (read && !fdcpio(fdc,bp->bio_cmd,
18328177437dSPoul-Henning Kamp 			    bp->bio_data+fd->skip,fdblk)) {
18333b178206SWarner Losh 				fd->tohandle = timeout(fd_iotimeout, fdc, hz);
183469acd21dSWarner Losh 				return(0);      /* will return later */
183564860614SJoerg Wunsch 			}
183669acd21dSWarner Losh 
183769acd21dSWarner Losh 		/*
1838246ed35dSJoerg Wunsch 		 * Write (or format) operation will fall through and
1839246ed35dSJoerg Wunsch 		 * await completion interrupt.
184069acd21dSWarner Losh 		 */
18415b81b6b3SRodney W. Grimes 		fdc->state = IOCOMPLETE;
18426182fdbdSPeter Wemm 		fd->tohandle = timeout(fd_iotimeout, fdc, hz);
18435b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
1844fb35bd37SJoerg Wunsch 
184569acd21dSWarner Losh 	case PIOREAD:
184669acd21dSWarner Losh 		/*
1847246ed35dSJoerg Wunsch 		 * Actually perform the PIO read.  The IOCOMPLETE case
184869acd21dSWarner Losh 		 * removes the timeout for us.
184969acd21dSWarner Losh 		 */
18508177437dSPoul-Henning Kamp 		(void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk);
185169acd21dSWarner Losh 		fdc->state = IOCOMPLETE;
185269acd21dSWarner Losh 		/* FALLTHROUGH */
1853246ed35dSJoerg Wunsch 	case IOCOMPLETE: /* IO done, post-analyze */
18546182fdbdSPeter Wemm 		untimeout(fd_iotimeout, fdc, fd->tohandle);
1855dc5df763SJoerg Wunsch 
185664860614SJoerg Wunsch 		if (fd_read_status(fdc)) {
1857250300ebSJoerg Wunsch 			if (!rdsectid && !(fdc->flags & FDC_NODMA))
18588177437dSPoul-Henning Kamp 				isa_dmadone(idf, bp->bio_data + fd->skip,
18598177437dSPoul-Henning Kamp 					    format ? bp->bio_bcount : fdblk,
18605c1a1eaeSBruce Evans 					    fdc->dmachan);
1861dc5df763SJoerg Wunsch 			if (fdc->retry < 6)
1862dc5df763SJoerg Wunsch 				fdc->retry = 6;	/* force a reset */
18636182fdbdSPeter Wemm 			return (retrier(fdc));
18645b81b6b3SRodney W. Grimes   		}
1865dc5df763SJoerg Wunsch 
18663a2f7427SDavid Greenman 		fdc->state = IOTIMEDOUT;
1867dc5df763SJoerg Wunsch 
18683a2f7427SDavid Greenman 		/* FALLTHROUGH */
18693a2f7427SDavid Greenman 	case IOTIMEDOUT:
1870250300ebSJoerg Wunsch 		if (!rdsectid && !(fdc->flags & FDC_NODMA))
18718177437dSPoul-Henning Kamp 			isa_dmadone(idf, bp->bio_data + fd->skip,
18728177437dSPoul-Henning Kamp 				format ? bp->bio_bcount : fdblk, fdc->dmachan);
18736182fdbdSPeter Wemm 		if (fdc->status[0] & NE7_ST0_IC) {
18743a2f7427SDavid Greenman                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
18753a2f7427SDavid Greenman 			    && fdc->status[1] & NE7_ST1_OR) {
1876b39c878eSAndrey A. Chernov                                 /*
18773fef646eSJoerg Wunsch 				 * DMA overrun. Someone hogged the bus and
18783fef646eSJoerg Wunsch 				 * didn't release it in time for the next
18793fef646eSJoerg Wunsch 				 * FDC transfer.
18803fef646eSJoerg Wunsch 				 *
18813fef646eSJoerg Wunsch 				 * We normally restart this without bumping
18823fef646eSJoerg Wunsch 				 * the retry counter.  However, in case
18833fef646eSJoerg Wunsch 				 * something is seriously messed up (like
18843fef646eSJoerg Wunsch 				 * broken hardware), we rather limit the
18853fef646eSJoerg Wunsch 				 * number of retries so the IO operation
18863fef646eSJoerg Wunsch 				 * doesn't block indefinately.
1887b39c878eSAndrey A. Chernov 				 */
18883fef646eSJoerg Wunsch 				if (fdc->dma_overruns++ < FDC_DMAOV_MAX) {
1889b39c878eSAndrey A. Chernov 					fdc->state = SEEKCOMPLETE;
1890246ed35dSJoerg Wunsch 					return (1);/* will return immediately */
18913fef646eSJoerg Wunsch 				} /* else fall through */
1892b39c878eSAndrey A. Chernov                         }
18933fef646eSJoerg Wunsch 			if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
18943a2f7427SDavid Greenman 				&& fdc->retry < 6)
18953a2f7427SDavid Greenman 				fdc->retry = 6;	/* force a reset */
18963a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
18973a2f7427SDavid Greenman 				&& fdc->status[2] & NE7_ST2_WC
18983a2f7427SDavid Greenman 				&& fdc->retry < 3)
18993a2f7427SDavid Greenman 				fdc->retry = 3;	/* force recalibrate */
19006182fdbdSPeter Wemm 			return (retrier(fdc));
19015b81b6b3SRodney W. Grimes 		}
19025b81b6b3SRodney W. Grimes 		/* All OK */
1903250300ebSJoerg Wunsch 		if (rdsectid) {
1904250300ebSJoerg Wunsch 			/* copy out ID field contents */
190564860614SJoerg Wunsch 			idp = (struct fdc_readid *)bp->bio_data;
1906250300ebSJoerg Wunsch 			idp->cyl = fdc->status[3];
1907250300ebSJoerg Wunsch 			idp->head = fdc->status[4];
1908250300ebSJoerg Wunsch 			idp->sec = fdc->status[5];
1909250300ebSJoerg Wunsch 			idp->secshift = fdc->status[6];
1910250300ebSJoerg Wunsch 		}
19113fef646eSJoerg Wunsch 		/* Operation successful, retry DMA overruns again next time. */
19123fef646eSJoerg Wunsch 		fdc->dma_overruns = 0;
19133a2f7427SDavid Greenman 		fd->skip += fdblk;
1914fb35bd37SJoerg Wunsch 		if (!rdsectid && !format && fd->skip < bp->bio_bcount) {
19155b81b6b3SRodney W. Grimes 			/* set up next transfer */
19165b81b6b3SRodney W. Grimes 			fdc->state = DOSEEK;
19176182fdbdSPeter Wemm 		} else {
19185b81b6b3SRodney W. Grimes 			/* ALL DONE */
19195b81b6b3SRodney W. Grimes 			fd->skip = 0;
1920fb35bd37SJoerg Wunsch 			bp->bio_resid = 0;
1921e93e63cbSBruce Evans 			fdc->bp = NULL;
19225f830ea2SJoerg Wunsch 			device_unbusy(fd->dev);
192380980460SPoul-Henning Kamp 			biofinish(bp, fd->device_stats, 0);
19245b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
19255b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
19265b81b6b3SRodney W. Grimes 			fdc->state = FINDWORK;
19275b81b6b3SRodney W. Grimes 		}
1928246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
1929fb35bd37SJoerg Wunsch 
19305b81b6b3SRodney W. Grimes 	case RESETCTLR:
19313a2f7427SDavid Greenman 		fdc_reset(fdc);
19325b81b6b3SRodney W. Grimes 		fdc->retry++;
19335c1a1eaeSBruce Evans 		fdc->state = RESETCOMPLETE;
1934246ed35dSJoerg Wunsch 		return (0);	/* will return later */
1935fb35bd37SJoerg Wunsch 
19365c1a1eaeSBruce Evans 	case RESETCOMPLETE:
19375c1a1eaeSBruce Evans 		/*
19385c1a1eaeSBruce Evans 		 * Discard all the results from the reset so that they
19395c1a1eaeSBruce Evans 		 * can't cause an unexpected interrupt later.
19405c1a1eaeSBruce Evans 		 */
19410e317d05SJoerg Wunsch 		for (i = 0; i < 4; i++)
19420e317d05SJoerg Wunsch 			(void)fd_sense_int(fdc, &st0, &cyl);
19435c1a1eaeSBruce Evans 		fdc->state = STARTRECAL;
1944fb35bd37SJoerg Wunsch 		/* FALLTHROUGH */
19455c1a1eaeSBruce Evans 	case STARTRECAL:
19466182fdbdSPeter Wemm 		if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) {
1947dc8603e3SJoerg Wunsch 			/* arrgl */
1948dc8603e3SJoerg Wunsch 			fdc->retry = 6;
19496182fdbdSPeter Wemm 			return (retrier(fdc));
1950dc8603e3SJoerg Wunsch 		}
19515b81b6b3SRodney W. Grimes 		fdc->state = RECALWAIT;
19525b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
1953fb35bd37SJoerg Wunsch 
19545b81b6b3SRodney W. Grimes 	case RECALWAIT:
19555b81b6b3SRodney W. Grimes 		/* allow heads to settle */
19566182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 8);
19575b81b6b3SRodney W. Grimes 		fdc->state = RECALCOMPLETE;
19585b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
1959fb35bd37SJoerg Wunsch 
19605b81b6b3SRodney W. Grimes 	case RECALCOMPLETE:
19613a2f7427SDavid Greenman 		do {
19623a2f7427SDavid Greenman 			/*
1963dc5df763SJoerg Wunsch 			 * See SEEKCOMPLETE for a comment on this:
19643a2f7427SDavid Greenman 			 */
1965dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
1966246ed35dSJoerg Wunsch 				return (0); /* will return later */
1967dc5df763SJoerg Wunsch 			if(fdc->fdct == FDC_NE765
1968dc5df763SJoerg Wunsch 			   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1969246ed35dSJoerg Wunsch 				return (0); /* hope for a real intr */
19703a2f7427SDavid Greenman 		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
19713a2f7427SDavid Greenman 		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
19725b81b6b3SRodney W. Grimes 		{
1973dc8603e3SJoerg Wunsch 			if(fdc->retry > 3)
1974dc8603e3SJoerg Wunsch 				/*
1975246ed35dSJoerg Wunsch 				 * A recalibrate from beyond cylinder 77
1976dc8603e3SJoerg Wunsch 				 * will "fail" due to the FDC limitations;
1977dc8603e3SJoerg Wunsch 				 * since people used to complain much about
1978dc8603e3SJoerg Wunsch 				 * the failure message, try not logging
1979dc8603e3SJoerg Wunsch 				 * this one if it seems to be the first
1980246ed35dSJoerg Wunsch 				 * time in a line.
1981dc8603e3SJoerg Wunsch 				 */
1982dc8603e3SJoerg Wunsch 				printf("fd%d: recal failed ST0 %b cyl %d\n",
1983dc8603e3SJoerg Wunsch 				       fdu, st0, NE7_ST0BITS, cyl);
19843a2f7427SDavid Greenman 			if(fdc->retry < 3) fdc->retry = 3;
19856182fdbdSPeter Wemm 			return (retrier(fdc));
19865b81b6b3SRodney W. Grimes 		}
19875b81b6b3SRodney W. Grimes 		fd->track = 0;
19885b81b6b3SRodney W. Grimes 		/* Seek (probably) necessary */
19895b81b6b3SRodney W. Grimes 		fdc->state = DOSEEK;
1990246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
1991fb35bd37SJoerg Wunsch 
19925b81b6b3SRodney W. Grimes 	case MOTORWAIT:
19935b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
19945b81b6b3SRodney W. Grimes 		{
19955b81b6b3SRodney W. Grimes 			return (0); /* time's not up yet */
19965b81b6b3SRodney W. Grimes 		}
19975c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
19985c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
19995c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
2000fb35bd37SJoerg Wunsch 		} else
2001fb35bd37SJoerg Wunsch 			fdc->state = DOSEEK;
2002246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
2003fb35bd37SJoerg Wunsch 
20045b81b6b3SRodney W. Grimes 	default:
2005b6e5f28eSPeter Wemm 		device_printf(fdc->fdc_dev, "unexpected FD int->");
200664860614SJoerg Wunsch 		if (fd_read_status(fdc) == 0)
2007a838d83dSBruce Evans 			printf("FDC status :%x %x %x %x %x %x %x   ",
20085b81b6b3SRodney W. Grimes 			       fdc->status[0],
20095b81b6b3SRodney W. Grimes 			       fdc->status[1],
20105b81b6b3SRodney W. Grimes 			       fdc->status[2],
20115b81b6b3SRodney W. Grimes 			       fdc->status[3],
20125b81b6b3SRodney W. Grimes 			       fdc->status[4],
20135b81b6b3SRodney W. Grimes 			       fdc->status[5],
20145b81b6b3SRodney W. Grimes 			       fdc->status[6] );
20153a2f7427SDavid Greenman 		else
2016dac0f2dbSJoerg Wunsch 			printf("No status available   ");
2017dac0f2dbSJoerg Wunsch 		if (fd_sense_int(fdc, &st0, &cyl) != 0)
2018dac0f2dbSJoerg Wunsch 		{
2019dac0f2dbSJoerg Wunsch 			printf("[controller is dead now]\n");
2020246ed35dSJoerg Wunsch 			return (0); /* will return later */
20215b81b6b3SRodney W. Grimes 		}
2022dac0f2dbSJoerg Wunsch 		printf("ST0 = %x, PCN = %x\n", st0, cyl);
2023246ed35dSJoerg Wunsch 		return (0);	/* will return later */
2024dac0f2dbSJoerg Wunsch 	}
2025246ed35dSJoerg Wunsch 	/* noone should ever get here */
20265b81b6b3SRodney W. Grimes }
20275b81b6b3SRodney W. Grimes 
2028aaf08d94SGarrett Wollman static int
20296182fdbdSPeter Wemm retrier(struct fdc_data *fdc)
20305b81b6b3SRodney W. Grimes {
20318177437dSPoul-Henning Kamp 	struct bio *bp;
20326182fdbdSPeter Wemm 	struct fd_data *fd;
20336182fdbdSPeter Wemm 	int fdu;
20345b81b6b3SRodney W. Grimes 
2035e93e63cbSBruce Evans 	bp = fdc->bp;
20365b81b6b3SRodney W. Grimes 
20376182fdbdSPeter Wemm 	/* XXX shouldn't this be cached somewhere?  */
2038a2f19df9SPoul-Henning Kamp 	fd = bp->bio_dev->si_drv1;
2039503799eaSPoul-Henning Kamp 	fdu = fd->fdu;
20406182fdbdSPeter Wemm 	if (fd->options & FDOPT_NORETRY)
20413a2f7427SDavid Greenman 		goto fail;
20426182fdbdSPeter Wemm 
20436182fdbdSPeter Wemm 	switch (fdc->retry) {
20445b81b6b3SRodney W. Grimes 	case 0: case 1: case 2:
20455b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
20465b81b6b3SRodney W. Grimes 		break;
20475b81b6b3SRodney W. Grimes 	case 3: case 4: case 5:
20485b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
20495b81b6b3SRodney W. Grimes 		break;
20505b81b6b3SRodney W. Grimes 	case 6:
20515b81b6b3SRodney W. Grimes 		fdc->state = RESETCTLR;
20525b81b6b3SRodney W. Grimes 		break;
20535b81b6b3SRodney W. Grimes 	case 7:
20545b81b6b3SRodney W. Grimes 		break;
20555b81b6b3SRodney W. Grimes 	default:
20563a2f7427SDavid Greenman 	fail:
2057fb35bd37SJoerg Wunsch 		if ((fd->options & FDOPT_NOERRLOG) == 0) {
2058f90c382cSPoul-Henning Kamp 			disk_err(bp, "hard error",
2059f90c382cSPoul-Henning Kamp 			    fdc->fd->skip / DEV_BSIZE, 0);
2060fb35bd37SJoerg Wunsch 			if (fdc->flags & FDC_STAT_VALID) {
2061dc5df763SJoerg Wunsch 				printf(
2062a838d83dSBruce Evans 				" (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n",
2063dc5df763SJoerg Wunsch 				       fdc->status[0], NE7_ST0BITS,
2064dc5df763SJoerg Wunsch 				       fdc->status[1], NE7_ST1BITS,
2065dc5df763SJoerg Wunsch 				       fdc->status[2], NE7_ST2BITS,
2066dc5df763SJoerg Wunsch 				       fdc->status[3], fdc->status[4],
2067dc5df763SJoerg Wunsch 				       fdc->status[5]);
2068dc5df763SJoerg Wunsch 			}
2069dc5df763SJoerg Wunsch 			else
2070dc5df763SJoerg Wunsch 				printf(" (No status)\n");
20715b81b6b3SRodney W. Grimes 		}
20722995d110SJoerg Wunsch 		if ((fd->options & FDOPT_NOERROR) == 0) {
20738177437dSPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
20748177437dSPoul-Henning Kamp 			bp->bio_error = EIO;
2075fb35bd37SJoerg Wunsch 			bp->bio_resid = bp->bio_bcount - fdc->fd->skip;
2076fb35bd37SJoerg Wunsch 		} else
2077fb35bd37SJoerg Wunsch 			bp->bio_resid = 0;
2078e93e63cbSBruce Evans 		fdc->bp = NULL;
20795b81b6b3SRodney W. Grimes 		fdc->fd->skip = 0;
20805f830ea2SJoerg Wunsch 		device_unbusy(fd->dev);
208180980460SPoul-Henning Kamp 		biofinish(bp, fdc->fd->device_stats, 0);
208292ed385aSRodney W. Grimes 		fdc->state = FINDWORK;
20835c1a1eaeSBruce Evans 		fdc->flags |= FDC_NEEDS_RESET;
20845b81b6b3SRodney W. Grimes 		fdc->fd = (fd_p) 0;
20855b81b6b3SRodney W. Grimes 		fdc->fdu = -1;
208692ed385aSRodney W. Grimes 		return (1);
20875b81b6b3SRodney W. Grimes 	}
20885b81b6b3SRodney W. Grimes 	fdc->retry++;
20895b81b6b3SRodney W. Grimes 	return (1);
20905b81b6b3SRodney W. Grimes }
20915b81b6b3SRodney W. Grimes 
20921fdb6e6cSPoul-Henning Kamp static void
20931fdb6e6cSPoul-Henning Kamp fdbiodone(struct bio *bp)
20941fdb6e6cSPoul-Henning Kamp {
20951fdb6e6cSPoul-Henning Kamp 	wakeup(bp);
20961fdb6e6cSPoul-Henning Kamp }
20971fdb6e6cSPoul-Henning Kamp 
2098b39c878eSAndrey A. Chernov static int
209989c9c53dSPoul-Henning Kamp fdmisccmd(struct cdev *dev, u_int cmd, void *data)
2100b39c878eSAndrey A. Chernov {
2101b39c878eSAndrey A. Chernov  	fdu_t fdu;
2102b39c878eSAndrey A. Chernov  	fd_p fd;
21031fdb6e6cSPoul-Henning Kamp 	struct bio *bp;
2104f664aeeeSJoerg Wunsch 	struct fd_formb *finfo;
2105f664aeeeSJoerg Wunsch 	struct fdc_readid *idfield;
21063a2f7427SDavid Greenman 	size_t fdblk;
210702aad38cSPoul-Henning Kamp 	int error;
2108b39c878eSAndrey A. Chernov 
2109a2f19df9SPoul-Henning Kamp 	fd = dev->si_drv1;
2110503799eaSPoul-Henning Kamp  	fdu = fd->fdu;
21113a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2112f664aeeeSJoerg Wunsch 	finfo = (struct fd_formb *)data;
2113f664aeeeSJoerg Wunsch 	idfield = (struct fdc_readid *)data;
2114b39c878eSAndrey A. Chernov 
21158595de4aSPoul-Henning Kamp 	bp = malloc(sizeof(struct bio), M_TEMP, M_WAITOK | M_ZERO);
2116b39c878eSAndrey A. Chernov 
2117b39c878eSAndrey A. Chernov 	/*
2118b52b7f46SPoul-Henning Kamp 	 * Set up a bio request for fdstrategy().  bio_offset is faked
2119f664aeeeSJoerg Wunsch 	 * so that fdstrategy() will seek to the the requested
2120817988beSPoul-Henning Kamp 	 * cylinder, and use the desired head.
2121b39c878eSAndrey A. Chernov 	 */
2122f664aeeeSJoerg Wunsch 	bp->bio_cmd = cmd;
2123419f39ceSPoul-Henning Kamp 	if (cmd == FDBIO_FORMAT) {
2124b52b7f46SPoul-Henning Kamp 		bp->bio_offset =
2125f664aeeeSJoerg Wunsch 		    (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) +
2126b52b7f46SPoul-Henning Kamp 		     finfo->head * fd->ft->sectrac) * fdblk;
2127f664aeeeSJoerg Wunsch 		bp->bio_bcount = sizeof(struct fd_idfield_data) *
2128f664aeeeSJoerg Wunsch 		    finfo->fd_formb_nsecs;
2129419f39ceSPoul-Henning Kamp 	} else if (cmd == FDBIO_RDSECTID) {
2130b52b7f46SPoul-Henning Kamp 		bp->bio_offset =
2131f664aeeeSJoerg Wunsch 		    (idfield->cyl * (fd->ft->sectrac * fd->ft->heads) +
2132b52b7f46SPoul-Henning Kamp 		     idfield->head * fd->ft->sectrac) * fdblk;
2133250300ebSJoerg Wunsch 		bp->bio_bcount = sizeof(struct fdc_readid);
2134f664aeeeSJoerg Wunsch 	} else
2135f664aeeeSJoerg Wunsch 		panic("wrong cmd in fdmisccmd()");
2136f664aeeeSJoerg Wunsch 	bp->bio_data = data;
2137250300ebSJoerg Wunsch 	bp->bio_dev = dev;
2138250300ebSJoerg Wunsch 	bp->bio_done = fdbiodone;
2139817988beSPoul-Henning Kamp 	bp->bio_flags = 0;
2140250300ebSJoerg Wunsch 
2141c3bdb2f7SPoul-Henning Kamp 	/* Now run the command. */
2142f664aeeeSJoerg Wunsch 	fdstrategy(bp);
214302aad38cSPoul-Henning Kamp 	error = biowait(bp, "fdcmd");
2144c3bdb2f7SPoul-Henning Kamp 
2145f664aeeeSJoerg Wunsch 	free(bp, M_TEMP);
214602aad38cSPoul-Henning Kamp 	return (error);
2147f664aeeeSJoerg Wunsch }
21485b81b6b3SRodney W. Grimes 
21493e425b96SJulian Elischer static int
215089c9c53dSPoul-Henning Kamp fdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
2151f5f7ba03SJordan K. Hubbard {
2152f664aeeeSJoerg Wunsch  	fdu_t fdu;
2153f664aeeeSJoerg Wunsch  	fd_p fd;
21542995d110SJoerg Wunsch 	struct fdc_status *fsp;
2155250300ebSJoerg Wunsch 	struct fdc_readid *rid;
2156503799eaSPoul-Henning Kamp 	int error;
2157f5f7ba03SJordan K. Hubbard 
2158a2f19df9SPoul-Henning Kamp  	fd = dev->si_drv1;
2159503799eaSPoul-Henning Kamp  	fdu = fd->fdu;
2160fb35bd37SJoerg Wunsch 
21611a6bed68SJoerg Wunsch 	/*
21621a6bed68SJoerg Wunsch 	 * First, handle everything that could be done with
21631a6bed68SJoerg Wunsch 	 * FD_NONBLOCK still being set.
21641a6bed68SJoerg Wunsch 	 */
21651a6bed68SJoerg Wunsch 	switch (cmd) {
2166c91a63aaSPoul-Henning Kamp 
2167c91a63aaSPoul-Henning Kamp 	case DIOCGMEDIASIZE:
216829ec21d1SYaroslav Tykhiy 		if (fd->ft == 0)
216929ec21d1SYaroslav Tykhiy 			return ((fd->flags & FD_NONBLOCK) ? EAGAIN : ENXIO);
2170c91a63aaSPoul-Henning Kamp 		*(off_t *)addr = (128 << (fd->ft->secsize)) * fd->ft->size;
2171c91a63aaSPoul-Henning Kamp 		return (0);
2172c91a63aaSPoul-Henning Kamp 
2173c91a63aaSPoul-Henning Kamp 	case DIOCGSECTORSIZE:
217429ec21d1SYaroslav Tykhiy 		if (fd->ft == 0)
217529ec21d1SYaroslav Tykhiy 			return ((fd->flags & FD_NONBLOCK) ? EAGAIN : ENXIO);
2176c91a63aaSPoul-Henning Kamp 		*(u_int *)addr = 128 << (fd->ft->secsize);
2177c91a63aaSPoul-Henning Kamp 		return (0);
2178c91a63aaSPoul-Henning Kamp 
21791a6bed68SJoerg Wunsch 	case FIONBIO:
21801a6bed68SJoerg Wunsch 		if (*(int *)addr != 0)
21811a6bed68SJoerg Wunsch 			fd->flags |= FD_NONBLOCK;
21821a6bed68SJoerg Wunsch 		else {
21831a6bed68SJoerg Wunsch 			if (fd->ft == 0) {
21841a6bed68SJoerg Wunsch 				/*
21851a6bed68SJoerg Wunsch 				 * No drive type has been selected yet,
21861a6bed68SJoerg Wunsch 				 * cannot turn FNONBLOCK off.
21871a6bed68SJoerg Wunsch 				 */
21881a6bed68SJoerg Wunsch 				return (EINVAL);
21891a6bed68SJoerg Wunsch 			}
21901a6bed68SJoerg Wunsch 			fd->flags &= ~FD_NONBLOCK;
21911a6bed68SJoerg Wunsch 		}
21921a6bed68SJoerg Wunsch 		return (0);
2193fb35bd37SJoerg Wunsch 
21941a6bed68SJoerg Wunsch 	case FIOASYNC:
21951a6bed68SJoerg Wunsch 		/* keep the generic fcntl() code happy */
21961a6bed68SJoerg Wunsch 		return (0);
21971a6bed68SJoerg Wunsch 
21981a6bed68SJoerg Wunsch 	case FD_GTYPE:                  /* get drive type */
21991a6bed68SJoerg Wunsch 		if (fd->ft == 0)
22001a6bed68SJoerg Wunsch 			/* no type known yet, return the native type */
22011a6bed68SJoerg Wunsch 			*(struct fd_type *)addr = fd_native_types[fd->type];
22021a6bed68SJoerg Wunsch 		else
22031a6bed68SJoerg Wunsch 			*(struct fd_type *)addr = *fd->ft;
22041a6bed68SJoerg Wunsch 		return (0);
22051a6bed68SJoerg Wunsch 
22061a6bed68SJoerg Wunsch 	case FD_STYPE:                  /* set drive type */
2207a3a10d1cSBruce Evans 		/*
2208a3a10d1cSBruce Evans 		 * Allow setting drive type temporarily iff
2209a3a10d1cSBruce Evans 		 * currently unset.  Used for fdformat so any
2210a3a10d1cSBruce Evans 		 * user can set it, and then start formatting.
2211a3a10d1cSBruce Evans 		 */
2212a3a10d1cSBruce Evans 		if (fd->ft)
2213a3a10d1cSBruce Evans 			return (EINVAL); /* already set */
2214503799eaSPoul-Henning Kamp 		fd->fts[0] = *(struct fd_type *)addr;
2215a3a10d1cSBruce Evans 		fd->ft = &fd->fts[0];
2216a3a10d1cSBruce Evans 		fd->flags |= FD_UA;
22171a6bed68SJoerg Wunsch 		return (0);
22181a6bed68SJoerg Wunsch 
22191a6bed68SJoerg Wunsch 	case FD_GOPTS:			/* get drive options */
2220503799eaSPoul-Henning Kamp 		*(int *)addr = fd->options + FDOPT_AUTOSEL;
22211a6bed68SJoerg Wunsch 		return (0);
22221a6bed68SJoerg Wunsch 
22231a6bed68SJoerg Wunsch 	case FD_SOPTS:			/* set drive options */
22241a6bed68SJoerg Wunsch 		fd->options = *(int *)addr & ~FDOPT_AUTOSEL;
22251a6bed68SJoerg Wunsch 		return (0);
22261a6bed68SJoerg Wunsch 
22271a6bed68SJoerg Wunsch #ifdef FDC_DEBUG
22281a6bed68SJoerg Wunsch 	case FD_DEBUG:
22291a6bed68SJoerg Wunsch 		if ((fd_debug != 0) != (*(int *)addr != 0)) {
22301a6bed68SJoerg Wunsch 			fd_debug = (*(int *)addr != 0);
22311a6bed68SJoerg Wunsch 			printf("fd%d: debugging turned %s\n",
22321a6bed68SJoerg Wunsch 			    fd->fdu, fd_debug ? "on" : "off");
22331a6bed68SJoerg Wunsch 		}
22341a6bed68SJoerg Wunsch 		return (0);
22351a6bed68SJoerg Wunsch #endif
22361a6bed68SJoerg Wunsch 
22371a6bed68SJoerg Wunsch 	case FD_CLRERR:
223844731cabSJohn Baldwin 		if (suser(td) != 0)
22391a6bed68SJoerg Wunsch 			return (EPERM);
22401a6bed68SJoerg Wunsch 		fd->fdc->fdc_errs = 0;
22411a6bed68SJoerg Wunsch 		return (0);
22421a6bed68SJoerg Wunsch 
22431a6bed68SJoerg Wunsch 	case FD_GSTAT:
22441a6bed68SJoerg Wunsch 		fsp = (struct fdc_status *)addr;
22451a6bed68SJoerg Wunsch 		if ((fd->fdc->flags & FDC_STAT_VALID) == 0)
22461a6bed68SJoerg Wunsch 			return (EINVAL);
22471a6bed68SJoerg Wunsch 		memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int));
22481a6bed68SJoerg Wunsch 		return (0);
22491a6bed68SJoerg Wunsch 
22501a6bed68SJoerg Wunsch 	case FD_GDTYPE:
22511a6bed68SJoerg Wunsch 		*(enum fd_drivetype *)addr = fd->type;
22521a6bed68SJoerg Wunsch 		return (0);
22531a6bed68SJoerg Wunsch 	}
22541a6bed68SJoerg Wunsch 
22551a6bed68SJoerg Wunsch 	/*
22561a6bed68SJoerg Wunsch 	 * Now handle everything else.  Make sure we have a valid
22571a6bed68SJoerg Wunsch 	 * drive type.
22581a6bed68SJoerg Wunsch 	 */
22591a6bed68SJoerg Wunsch 	if (fd->flags & FD_NONBLOCK)
22601a6bed68SJoerg Wunsch 		return (EAGAIN);
22611a6bed68SJoerg Wunsch 	if (fd->ft == 0)
22621a6bed68SJoerg Wunsch 		return (ENXIO);
2263fb35bd37SJoerg Wunsch 	error = 0;
2264f5f7ba03SJordan K. Hubbard 
22656182fdbdSPeter Wemm 	switch (cmd) {
2266f664aeeeSJoerg Wunsch 
2267b39c878eSAndrey A. Chernov 	case FD_FORM:
2268b39c878eSAndrey A. Chernov 		if ((flag & FWRITE) == 0)
2269fb35bd37SJoerg Wunsch 			return (EBADF);	/* must be opened for writing */
2270fb35bd37SJoerg Wunsch 		if (((struct fd_formb *)addr)->format_version !=
2271b39c878eSAndrey A. Chernov 		    FD_FORMAT_VERSION)
2272fb35bd37SJoerg Wunsch 			return (EINVAL); /* wrong version of formatting prog */
2273419f39ceSPoul-Henning Kamp 		error = fdmisccmd(dev, FDBIO_FORMAT, addr);
2274b39c878eSAndrey A. Chernov 		break;
2275b39c878eSAndrey A. Chernov 
2276b39c878eSAndrey A. Chernov 	case FD_GTYPE:                  /* get drive type */
22773e425b96SJulian Elischer 		*(struct fd_type *)addr = *fd->ft;
2278f5f7ba03SJordan K. Hubbard 		break;
2279f5f7ba03SJordan K. Hubbard 
22803a2f7427SDavid Greenman 	case FD_STYPE:                  /* set drive type */
22813a2f7427SDavid Greenman 		/* this is considered harmful; only allow for superuser */
228244731cabSJohn Baldwin 		if (suser(td) != 0)
2283fb35bd37SJoerg Wunsch 			return (EPERM);
22843e425b96SJulian Elischer 		*fd->ft = *(struct fd_type *)addr;
22853a2f7427SDavid Greenman 		break;
22863a2f7427SDavid Greenman 
22873a2f7427SDavid Greenman 	case FD_GOPTS:			/* get drive options */
22883e425b96SJulian Elischer 		*(int *)addr = fd->options;
22893a2f7427SDavid Greenman 		break;
22903a2f7427SDavid Greenman 
22913a2f7427SDavid Greenman 	case FD_SOPTS:			/* set drive options */
22923e425b96SJulian Elischer 		fd->options = *(int *)addr;
22933a2f7427SDavid Greenman 		break;
22943a2f7427SDavid Greenman 
2295f664aeeeSJoerg Wunsch #ifdef FDC_DEBUG
2296f664aeeeSJoerg Wunsch 	case FD_DEBUG:
22970e17a5bcSJoerg Wunsch 		if ((fd_debug != 0) != (*(int *)addr != 0)) {
22980e17a5bcSJoerg Wunsch 			fd_debug = (*(int *)addr != 0);
22990e17a5bcSJoerg Wunsch 			printf("fd%d: debugging turned %s\n",
23000e17a5bcSJoerg Wunsch 			    fd->fdu, fd_debug ? "on" : "off");
23010e17a5bcSJoerg Wunsch 		}
2302f664aeeeSJoerg Wunsch 		break;
2303f664aeeeSJoerg Wunsch #endif
2304f664aeeeSJoerg Wunsch 
23052995d110SJoerg Wunsch 	case FD_CLRERR:
230644731cabSJohn Baldwin 		if (suser(td) != 0)
2307fb35bd37SJoerg Wunsch 			return (EPERM);
23082995d110SJoerg Wunsch 		fd->fdc->fdc_errs = 0;
23092995d110SJoerg Wunsch 		break;
23102995d110SJoerg Wunsch 
23112995d110SJoerg Wunsch 	case FD_GSTAT:
23122995d110SJoerg Wunsch 		fsp = (struct fdc_status *)addr;
23132995d110SJoerg Wunsch 		if ((fd->fdc->flags & FDC_STAT_VALID) == 0)
2314fb35bd37SJoerg Wunsch 			return (EINVAL);
23152995d110SJoerg Wunsch 		memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int));
23162995d110SJoerg Wunsch 		break;
23172995d110SJoerg Wunsch 
2318250300ebSJoerg Wunsch 	case FD_READID:
2319250300ebSJoerg Wunsch 		rid = (struct fdc_readid *)addr;
2320250300ebSJoerg Wunsch 		if (rid->cyl > MAX_CYLINDER || rid->head > MAX_HEAD)
2321fb35bd37SJoerg Wunsch 			return (EINVAL);
2322419f39ceSPoul-Henning Kamp 		error = fdmisccmd(dev, FDBIO_RDSECTID, addr);
2323250300ebSJoerg Wunsch 		break;
2324250300ebSJoerg Wunsch 
2325f5f7ba03SJordan K. Hubbard 	default:
23263a2f7427SDavid Greenman 		error = ENOTTY;
2327f5f7ba03SJordan K. Hubbard 		break;
2328f5f7ba03SJordan K. Hubbard 	}
2329f5f7ba03SJordan K. Hubbard 	return (error);
2330f5f7ba03SJordan K. Hubbard }
2331