xref: /freebsd/sys/dev/fdc/fdc.c (revision b40ce4165d5eb3a5de1515245055350ae3dbab8e)
187f6c662SJulian Elischer /*
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  * 3. All advertising materials mentioning features or use of this software
3364860614SJoerg Wunsch  *    must display the following acknowledgement:
3464860614SJoerg Wunsch  *	This product includes software developed by the University of
3564860614SJoerg Wunsch  *	California, Berkeley and its contributors.
3664860614SJoerg Wunsch  * 4. Neither the name of the University nor the names of its contributors
3764860614SJoerg Wunsch  *    may be used to endorse or promote products derived from this software
3864860614SJoerg Wunsch  *    without specific prior written permission.
395b81b6b3SRodney W. Grimes  *
405b81b6b3SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
415b81b6b3SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
425b81b6b3SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
435b81b6b3SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
445b81b6b3SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
455b81b6b3SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
465b81b6b3SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
475b81b6b3SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
485b81b6b3SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
495b81b6b3SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
505b81b6b3SRodney W. Grimes  * SUCH DAMAGE.
515b81b6b3SRodney W. Grimes  *
52dc4ff321SRodney W. Grimes  *	from:	@(#)fd.c	7.4 (Berkeley) 5/25/91
53c3aac50fSPeter Wemm  * $FreeBSD$
545b81b6b3SRodney W. Grimes  */
555b81b6b3SRodney W. Grimes 
56d2fb4892SJoerg Wunsch #include "opt_fdc.h"
575f830ea2SJoerg Wunsch #include "card.h"
585b81b6b3SRodney W. Grimes 
59b99f0a4aSAndrew Moore #include <sys/param.h>
60b99f0a4aSAndrew Moore #include <sys/systm.h>
619626b608SPoul-Henning Kamp #include <sys/bio.h>
626182fdbdSPeter Wemm #include <sys/bus.h>
636182fdbdSPeter Wemm #include <sys/conf.h>
64b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h>
65fb919e4dSMark Murray #include <sys/disklabel.h>
666182fdbdSPeter Wemm #include <sys/fcntl.h>
67e774b251SJoerg Wunsch #include <sys/fdcio.h>
68fb919e4dSMark Murray #include <sys/kernel.h>
69fb919e4dSMark Murray #include <sys/lock.h>
70b99f0a4aSAndrew Moore #include <sys/malloc.h>
716182fdbdSPeter Wemm #include <sys/module.h>
72fb919e4dSMark Murray #include <sys/mutex.h>
733a2f7427SDavid Greenman #include <sys/proc.h>
74b99f0a4aSAndrew Moore #include <sys/syslog.h>
756182fdbdSPeter Wemm 
766182fdbdSPeter Wemm #include <machine/bus.h>
776182fdbdSPeter Wemm #include <sys/rman.h>
786182fdbdSPeter Wemm 
796182fdbdSPeter Wemm #include <machine/clock.h>
806182fdbdSPeter Wemm #include <machine/resource.h>
81dc5df763SJoerg Wunsch #include <machine/stdarg.h>
826182fdbdSPeter Wemm 
836182fdbdSPeter Wemm #include <isa/isavar.h>
84a97c75b7SDoug Rabson #include <isa/isareg.h>
85a97c75b7SDoug Rabson #include <isa/fdreg.h>
86a97c75b7SDoug Rabson #include <isa/rtc.h>
876182fdbdSPeter Wemm 
88246ed35dSJoerg Wunsch enum fdc_type
89246ed35dSJoerg Wunsch {
90246ed35dSJoerg Wunsch 	FDC_NE765, FDC_I82077, FDC_NE72065, FDC_UNKNOWN = -1
91246ed35dSJoerg Wunsch };
92246ed35dSJoerg Wunsch 
93246ed35dSJoerg Wunsch enum fdc_states {
94246ed35dSJoerg Wunsch 	DEVIDLE,
95246ed35dSJoerg Wunsch 	FINDWORK,
96246ed35dSJoerg Wunsch 	DOSEEK,
97246ed35dSJoerg Wunsch 	SEEKCOMPLETE ,
98246ed35dSJoerg Wunsch 	IOCOMPLETE,
99246ed35dSJoerg Wunsch 	RECALCOMPLETE,
100246ed35dSJoerg Wunsch 	STARTRECAL,
101246ed35dSJoerg Wunsch 	RESETCTLR,
102246ed35dSJoerg Wunsch 	SEEKWAIT,
103246ed35dSJoerg Wunsch 	RECALWAIT,
104246ed35dSJoerg Wunsch 	MOTORWAIT,
105246ed35dSJoerg Wunsch 	IOTIMEDOUT,
106246ed35dSJoerg Wunsch 	RESETCOMPLETE,
107246ed35dSJoerg Wunsch 	PIOREAD
108246ed35dSJoerg Wunsch };
109246ed35dSJoerg Wunsch 
110246ed35dSJoerg Wunsch #ifdef	FDC_DEBUG
111246ed35dSJoerg Wunsch static char const * const fdstates[] = {
112246ed35dSJoerg Wunsch 	"DEVIDLE",
113246ed35dSJoerg Wunsch 	"FINDWORK",
114246ed35dSJoerg Wunsch 	"DOSEEK",
115246ed35dSJoerg Wunsch 	"SEEKCOMPLETE",
116246ed35dSJoerg Wunsch 	"IOCOMPLETE",
117246ed35dSJoerg Wunsch 	"RECALCOMPLETE",
118246ed35dSJoerg Wunsch 	"STARTRECAL",
119246ed35dSJoerg Wunsch 	"RESETCTLR",
120246ed35dSJoerg Wunsch 	"SEEKWAIT",
121246ed35dSJoerg Wunsch 	"RECALWAIT",
122246ed35dSJoerg Wunsch 	"MOTORWAIT",
123246ed35dSJoerg Wunsch 	"IOTIMEDOUT",
124246ed35dSJoerg Wunsch 	"RESETCOMPLETE",
125246ed35dSJoerg Wunsch 	"PIOREAD"
126246ed35dSJoerg Wunsch };
127246ed35dSJoerg Wunsch #endif
128246ed35dSJoerg Wunsch 
129246ed35dSJoerg Wunsch /*
130246ed35dSJoerg Wunsch  * Per controller structure (softc).
131246ed35dSJoerg Wunsch  */
132246ed35dSJoerg Wunsch struct fdc_data
133246ed35dSJoerg Wunsch {
134246ed35dSJoerg Wunsch 	int	fdcu;		/* our unit number */
135246ed35dSJoerg Wunsch 	int	dmachan;
136246ed35dSJoerg Wunsch 	int	flags;
137246ed35dSJoerg Wunsch #define FDC_ATTACHED	0x01
138246ed35dSJoerg Wunsch #define FDC_STAT_VALID	0x08
139246ed35dSJoerg Wunsch #define FDC_HAS_FIFO	0x10
140246ed35dSJoerg Wunsch #define FDC_NEEDS_RESET	0x20
141246ed35dSJoerg Wunsch #define FDC_NODMA	0x40
142246ed35dSJoerg Wunsch #define FDC_ISPNP	0x80
143246ed35dSJoerg Wunsch #define FDC_ISPCMCIA	0x100
144246ed35dSJoerg Wunsch 	struct	fd_data *fd;
145246ed35dSJoerg Wunsch 	int	fdu;		/* the active drive	*/
146246ed35dSJoerg Wunsch 	enum	fdc_states state;
147246ed35dSJoerg Wunsch 	int	retry;
1483a5c2c86SJoerg Wunsch #ifndef PC98
149246ed35dSJoerg Wunsch 	int	fdout;		/* mirror of the w/o digital output reg */
1503a5c2c86SJoerg Wunsch #endif
151246ed35dSJoerg Wunsch 	u_int	status[7];	/* copy of the registers */
152246ed35dSJoerg Wunsch 	enum	fdc_type fdct;	/* chip version of FDC */
153246ed35dSJoerg Wunsch 	int	fdc_errs;	/* number of logged errors */
154246ed35dSJoerg Wunsch 	int	dma_overruns;	/* number of DMA overruns */
155246ed35dSJoerg Wunsch 	struct	bio_queue_head head;
156246ed35dSJoerg Wunsch 	struct	bio *bp;	/* active buffer */
1573a5c2c86SJoerg Wunsch #ifdef PC98
1583a5c2c86SJoerg Wunsch 	struct	resource *res_ioport, *res_fdsio, *res_fdemsio;
1593a5c2c86SJoerg Wunsch 	struct	resource *res_irq, *res_drq;
1603a5c2c86SJoerg Wunsch 	int	rid_ioport, rid_irq, rid_drq;
1613a5c2c86SJoerg Wunsch #else
162246ed35dSJoerg Wunsch 	struct	resource *res_ioport, *res_ctl, *res_irq, *res_drq;
163246ed35dSJoerg Wunsch 	int	rid_ioport, rid_ctl, rid_irq, rid_drq;
1643a5c2c86SJoerg Wunsch #endif
165246ed35dSJoerg Wunsch 	int	port_off;
166246ed35dSJoerg Wunsch 	bus_space_tag_t portt;
167246ed35dSJoerg Wunsch 	bus_space_handle_t porth;
1683a5c2c86SJoerg Wunsch #ifdef PC98
1693a5c2c86SJoerg Wunsch         bus_space_tag_t		sc_fdsiot;
1703a5c2c86SJoerg Wunsch         bus_space_handle_t	sc_fdsioh;
1713a5c2c86SJoerg Wunsch         bus_space_tag_t		sc_fdemsiot;
1723a5c2c86SJoerg Wunsch         bus_space_handle_t	sc_fdemsioh;
1733a5c2c86SJoerg Wunsch #else
174246ed35dSJoerg Wunsch 	bus_space_tag_t ctlt;
175246ed35dSJoerg Wunsch 	bus_space_handle_t ctlh;
1763a5c2c86SJoerg Wunsch #endif
177246ed35dSJoerg Wunsch 	void	*fdc_intr;
178246ed35dSJoerg Wunsch 	struct	device *fdc_dev;
1793a5c2c86SJoerg Wunsch #ifndef PC98
180246ed35dSJoerg Wunsch 	void	(*fdctl_wr)(struct fdc_data *fdc, u_int8_t v);
1813a5c2c86SJoerg Wunsch #endif
182246ed35dSJoerg Wunsch };
183246ed35dSJoerg Wunsch 
184246ed35dSJoerg Wunsch typedef int	fdu_t;
185246ed35dSJoerg Wunsch typedef int	fdcu_t;
186246ed35dSJoerg Wunsch typedef int	fdsu_t;
187246ed35dSJoerg Wunsch typedef	struct fd_data *fd_p;
188246ed35dSJoerg Wunsch typedef struct fdc_data *fdc_p;
189246ed35dSJoerg Wunsch typedef enum fdc_type fdc_t;
190246ed35dSJoerg Wunsch 
191246ed35dSJoerg Wunsch #define FDUNIT(s)	(((s) >> 6) & 3)
192246ed35dSJoerg Wunsch #define FDTYPE(s)	((s) & 0x3f)
193246ed35dSJoerg Wunsch 
194246ed35dSJoerg Wunsch /*
195246ed35dSJoerg Wunsch  * fdc maintains a set (1!) of ivars per child of each controller.
196246ed35dSJoerg Wunsch  */
197246ed35dSJoerg Wunsch enum fdc_device_ivars {
198246ed35dSJoerg Wunsch 	FDC_IVAR_FDUNIT,
199246ed35dSJoerg Wunsch };
200246ed35dSJoerg Wunsch 
201246ed35dSJoerg Wunsch /*
202246ed35dSJoerg Wunsch  * Simple access macros for the ivars.
203246ed35dSJoerg Wunsch  */
204246ed35dSJoerg Wunsch #define FDC_ACCESSOR(A, B, T)						\
205246ed35dSJoerg Wunsch static __inline T fdc_get_ ## A(device_t dev)				\
206246ed35dSJoerg Wunsch {									\
207246ed35dSJoerg Wunsch 	uintptr_t v;							\
208246ed35dSJoerg Wunsch 	BUS_READ_IVAR(device_get_parent(dev), dev, FDC_IVAR_ ## B, &v);	\
209246ed35dSJoerg Wunsch 	return (T) v;							\
210246ed35dSJoerg Wunsch }
211246ed35dSJoerg Wunsch FDC_ACCESSOR(fdunit,	FDUNIT,	int)
212246ed35dSJoerg Wunsch 
2130722d6abSJoerg Wunsch /* configuration flags */
2140722d6abSJoerg Wunsch #define FDC_PRETEND_D0	(1 << 0)	/* pretend drive 0 to be there */
215e34c71eaSJoerg Wunsch #define FDC_NO_FIFO	(1 << 2)	/* do not enable FIFO  */
2160722d6abSJoerg Wunsch 
2170722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */
2180722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED	0x1000
2190722d6abSJoerg Wunsch 
220dc5df763SJoerg Wunsch /* error returns for fd_cmd() */
221dc5df763SJoerg Wunsch #define FD_FAILED -1
222dc5df763SJoerg Wunsch #define FD_NOT_VALID -2
223dc5df763SJoerg Wunsch #define FDC_ERRMAX	100	/* do not log more */
2243fef646eSJoerg Wunsch /*
2253fef646eSJoerg Wunsch  * Stop retrying after this many DMA overruns.  Since each retry takes
22683edbfa5SJoerg Wunsch  * one revolution, with 300 rpm., 25 retries take approximately 5
2273fef646eSJoerg Wunsch  * seconds which the read attempt will block in case the DMA overrun
2283fef646eSJoerg Wunsch  * is persistent.
2293fef646eSJoerg Wunsch  */
2303fef646eSJoerg Wunsch #define FDC_DMAOV_MAX	25
231dc5df763SJoerg Wunsch 
2326fb89845SKATO Takenori #define NUMTYPES 17
2336fb89845SKATO Takenori #define NUMDENS  (NUMTYPES - 7)
2347ca0641bSAndrey A. Chernov 
235246ed35dSJoerg Wunsch #define NO_TYPE		0
236b99f0a4aSAndrew Moore #define FD_1720         1
237b99f0a4aSAndrew Moore #define FD_1480         2
238b99f0a4aSAndrew Moore #define FD_1440         3
239b99f0a4aSAndrew Moore #define FD_1200         4
240b99f0a4aSAndrew Moore #define FD_820          5
241b99f0a4aSAndrew Moore #define FD_800          6
242b99f0a4aSAndrew Moore #define FD_720          7
243b99f0a4aSAndrew Moore #define FD_360          8
2446fb89845SKATO Takenori #define FD_640          9
2456fb89845SKATO Takenori #define FD_1232         10
246ed2fa05eSAndrey A. Chernov 
2476fb89845SKATO Takenori #define FD_1480in5_25   11
2486fb89845SKATO Takenori #define FD_1440in5_25   12
2496fb89845SKATO Takenori #define FD_820in5_25    13
2506fb89845SKATO Takenori #define FD_800in5_25    14
2516fb89845SKATO Takenori #define FD_720in5_25    15
2526fb89845SKATO Takenori #define FD_360in5_25    16
2536fb89845SKATO Takenori #define FD_640in5_25    17
254b99f0a4aSAndrew Moore 
255f664aeeeSJoerg Wunsch #define BIO_RDSECTID	BIO_CMD1
2567ca0641bSAndrey A. Chernov 
2576f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] =
2585b81b6b3SRodney W. Grimes {
259126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
260126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
261126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
262126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
263126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
264126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
265126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
266b0568305SAndrey A. Chernov {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
2676fb89845SKATO Takenori {  8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /*  640K in DD 5.25in */
2686fb89845SKATO Takenori {  8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */
269ed2fa05eSAndrey A. Chernov 
270126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
271126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
272126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
273126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
274126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
275126518a1SAndrey A. Chernov {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
2766fb89845SKATO Takenori {  8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /*  640K in HD 5.25in */
2775b81b6b3SRodney W. Grimes };
2785b81b6b3SRodney W. Grimes 
279b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2		/* 2 floppies */
280dc16046fSJoerg Wunsch 
281f8ce7dd5SJoerg Wunsch #define MAX_SEC_SIZE	(128 << 3)
28264860614SJoerg Wunsch #define MAX_CYLINDER	85	/* some people really stress their drives
28364860614SJoerg Wunsch 				 * up to cyl 82 */
284250300ebSJoerg Wunsch #define MAX_HEAD	1
285f8ce7dd5SJoerg Wunsch 
2866182fdbdSPeter Wemm static devclass_t fdc_devclass;
2875b81b6b3SRodney W. Grimes 
288246ed35dSJoerg Wunsch /*
289246ed35dSJoerg Wunsch  * Per drive structure (softc).
290246ed35dSJoerg Wunsch  */
2916182fdbdSPeter Wemm struct fd_data {
292b99f0a4aSAndrew Moore 	struct	fdc_data *fdc;	/* pointer to controller structure */
2935b81b6b3SRodney W. Grimes 	int	fdsu;		/* this units number on this controller */
2943a2f7427SDavid Greenman 	int	type;		/* Drive type (FD_1440...) */
2955b81b6b3SRodney W. Grimes 	struct	fd_type *ft;	/* pointer to the type descriptor */
2965b81b6b3SRodney W. Grimes 	int	flags;
2975b81b6b3SRodney W. Grimes #define	FD_OPEN		0x01	/* it's open		*/
2985b81b6b3SRodney W. Grimes #define	FD_ACTIVE	0x02	/* it's active		*/
2995b81b6b3SRodney W. Grimes #define	FD_MOTOR	0x04	/* motor should be on	*/
3005b81b6b3SRodney W. Grimes #define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
3015b81b6b3SRodney W. Grimes 	int	skip;
3025b81b6b3SRodney W. Grimes 	int	hddrv;
303dc5df763SJoerg Wunsch #define FD_NO_TRACK -2
3045b81b6b3SRodney W. Grimes 	int	track;		/* where we think the head is */
3053a2f7427SDavid Greenman 	int	options;	/* user configurable options, see ioctl_fd.h */
30602a19910SJustin T. Gibbs 	struct	callout_handle toffhandle;
30702a19910SJustin T. Gibbs 	struct	callout_handle tohandle;
308b2dfb1f9SJustin T. Gibbs 	struct	devstat device_stats;
309e219897aSJoerg Wunsch 	eventhandler_tag clonetag;
310e219897aSJoerg Wunsch 	dev_t	masterdev;
31160444853SJoerg Wunsch #define NCLONEDEVS	10	/* must match the table below */
31260444853SJoerg Wunsch 	dev_t	clonedevs[NCLONEDEVS];
3136182fdbdSPeter Wemm 	device_t dev;
3146182fdbdSPeter Wemm 	fdu_t	fdu;
3156182fdbdSPeter Wemm };
31637286586SPeter Wemm 
31737286586SPeter Wemm struct fdc_ivars {
31837286586SPeter Wemm 	int	fdunit;
31937286586SPeter Wemm };
3206182fdbdSPeter Wemm static devclass_t fd_devclass;
3215b81b6b3SRodney W. Grimes 
322246ed35dSJoerg Wunsch /*
323246ed35dSJoerg Wunsch  * Throughout this file the following conventions will be used:
324246ed35dSJoerg Wunsch  *
325246ed35dSJoerg Wunsch  * fd is a pointer to the fd_data struct for the drive in question
326246ed35dSJoerg Wunsch  * fdc is a pointer to the fdc_data struct for the controller
327246ed35dSJoerg Wunsch  * fdu is the floppy drive unit number
328246ed35dSJoerg Wunsch  * fdcu is the floppy controller unit number
329246ed35dSJoerg Wunsch  * fdsu is the floppy drive unit number on that controller. (sub-unit)
330246ed35dSJoerg Wunsch  */
331b99f0a4aSAndrew Moore 
332246ed35dSJoerg Wunsch /*
333246ed35dSJoerg Wunsch  * Function declarations, same (chaotic) order as they appear in the
334246ed35dSJoerg Wunsch  * file.  Re-ordering is too late now, it would only obfuscate the
335246ed35dSJoerg Wunsch  * diffs against old and offspring versions (like the PC98 one).
336246ed35dSJoerg Wunsch  *
337246ed35dSJoerg Wunsch  * Anyone adding functions here, please keep this sequence the same
338246ed35dSJoerg Wunsch  * as below -- makes locating a particular function in the body much
339246ed35dSJoerg Wunsch  * easier.
340246ed35dSJoerg Wunsch  */
341246ed35dSJoerg Wunsch static void fdout_wr(fdc_p, u_int8_t);
342246ed35dSJoerg Wunsch static u_int8_t fdsts_rd(fdc_p);
343246ed35dSJoerg Wunsch static void fddata_wr(fdc_p, u_int8_t);
344246ed35dSJoerg Wunsch static u_int8_t fddata_rd(fdc_p);
345246ed35dSJoerg Wunsch static void fdctl_wr_isa(fdc_p, u_int8_t);
346246ed35dSJoerg Wunsch #if NCARD > 0
347246ed35dSJoerg Wunsch static void fdctl_wr_pcmcia(fdc_p, u_int8_t);
348246ed35dSJoerg Wunsch #endif
349246ed35dSJoerg Wunsch #if 0
350246ed35dSJoerg Wunsch static u_int8_t fdin_rd(fdc_p);
351246ed35dSJoerg Wunsch #endif
352246ed35dSJoerg Wunsch static int fdc_err(struct fdc_data *, const char *);
353246ed35dSJoerg Wunsch static int fd_cmd(struct fdc_data *, int, ...);
354246ed35dSJoerg Wunsch static int enable_fifo(fdc_p fdc);
355246ed35dSJoerg Wunsch static int fd_sense_drive_status(fdc_p, int *);
356246ed35dSJoerg Wunsch static int fd_sense_int(fdc_p, int *, int *);
357246ed35dSJoerg Wunsch static int fd_read_status(fdc_p);
358246ed35dSJoerg Wunsch static int fdc_alloc_resources(struct fdc_data *);
359246ed35dSJoerg Wunsch static void fdc_release_resources(struct fdc_data *);
360246ed35dSJoerg Wunsch static int fdc_read_ivar(device_t, device_t, int, uintptr_t *);
361246ed35dSJoerg Wunsch static int fdc_probe(device_t);
362246ed35dSJoerg Wunsch #if NCARD > 0
363246ed35dSJoerg Wunsch static int fdc_pccard_probe(device_t);
364246ed35dSJoerg Wunsch #endif
365246ed35dSJoerg Wunsch static int fdc_detach(device_t dev);
366246ed35dSJoerg Wunsch static void fdc_add_child(device_t, const char *, int);
367246ed35dSJoerg Wunsch static int fdc_attach(device_t);
368246ed35dSJoerg Wunsch static int fdc_print_child(device_t, device_t);
369246ed35dSJoerg Wunsch static void fd_clone (void *, char *, int, dev_t *);
370246ed35dSJoerg Wunsch static int fd_probe(device_t);
371246ed35dSJoerg Wunsch static int fd_attach(device_t);
372246ed35dSJoerg Wunsch static int fd_detach(device_t);
3736182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int);
3743a2f7427SDavid Greenman #  define TURNON 1
3753a2f7427SDavid Greenman #  define TURNOFF 0
3763a2f7427SDavid Greenman static timeout_t fd_turnoff;
3773a2f7427SDavid Greenman static timeout_t fd_motor_on;
3786182fdbdSPeter Wemm static void fd_turnon(struct fd_data *);
3793a2f7427SDavid Greenman static void fdc_reset(fdc_p);
3806182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *);
38180909a7dSJoerg Wunsch static int out_fdc(struct fdc_data *, int);
382246ed35dSJoerg Wunsch /*
383246ed35dSJoerg Wunsch  * The open function is named Fdopen() to avoid confusion with fdopen()
384246ed35dSJoerg Wunsch  * in fd(4).  The difference is now only meaningful for debuggers.
385246ed35dSJoerg Wunsch  */
386246ed35dSJoerg Wunsch static	d_open_t	Fdopen;
387246ed35dSJoerg Wunsch static	d_close_t	fdclose;
388246ed35dSJoerg Wunsch static	d_strategy_t	fdstrategy;
3896182fdbdSPeter Wemm static void fdstart(struct fdc_data *);
3905c1a1eaeSBruce Evans static timeout_t fd_iotimeout;
3913a2f7427SDavid Greenman static timeout_t fd_pseudointr;
392246ed35dSJoerg Wunsch static driver_intr_t fdc_intr;
393246ed35dSJoerg Wunsch static int fdcpio(fdc_p, long, caddr_t, u_int);
3946182fdbdSPeter Wemm static int fdstate(struct fdc_data *);
3956182fdbdSPeter Wemm static int retrier(struct fdc_data *);
396246ed35dSJoerg Wunsch static void fdbiodone(struct bio *);
397f664aeeeSJoerg Wunsch static int fdmisccmd(dev_t, u_int, void *);
398246ed35dSJoerg Wunsch static	d_ioctl_t	fdioctl;
399d66c374fSTor Egge 
400d66c374fSTor Egge static int fifo_threshold = 8;	/* XXX: should be accessible via sysctl */
401d66c374fSTor Egge 
402d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
4033a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */
404cba2a7c6SBruce Evans static int volatile fd_debug = 0;
4050e17a5bcSJoerg Wunsch #define TRACE0(arg) do { if (fd_debug) printf(arg); } while (0)
4060e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { if (fd_debug) printf(arg1, arg2); } while (0)
407d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */
4080e17a5bcSJoerg Wunsch #define TRACE0(arg) do { } while (0)
4090e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { } while (0)
410d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */
4115b81b6b3SRodney W. Grimes 
412246ed35dSJoerg Wunsch /*
413246ed35dSJoerg Wunsch  * Bus space handling (access to low-level IO).
414246ed35dSJoerg Wunsch  */
415427ccf00SDoug Rabson static void
416427ccf00SDoug Rabson fdout_wr(fdc_p fdc, u_int8_t v)
417427ccf00SDoug Rabson {
418427ccf00SDoug Rabson 	bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v);
419427ccf00SDoug Rabson }
420427ccf00SDoug Rabson 
421427ccf00SDoug Rabson static u_int8_t
422427ccf00SDoug Rabson fdsts_rd(fdc_p fdc)
423427ccf00SDoug Rabson {
424427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off);
425427ccf00SDoug Rabson }
426427ccf00SDoug Rabson 
427427ccf00SDoug Rabson static void
428427ccf00SDoug Rabson fddata_wr(fdc_p fdc, u_int8_t v)
429427ccf00SDoug Rabson {
430427ccf00SDoug Rabson 	bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v);
431427ccf00SDoug Rabson }
432427ccf00SDoug Rabson 
433427ccf00SDoug Rabson static u_int8_t
434427ccf00SDoug Rabson fddata_rd(fdc_p fdc)
435427ccf00SDoug Rabson {
436427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off);
437427ccf00SDoug Rabson }
438427ccf00SDoug Rabson 
439427ccf00SDoug Rabson static void
4405f830ea2SJoerg Wunsch fdctl_wr_isa(fdc_p fdc, u_int8_t v)
441427ccf00SDoug Rabson {
442427ccf00SDoug Rabson 	bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v);
443427ccf00SDoug Rabson }
444427ccf00SDoug Rabson 
44558c9d623SPeter Wemm #if NCARD > 0
4465f830ea2SJoerg Wunsch static void
4475f830ea2SJoerg Wunsch fdctl_wr_pcmcia(fdc_p fdc, u_int8_t v)
4485f830ea2SJoerg Wunsch {
4495f830ea2SJoerg Wunsch 	bus_space_write_1(fdc->portt, fdc->porth, FDCTL+fdc->port_off, v);
4505f830ea2SJoerg Wunsch }
45158c9d623SPeter Wemm #endif
4525f830ea2SJoerg Wunsch 
453427ccf00SDoug Rabson #if 0
454427ccf00SDoug Rabson 
455427ccf00SDoug Rabson static u_int8_t
456427ccf00SDoug Rabson fdin_rd(fdc_p fdc)
457427ccf00SDoug Rabson {
458427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDIN);
459427ccf00SDoug Rabson }
460427ccf00SDoug Rabson 
461427ccf00SDoug Rabson #endif
462427ccf00SDoug Rabson 
46387f6c662SJulian Elischer #define CDEV_MAJOR 9
4644e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = {
4654e2f199eSPoul-Henning Kamp 	/* open */	Fdopen,
4664e2f199eSPoul-Henning Kamp 	/* close */	fdclose,
4674e2f199eSPoul-Henning Kamp 	/* read */	physread,
4684e2f199eSPoul-Henning Kamp 	/* write */	physwrite,
4694e2f199eSPoul-Henning Kamp 	/* ioctl */	fdioctl,
4704e2f199eSPoul-Henning Kamp 	/* poll */	nopoll,
4714e2f199eSPoul-Henning Kamp 	/* mmap */	nommap,
4724e2f199eSPoul-Henning Kamp 	/* strategy */	fdstrategy,
4734e2f199eSPoul-Henning Kamp 	/* name */	"fd",
4744e2f199eSPoul-Henning Kamp 	/* maj */	CDEV_MAJOR,
4754e2f199eSPoul-Henning Kamp 	/* dump */	nodump,
4764e2f199eSPoul-Henning Kamp 	/* psize */	nopsize,
4774e2f199eSPoul-Henning Kamp 	/* flags */	D_DISK,
4784e2f199eSPoul-Henning Kamp };
4794e2f199eSPoul-Henning Kamp 
480246ed35dSJoerg Wunsch /*
481246ed35dSJoerg Wunsch  * Auxiliary functions.  Well, some only.  Others are scattered
482246ed35dSJoerg Wunsch  * throughout the entire file.
483246ed35dSJoerg Wunsch  */
484dc5df763SJoerg Wunsch static int
4856182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s)
486dc5df763SJoerg Wunsch {
4876182fdbdSPeter Wemm 	fdc->fdc_errs++;
48816b04b6aSJoerg Wunsch 	if (s) {
489b6e5f28eSPeter Wemm 		if (fdc->fdc_errs < FDC_ERRMAX)
490b6e5f28eSPeter Wemm 			device_printf(fdc->fdc_dev, "%s", s);
491b6e5f28eSPeter Wemm 		else if (fdc->fdc_errs == FDC_ERRMAX)
492b6e5f28eSPeter Wemm 			device_printf(fdc->fdc_dev, "too many errors, not "
493b6e5f28eSPeter Wemm 						    "logging any more\n");
49416b04b6aSJoerg Wunsch 	}
495dc5df763SJoerg Wunsch 
496dc5df763SJoerg Wunsch 	return FD_FAILED;
497dc5df763SJoerg Wunsch }
498dc5df763SJoerg Wunsch 
499dc5df763SJoerg Wunsch /*
500dc5df763SJoerg Wunsch  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
501dc5df763SJoerg Wunsch  * Unit number,
502dc5df763SJoerg Wunsch  * # of output bytes, output bytes as ints ...,
503dc5df763SJoerg Wunsch  * # of input bytes, input bytes as ints ...
504dc5df763SJoerg Wunsch  */
5056f4e0bebSPoul-Henning Kamp static int
5066182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...)
507dc5df763SJoerg Wunsch {
508dc5df763SJoerg Wunsch 	u_char cmd;
509dc5df763SJoerg Wunsch 	int n_in;
510dc5df763SJoerg Wunsch 	int n;
511dc5df763SJoerg Wunsch 	va_list ap;
512dc5df763SJoerg Wunsch 
513dc5df763SJoerg Wunsch 	va_start(ap, n_out);
514dc5df763SJoerg Wunsch 	cmd = (u_char)(va_arg(ap, int));
515dc5df763SJoerg Wunsch 	va_end(ap);
516dc5df763SJoerg Wunsch 	va_start(ap, n_out);
517dc5df763SJoerg Wunsch 	for (n = 0; n < n_out; n++)
518dc5df763SJoerg Wunsch 	{
5196182fdbdSPeter Wemm 		if (out_fdc(fdc, va_arg(ap, int)) < 0)
520dc5df763SJoerg Wunsch 		{
521dc5df763SJoerg Wunsch 			char msg[50];
5222127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
523dc5df763SJoerg Wunsch 				"cmd %x failed at out byte %d of %d\n",
524dc5df763SJoerg Wunsch 				cmd, n + 1, n_out);
5256182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
526dc5df763SJoerg Wunsch 		}
527dc5df763SJoerg Wunsch 	}
528dc5df763SJoerg Wunsch 	n_in = va_arg(ap, int);
529dc5df763SJoerg Wunsch 	for (n = 0; n < n_in; n++)
530dc5df763SJoerg Wunsch 	{
531dc5df763SJoerg Wunsch 		int *ptr = va_arg(ap, int *);
5326182fdbdSPeter Wemm 		if (fd_in(fdc, ptr) < 0)
533dc5df763SJoerg Wunsch 		{
534dc5df763SJoerg Wunsch 			char msg[50];
5352127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
536dc5df763SJoerg Wunsch 				"cmd %02x failed at in byte %d of %d\n",
537dc5df763SJoerg Wunsch 				cmd, n + 1, n_in);
5386182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
539dc5df763SJoerg Wunsch 		}
540dc5df763SJoerg Wunsch 	}
541dc5df763SJoerg Wunsch 
542dc5df763SJoerg Wunsch 	return 0;
543dc5df763SJoerg Wunsch }
544dc5df763SJoerg Wunsch 
5456f4e0bebSPoul-Henning Kamp static int
546d66c374fSTor Egge enable_fifo(fdc_p fdc)
547d66c374fSTor Egge {
548d66c374fSTor Egge 	int i, j;
549d66c374fSTor Egge 
550d66c374fSTor Egge 	if ((fdc->flags & FDC_HAS_FIFO) == 0) {
551d66c374fSTor Egge 
552d66c374fSTor Egge 		/*
553d66c374fSTor Egge 		 * Cannot use fd_cmd the normal way here, since
554d66c374fSTor Egge 		 * this might be an invalid command. Thus we send the
555d66c374fSTor Egge 		 * first byte, and check for an early turn of data directon.
556d66c374fSTor Egge 		 */
557d66c374fSTor Egge 
5586182fdbdSPeter Wemm 		if (out_fdc(fdc, I8207X_CONFIGURE) < 0)
5596182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
560d66c374fSTor Egge 
561d66c374fSTor Egge 		/* If command is invalid, return */
562d66c374fSTor Egge 		j = 100000;
563427ccf00SDoug Rabson 		while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM))
564d66c374fSTor Egge 		       != NE7_RQM && j-- > 0)
565d66c374fSTor Egge 			if (i == (NE7_DIO | NE7_RQM)) {
566d66c374fSTor Egge 				fdc_reset(fdc);
567d66c374fSTor Egge 				return FD_FAILED;
568d66c374fSTor Egge 			}
569d66c374fSTor Egge 		if (j<0 ||
5706182fdbdSPeter Wemm 		    fd_cmd(fdc, 3,
571d66c374fSTor Egge 			   0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) {
572d66c374fSTor Egge 			fdc_reset(fdc);
5736182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
574d66c374fSTor Egge 		}
575d66c374fSTor Egge 		fdc->flags |= FDC_HAS_FIFO;
576d66c374fSTor Egge 		return 0;
577d66c374fSTor Egge 	}
5786182fdbdSPeter Wemm 	if (fd_cmd(fdc, 4,
579d66c374fSTor Egge 		   I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0)
5806182fdbdSPeter Wemm 		return fdc_err(fdc, "Re-enable FIFO failed\n");
581d66c374fSTor Egge 	return 0;
582d66c374fSTor Egge }
583d66c374fSTor Egge 
584d66c374fSTor Egge static int
585dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p)
586dc5df763SJoerg Wunsch {
587dc5df763SJoerg Wunsch 	int st3;
588dc5df763SJoerg Wunsch 
5896182fdbdSPeter Wemm 	if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
590dc5df763SJoerg Wunsch 	{
5916182fdbdSPeter Wemm 		return fdc_err(fdc, "Sense Drive Status failed\n");
592dc5df763SJoerg Wunsch 	}
593dc5df763SJoerg Wunsch 	if (st3p)
594dc5df763SJoerg Wunsch 		*st3p = st3;
595dc5df763SJoerg Wunsch 
596dc5df763SJoerg Wunsch 	return 0;
597dc5df763SJoerg Wunsch }
598dc5df763SJoerg Wunsch 
5996f4e0bebSPoul-Henning Kamp static int
600dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
601dc5df763SJoerg Wunsch {
6026182fdbdSPeter Wemm 	int cyl, st0, ret;
603dc5df763SJoerg Wunsch 
6046182fdbdSPeter Wemm 	ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0);
6056182fdbdSPeter Wemm 	if (ret) {
6066182fdbdSPeter Wemm 		(void)fdc_err(fdc,
607dc5df763SJoerg Wunsch 			      "sense intr err reading stat reg 0\n");
608dc5df763SJoerg Wunsch 		return ret;
609dc5df763SJoerg Wunsch 	}
610dc5df763SJoerg Wunsch 
611dc5df763SJoerg Wunsch 	if (st0p)
612dc5df763SJoerg Wunsch 		*st0p = st0;
613dc5df763SJoerg Wunsch 
6146182fdbdSPeter Wemm 	if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) {
615dc5df763SJoerg Wunsch 		/*
616dc5df763SJoerg Wunsch 		 * There doesn't seem to have been an interrupt.
617dc5df763SJoerg Wunsch 		 */
618dc5df763SJoerg Wunsch 		return FD_NOT_VALID;
619dc5df763SJoerg Wunsch 	}
620dc5df763SJoerg Wunsch 
6216182fdbdSPeter Wemm 	if (fd_in(fdc, &cyl) < 0) {
6226182fdbdSPeter Wemm 		return fdc_err(fdc, "can't get cyl num\n");
623dc5df763SJoerg Wunsch 	}
624dc5df763SJoerg Wunsch 
625dc5df763SJoerg Wunsch 	if (cylp)
626dc5df763SJoerg Wunsch 		*cylp = cyl;
627dc5df763SJoerg Wunsch 
628dc5df763SJoerg Wunsch 	return 0;
629dc5df763SJoerg Wunsch }
630dc5df763SJoerg Wunsch 
631dc5df763SJoerg Wunsch 
6326f4e0bebSPoul-Henning Kamp static int
63364860614SJoerg Wunsch fd_read_status(fdc_p fdc)
634dc5df763SJoerg Wunsch {
635dc5df763SJoerg Wunsch 	int i, ret;
636b5e8ce9fSBruce Evans 
637246ed35dSJoerg Wunsch 	for (i = ret = 0; i < 7; i++) {
638b5e8ce9fSBruce Evans 		/*
63964860614SJoerg Wunsch 		 * XXX types are poorly chosen.  Only bytes can be read
640a838d83dSBruce Evans 		 * from the hardware, but fdc->status[] wants u_ints and
641b5e8ce9fSBruce Evans 		 * fd_in() gives ints.
642b5e8ce9fSBruce Evans 		 */
643b5e8ce9fSBruce Evans 		int status;
644b5e8ce9fSBruce Evans 
6456182fdbdSPeter Wemm 		ret = fd_in(fdc, &status);
646b5e8ce9fSBruce Evans 		fdc->status[i] = status;
647b5e8ce9fSBruce Evans 		if (ret != 0)
648dc5df763SJoerg Wunsch 			break;
649dc5df763SJoerg Wunsch 	}
650dc5df763SJoerg Wunsch 
651dc5df763SJoerg Wunsch 	if (ret == 0)
652dc5df763SJoerg Wunsch 		fdc->flags |= FDC_STAT_VALID;
653dc5df763SJoerg Wunsch 	else
654dc5df763SJoerg Wunsch 		fdc->flags &= ~FDC_STAT_VALID;
655dc5df763SJoerg Wunsch 
656dc5df763SJoerg Wunsch 	return ret;
657dc5df763SJoerg Wunsch }
658dc5df763SJoerg Wunsch 
6593a2f7427SDavid Greenman static int
66037286586SPeter Wemm fdc_alloc_resources(struct fdc_data *fdc)
6615b81b6b3SRodney W. Grimes {
66237286586SPeter Wemm 	device_t dev;
663e219897aSJoerg Wunsch 	int ispnp, ispcmcia, nports;
6645b81b6b3SRodney W. Grimes 
66537286586SPeter Wemm 	dev = fdc->fdc_dev;
6665f830ea2SJoerg Wunsch 	ispnp = (fdc->flags & FDC_ISPNP) != 0;
6675f830ea2SJoerg Wunsch 	ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0;
6686182fdbdSPeter Wemm 	fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0;
6696182fdbdSPeter Wemm 	fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0;
6706182fdbdSPeter Wemm 
671b6e5f28eSPeter Wemm 	/*
6725f830ea2SJoerg Wunsch 	 * On standard ISA, we don't just use an 8 port range
6735f830ea2SJoerg Wunsch 	 * (e.g. 0x3f0-0x3f7) since that covers an IDE control
6745f830ea2SJoerg Wunsch 	 * register at 0x3f6.
6755f830ea2SJoerg Wunsch 	 *
676b6e5f28eSPeter Wemm 	 * Isn't PC hardware wonderful.
6775f830ea2SJoerg Wunsch 	 *
6785f830ea2SJoerg Wunsch 	 * The Y-E Data PCMCIA FDC doesn't have this problem, it
6795f830ea2SJoerg Wunsch 	 * uses the register with offset 6 for pseudo-DMA, and the
6805f830ea2SJoerg Wunsch 	 * one with offset 7 as control register.
681b6e5f28eSPeter Wemm 	 */
682e219897aSJoerg Wunsch 	nports = ispcmcia ? 8 : (ispnp ? 1 : 6);
6836182fdbdSPeter Wemm 	fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
6846182fdbdSPeter Wemm 					     &fdc->rid_ioport, 0ul, ~0ul,
685e219897aSJoerg Wunsch 					     nports, RF_ACTIVE);
6866182fdbdSPeter Wemm 	if (fdc->res_ioport == 0) {
687e219897aSJoerg Wunsch 		device_printf(dev, "cannot reserve I/O port range (%d ports)\n",
688e219897aSJoerg Wunsch 			      nports);
68937286586SPeter Wemm 		return ENXIO;
6906182fdbdSPeter Wemm 	}
691427ccf00SDoug Rabson 	fdc->portt = rman_get_bustag(fdc->res_ioport);
692427ccf00SDoug Rabson 	fdc->porth = rman_get_bushandle(fdc->res_ioport);
693427ccf00SDoug Rabson 
6945f830ea2SJoerg Wunsch 	if (!ispcmcia) {
695427ccf00SDoug Rabson 		/*
6965f830ea2SJoerg Wunsch 		 * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7
6975f830ea2SJoerg Wunsch 		 * and some at 0x3f0-0x3f5,0x3f7. We detect the former
6985f830ea2SJoerg Wunsch 		 * by checking the size and adjust the port address
6995f830ea2SJoerg Wunsch 		 * accordingly.
700b6e5f28eSPeter Wemm 		 */
701b6e5f28eSPeter Wemm 		if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4)
702b6e5f28eSPeter Wemm 			fdc->port_off = -2;
703b6e5f28eSPeter Wemm 
704b6e5f28eSPeter Wemm 		/*
7055f830ea2SJoerg Wunsch 		 * Register the control port range as rid 1 if it
7065f830ea2SJoerg Wunsch 		 * isn't there already. Most PnP BIOSen will have
7075f830ea2SJoerg Wunsch 		 * already done this but non-PnP configurations don't.
708a2639a18SPeter Wemm 		 *
7095f830ea2SJoerg Wunsch 		 * And some (!!) report 0x3f2-0x3f5 and completely
7105f830ea2SJoerg Wunsch 		 * leave out the control register!  It seems that some
7115f830ea2SJoerg Wunsch 		 * non-antique controller chips have a different
7125f830ea2SJoerg Wunsch 		 * method of programming the transfer speed which
7135f830ea2SJoerg Wunsch 		 * doesn't require the control register, but it's
7145f830ea2SJoerg Wunsch 		 * mighty bogus as the chip still responds to the
7155f830ea2SJoerg Wunsch 		 * address for the control register.
716427ccf00SDoug Rabson 		 */
717b6e5f28eSPeter Wemm 		if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) {
718b9da888fSPeter Wemm 			u_long ctlstart;
719b9da888fSPeter Wemm 
720b6e5f28eSPeter Wemm 			/* Find the control port, usually 0x3f7 */
7215f830ea2SJoerg Wunsch 			ctlstart = rman_get_start(fdc->res_ioport) +
7225f830ea2SJoerg Wunsch 				fdc->port_off + 7;
723b6e5f28eSPeter Wemm 
724b6e5f28eSPeter Wemm 			bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1);
725b9da888fSPeter Wemm 		}
726b6e5f28eSPeter Wemm 
727b6e5f28eSPeter Wemm 		/*
728b6e5f28eSPeter Wemm 		 * Now (finally!) allocate the control port.
729b6e5f28eSPeter Wemm 		 */
730427ccf00SDoug Rabson 		fdc->rid_ctl = 1;
7315f830ea2SJoerg Wunsch 		fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT,
7325f830ea2SJoerg Wunsch 						  &fdc->rid_ctl,
733b6e5f28eSPeter Wemm 						  0ul, ~0ul, 1, RF_ACTIVE);
734427ccf00SDoug Rabson 		if (fdc->res_ctl == 0) {
7355f830ea2SJoerg Wunsch 			device_printf(dev,
736e219897aSJoerg Wunsch 		"cannot reserve control I/O port range (control port)\n");
73737286586SPeter Wemm 			return ENXIO;
738427ccf00SDoug Rabson 		}
739427ccf00SDoug Rabson 		fdc->ctlt = rman_get_bustag(fdc->res_ctl);
740427ccf00SDoug Rabson 		fdc->ctlh = rman_get_bushandle(fdc->res_ctl);
7415f830ea2SJoerg Wunsch 	}
7426182fdbdSPeter Wemm 
7436182fdbdSPeter Wemm 	fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ,
7446182fdbdSPeter Wemm 					  &fdc->rid_irq, 0ul, ~0ul, 1,
7456182fdbdSPeter Wemm 					  RF_ACTIVE);
7466182fdbdSPeter Wemm 	if (fdc->res_irq == 0) {
747427ccf00SDoug Rabson 		device_printf(dev, "cannot reserve interrupt line\n");
74837286586SPeter Wemm 		return ENXIO;
7496182fdbdSPeter Wemm 	}
7505f830ea2SJoerg Wunsch 
7515f830ea2SJoerg Wunsch 	if ((fdc->flags & FDC_NODMA) == 0) {
7526182fdbdSPeter Wemm 		fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ,
7536182fdbdSPeter Wemm 						  &fdc->rid_drq, 0ul, ~0ul, 1,
7546182fdbdSPeter Wemm 						  RF_ACTIVE);
7556182fdbdSPeter Wemm 		if (fdc->res_drq == 0) {
756427ccf00SDoug Rabson 			device_printf(dev, "cannot reserve DMA request line\n");
75737286586SPeter Wemm 			return ENXIO;
7586182fdbdSPeter Wemm 		}
7596182fdbdSPeter Wemm 		fdc->dmachan = fdc->res_drq->r_start;
7605f830ea2SJoerg Wunsch 	}
76137286586SPeter Wemm 
76237286586SPeter Wemm 	return 0;
76337286586SPeter Wemm }
76437286586SPeter Wemm 
76537286586SPeter Wemm static void
76637286586SPeter Wemm fdc_release_resources(struct fdc_data *fdc)
76737286586SPeter Wemm {
76837286586SPeter Wemm 	device_t dev;
76937286586SPeter Wemm 
77037286586SPeter Wemm 	dev = fdc->fdc_dev;
77137286586SPeter Wemm 	if (fdc->res_irq != 0) {
77237286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
77337286586SPeter Wemm 					fdc->res_irq);
77437286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
77537286586SPeter Wemm 				     fdc->res_irq);
77637286586SPeter Wemm 	}
77737286586SPeter Wemm 	if (fdc->res_ctl != 0) {
77837286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
77937286586SPeter Wemm 					fdc->res_ctl);
78037286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
78137286586SPeter Wemm 				     fdc->res_ctl);
78237286586SPeter Wemm 	}
78337286586SPeter Wemm 	if (fdc->res_ioport != 0) {
78437286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
78537286586SPeter Wemm 					fdc->res_ioport);
78637286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
78737286586SPeter Wemm 				     fdc->res_ioport);
78837286586SPeter Wemm 	}
78937286586SPeter Wemm 	if (fdc->res_drq != 0) {
79037286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
79137286586SPeter Wemm 					fdc->res_drq);
79237286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
79337286586SPeter Wemm 				     fdc->res_drq);
79437286586SPeter Wemm 	}
79537286586SPeter Wemm }
79637286586SPeter Wemm 
797246ed35dSJoerg Wunsch /*
798246ed35dSJoerg Wunsch  * Configuration/initialization stuff, per controller.
799246ed35dSJoerg Wunsch  */
80037286586SPeter Wemm 
80137286586SPeter Wemm static struct isa_pnp_id fdc_ids[] = {
80237286586SPeter Wemm 	{0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */
80337286586SPeter Wemm 	{0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */
80437286586SPeter Wemm 	{0}
80537286586SPeter Wemm };
80637286586SPeter Wemm 
80737286586SPeter Wemm static int
8083aae7b16SBruce Evans fdc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
80937286586SPeter Wemm {
81037286586SPeter Wemm 	struct fdc_ivars *ivars = device_get_ivars(child);
81137286586SPeter Wemm 
81237286586SPeter Wemm 	switch (which) {
81337286586SPeter Wemm 	case FDC_IVAR_FDUNIT:
81437286586SPeter Wemm 		*result = ivars->fdunit;
81537286586SPeter Wemm 		break;
81637286586SPeter Wemm 	default:
81737286586SPeter Wemm 		return ENOENT;
81837286586SPeter Wemm 	}
81937286586SPeter Wemm 	return 0;
82037286586SPeter Wemm }
82137286586SPeter Wemm 
82237286586SPeter Wemm static int
82337286586SPeter Wemm fdc_probe(device_t dev)
82437286586SPeter Wemm {
82537286586SPeter Wemm 	int	error, ic_type;
82637286586SPeter Wemm 	struct	fdc_data *fdc;
82737286586SPeter Wemm 
82837286586SPeter Wemm 	fdc = device_get_softc(dev);
82937286586SPeter Wemm 	bzero(fdc, sizeof *fdc);
83037286586SPeter Wemm 	fdc->fdc_dev = dev;
8315f830ea2SJoerg Wunsch 	fdc->fdctl_wr = fdctl_wr_isa;
83237286586SPeter Wemm 
83337286586SPeter Wemm 	/* Check pnp ids */
83437286586SPeter Wemm 	error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids);
83537286586SPeter Wemm 	if (error == ENXIO)
83637286586SPeter Wemm 		return ENXIO;
8375f830ea2SJoerg Wunsch 	if (error == 0)
8385f830ea2SJoerg Wunsch 		fdc->flags |= FDC_ISPNP;
83937286586SPeter Wemm 
84037286586SPeter Wemm 	/* Attempt to allocate our resources for the duration of the probe */
84137286586SPeter Wemm 	error = fdc_alloc_resources(fdc);
84237286586SPeter Wemm 	if (error)
84337286586SPeter Wemm 		goto out;
8445b81b6b3SRodney W. Grimes 
84516111cedSAndrew Moore 	/* First - lets reset the floppy controller */
846427ccf00SDoug Rabson 	fdout_wr(fdc, 0);
84716111cedSAndrew Moore 	DELAY(100);
848427ccf00SDoug Rabson 	fdout_wr(fdc, FDO_FRST);
84916111cedSAndrew Moore 
8505b81b6b3SRodney W. Grimes 	/* see if it can handle a command */
8516182fdbdSPeter Wemm 	if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240),
8526182fdbdSPeter Wemm 		   NE7_SPEC_2(2, 0), 0)) {
8536182fdbdSPeter Wemm 		error = ENXIO;
8546182fdbdSPeter Wemm 		goto out;
8555b81b6b3SRodney W. Grimes 	}
8566182fdbdSPeter Wemm 
8576182fdbdSPeter Wemm 	if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) {
8586182fdbdSPeter Wemm 		ic_type = (u_char)ic_type;
8596182fdbdSPeter Wemm 		switch (ic_type) {
8606182fdbdSPeter Wemm 		case 0x80:
8616182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 765 or clone");
8626182fdbdSPeter Wemm 			fdc->fdct = FDC_NE765;
8636182fdbdSPeter Wemm 			break;
8646182fdbdSPeter Wemm 		case 0x81:
8656182fdbdSPeter Wemm 			device_set_desc(dev, "Intel 82077 or clone");
8666182fdbdSPeter Wemm 			fdc->fdct = FDC_I82077;
8676182fdbdSPeter Wemm 			break;
8686182fdbdSPeter Wemm 		case 0x90:
8696182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 72065B or clone");
8706182fdbdSPeter Wemm 			fdc->fdct = FDC_NE72065;
8716182fdbdSPeter Wemm 			break;
8726182fdbdSPeter Wemm 		default:
8736182fdbdSPeter Wemm 			device_set_desc(dev, "generic floppy controller");
8746182fdbdSPeter Wemm 			fdc->fdct = FDC_UNKNOWN;
8756182fdbdSPeter Wemm 			break;
8766182fdbdSPeter Wemm 		}
8776182fdbdSPeter Wemm 	}
8786182fdbdSPeter Wemm 
8796182fdbdSPeter Wemm out:
88037286586SPeter Wemm 	fdc_release_resources(fdc);
8816182fdbdSPeter Wemm 	return (error);
8825b81b6b3SRodney W. Grimes }
8835b81b6b3SRodney W. Grimes 
8845f830ea2SJoerg Wunsch #if NCARD > 0
8855f830ea2SJoerg Wunsch 
8865f830ea2SJoerg Wunsch static int
8875f830ea2SJoerg Wunsch fdc_pccard_probe(device_t dev)
8885f830ea2SJoerg Wunsch {
8895f830ea2SJoerg Wunsch 	int	error;
8905f830ea2SJoerg Wunsch 	struct	fdc_data *fdc;
8915f830ea2SJoerg Wunsch 
8925f830ea2SJoerg Wunsch 	fdc = device_get_softc(dev);
8935f830ea2SJoerg Wunsch 	bzero(fdc, sizeof *fdc);
8945f830ea2SJoerg Wunsch 	fdc->fdc_dev = dev;
8955f830ea2SJoerg Wunsch 	fdc->fdctl_wr = fdctl_wr_pcmcia;
8965f830ea2SJoerg Wunsch 
8975f830ea2SJoerg Wunsch 	fdc->flags |= FDC_ISPCMCIA | FDC_NODMA;
8985f830ea2SJoerg Wunsch 
8995f830ea2SJoerg Wunsch 	/* Attempt to allocate our resources for the duration of the probe */
9005f830ea2SJoerg Wunsch 	error = fdc_alloc_resources(fdc);
9015f830ea2SJoerg Wunsch 	if (error)
9025f830ea2SJoerg Wunsch 		goto out;
9035f830ea2SJoerg Wunsch 
9045f830ea2SJoerg Wunsch 	/* First - lets reset the floppy controller */
9055f830ea2SJoerg Wunsch 	fdout_wr(fdc, 0);
9065f830ea2SJoerg Wunsch 	DELAY(100);
9075f830ea2SJoerg Wunsch 	fdout_wr(fdc, FDO_FRST);
9085f830ea2SJoerg Wunsch 
9095f830ea2SJoerg Wunsch 	/* see if it can handle a command */
9105f830ea2SJoerg Wunsch 	if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240),
9115f830ea2SJoerg Wunsch 		   NE7_SPEC_2(2, 0), 0)) {
9125f830ea2SJoerg Wunsch 		error = ENXIO;
9135f830ea2SJoerg Wunsch 		goto out;
9145f830ea2SJoerg Wunsch 	}
9155f830ea2SJoerg Wunsch 
9165f830ea2SJoerg Wunsch 	device_set_desc(dev, "Y-E Data PCMCIA floppy");
9175f830ea2SJoerg Wunsch 	fdc->fdct = FDC_NE765;
9185f830ea2SJoerg Wunsch 
9195f830ea2SJoerg Wunsch out:
9205f830ea2SJoerg Wunsch 	fdc_release_resources(fdc);
9215f830ea2SJoerg Wunsch 	return (error);
9225f830ea2SJoerg Wunsch }
9235f830ea2SJoerg Wunsch 
924e219897aSJoerg Wunsch #endif /* NCARD > 0 */
925e219897aSJoerg Wunsch 
9265f830ea2SJoerg Wunsch static int
927e219897aSJoerg Wunsch fdc_detach(device_t dev)
9285f830ea2SJoerg Wunsch {
9295f830ea2SJoerg Wunsch 	struct	fdc_data *fdc;
9305f830ea2SJoerg Wunsch 	int	error;
9315f830ea2SJoerg Wunsch 
9325f830ea2SJoerg Wunsch 	fdc = device_get_softc(dev);
9335f830ea2SJoerg Wunsch 
9345f830ea2SJoerg Wunsch 	/* have our children detached first */
9355f830ea2SJoerg Wunsch 	if ((error = bus_generic_detach(dev)))
9365f830ea2SJoerg Wunsch 		return (error);
9375f830ea2SJoerg Wunsch 
938e219897aSJoerg Wunsch 	/* reset controller, turn motor off */
939e219897aSJoerg Wunsch 	fdout_wr(fdc, 0);
940e219897aSJoerg Wunsch 
941e219897aSJoerg Wunsch 	if ((fdc->flags & FDC_NODMA) == 0)
942e219897aSJoerg Wunsch 		isa_dma_release(fdc->dmachan);
943e219897aSJoerg Wunsch 
9445f830ea2SJoerg Wunsch 	if ((fdc->flags & FDC_ATTACHED) == 0) {
9455f830ea2SJoerg Wunsch 		device_printf(dev, "already unloaded\n");
9465f830ea2SJoerg Wunsch 		return (0);
9475f830ea2SJoerg Wunsch 	}
9485f830ea2SJoerg Wunsch 	fdc->flags &= ~FDC_ATTACHED;
9495f830ea2SJoerg Wunsch 
9505f830ea2SJoerg Wunsch 	BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq,
9515f830ea2SJoerg Wunsch 			  fdc->fdc_intr);
9525f830ea2SJoerg Wunsch 	fdc_release_resources(fdc);
9535f830ea2SJoerg Wunsch 	device_printf(dev, "unload\n");
9545f830ea2SJoerg Wunsch 	return (0);
9555f830ea2SJoerg Wunsch }
9565f830ea2SJoerg Wunsch 
9575b81b6b3SRodney W. Grimes /*
95837286586SPeter Wemm  * Add a child device to the fdc controller.  It will then be probed etc.
9595b81b6b3SRodney W. Grimes  */
9606182fdbdSPeter Wemm static void
96137286586SPeter Wemm fdc_add_child(device_t dev, const char *name, int unit)
9625b81b6b3SRodney W. Grimes {
96337286586SPeter Wemm 	int	disabled;
96437286586SPeter Wemm 	struct fdc_ivars *ivar;
9656182fdbdSPeter Wemm 	device_t child;
96692200632SGarrett Wollman 
9677cc0979fSDavid Malone 	ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT | M_ZERO);
96837286586SPeter Wemm 	if (ivar == NULL)
9696182fdbdSPeter Wemm 		return;
97037286586SPeter Wemm 	if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0)
97137286586SPeter Wemm 		ivar->fdunit = 0;
972fe0d4089SMatthew N. Dodd 	child = device_add_child(dev, name, unit);
97337286586SPeter Wemm 	if (child == NULL)
9746182fdbdSPeter Wemm 		return;
97537286586SPeter Wemm 	device_set_ivars(child, ivar);
976a97c75b7SDoug Rabson 	if (resource_int_value(name, unit, "disabled", &disabled) == 0
977a97c75b7SDoug Rabson 	    && disabled != 0)
9786182fdbdSPeter Wemm 		device_disable(child);
9796182fdbdSPeter Wemm }
9806182fdbdSPeter Wemm 
9816182fdbdSPeter Wemm static int
9826182fdbdSPeter Wemm fdc_attach(device_t dev)
9836182fdbdSPeter Wemm {
98437286586SPeter Wemm 	struct	fdc_data *fdc;
9852398f0cdSPeter Wemm 	const char *name, *dname;
9865d54fe91SJoerg Wunsch 	int	i, error, dunit;
987427ccf00SDoug Rabson 
98837286586SPeter Wemm 	fdc = device_get_softc(dev);
98937286586SPeter Wemm 	error = fdc_alloc_resources(fdc);
99037286586SPeter Wemm 	if (error) {
991f8ce7dd5SJoerg Wunsch 		device_printf(dev, "cannot re-acquire resources\n");
99237286586SPeter Wemm 		return error;
99337286586SPeter Wemm 	}
99437286586SPeter Wemm 	error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq,
9953c36743eSMark Murray 			       INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc,
9963c36743eSMark Murray 			       &fdc->fdc_intr);
99737286586SPeter Wemm 	if (error) {
99837286586SPeter Wemm 		device_printf(dev, "cannot setup interrupt\n");
99937286586SPeter Wemm 		return error;
100037286586SPeter Wemm 	}
100137286586SPeter Wemm 	fdc->fdcu = device_get_unit(dev);
1002fb35bd37SJoerg Wunsch 	fdc->flags |= FDC_ATTACHED | FDC_NEEDS_RESET;
10036182fdbdSPeter Wemm 
10045f830ea2SJoerg Wunsch 	if ((fdc->flags & FDC_NODMA) == 0) {
1005f8ce7dd5SJoerg Wunsch 		/*
1006f8ce7dd5SJoerg Wunsch 		 * Acquire the DMA channel forever, the driver will do
1007f8ce7dd5SJoerg Wunsch 		 * the rest
1008f8ce7dd5SJoerg Wunsch 		 * XXX should integrate with rman
1009f8ce7dd5SJoerg Wunsch 		 */
1010100f78bbSSujal Patel 		isa_dma_acquire(fdc->dmachan);
1011f8ce7dd5SJoerg Wunsch 		isa_dmainit(fdc->dmachan, MAX_SEC_SIZE);
10125f830ea2SJoerg Wunsch 	}
10135b81b6b3SRodney W. Grimes 	fdc->state = DEVIDLE;
10146182fdbdSPeter Wemm 
10153a2f7427SDavid Greenman 	/* reset controller, turn motor off, clear fdout mirror reg */
101664860614SJoerg Wunsch 	fdout_wr(fdc, fdc->fdout = 0);
10178177437dSPoul-Henning Kamp 	bioq_init(&fdc->head);
10185b81b6b3SRodney W. Grimes 
10196182fdbdSPeter Wemm 	/*
102037286586SPeter Wemm 	 * Probe and attach any children.  We should probably detect
102137286586SPeter Wemm 	 * devices from the BIOS unless overridden.
10226182fdbdSPeter Wemm 	 */
1023ada54f9eSPeter Wemm 	name = device_get_nameunit(dev);
10242398f0cdSPeter Wemm 	i = 0;
10252398f0cdSPeter Wemm 	while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0)
10262398f0cdSPeter Wemm 		fdc_add_child(dev, dname, dunit);
102737286586SPeter Wemm 
102860444853SJoerg Wunsch 	if ((error = bus_generic_attach(dev)) != 0)
102960444853SJoerg Wunsch 		return (error);
103060444853SJoerg Wunsch 
103160444853SJoerg Wunsch 	return (0);
10326182fdbdSPeter Wemm }
10336182fdbdSPeter Wemm 
103415317dd8SMatthew N. Dodd static int
10356182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child)
10366182fdbdSPeter Wemm {
103715317dd8SMatthew N. Dodd 	int retval = 0;
103815317dd8SMatthew N. Dodd 
103915317dd8SMatthew N. Dodd 	retval += bus_print_child_header(me, child);
104015317dd8SMatthew N. Dodd 	retval += printf(" on %s drive %d\n", device_get_nameunit(me),
104137286586SPeter Wemm 	       fdc_get_fdunit(child));
104215317dd8SMatthew N. Dodd 
104315317dd8SMatthew N. Dodd 	return (retval);
10446182fdbdSPeter Wemm }
10456182fdbdSPeter Wemm 
104616e68fc6SPeter Wemm static device_method_t fdc_methods[] = {
104716e68fc6SPeter Wemm 	/* Device interface */
104816e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fdc_probe),
104916e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fdc_attach),
1050e219897aSJoerg Wunsch 	DEVMETHOD(device_detach,	fdc_detach),
105116e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
105216e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend),
105316e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume),
105416e68fc6SPeter Wemm 
105516e68fc6SPeter Wemm 	/* Bus interface */
105616e68fc6SPeter Wemm 	DEVMETHOD(bus_print_child,	fdc_print_child),
105737286586SPeter Wemm 	DEVMETHOD(bus_read_ivar,	fdc_read_ivar),
105816e68fc6SPeter Wemm 	/* Our children never use any other bus interface methods. */
105916e68fc6SPeter Wemm 
106016e68fc6SPeter Wemm 	{ 0, 0 }
106116e68fc6SPeter Wemm };
106216e68fc6SPeter Wemm 
106316e68fc6SPeter Wemm static driver_t fdc_driver = {
106416e68fc6SPeter Wemm 	"fdc",
106516e68fc6SPeter Wemm 	fdc_methods,
106616e68fc6SPeter Wemm 	sizeof(struct fdc_data)
106716e68fc6SPeter Wemm };
106816e68fc6SPeter Wemm 
106916e68fc6SPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0);
10705f063c7bSMike Smith DRIVER_MODULE(fdc, acpi, fdc_driver, fdc_devclass, 0, 0);
107116e68fc6SPeter Wemm 
10725f830ea2SJoerg Wunsch #if NCARD > 0
10735f830ea2SJoerg Wunsch 
10745f830ea2SJoerg Wunsch static device_method_t fdc_pccard_methods[] = {
10755f830ea2SJoerg Wunsch 	/* Device interface */
10765f830ea2SJoerg Wunsch 	DEVMETHOD(device_probe,		fdc_pccard_probe),
10775f830ea2SJoerg Wunsch 	DEVMETHOD(device_attach,	fdc_attach),
1078e219897aSJoerg Wunsch 	DEVMETHOD(device_detach,	fdc_detach),
10795f830ea2SJoerg Wunsch 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
10805f830ea2SJoerg Wunsch 	DEVMETHOD(device_suspend,	bus_generic_suspend),
10815f830ea2SJoerg Wunsch 	DEVMETHOD(device_resume,	bus_generic_resume),
10825f830ea2SJoerg Wunsch 
10835f830ea2SJoerg Wunsch 	/* Bus interface */
10845f830ea2SJoerg Wunsch 	DEVMETHOD(bus_print_child,	fdc_print_child),
10855f830ea2SJoerg Wunsch 	DEVMETHOD(bus_read_ivar,	fdc_read_ivar),
10865f830ea2SJoerg Wunsch 	/* Our children never use any other bus interface methods. */
10875f830ea2SJoerg Wunsch 
10885f830ea2SJoerg Wunsch 	{ 0, 0 }
10895f830ea2SJoerg Wunsch };
10905f830ea2SJoerg Wunsch 
10915f830ea2SJoerg Wunsch static driver_t fdc_pccard_driver = {
10925f830ea2SJoerg Wunsch 	"fdc",
10935f830ea2SJoerg Wunsch 	fdc_pccard_methods,
10945f830ea2SJoerg Wunsch 	sizeof(struct fdc_data)
10955f830ea2SJoerg Wunsch };
10965f830ea2SJoerg Wunsch 
10975f830ea2SJoerg Wunsch DRIVER_MODULE(fdc, pccard, fdc_pccard_driver, fdc_devclass, 0, 0);
10985f830ea2SJoerg Wunsch 
10995f830ea2SJoerg Wunsch #endif /* NCARD > 0 */
11005f830ea2SJoerg Wunsch 
11013f54a085SPoul-Henning Kamp static struct {
11023f54a085SPoul-Henning Kamp 	char *match;
11033f54a085SPoul-Henning Kamp 	int minor;
11043f54a085SPoul-Henning Kamp 	int link;
11053f54a085SPoul-Henning Kamp } fd_suffix[] = {
110660444853SJoerg Wunsch 	/*
110760444853SJoerg Wunsch 	 * Genuine clone devices must come first, and their number must
110860444853SJoerg Wunsch 	 * match NCLONEDEVS above.
110960444853SJoerg Wunsch 	 */
11103f54a085SPoul-Henning Kamp 	{ ".1720",	1,	0 },
11113f54a085SPoul-Henning Kamp 	{ ".1480",	2,	0 },
11123f54a085SPoul-Henning Kamp 	{ ".1440",	3,	0 },
11133f54a085SPoul-Henning Kamp 	{ ".1200",	4,	0 },
11143f54a085SPoul-Henning Kamp 	{ ".820",	5,	0 },
11153f54a085SPoul-Henning Kamp 	{ ".800",	6,	0 },
11163f54a085SPoul-Henning Kamp 	{ ".720",	7,	0 },
11173f54a085SPoul-Henning Kamp 	{ ".360",	8,	0 },
11183f54a085SPoul-Henning Kamp 	{ ".640",	9,	0 },
11193f54a085SPoul-Henning Kamp 	{ ".1232",	10,	0 },
112060444853SJoerg Wunsch 	{ "a",		0,	1 },
112160444853SJoerg Wunsch 	{ "b",		0,	1 },
112260444853SJoerg Wunsch 	{ "c",		0,	1 },
112360444853SJoerg Wunsch 	{ "d",		0,	1 },
112460444853SJoerg Wunsch 	{ "e",		0,	1 },
112560444853SJoerg Wunsch 	{ "f",		0,	1 },
112660444853SJoerg Wunsch 	{ "g",		0,	1 },
112760444853SJoerg Wunsch 	{ "h",		0,	1 },
11283f54a085SPoul-Henning Kamp 	{ 0, 0 }
11293f54a085SPoul-Henning Kamp };
1130246ed35dSJoerg Wunsch 
11313f54a085SPoul-Henning Kamp static void
113280909a7dSJoerg Wunsch fd_clone(void *arg, char *name, int namelen, dev_t *dev)
11333f54a085SPoul-Henning Kamp {
113460444853SJoerg Wunsch 	struct	fd_data *fd;
11353f54a085SPoul-Henning Kamp 	int u, d, i;
11363f54a085SPoul-Henning Kamp 	char *n;
11373f54a085SPoul-Henning Kamp 
113860444853SJoerg Wunsch 	fd = (struct fd_data *)arg;
11393f54a085SPoul-Henning Kamp 	if (*dev != NODEV)
11403f54a085SPoul-Henning Kamp 		return;
1141db901281SPoul-Henning Kamp 	if (dev_stdclone(name, &n, "fd", &u) != 2)
11423f54a085SPoul-Henning Kamp 		return;
11433f54a085SPoul-Henning Kamp 	for (i = 0; ; i++) {
11443f54a085SPoul-Henning Kamp 		if (fd_suffix[i].match == NULL)
11453f54a085SPoul-Henning Kamp 			return;
11463f54a085SPoul-Henning Kamp 		if (strcmp(n, fd_suffix[i].match))
11473f54a085SPoul-Henning Kamp 			continue;
11483f54a085SPoul-Henning Kamp 		d = fd_suffix[i].minor;
11493f54a085SPoul-Henning Kamp 		break;
11503f54a085SPoul-Henning Kamp 	}
11513f54a085SPoul-Henning Kamp 	if (fd_suffix[i].link == 0) {
11523f54a085SPoul-Henning Kamp 		*dev = make_dev(&fd_cdevsw, (u << 6) + d,
11533f54a085SPoul-Henning Kamp 			UID_ROOT, GID_OPERATOR, 0640, name);
115460444853SJoerg Wunsch 		fd->clonedevs[i] = *dev;
11553f54a085SPoul-Henning Kamp 	} else {
115660444853SJoerg Wunsch 		*dev = make_dev_alias(fd->masterdev, name);
11573f54a085SPoul-Henning Kamp 	}
11583f54a085SPoul-Henning Kamp }
11593f54a085SPoul-Henning Kamp 
116016e68fc6SPeter Wemm /*
1161246ed35dSJoerg Wunsch  * Configuration/initialization, per drive.
116216e68fc6SPeter Wemm  */
11636182fdbdSPeter Wemm static int
11646182fdbdSPeter Wemm fd_probe(device_t dev)
11656182fdbdSPeter Wemm {
11666182fdbdSPeter Wemm 	int	i;
11676182fdbdSPeter Wemm 	u_int	fdt, st0, st3;
11686182fdbdSPeter Wemm 	struct	fd_data *fd;
11696182fdbdSPeter Wemm 	struct	fdc_data *fdc;
11706182fdbdSPeter Wemm 	fdsu_t	fdsu;
11716182fdbdSPeter Wemm 	static int fd_fifo = 0;
11726182fdbdSPeter Wemm 
11736182fdbdSPeter Wemm 	fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */
11746182fdbdSPeter Wemm 	fd = device_get_softc(dev);
11756182fdbdSPeter Wemm 	fdc = device_get_softc(device_get_parent(dev));
11766182fdbdSPeter Wemm 
11776182fdbdSPeter Wemm 	bzero(fd, sizeof *fd);
11786182fdbdSPeter Wemm 	fd->dev = dev;
11796182fdbdSPeter Wemm 	fd->fdc = fdc;
11806182fdbdSPeter Wemm 	fd->fdsu = fdsu;
11816182fdbdSPeter Wemm 	fd->fdu = device_get_unit(dev);
11826182fdbdSPeter Wemm 
1183a97c75b7SDoug Rabson #ifdef __i386__
1184b99f0a4aSAndrew Moore 	/* look up what bios thinks we have */
11856182fdbdSPeter Wemm 	switch (fd->fdu) {
11866182fdbdSPeter Wemm 	case 0:
11875f830ea2SJoerg Wunsch 		if ((fdc->flags & FDC_ISPCMCIA))
11885f830ea2SJoerg Wunsch 			fdt = RTCFDT_144M;
11895f830ea2SJoerg Wunsch 		else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0)
11900722d6abSJoerg Wunsch 			fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
11910722d6abSJoerg Wunsch 		else
11920722d6abSJoerg Wunsch 			fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
1193b99f0a4aSAndrew Moore 		break;
11946182fdbdSPeter Wemm 	case 1:
11956182fdbdSPeter Wemm 		fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
1196dc5df763SJoerg Wunsch 		break;
1197dc5df763SJoerg Wunsch 	default:
11986182fdbdSPeter Wemm 		fdt = RTCFDT_NONE;
1199dc5df763SJoerg Wunsch 		break;
12006b7bd95bSJoerg Wunsch 	}
1201a97c75b7SDoug Rabson #else
1202a97c75b7SDoug Rabson 	fdt = RTCFDT_144M;	/* XXX probably */
1203a97c75b7SDoug Rabson #endif
12046182fdbdSPeter Wemm 
12056182fdbdSPeter Wemm 	/* is there a unit? */
12066182fdbdSPeter Wemm 	if (fdt == RTCFDT_NONE)
12076182fdbdSPeter Wemm 		return (ENXIO);
12086182fdbdSPeter Wemm 
12096182fdbdSPeter Wemm 	/* select it */
12106182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNON);
1211fb35bd37SJoerg Wunsch 	fdc_reset(fdc);		/* XXX reset, then unreset, etc. */
12126182fdbdSPeter Wemm 	DELAY(1000000);	/* 1 sec */
12136182fdbdSPeter Wemm 
12148de0675cSPeter Wemm 	/* XXX This doesn't work before the first set_motor() */
12156182fdbdSPeter Wemm 	if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN
1216e34c71eaSJoerg Wunsch 	    && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0
12176182fdbdSPeter Wemm 	    && enable_fifo(fdc) == 0) {
1218b6e5f28eSPeter Wemm 		device_printf(device_get_parent(dev),
1219b6e5f28eSPeter Wemm 		    "FIFO enabled, %d bytes threshold\n", fifo_threshold);
1220d66c374fSTor Egge 	}
12216182fdbdSPeter Wemm 	fd_fifo = 1;
12226182fdbdSPeter Wemm 
12236182fdbdSPeter Wemm 	if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0)
12246182fdbdSPeter Wemm 	    && (st3 & NE7_ST3_T0)) {
1225dc5df763SJoerg Wunsch 		/* if at track 0, first seek inwards */
1226dc5df763SJoerg Wunsch 		/* seek some steps: */
12276182fdbdSPeter Wemm 		fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0);
1228dc5df763SJoerg Wunsch 		DELAY(300000); /* ...wait a moment... */
12296182fdbdSPeter Wemm 		fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
1230dc5df763SJoerg Wunsch 	}
1231dc5df763SJoerg Wunsch 
1232dc5df763SJoerg Wunsch 	/* If we're at track 0 first seek inwards. */
12336182fdbdSPeter Wemm 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
1234dc5df763SJoerg Wunsch 		/* Seek some steps... */
12356182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
1236dc5df763SJoerg Wunsch 			/* ...wait a moment... */
1237dc5df763SJoerg Wunsch 			DELAY(300000);
1238dc5df763SJoerg Wunsch 			/* make ctrlr happy: */
12396182fdbdSPeter Wemm 			fd_sense_int(fdc, 0, 0);
1240dc5df763SJoerg Wunsch 		}
1241dc5df763SJoerg Wunsch 	}
1242dc5df763SJoerg Wunsch 
12436b7bd95bSJoerg Wunsch 	for (i = 0; i < 2; i++) {
12446b7bd95bSJoerg Wunsch 		/*
12456b7bd95bSJoerg Wunsch 		 * we must recalibrate twice, just in case the
12466b7bd95bSJoerg Wunsch 		 * heads have been beyond cylinder 76, since most
12476b7bd95bSJoerg Wunsch 		 * FDCs still barf when attempting to recalibrate
12486b7bd95bSJoerg Wunsch 		 * more than 77 steps
12496b7bd95bSJoerg Wunsch 		 */
1250dc5df763SJoerg Wunsch 		/* go back to 0: */
12516182fdbdSPeter Wemm 		if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
12526b7bd95bSJoerg Wunsch 			/* a second being enough for full stroke seek*/
12536b7bd95bSJoerg Wunsch 			DELAY(i == 0 ? 1000000 : 300000);
12545b81b6b3SRodney W. Grimes 
12556b7bd95bSJoerg Wunsch 			/* anything responding? */
1256dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
1257dc5df763SJoerg Wunsch 			    (st0 & NE7_ST0_EC) == 0)
12586b7bd95bSJoerg Wunsch 				break; /* already probed succesfully */
12596b7bd95bSJoerg Wunsch 		}
1260dc5df763SJoerg Wunsch 	}
12616b7bd95bSJoerg Wunsch 
12626182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNOFF);
12633a2f7427SDavid Greenman 
12643a2f7427SDavid Greenman 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
12656182fdbdSPeter Wemm 		return (ENXIO);
12665b81b6b3SRodney W. Grimes 
1267dc5df763SJoerg Wunsch 	fd->track = FD_NO_TRACK;
1268b99f0a4aSAndrew Moore 	fd->fdc = fdc;
1269b99f0a4aSAndrew Moore 	fd->fdsu = fdsu;
12703a2f7427SDavid Greenman 	fd->options = 0;
127102a19910SJustin T. Gibbs 	callout_handle_init(&fd->toffhandle);
127202a19910SJustin T. Gibbs 	callout_handle_init(&fd->tohandle);
12735b81b6b3SRodney W. Grimes 
1274b99f0a4aSAndrew Moore 	switch (fdt) {
12757ca0641bSAndrey A. Chernov 	case RTCFDT_12M:
12766182fdbdSPeter Wemm 		device_set_desc(dev, "1200-KB 5.25\" drive");
1277b99f0a4aSAndrew Moore 		fd->type = FD_1200;
12787ca0641bSAndrey A. Chernov 		break;
12790722d6abSJoerg Wunsch 	case RTCFDT_144M | RTCFDT_144M_PRETENDED:
12806182fdbdSPeter Wemm 		device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive");
12810722d6abSJoerg Wunsch 		fdt = RTCFDT_144M;
12826182fdbdSPeter Wemm 		fd->type = FD_1440;
12837ca0641bSAndrey A. Chernov 	case RTCFDT_144M:
12846182fdbdSPeter Wemm 		device_set_desc(dev, "1440-KB 3.5\" drive");
1285b99f0a4aSAndrew Moore 		fd->type = FD_1440;
12867ca0641bSAndrey A. Chernov 		break;
1287290dd077SJoerg Wunsch 	case RTCFDT_288M:
128886a727d9SJoerg Wunsch 	case RTCFDT_288M_1:
12896182fdbdSPeter Wemm 		device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)");
1290290dd077SJoerg Wunsch 		fd->type = FD_1440;
1291290dd077SJoerg Wunsch 		break;
12927ca0641bSAndrey A. Chernov 	case RTCFDT_360K:
12936182fdbdSPeter Wemm 		device_set_desc(dev, "360-KB 5.25\" drive");
1294b99f0a4aSAndrew Moore 		fd->type = FD_360;
12957ca0641bSAndrey A. Chernov 		break;
1296ed2fa05eSAndrey A. Chernov 	case RTCFDT_720K:
12976182fdbdSPeter Wemm 		printf("720-KB 3.5\" drive");
1298b99f0a4aSAndrew Moore 		fd->type = FD_720;
1299ed2fa05eSAndrey A. Chernov 		break;
13007ca0641bSAndrey A. Chernov 	default:
13016182fdbdSPeter Wemm 		return (ENXIO);
13025b81b6b3SRodney W. Grimes 	}
13036182fdbdSPeter Wemm 	return (0);
13046182fdbdSPeter Wemm }
13056182fdbdSPeter Wemm 
13066182fdbdSPeter Wemm static int
13076182fdbdSPeter Wemm fd_attach(device_t dev)
13086182fdbdSPeter Wemm {
13096182fdbdSPeter Wemm 	struct	fd_data *fd;
131064860614SJoerg Wunsch 	static	int cdevsw_add_done;
131160444853SJoerg Wunsch 	int i;
13126182fdbdSPeter Wemm 
131302211baeSNick Hibma 	if (!cdevsw_add_done) {
1314475ad603SPeter Wemm 		cdevsw_add(&fd_cdevsw);	/* XXX */
131564860614SJoerg Wunsch 		cdevsw_add_done = 1;
131602211baeSNick Hibma 	}
131764860614SJoerg Wunsch 	fd = device_get_softc(dev);
131860444853SJoerg Wunsch 	fd->clonetag = EVENTHANDLER_REGISTER(dev_clone, fd_clone, fd, 1000);
13195f431174SJoerg Wunsch 	fd->masterdev = make_dev(&fd_cdevsw, fd->fdu << 6,
13203f54a085SPoul-Henning Kamp 				 UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu);
132160444853SJoerg Wunsch 	for (i = 0; i < NCLONEDEVS; i++)
132260444853SJoerg Wunsch 		fd->clonedevs[i] = NODEV;
13236182fdbdSPeter Wemm 	devstat_add_entry(&fd->device_stats, device_get_name(dev),
1324f8ce7dd5SJoerg Wunsch 			  device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS,
13252a888f93SKenneth D. Merry 			  DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER,
13262a888f93SKenneth D. Merry 			  DEVSTAT_PRIORITY_FD);
13276182fdbdSPeter Wemm 	return (0);
13285b81b6b3SRodney W. Grimes }
13295b81b6b3SRodney W. Grimes 
13305f830ea2SJoerg Wunsch static int
13315f830ea2SJoerg Wunsch fd_detach(device_t dev)
13325f830ea2SJoerg Wunsch {
13335f830ea2SJoerg Wunsch 	struct	fd_data *fd;
133460444853SJoerg Wunsch 	int i;
13355f830ea2SJoerg Wunsch 
13365f830ea2SJoerg Wunsch 	fd = device_get_softc(dev);
133760444853SJoerg Wunsch 	untimeout(fd_turnoff, fd, fd->toffhandle);
1338e219897aSJoerg Wunsch 	devstat_remove_entry(&fd->device_stats);
1339e219897aSJoerg Wunsch 	destroy_dev(fd->masterdev);
134060444853SJoerg Wunsch 	for (i = 0; i < NCLONEDEVS; i++)
134160444853SJoerg Wunsch 		if (fd->clonedevs[i] != NODEV)
134260444853SJoerg Wunsch 			destroy_dev(fd->clonedevs[i]);
1343e219897aSJoerg Wunsch 	cdevsw_remove(&fd_cdevsw);
1344e219897aSJoerg Wunsch 	EVENTHANDLER_DEREGISTER(dev_clone, fd->clonetag);
13455f830ea2SJoerg Wunsch 
13465f830ea2SJoerg Wunsch 	return (0);
13475f830ea2SJoerg Wunsch }
13485f830ea2SJoerg Wunsch 
134916e68fc6SPeter Wemm static device_method_t fd_methods[] = {
135016e68fc6SPeter Wemm 	/* Device interface */
135116e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fd_probe),
135216e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fd_attach),
13535f830ea2SJoerg Wunsch 	DEVMETHOD(device_detach,	fd_detach),
135416e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
135516e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend), /* XXX */
135616e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume), /* XXX */
135716e68fc6SPeter Wemm 
135816e68fc6SPeter Wemm 	{ 0, 0 }
135916e68fc6SPeter Wemm };
136016e68fc6SPeter Wemm 
136116e68fc6SPeter Wemm static driver_t fd_driver = {
136216e68fc6SPeter Wemm 	"fd",
136316e68fc6SPeter Wemm 	fd_methods,
136416e68fc6SPeter Wemm 	sizeof(struct fd_data)
136516e68fc6SPeter Wemm };
136616e68fc6SPeter Wemm 
1367475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0);
136816e68fc6SPeter Wemm 
1369246ed35dSJoerg Wunsch /*
1370246ed35dSJoerg Wunsch  * More auxiliary functions.
1371246ed35dSJoerg Wunsch  */
1372246ed35dSJoerg Wunsch /*
1373246ed35dSJoerg Wunsch  * Motor control stuff.
1374246ed35dSJoerg Wunsch  * Remember to not deselect the drive we're working on.
1375246ed35dSJoerg Wunsch  */
13763a2f7427SDavid Greenman static void
13776182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon)
13785b81b6b3SRodney W. Grimes {
1379fb35bd37SJoerg Wunsch 	int fdout;
13803a2f7427SDavid Greenman 
1381fb35bd37SJoerg Wunsch 	fdout = fdc->fdout;
13823a2f7427SDavid Greenman 	if (turnon) {
13833a2f7427SDavid Greenman 		fdout &= ~FDO_FDSEL;
1384fb35bd37SJoerg Wunsch 		fdout |= (FDO_MOEN0 << fdsu) | FDO_FDMAEN | FDO_FRST | fdsu;
13853a2f7427SDavid Greenman 	} else
13863a2f7427SDavid Greenman 		fdout &= ~(FDO_MOEN0 << fdsu);
13876182fdbdSPeter Wemm 	fdc->fdout = fdout;
1388fb35bd37SJoerg Wunsch 	fdout_wr(fdc, fdout);
13893a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdout);
13903a2f7427SDavid Greenman }
13913a2f7427SDavid Greenman 
1392381fe1aaSGarrett Wollman static void
13936182fdbdSPeter Wemm fd_turnoff(void *xfd)
13945b81b6b3SRodney W. Grimes {
1395f5f7ba03SJordan K. Hubbard 	int	s;
13966182fdbdSPeter Wemm 	fd_p fd = xfd;
1397dc16046fSJoerg Wunsch 
13986182fdbdSPeter Wemm 	TRACE1("[fd%d: turnoff]", fd->fdu);
13998335c1b8SBruce Evans 
14005f830ea2SJoerg Wunsch 	s = splbio();
14018335c1b8SBruce Evans 	/*
14028335c1b8SBruce Evans 	 * Don't turn off the motor yet if the drive is active.
14035f830ea2SJoerg Wunsch 	 *
14045f830ea2SJoerg Wunsch 	 * If we got here, this could only mean we missed an interrupt.
14055f830ea2SJoerg Wunsch 	 * This can e. g. happen on the Y-E Date PCMCIA floppy controller
14065f830ea2SJoerg Wunsch 	 * after a controller reset.  Just schedule a pseudo-interrupt
14075f830ea2SJoerg Wunsch 	 * so the state machine gets re-entered.
14088335c1b8SBruce Evans 	 */
14096182fdbdSPeter Wemm 	if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) {
14105f830ea2SJoerg Wunsch 		fdc_intr(fd->fdc);
14115f830ea2SJoerg Wunsch 		splx(s);
14128335c1b8SBruce Evans 		return;
14138335c1b8SBruce Evans 	}
14148335c1b8SBruce Evans 
14155b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR;
14166182fdbdSPeter Wemm 	set_motor(fd->fdc, fd->fdsu, TURNOFF);
1417f5f7ba03SJordan K. Hubbard 	splx(s);
14185b81b6b3SRodney W. Grimes }
14195b81b6b3SRodney W. Grimes 
14203a2f7427SDavid Greenman static void
14216182fdbdSPeter Wemm fd_motor_on(void *xfd)
14225b81b6b3SRodney W. Grimes {
1423f5f7ba03SJordan K. Hubbard 	int	s;
14246182fdbdSPeter Wemm 	fd_p fd = xfd;
1425f5f7ba03SJordan K. Hubbard 
1426f5f7ba03SJordan K. Hubbard 	s = splbio();
14275b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR_WAIT;
14285b81b6b3SRodney W. Grimes 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
14295b81b6b3SRodney W. Grimes 	{
14306182fdbdSPeter Wemm 		fdc_intr(fd->fdc);
14315b81b6b3SRodney W. Grimes 	}
1432f5f7ba03SJordan K. Hubbard 	splx(s);
14335b81b6b3SRodney W. Grimes }
14345b81b6b3SRodney W. Grimes 
14353a2f7427SDavid Greenman static void
14366182fdbdSPeter Wemm fd_turnon(fd_p fd)
14375b81b6b3SRodney W. Grimes {
14385b81b6b3SRodney W. Grimes 	if(!(fd->flags & FD_MOTOR))
14395b81b6b3SRodney W. Grimes 	{
14403a2f7427SDavid Greenman 		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
14416182fdbdSPeter Wemm 		set_motor(fd->fdc, fd->fdsu, TURNON);
14426182fdbdSPeter Wemm 		timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */
14435b81b6b3SRodney W. Grimes 	}
14445b81b6b3SRodney W. Grimes }
14455b81b6b3SRodney W. Grimes 
1446381fe1aaSGarrett Wollman static void
1447dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc)
14485b81b6b3SRodney W. Grimes {
14493a2f7427SDavid Greenman 	/* Try a reset, keep motor on */
1450427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
14513a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
14523a2f7427SDavid Greenman 	DELAY(100);
14533a2f7427SDavid Greenman 	/* enable FDC, but defer interrupts a moment */
1454427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN);
14553a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
14563a2f7427SDavid Greenman 	DELAY(100);
1457427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout);
14583a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout);
14593a2f7427SDavid Greenman 
146064860614SJoerg Wunsch 	/* XXX after a reset, silently believe the FDC will accept commands */
14616182fdbdSPeter Wemm 	(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1462dc5df763SJoerg Wunsch 		     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1463dc5df763SJoerg Wunsch 		     0);
1464d66c374fSTor Egge 	if (fdc->flags & FDC_HAS_FIFO)
1465d66c374fSTor Egge 		(void) enable_fifo(fdc);
14665b81b6b3SRodney W. Grimes }
14675b81b6b3SRodney W. Grimes 
1468246ed35dSJoerg Wunsch /*
1469246ed35dSJoerg Wunsch  * FDC IO functions, take care of the main status register, timeout
1470246ed35dSJoerg Wunsch  * in case the desired status bits are never set.
1471246ed35dSJoerg Wunsch  */
1472b5e8ce9fSBruce Evans static int
14736182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr)
1474dc5df763SJoerg Wunsch {
1475dc5df763SJoerg Wunsch 	int i, j = 100000;
1476427ccf00SDoug Rabson 	while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM))
1477dc5df763SJoerg Wunsch 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1478dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
14796182fdbdSPeter Wemm 			return fdc_err(fdc, "ready for output in input\n");
1480dc5df763SJoerg Wunsch 	if (j <= 0)
14816182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "input ready timeout\n": 0);
1482d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
1483427ccf00SDoug Rabson 	i = fddata_rd(fdc);
1484dc5df763SJoerg Wunsch 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
1485dc5df763SJoerg Wunsch 	*ptr = i;
1486dc5df763SJoerg Wunsch 	return 0;
1487d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
1488427ccf00SDoug Rabson 	i = fddata_rd(fdc);
1489dc5df763SJoerg Wunsch 	if (ptr)
1490dc5df763SJoerg Wunsch 		*ptr = i;
1491dc5df763SJoerg Wunsch 	return 0;
1492d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
1493dc5df763SJoerg Wunsch }
1494dc5df763SJoerg Wunsch 
1495dc5df763SJoerg Wunsch int
14966182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x)
14975b81b6b3SRodney W. Grimes {
14983b3837dbSRodney W. Grimes 	int i;
14995b81b6b3SRodney W. Grimes 
15003b3837dbSRodney W. Grimes 	/* Check that the direction bit is set */
15013b3837dbSRodney W. Grimes 	i = 100000;
1502427ccf00SDoug Rabson 	while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0);
15036182fdbdSPeter Wemm 	if (i <= 0) return fdc_err(fdc, "direction bit not set\n");
15043b3837dbSRodney W. Grimes 
15053b3837dbSRodney W. Grimes 	/* Check that the floppy controller is ready for a command */
15063b3837dbSRodney W. Grimes 	i = 100000;
1507427ccf00SDoug Rabson 	while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0);
150816b04b6aSJoerg Wunsch 	if (i <= 0)
15096182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "output ready timeout\n": 0);
15103b3837dbSRodney W. Grimes 
15113b3837dbSRodney W. Grimes 	/* Send the command and return */
1512427ccf00SDoug Rabson 	fddata_wr(fdc, x);
15133a2f7427SDavid Greenman 	TRACE1("[0x%x->FDDATA]", x);
15145b81b6b3SRodney W. Grimes 	return (0);
15155b81b6b3SRodney W. Grimes }
15165b81b6b3SRodney W. Grimes 
1517246ed35dSJoerg Wunsch /*
1518246ed35dSJoerg Wunsch  * Block device driver interface functions (interspersed with even more
1519246ed35dSJoerg Wunsch  * auxiliary functions).
1520246ed35dSJoerg Wunsch  */
1521381fe1aaSGarrett Wollman int
1522b40ce416SJulian Elischer Fdopen(dev_t dev, int flags, int mode, struct thread *td)
15235b81b6b3SRodney W. Grimes {
15245b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
152520a29168SAndrey A. Chernov 	int type = FDTYPE(minor(dev));
15266182fdbdSPeter Wemm 	fd_p	fd;
1527b99f0a4aSAndrew Moore 	fdc_p	fdc;
15285b81b6b3SRodney W. Grimes 
15295b81b6b3SRodney W. Grimes 	/* check bounds */
15306182fdbdSPeter Wemm 	if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0)
1531b99f0a4aSAndrew Moore 		return (ENXIO);
15326182fdbdSPeter Wemm 	fdc = fd->fdc;
15336182fdbdSPeter Wemm 	if ((fdc == NULL) || (fd->type == NO_TYPE))
1534b99f0a4aSAndrew Moore 		return (ENXIO);
1535b99f0a4aSAndrew Moore 	if (type > NUMDENS)
1536b99f0a4aSAndrew Moore 		return (ENXIO);
15377ca0641bSAndrey A. Chernov 	if (type == 0)
15386182fdbdSPeter Wemm 		type = fd->type;
15397ca0641bSAndrey A. Chernov 	else {
15403e425b96SJulian Elischer 		/*
15413e425b96SJulian Elischer 		 * For each type of basic drive, make sure we are trying
15423e425b96SJulian Elischer 		 * to open a type it can do,
15433e425b96SJulian Elischer 		 */
15446182fdbdSPeter Wemm 		if (type != fd->type) {
15456182fdbdSPeter Wemm 			switch (fd->type) {
15467ca0641bSAndrey A. Chernov 			case FD_360:
15477ca0641bSAndrey A. Chernov 				return (ENXIO);
1548ed2fa05eSAndrey A. Chernov 			case FD_720:
1549b39c878eSAndrey A. Chernov 				if (   type != FD_820
1550b39c878eSAndrey A. Chernov 				    && type != FD_800
15516fb89845SKATO Takenori 				    && type != FD_640
1552ed2fa05eSAndrey A. Chernov 				   )
1553ed2fa05eSAndrey A. Chernov 					return (ENXIO);
1554ed2fa05eSAndrey A. Chernov 				break;
15557ca0641bSAndrey A. Chernov 			case FD_1200:
1556b39c878eSAndrey A. Chernov 				switch (type) {
1557b39c878eSAndrey A. Chernov 				case FD_1480:
1558b39c878eSAndrey A. Chernov 					type = FD_1480in5_25;
1559fa4700b4SAndrey A. Chernov 					break;
15607ca0641bSAndrey A. Chernov 				case FD_1440:
1561b39c878eSAndrey A. Chernov 					type = FD_1440in5_25;
1562b39c878eSAndrey A. Chernov 					break;
15636fb89845SKATO Takenori 				case FD_1232:
15646fb89845SKATO Takenori 					break;
1565b39c878eSAndrey A. Chernov 				case FD_820:
1566b39c878eSAndrey A. Chernov 					type = FD_820in5_25;
1567b39c878eSAndrey A. Chernov 					break;
1568b39c878eSAndrey A. Chernov 				case FD_800:
1569b39c878eSAndrey A. Chernov 					type = FD_800in5_25;
1570b39c878eSAndrey A. Chernov 					break;
1571b39c878eSAndrey A. Chernov 				case FD_720:
1572b39c878eSAndrey A. Chernov 					type = FD_720in5_25;
1573b39c878eSAndrey A. Chernov 					break;
15746fb89845SKATO Takenori 				case FD_640:
15756fb89845SKATO Takenori 					type = FD_640in5_25;
15766fb89845SKATO Takenori 					break;
1577b39c878eSAndrey A. Chernov 				case FD_360:
1578b39c878eSAndrey A. Chernov 					type = FD_360in5_25;
1579b39c878eSAndrey A. Chernov 					break;
1580b39c878eSAndrey A. Chernov 				default:
1581b39c878eSAndrey A. Chernov 					return(ENXIO);
1582b39c878eSAndrey A. Chernov 				}
1583b39c878eSAndrey A. Chernov 				break;
1584b39c878eSAndrey A. Chernov 			case FD_1440:
1585b39c878eSAndrey A. Chernov 				if (   type != FD_1720
1586b39c878eSAndrey A. Chernov 				    && type != FD_1480
1587ed2fa05eSAndrey A. Chernov 				    && type != FD_1200
1588b39c878eSAndrey A. Chernov 				    && type != FD_820
1589b39c878eSAndrey A. Chernov 				    && type != FD_800
1590b39c878eSAndrey A. Chernov 				    && type != FD_720
15916fb89845SKATO Takenori 				    && type != FD_640
15927ca0641bSAndrey A. Chernov 				    )
1593dffff499SAndrey A. Chernov 					return(ENXIO);
1594fa4700b4SAndrey A. Chernov 				break;
15957ca0641bSAndrey A. Chernov 			}
15967ca0641bSAndrey A. Chernov 		}
1597fa4700b4SAndrey A. Chernov 	}
15986182fdbdSPeter Wemm 	fd->ft = fd_types + type - 1;
15996182fdbdSPeter Wemm 	fd->flags |= FD_OPEN;
16003fef646eSJoerg Wunsch 	/*
16013fef646eSJoerg Wunsch 	 * Clearing the DMA overrun counter at open time is a bit messy.
16023fef646eSJoerg Wunsch 	 * Since we're only managing one counter per controller, opening
16033fef646eSJoerg Wunsch 	 * the second drive could mess it up.  Anyway, if the DMA overrun
16043fef646eSJoerg Wunsch 	 * condition is really persistent, it will eventually time out
16053fef646eSJoerg Wunsch 	 * still.  OTOH, clearing it here will ensure we'll at least start
16063fef646eSJoerg Wunsch 	 * trying again after a previous (maybe even long ago) failure.
16073fef646eSJoerg Wunsch 	 * Also, this is merely a stop-gap measure only that should not
16083fef646eSJoerg Wunsch 	 * happen during normal operation, so we can tolerate it to be a
16093fef646eSJoerg Wunsch 	 * bit sloppy about this.
16103fef646eSJoerg Wunsch 	 */
16113fef646eSJoerg Wunsch 	fdc->dma_overruns = 0;
16125f830ea2SJoerg Wunsch 
16135b81b6b3SRodney W. Grimes 	return 0;
16145b81b6b3SRodney W. Grimes }
16155b81b6b3SRodney W. Grimes 
1616381fe1aaSGarrett Wollman int
1617b40ce416SJulian Elischer fdclose(dev_t dev, int flags, int mode, struct thread *td)
16185b81b6b3SRodney W. Grimes {
16195b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
16206182fdbdSPeter Wemm 	struct fd_data *fd;
1621b99f0a4aSAndrew Moore 
16226182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
16236182fdbdSPeter Wemm 	fd->flags &= ~FD_OPEN;
16242995d110SJoerg Wunsch 	fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR);
1625dc16046fSJoerg Wunsch 
16265b81b6b3SRodney W. Grimes 	return (0);
16275b81b6b3SRodney W. Grimes }
16285b81b6b3SRodney W. Grimes 
16293a2f7427SDavid Greenman void
16308177437dSPoul-Henning Kamp fdstrategy(struct bio *bp)
16313a2f7427SDavid Greenman {
1632fb35bd37SJoerg Wunsch 	long blknum, nblocks;
16333a2f7427SDavid Greenman  	int	s;
16343a2f7427SDavid Greenman  	fdu_t	fdu;
16353a2f7427SDavid Greenman  	fdc_p	fdc;
16363a2f7427SDavid Greenman  	fd_p	fd;
16373a2f7427SDavid Greenman 	size_t	fdblk;
16383a2f7427SDavid Greenman 
16398177437dSPoul-Henning Kamp  	fdu = FDUNIT(minor(bp->bio_dev));
16406182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
16416182fdbdSPeter Wemm 	if (fd == 0)
16426182fdbdSPeter Wemm 		panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)",
16438177437dSPoul-Henning Kamp 		      (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev));
16443a2f7427SDavid Greenman 	fdc = fd->fdc;
164569acd21dSWarner Losh 	if (fd->type == NO_TYPE) {
16468177437dSPoul-Henning Kamp 		bp->bio_error = ENXIO;
16478177437dSPoul-Henning Kamp 		bp->bio_flags |= BIO_ERROR;
16483b178206SWarner Losh 		goto bad;
164964860614SJoerg Wunsch 	}
1650d3628763SRodney W. Grimes 	fdblk = 128 << (fd->ft->secsize);
1651f664aeeeSJoerg Wunsch 	if (bp->bio_cmd != BIO_FORMAT && bp->bio_cmd != BIO_RDSECTID) {
16528177437dSPoul-Henning Kamp 		if (bp->bio_blkno < 0) {
1653dc5df763SJoerg Wunsch 			printf(
16546a0e6f42SRodney W. Grimes 		"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
16558177437dSPoul-Henning Kamp 			       fdu, (u_long)bp->bio_blkno, bp->bio_bcount);
16568177437dSPoul-Henning Kamp 			bp->bio_error = EINVAL;
16578177437dSPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
16583a2f7427SDavid Greenman 			goto bad;
16593a2f7427SDavid Greenman 		}
16608177437dSPoul-Henning Kamp 		if ((bp->bio_bcount % fdblk) != 0) {
16618177437dSPoul-Henning Kamp 			bp->bio_error = EINVAL;
16628177437dSPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
16633a2f7427SDavid Greenman 			goto bad;
16643a2f7427SDavid Greenman 		}
16653a2f7427SDavid Greenman 	}
16663a2f7427SDavid Greenman 
16673a2f7427SDavid Greenman 	/*
16683a2f7427SDavid Greenman 	 * Set up block calculations.
16693a2f7427SDavid Greenman 	 */
16708177437dSPoul-Henning Kamp 	if (bp->bio_blkno > 20000000) {
1671bb6382faSJoerg Wunsch 		/*
1672bb6382faSJoerg Wunsch 		 * Reject unreasonably high block number, prevent the
1673bb6382faSJoerg Wunsch 		 * multiplication below from overflowing.
1674bb6382faSJoerg Wunsch 		 */
16758177437dSPoul-Henning Kamp 		bp->bio_error = EINVAL;
16768177437dSPoul-Henning Kamp 		bp->bio_flags |= BIO_ERROR;
16773a2f7427SDavid Greenman 		goto bad;
16783a2f7427SDavid Greenman 	}
1679fb35bd37SJoerg Wunsch 	blknum = bp->bio_blkno * DEV_BSIZE / fdblk;
1680bb6382faSJoerg Wunsch  	nblocks = fd->ft->size;
1681fb35bd37SJoerg Wunsch 	if (blknum + bp->bio_bcount / fdblk > nblocks) {
1682fb35bd37SJoerg Wunsch 		if (blknum >= nblocks) {
1683fb35bd37SJoerg Wunsch 			if (bp->bio_cmd == BIO_READ)
1684fb35bd37SJoerg Wunsch 				bp->bio_resid = bp->bio_bcount;
1685fb35bd37SJoerg Wunsch 			else {
1686fb35bd37SJoerg Wunsch 				bp->bio_error = ENOSPC;
16878177437dSPoul-Henning Kamp 				bp->bio_flags |= BIO_ERROR;
1688bb6382faSJoerg Wunsch 			}
1689fb35bd37SJoerg Wunsch 			goto bad;	/* not always bad, but EOF */
1690fb35bd37SJoerg Wunsch 		}
1691fb35bd37SJoerg Wunsch 		bp->bio_bcount = (nblocks - blknum) * fdblk;
1692bb6382faSJoerg Wunsch 	}
16938177437dSPoul-Henning Kamp  	bp->bio_pblkno = bp->bio_blkno;
16943a2f7427SDavid Greenman 	s = splbio();
16958177437dSPoul-Henning Kamp 	bioqdisksort(&fdc->head, bp);
16966182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */
1697b2dfb1f9SJustin T. Gibbs 	devstat_start_transaction(&fd->device_stats);
16985f830ea2SJoerg Wunsch 	device_busy(fd->dev);
16996182fdbdSPeter Wemm 	fdstart(fdc);
17003a2f7427SDavid Greenman 	splx(s);
17013a2f7427SDavid Greenman 	return;
17023a2f7427SDavid Greenman 
17033a2f7427SDavid Greenman bad:
17043a2f7427SDavid Greenman 	biodone(bp);
17053a2f7427SDavid Greenman }
17063a2f7427SDavid Greenman 
1707246ed35dSJoerg Wunsch /*
1708246ed35dSJoerg Wunsch  * fdstart
1709246ed35dSJoerg Wunsch  *
1710246ed35dSJoerg Wunsch  * We have just queued something.  If the controller is not busy
1711246ed35dSJoerg Wunsch  * then simulate the case where it has just finished a command
1712246ed35dSJoerg Wunsch  * So that it (the interrupt routine) looks on the queue for more
1713246ed35dSJoerg Wunsch  * work to do and picks up what we just added.
1714246ed35dSJoerg Wunsch  *
1715246ed35dSJoerg Wunsch  * If the controller is already busy, we need do nothing, as it
1716246ed35dSJoerg Wunsch  * will pick up our work when the present work completes.
1717246ed35dSJoerg Wunsch  */
1718381fe1aaSGarrett Wollman static void
17196182fdbdSPeter Wemm fdstart(struct fdc_data *fdc)
17205b81b6b3SRodney W. Grimes {
17215b81b6b3SRodney W. Grimes 	int s;
17225b81b6b3SRodney W. Grimes 
17235b81b6b3SRodney W. Grimes 	s = splbio();
17246182fdbdSPeter Wemm 	if(fdc->state == DEVIDLE)
17255b81b6b3SRodney W. Grimes 	{
17266182fdbdSPeter Wemm 		fdc_intr(fdc);
17275b81b6b3SRodney W. Grimes 	}
17285b81b6b3SRodney W. Grimes 	splx(s);
17295b81b6b3SRodney W. Grimes }
17305b81b6b3SRodney W. Grimes 
1731381fe1aaSGarrett Wollman static void
17326182fdbdSPeter Wemm fd_iotimeout(void *xfdc)
17335b81b6b3SRodney W. Grimes {
17345c1a1eaeSBruce Evans  	fdc_p fdc;
1735f5f7ba03SJordan K. Hubbard 	int s;
17365b81b6b3SRodney W. Grimes 
17376182fdbdSPeter Wemm 	fdc = xfdc;
17385c1a1eaeSBruce Evans 	TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
17395b81b6b3SRodney W. Grimes 
17403a2f7427SDavid Greenman 	/*
17413a2f7427SDavid Greenman 	 * Due to IBM's brain-dead design, the FDC has a faked ready
17423a2f7427SDavid Greenman 	 * signal, hardwired to ready == true. Thus, any command
17433a2f7427SDavid Greenman 	 * issued if there's no diskette in the drive will _never_
17443a2f7427SDavid Greenman 	 * complete, and must be aborted by resetting the FDC.
17453a2f7427SDavid Greenman 	 * Many thanks, Big Blue!
17465c1a1eaeSBruce Evans 	 * The FDC must not be reset directly, since that would
17475c1a1eaeSBruce Evans 	 * interfere with the state machine.  Instead, pretend that
17485c1a1eaeSBruce Evans 	 * the command completed but was invalid.  The state machine
17495c1a1eaeSBruce Evans 	 * will reset the FDC and retry once.
17503a2f7427SDavid Greenman 	 */
17513a2f7427SDavid Greenman 	s = splbio();
17525c1a1eaeSBruce Evans 	fdc->status[0] = NE7_ST0_IC_IV;
17535c1a1eaeSBruce Evans 	fdc->flags &= ~FDC_STAT_VALID;
17545c1a1eaeSBruce Evans 	fdc->state = IOTIMEDOUT;
17556182fdbdSPeter Wemm 	fdc_intr(fdc);
1756f5f7ba03SJordan K. Hubbard 	splx(s);
17575b81b6b3SRodney W. Grimes }
17585b81b6b3SRodney W. Grimes 
1759246ed35dSJoerg Wunsch /* Just ensure it has the right spl. */
1760381fe1aaSGarrett Wollman static void
17616182fdbdSPeter Wemm fd_pseudointr(void *xfdc)
17625b81b6b3SRodney W. Grimes {
17635b81b6b3SRodney W. Grimes 	int	s;
17643a2f7427SDavid Greenman 
17655b81b6b3SRodney W. Grimes 	s = splbio();
17666182fdbdSPeter Wemm 	fdc_intr(xfdc);
17675b81b6b3SRodney W. Grimes 	splx(s);
17685b81b6b3SRodney W. Grimes }
17695b81b6b3SRodney W. Grimes 
1770246ed35dSJoerg Wunsch /*
1771246ed35dSJoerg Wunsch  * fdc_intr
1772246ed35dSJoerg Wunsch  *
1773246ed35dSJoerg Wunsch  * Keep calling the state machine until it returns a 0.
1774246ed35dSJoerg Wunsch  * Always called at splbio.
1775246ed35dSJoerg Wunsch  */
1776fe310de8SBruce Evans static void
17776182fdbdSPeter Wemm fdc_intr(void *xfdc)
17785b81b6b3SRodney W. Grimes {
17796182fdbdSPeter Wemm 	fdc_p fdc = xfdc;
17806182fdbdSPeter Wemm 	while(fdstate(fdc))
1781381fe1aaSGarrett Wollman 		;
17825b81b6b3SRodney W. Grimes }
17835b81b6b3SRodney W. Grimes 
178469acd21dSWarner Losh /*
1785246ed35dSJoerg Wunsch  * Magic pseudo-DMA initialization for YE FDC. Sets count and
1786246ed35dSJoerg Wunsch  * direction.
178769acd21dSWarner Losh  */
17883b178206SWarner Losh #define SET_BCDR(fdc,wr,cnt,port) \
17893b178206SWarner Losh 	bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port,	 \
17903b178206SWarner Losh 	    ((cnt)-1) & 0xff);						 \
17913b178206SWarner Losh 	bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \
17923b178206SWarner Losh 	    ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f)));
179369acd21dSWarner Losh 
179469acd21dSWarner Losh /*
1795246ed35dSJoerg Wunsch  * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy.
179669acd21dSWarner Losh  */
17974c34deeeSJoerg Wunsch static int
17984c34deeeSJoerg Wunsch fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count)
179969acd21dSWarner Losh {
180069acd21dSWarner Losh 	u_char *cptr = (u_char *)addr;
180169acd21dSWarner Losh 
180221144e3bSPoul-Henning Kamp 	if (flags == BIO_READ) {
180369acd21dSWarner Losh 		if (fdc->state != PIOREAD) {
180469acd21dSWarner Losh 			fdc->state = PIOREAD;
180569acd21dSWarner Losh 			return(0);
180664860614SJoerg Wunsch 		}
18073b178206SWarner Losh 		SET_BCDR(fdc, 0, count, 0);
18083b178206SWarner Losh 		bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off +
18093b178206SWarner Losh 		    FDC_YE_DATAPORT, cptr, count);
181069acd21dSWarner Losh 	} else {
18113b178206SWarner Losh 		bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off +
18123b178206SWarner Losh 		    FDC_YE_DATAPORT, cptr, count);
18133b178206SWarner Losh 		SET_BCDR(fdc, 0, count, 0);
181464860614SJoerg Wunsch 	}
181569acd21dSWarner Losh 	return(1);
181669acd21dSWarner Losh }
181769acd21dSWarner Losh 
1818246ed35dSJoerg Wunsch /*
1819246ed35dSJoerg Wunsch  * The controller state machine.
1820246ed35dSJoerg Wunsch  *
1821246ed35dSJoerg Wunsch  * If it returns a non zero value, it should be called again immediately.
1822246ed35dSJoerg Wunsch  */
18233a2f7427SDavid Greenman static int
18246182fdbdSPeter Wemm fdstate(fdc_p fdc)
18255b81b6b3SRodney W. Grimes {
182664860614SJoerg Wunsch 	struct fdc_readid *idp;
1827fb35bd37SJoerg Wunsch 	int read, format, rdsectid, cylinder, head, i, sec = 0, sectrac;
1828fb35bd37SJoerg Wunsch 	int st0, cyl, st3, idf;
1829fb35bd37SJoerg Wunsch 	unsigned long blknum;
18305b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc->fdu;
18315b81b6b3SRodney W. Grimes 	fd_p fd;
18328177437dSPoul-Henning Kamp 	register struct bio *bp;
1833b39c878eSAndrey A. Chernov 	struct fd_formb *finfo = NULL;
18343a2f7427SDavid Greenman 	size_t fdblk;
18355b81b6b3SRodney W. Grimes 
1836e93e63cbSBruce Evans 	bp = fdc->bp;
1837e93e63cbSBruce Evans 	if (bp == NULL) {
18388177437dSPoul-Henning Kamp 		bp = bioq_first(&fdc->head);
1839e93e63cbSBruce Evans 		if (bp != NULL) {
18408177437dSPoul-Henning Kamp 			bioq_remove(&fdc->head, bp);
1841e93e63cbSBruce Evans 			fdc->bp = bp;
1842e93e63cbSBruce Evans 		}
1843e93e63cbSBruce Evans 	}
1844e93e63cbSBruce Evans 	if (bp == NULL) {
1845246ed35dSJoerg Wunsch 		/*
1846246ed35dSJoerg Wunsch 		 * Nothing left for this controller to do,
1847246ed35dSJoerg Wunsch 		 * force into the IDLE state.
1848246ed35dSJoerg Wunsch 		 */
18495b81b6b3SRodney W. Grimes 		fdc->state = DEVIDLE;
18506182fdbdSPeter Wemm 		if (fdc->fd) {
1851b6e5f28eSPeter Wemm 			device_printf(fdc->fdc_dev,
1852b6e5f28eSPeter Wemm 			    "unexpected valid fd pointer\n");
18535b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
18545b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
18555b81b6b3SRodney W. Grimes 		}
18566182fdbdSPeter Wemm 		TRACE1("[fdc%d IDLE]", fdc->fdcu);
18575b81b6b3SRodney W. Grimes  		return (0);
18585b81b6b3SRodney W. Grimes 	}
18598177437dSPoul-Henning Kamp 	fdu = FDUNIT(minor(bp->bio_dev));
18606182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
18613a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
1862b6e5f28eSPeter Wemm 	if (fdc->fd && (fd != fdc->fd))
1863b6e5f28eSPeter Wemm 		device_printf(fd->dev, "confused fd pointers\n");
18648177437dSPoul-Henning Kamp 	read = bp->bio_cmd == BIO_READ;
186556a23089SPoul-Henning Kamp 	if (read)
186656a23089SPoul-Henning Kamp 		idf = ISADMA_READ;
186756a23089SPoul-Henning Kamp 	else
186856a23089SPoul-Henning Kamp 		idf = ISADMA_WRITE;
186964860614SJoerg Wunsch 	format = bp->bio_cmd == BIO_FORMAT;
1870f664aeeeSJoerg Wunsch 	rdsectid = bp->bio_cmd == BIO_RDSECTID;
1871fb35bd37SJoerg Wunsch 	if (format)
18728177437dSPoul-Henning Kamp 		finfo = (struct fd_formb *)bp->bio_data;
18735b81b6b3SRodney W. Grimes 	TRACE1("fd%d", fdu);
18745b81b6b3SRodney W. Grimes 	TRACE1("[%s]", fdstates[fdc->state]);
18755b81b6b3SRodney W. Grimes 	TRACE1("(0x%x)", fd->flags);
18766182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle);
18776182fdbdSPeter Wemm 	fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
18785b81b6b3SRodney W. Grimes 	switch (fdc->state)
18795b81b6b3SRodney W. Grimes 	{
18805b81b6b3SRodney W. Grimes 	case DEVIDLE:
18815b81b6b3SRodney W. Grimes 	case FINDWORK:	/* we have found new work */
18825b81b6b3SRodney W. Grimes 		fdc->retry = 0;
18835b81b6b3SRodney W. Grimes 		fd->skip = 0;
18845b81b6b3SRodney W. Grimes 		fdc->fd = fd;
18855b81b6b3SRodney W. Grimes 		fdc->fdu = fdu;
18865f830ea2SJoerg Wunsch 		fdc->fdctl_wr(fdc, fd->ft->trans);
18873a2f7427SDavid Greenman 		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
1888246ed35dSJoerg Wunsch 		/*
1889246ed35dSJoerg Wunsch 		 * If the next drive has a motor startup pending, then
1890246ed35dSJoerg Wunsch 		 * it will start up in its own good time.
1891246ed35dSJoerg Wunsch 		 */
18926182fdbdSPeter Wemm 		if(fd->flags & FD_MOTOR_WAIT) {
18935b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
1894246ed35dSJoerg Wunsch 			return (0); /* will return later */
18955b81b6b3SRodney W. Grimes 		}
1896246ed35dSJoerg Wunsch 		/*
1897246ed35dSJoerg Wunsch 		 * Maybe if it's not starting, it SHOULD be starting.
1898246ed35dSJoerg Wunsch 		 */
18995b81b6b3SRodney W. Grimes 		if (!(fd->flags & FD_MOTOR))
19005b81b6b3SRodney W. Grimes 		{
19015b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
19026182fdbdSPeter Wemm 			fd_turnon(fd);
1903246ed35dSJoerg Wunsch 			return (0); /* will return later */
19045b81b6b3SRodney W. Grimes 		}
19055b81b6b3SRodney W. Grimes 		else	/* at least make sure we are selected */
19065b81b6b3SRodney W. Grimes 		{
19076182fdbdSPeter Wemm 			set_motor(fdc, fd->fdsu, TURNON);
19085b81b6b3SRodney W. Grimes 		}
19095c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
19105c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
19115c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
19125c1a1eaeSBruce Evans 		} else
19135e235068SJordan K. Hubbard 			fdc->state = DOSEEK;
1914246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
1915fb35bd37SJoerg Wunsch 
19165b81b6b3SRodney W. Grimes 	case DOSEEK:
1917fb35bd37SJoerg Wunsch 		blknum = bp->bio_pblkno + fd->skip / fdblk;
1918fb35bd37SJoerg Wunsch 		cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1919fb35bd37SJoerg Wunsch 		if (cylinder == fd->track)
19205b81b6b3SRodney W. Grimes 		{
19215b81b6b3SRodney W. Grimes 			fdc->state = SEEKCOMPLETE;
1922246ed35dSJoerg Wunsch 			return (1); /* will return immediately */
19235b81b6b3SRodney W. Grimes 		}
19246182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK,
1925fb35bd37SJoerg Wunsch 			   fd->fdsu, cylinder * fd->ft->steptrac,
1926dc5df763SJoerg Wunsch 			   0))
1927dc8603e3SJoerg Wunsch 		{
1928dc8603e3SJoerg Wunsch 			/*
1929246ed35dSJoerg Wunsch 			 * Seek command not accepted, looks like
1930dc8603e3SJoerg Wunsch 			 * the FDC went off to the Saints...
1931dc8603e3SJoerg Wunsch 			 */
1932dc8603e3SJoerg Wunsch 			fdc->retry = 6;	/* try a reset */
19336182fdbdSPeter Wemm 			return(retrier(fdc));
1934dc8603e3SJoerg Wunsch 		}
1935dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
19365b81b6b3SRodney W. Grimes 		fdc->state = SEEKWAIT;
19375b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
1938fb35bd37SJoerg Wunsch 
19395b81b6b3SRodney W. Grimes 	case SEEKWAIT:
19405b81b6b3SRodney W. Grimes 		/* allow heads to settle */
19416182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 16);
19425b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
19435b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
1944fb35bd37SJoerg Wunsch 
1945246ed35dSJoerg Wunsch 	case SEEKCOMPLETE : /* seek done, start DMA */
1946fb35bd37SJoerg Wunsch 		blknum = bp->bio_pblkno + fd->skip / fdblk;
1947fb35bd37SJoerg Wunsch 		cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1948fb35bd37SJoerg Wunsch 
1949246ed35dSJoerg Wunsch 		/* Make sure seek really happened. */
19506182fdbdSPeter Wemm 		if(fd->track == FD_NO_TRACK) {
1951fb35bd37SJoerg Wunsch 			int descyl = cylinder * fd->ft->steptrac;
19523a2f7427SDavid Greenman 			do {
19533a2f7427SDavid Greenman 				/*
1954dc5df763SJoerg Wunsch 				 * This might be a "ready changed" interrupt,
1955dc5df763SJoerg Wunsch 				 * which cannot really happen since the
1956dc5df763SJoerg Wunsch 				 * RDY pin is hardwired to + 5 volts.  This
1957dc5df763SJoerg Wunsch 				 * generally indicates a "bouncing" intr
1958dc5df763SJoerg Wunsch 				 * line, so do one of the following:
1959dc5df763SJoerg Wunsch 				 *
1960dc5df763SJoerg Wunsch 				 * When running on an enhanced FDC that is
1961dc5df763SJoerg Wunsch 				 * known to not go stuck after responding
1962dc5df763SJoerg Wunsch 				 * with INVALID, fetch all interrupt states
1963dc5df763SJoerg Wunsch 				 * until seeing either an INVALID or a
1964dc5df763SJoerg Wunsch 				 * real interrupt condition.
1965dc5df763SJoerg Wunsch 				 *
1966dc5df763SJoerg Wunsch 				 * When running on a dumb old NE765, give
1967dc5df763SJoerg Wunsch 				 * up immediately.  The controller will
1968dc5df763SJoerg Wunsch 				 * provide up to four dummy RC interrupt
1969dc5df763SJoerg Wunsch 				 * conditions right after reset (for the
1970dc5df763SJoerg Wunsch 				 * corresponding four drives), so this is
1971dc5df763SJoerg Wunsch 				 * our only chance to get notice that it
1972dc5df763SJoerg Wunsch 				 * was not the FDC that caused the interrupt.
19733a2f7427SDavid Greenman 				 */
1974dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, &cyl)
1975dc5df763SJoerg Wunsch 				    == FD_NOT_VALID)
1976246ed35dSJoerg Wunsch 					return (0); /* will return later */
1977dc5df763SJoerg Wunsch 				if(fdc->fdct == FDC_NE765
1978dc5df763SJoerg Wunsch 				   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1979246ed35dSJoerg Wunsch 					return (0); /* hope for a real intr */
19803a2f7427SDavid Greenman 			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1981dc5df763SJoerg Wunsch 
19826182fdbdSPeter Wemm 			if (0 == descyl) {
1983dc5df763SJoerg Wunsch 				int failed = 0;
19843a2f7427SDavid Greenman 				/*
19853a2f7427SDavid Greenman 				 * seek to cyl 0 requested; make sure we are
19863a2f7427SDavid Greenman 				 * really there
19873a2f7427SDavid Greenman 				 */
1988dc5df763SJoerg Wunsch 				if (fd_sense_drive_status(fdc, &st3))
1989dc5df763SJoerg Wunsch 					failed = 1;
19903a2f7427SDavid Greenman 				if ((st3 & NE7_ST3_T0) == 0) {
19913a2f7427SDavid Greenman 					printf(
19923a2f7427SDavid Greenman 		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
19933a2f7427SDavid Greenman 					       fdu, st3, NE7_ST3BITS);
1994dc5df763SJoerg Wunsch 					failed = 1;
1995dc5df763SJoerg Wunsch 				}
1996dc5df763SJoerg Wunsch 
19976182fdbdSPeter Wemm 				if (failed) {
19983a2f7427SDavid Greenman 					if(fdc->retry < 3)
19993a2f7427SDavid Greenman 						fdc->retry = 3;
20006182fdbdSPeter Wemm 					return (retrier(fdc));
20013a2f7427SDavid Greenman 				}
20023a2f7427SDavid Greenman 			}
2003dc5df763SJoerg Wunsch 
20046182fdbdSPeter Wemm 			if (cyl != descyl) {
20053a2f7427SDavid Greenman 				printf(
20063a2f7427SDavid Greenman 		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
20072d9d0204SRodney W. Grimes 				       fdu, descyl, cyl, st0);
2008e5d7d243SBruce Evans 				if (fdc->retry < 3)
2009e5d7d243SBruce Evans 					fdc->retry = 3;
20106182fdbdSPeter Wemm 				return (retrier(fdc));
20115b81b6b3SRodney W. Grimes 			}
20125b81b6b3SRodney W. Grimes 		}
20135b81b6b3SRodney W. Grimes 
2014fb35bd37SJoerg Wunsch 		fd->track = cylinder;
2015fb35bd37SJoerg Wunsch 		if (format)
2016fb35bd37SJoerg Wunsch 			fd->skip = (char *)&(finfo->fd_formb_cylno(0))
2017fb35bd37SJoerg Wunsch 			    - (char *)finfo;
2018250300ebSJoerg Wunsch 		if (!rdsectid && !(fdc->flags & FDC_NODMA))
20198177437dSPoul-Henning Kamp 			isa_dmastart(idf, bp->bio_data+fd->skip,
20208177437dSPoul-Henning Kamp 				format ? bp->bio_bcount : fdblk, fdc->dmachan);
2021fb35bd37SJoerg Wunsch 		blknum = bp->bio_pblkno + fd->skip / fdblk;
20225b81b6b3SRodney W. Grimes 		sectrac = fd->ft->sectrac;
20235b81b6b3SRodney W. Grimes 		sec = blknum %  (sectrac * fd->ft->heads);
20245b81b6b3SRodney W. Grimes 		head = sec / sectrac;
20255b81b6b3SRodney W. Grimes 		sec = sec % sectrac + 1;
20263a2f7427SDavid Greenman 		fd->hddrv = ((head&1)<<2)+fdu;
20273a2f7427SDavid Greenman 
2028250300ebSJoerg Wunsch 		if(format || !(read || rdsectid))
20293a2f7427SDavid Greenman 		{
20303a2f7427SDavid Greenman 			/* make sure the drive is writable */
2031dc5df763SJoerg Wunsch 			if(fd_sense_drive_status(fdc, &st3) != 0)
2032dc8603e3SJoerg Wunsch 			{
2033dc8603e3SJoerg Wunsch 				/* stuck controller? */
20345f830ea2SJoerg Wunsch 				if (!(fdc->flags & FDC_NODMA))
203556a23089SPoul-Henning Kamp 					isa_dmadone(idf,
20368177437dSPoul-Henning Kamp 						    bp->bio_data + fd->skip,
20378177437dSPoul-Henning Kamp 						    format ? bp->bio_bcount : fdblk,
20385c1a1eaeSBruce Evans 						    fdc->dmachan);
2039dc8603e3SJoerg Wunsch 				fdc->retry = 6;	/* reset the beast */
20406182fdbdSPeter Wemm 				return (retrier(fdc));
2041dc8603e3SJoerg Wunsch 			}
20423a2f7427SDavid Greenman 			if(st3 & NE7_ST3_WP)
20433a2f7427SDavid Greenman 			{
20443a2f7427SDavid Greenman 				/*
20453a2f7427SDavid Greenman 				 * XXX YES! this is ugly.
20463a2f7427SDavid Greenman 				 * in order to force the current operation
20473a2f7427SDavid Greenman 				 * to fail, we will have to fake an FDC
20483a2f7427SDavid Greenman 				 * error - all error handling is done
20493a2f7427SDavid Greenman 				 * by the retrier()
20503a2f7427SDavid Greenman 				 */
20513a2f7427SDavid Greenman 				fdc->status[0] = NE7_ST0_IC_AT;
20523a2f7427SDavid Greenman 				fdc->status[1] = NE7_ST1_NW;
20533a2f7427SDavid Greenman 				fdc->status[2] = 0;
20543a2f7427SDavid Greenman 				fdc->status[3] = fd->track;
20553a2f7427SDavid Greenman 				fdc->status[4] = head;
20563a2f7427SDavid Greenman 				fdc->status[5] = sec;
20573a2f7427SDavid Greenman 				fdc->retry = 8;	/* break out immediately */
20583a2f7427SDavid Greenman 				fdc->state = IOTIMEDOUT; /* not really... */
2059246ed35dSJoerg Wunsch 				return (1); /* will return immediately */
20603a2f7427SDavid Greenman 			}
20613a2f7427SDavid Greenman 		}
20625b81b6b3SRodney W. Grimes 
20636182fdbdSPeter Wemm 		if (format) {
20645f830ea2SJoerg Wunsch 			if (fdc->flags & FDC_NODMA) {
20655f830ea2SJoerg Wunsch 				/*
20665f830ea2SJoerg Wunsch 				 * This seems to be necessary for
20675f830ea2SJoerg Wunsch 				 * whatever obscure reason; if we omit
20685f830ea2SJoerg Wunsch 				 * it, we end up filling the sector ID
20695f830ea2SJoerg Wunsch 				 * fields of the newly formatted track
20705f830ea2SJoerg Wunsch 				 * entirely with garbage, causing
20715f830ea2SJoerg Wunsch 				 * `wrong cylinder' errors all over
20725f830ea2SJoerg Wunsch 				 * the place when trying to read them
20735f830ea2SJoerg Wunsch 				 * back.
20745f830ea2SJoerg Wunsch 				 *
20755f830ea2SJoerg Wunsch 				 * Umpf.
20765f830ea2SJoerg Wunsch 				 */
20778177437dSPoul-Henning Kamp 				SET_BCDR(fdc, 1, bp->bio_bcount, 0);
20785f830ea2SJoerg Wunsch 
20798177437dSPoul-Henning Kamp 				(void)fdcpio(fdc,bp->bio_cmd,
20808177437dSPoul-Henning Kamp 					bp->bio_data+fd->skip,
20818177437dSPoul-Henning Kamp 					bp->bio_bcount);
20825f830ea2SJoerg Wunsch 
20835f830ea2SJoerg Wunsch 			}
2084b39c878eSAndrey A. Chernov 			/* formatting */
20856182fdbdSPeter Wemm 			if(fd_cmd(fdc, 6,  NE7CMD_FORMAT, head << 2 | fdu,
2086dc5df763SJoerg Wunsch 				  finfo->fd_formb_secshift,
2087dc5df763SJoerg Wunsch 				  finfo->fd_formb_nsecs,
2088dc5df763SJoerg Wunsch 				  finfo->fd_formb_gaplen,
20896182fdbdSPeter Wemm 				  finfo->fd_formb_fillbyte, 0)) {
2090dc8603e3SJoerg Wunsch 				/* controller fell over */
20915f830ea2SJoerg Wunsch 				if (!(fdc->flags & FDC_NODMA))
209256a23089SPoul-Henning Kamp 					isa_dmadone(idf,
20938177437dSPoul-Henning Kamp 						    bp->bio_data + fd->skip,
20948177437dSPoul-Henning Kamp 						    format ? bp->bio_bcount : fdblk,
20955c1a1eaeSBruce Evans 						    fdc->dmachan);
2096dc8603e3SJoerg Wunsch 				fdc->retry = 6;
20976182fdbdSPeter Wemm 				return (retrier(fdc));
2098dc8603e3SJoerg Wunsch 			}
2099250300ebSJoerg Wunsch 		} else if (rdsectid) {
2100250300ebSJoerg Wunsch 			if (fd_cmd(fdc, 2, NE7CMD_READID, head << 2 | fdu, 0)) {
2101250300ebSJoerg Wunsch 				/* controller jamming */
2102250300ebSJoerg Wunsch 				fdc->retry = 6;
2103250300ebSJoerg Wunsch 				return (retrier(fdc));
2104250300ebSJoerg Wunsch 			}
21056182fdbdSPeter Wemm 		} else {
2106250300ebSJoerg Wunsch 			/* read or write operation */
21073b178206SWarner Losh 			if (fdc->flags & FDC_NODMA) {
210869acd21dSWarner Losh 				/*
2109246ed35dSJoerg Wunsch 				 * This seems to be necessary even when
2110246ed35dSJoerg Wunsch 				 * reading data.
211169acd21dSWarner Losh 				 */
21123b178206SWarner Losh 				SET_BCDR(fdc, 1, fdblk, 0);
211369acd21dSWarner Losh 
211469acd21dSWarner Losh 				/*
2115246ed35dSJoerg Wunsch 				 * Perform the write pseudo-DMA before
2116246ed35dSJoerg Wunsch 				 * the WRITE command is sent.
211769acd21dSWarner Losh 				 */
211869acd21dSWarner Losh 				if (!read)
21198177437dSPoul-Henning Kamp 					(void)fdcpio(fdc,bp->bio_cmd,
21208177437dSPoul-Henning Kamp 					    bp->bio_data+fd->skip,
212169acd21dSWarner Losh 					    fdblk);
212269acd21dSWarner Losh 			}
21236182fdbdSPeter Wemm 			if (fd_cmd(fdc, 9,
2124dc5df763SJoerg Wunsch 				   (read ? NE7CMD_READ : NE7CMD_WRITE),
2125dc5df763SJoerg Wunsch 				   head << 2 | fdu,  /* head & unit */
2126dc5df763SJoerg Wunsch 				   fd->track,        /* track */
2127dc5df763SJoerg Wunsch 				   head,
2128dc5df763SJoerg Wunsch 				   sec,              /* sector + 1 */
2129dc5df763SJoerg Wunsch 				   fd->ft->secsize,  /* sector size */
2130dc5df763SJoerg Wunsch 				   sectrac,          /* sectors/track */
2131dc5df763SJoerg Wunsch 				   fd->ft->gap,      /* gap size */
2132dc5df763SJoerg Wunsch 				   fd->ft->datalen,  /* data length */
21336182fdbdSPeter Wemm 				   0)) {
2134dc8603e3SJoerg Wunsch 				/* the beast is sleeping again */
21355f830ea2SJoerg Wunsch 				if (!(fdc->flags & FDC_NODMA))
213656a23089SPoul-Henning Kamp 					isa_dmadone(idf,
21378177437dSPoul-Henning Kamp 						    bp->bio_data + fd->skip,
21388177437dSPoul-Henning Kamp 						    format ? bp->bio_bcount : fdblk,
21395c1a1eaeSBruce Evans 						    fdc->dmachan);
2140dc8603e3SJoerg Wunsch 				fdc->retry = 6;
21416182fdbdSPeter Wemm 				return (retrier(fdc));
21425b81b6b3SRodney W. Grimes 			}
2143b39c878eSAndrey A. Chernov 		}
2144250300ebSJoerg Wunsch 		if (!rdsectid && (fdc->flags & FDC_NODMA))
214569acd21dSWarner Losh 			/*
2146246ed35dSJoerg Wunsch 			 * If this is a read, then simply await interrupt
2147246ed35dSJoerg Wunsch 			 * before performing PIO.
214869acd21dSWarner Losh 			 */
21498177437dSPoul-Henning Kamp 			if (read && !fdcpio(fdc,bp->bio_cmd,
21508177437dSPoul-Henning Kamp 			    bp->bio_data+fd->skip,fdblk)) {
21513b178206SWarner Losh 				fd->tohandle = timeout(fd_iotimeout, fdc, hz);
215269acd21dSWarner Losh 				return(0);      /* will return later */
215364860614SJoerg Wunsch 			}
215469acd21dSWarner Losh 
215569acd21dSWarner Losh 		/*
2156246ed35dSJoerg Wunsch 		 * Write (or format) operation will fall through and
2157246ed35dSJoerg Wunsch 		 * await completion interrupt.
215869acd21dSWarner Losh 		 */
21595b81b6b3SRodney W. Grimes 		fdc->state = IOCOMPLETE;
21606182fdbdSPeter Wemm 		fd->tohandle = timeout(fd_iotimeout, fdc, hz);
21615b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
2162fb35bd37SJoerg Wunsch 
216369acd21dSWarner Losh 	case PIOREAD:
216469acd21dSWarner Losh 		/*
2165246ed35dSJoerg Wunsch 		 * Actually perform the PIO read.  The IOCOMPLETE case
216669acd21dSWarner Losh 		 * removes the timeout for us.
216769acd21dSWarner Losh 		 */
21688177437dSPoul-Henning Kamp 		(void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk);
216969acd21dSWarner Losh 		fdc->state = IOCOMPLETE;
217069acd21dSWarner Losh 		/* FALLTHROUGH */
2171246ed35dSJoerg Wunsch 	case IOCOMPLETE: /* IO done, post-analyze */
21726182fdbdSPeter Wemm 		untimeout(fd_iotimeout, fdc, fd->tohandle);
2173dc5df763SJoerg Wunsch 
217464860614SJoerg Wunsch 		if (fd_read_status(fdc)) {
2175250300ebSJoerg Wunsch 			if (!rdsectid && !(fdc->flags & FDC_NODMA))
21768177437dSPoul-Henning Kamp 				isa_dmadone(idf, bp->bio_data + fd->skip,
21778177437dSPoul-Henning Kamp 					    format ? bp->bio_bcount : fdblk,
21785c1a1eaeSBruce Evans 					    fdc->dmachan);
2179dc5df763SJoerg Wunsch 			if (fdc->retry < 6)
2180dc5df763SJoerg Wunsch 				fdc->retry = 6;	/* force a reset */
21816182fdbdSPeter Wemm 			return (retrier(fdc));
21825b81b6b3SRodney W. Grimes   		}
2183dc5df763SJoerg Wunsch 
21843a2f7427SDavid Greenman 		fdc->state = IOTIMEDOUT;
2185dc5df763SJoerg Wunsch 
21863a2f7427SDavid Greenman 		/* FALLTHROUGH */
21873a2f7427SDavid Greenman 	case IOTIMEDOUT:
2188250300ebSJoerg Wunsch 		if (!rdsectid && !(fdc->flags & FDC_NODMA))
21898177437dSPoul-Henning Kamp 			isa_dmadone(idf, bp->bio_data + fd->skip,
21908177437dSPoul-Henning Kamp 				format ? bp->bio_bcount : fdblk, fdc->dmachan);
21916182fdbdSPeter Wemm 		if (fdc->status[0] & NE7_ST0_IC) {
21923a2f7427SDavid Greenman                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
21933a2f7427SDavid Greenman 			    && fdc->status[1] & NE7_ST1_OR) {
2194b39c878eSAndrey A. Chernov                                 /*
21953fef646eSJoerg Wunsch 				 * DMA overrun. Someone hogged the bus and
21963fef646eSJoerg Wunsch 				 * didn't release it in time for the next
21973fef646eSJoerg Wunsch 				 * FDC transfer.
21983fef646eSJoerg Wunsch 				 *
21993fef646eSJoerg Wunsch 				 * We normally restart this without bumping
22003fef646eSJoerg Wunsch 				 * the retry counter.  However, in case
22013fef646eSJoerg Wunsch 				 * something is seriously messed up (like
22023fef646eSJoerg Wunsch 				 * broken hardware), we rather limit the
22033fef646eSJoerg Wunsch 				 * number of retries so the IO operation
22043fef646eSJoerg Wunsch 				 * doesn't block indefinately.
2205b39c878eSAndrey A. Chernov 				 */
22063fef646eSJoerg Wunsch 				if (fdc->dma_overruns++ < FDC_DMAOV_MAX) {
2207b39c878eSAndrey A. Chernov 					fdc->state = SEEKCOMPLETE;
2208246ed35dSJoerg Wunsch 					return (1);/* will return immediately */
22093fef646eSJoerg Wunsch 				} /* else fall through */
2210b39c878eSAndrey A. Chernov                         }
22113fef646eSJoerg Wunsch 			if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
22123a2f7427SDavid Greenman 				&& fdc->retry < 6)
22133a2f7427SDavid Greenman 				fdc->retry = 6;	/* force a reset */
22143a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
22153a2f7427SDavid Greenman 				&& fdc->status[2] & NE7_ST2_WC
22163a2f7427SDavid Greenman 				&& fdc->retry < 3)
22173a2f7427SDavid Greenman 				fdc->retry = 3;	/* force recalibrate */
22186182fdbdSPeter Wemm 			return (retrier(fdc));
22195b81b6b3SRodney W. Grimes 		}
22205b81b6b3SRodney W. Grimes 		/* All OK */
2221250300ebSJoerg Wunsch 		if (rdsectid) {
2222250300ebSJoerg Wunsch 			/* copy out ID field contents */
222364860614SJoerg Wunsch 			idp = (struct fdc_readid *)bp->bio_data;
2224250300ebSJoerg Wunsch 			idp->cyl = fdc->status[3];
2225250300ebSJoerg Wunsch 			idp->head = fdc->status[4];
2226250300ebSJoerg Wunsch 			idp->sec = fdc->status[5];
2227250300ebSJoerg Wunsch 			idp->secshift = fdc->status[6];
2228250300ebSJoerg Wunsch 		}
22293fef646eSJoerg Wunsch 		/* Operation successful, retry DMA overruns again next time. */
22303fef646eSJoerg Wunsch 		fdc->dma_overruns = 0;
22313a2f7427SDavid Greenman 		fd->skip += fdblk;
2232fb35bd37SJoerg Wunsch 		if (!rdsectid && !format && fd->skip < bp->bio_bcount) {
22335b81b6b3SRodney W. Grimes 			/* set up next transfer */
22345b81b6b3SRodney W. Grimes 			fdc->state = DOSEEK;
22356182fdbdSPeter Wemm 		} else {
22365b81b6b3SRodney W. Grimes 			/* ALL DONE */
22375b81b6b3SRodney W. Grimes 			fd->skip = 0;
2238fb35bd37SJoerg Wunsch 			bp->bio_resid = 0;
2239e93e63cbSBruce Evans 			fdc->bp = NULL;
22405f830ea2SJoerg Wunsch 			device_unbusy(fd->dev);
2241a468031cSPoul-Henning Kamp 			biofinish(bp, &fd->device_stats, 0);
22425b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
22435b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
22445b81b6b3SRodney W. Grimes 			fdc->state = FINDWORK;
22455b81b6b3SRodney W. Grimes 		}
2246246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
2247fb35bd37SJoerg Wunsch 
22485b81b6b3SRodney W. Grimes 	case RESETCTLR:
22493a2f7427SDavid Greenman 		fdc_reset(fdc);
22505b81b6b3SRodney W. Grimes 		fdc->retry++;
22515c1a1eaeSBruce Evans 		fdc->state = RESETCOMPLETE;
2252246ed35dSJoerg Wunsch 		return (0);	/* will return later */
2253fb35bd37SJoerg Wunsch 
22545c1a1eaeSBruce Evans 	case RESETCOMPLETE:
22555c1a1eaeSBruce Evans 		/*
22565c1a1eaeSBruce Evans 		 * Discard all the results from the reset so that they
22575c1a1eaeSBruce Evans 		 * can't cause an unexpected interrupt later.
22585c1a1eaeSBruce Evans 		 */
22590e317d05SJoerg Wunsch 		for (i = 0; i < 4; i++)
22600e317d05SJoerg Wunsch 			(void)fd_sense_int(fdc, &st0, &cyl);
22615c1a1eaeSBruce Evans 		fdc->state = STARTRECAL;
2262fb35bd37SJoerg Wunsch 		/* FALLTHROUGH */
22635c1a1eaeSBruce Evans 	case STARTRECAL:
22646182fdbdSPeter Wemm 		if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) {
2265dc8603e3SJoerg Wunsch 			/* arrgl */
2266dc8603e3SJoerg Wunsch 			fdc->retry = 6;
22676182fdbdSPeter Wemm 			return (retrier(fdc));
2268dc8603e3SJoerg Wunsch 		}
22695b81b6b3SRodney W. Grimes 		fdc->state = RECALWAIT;
22705b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
2271fb35bd37SJoerg Wunsch 
22725b81b6b3SRodney W. Grimes 	case RECALWAIT:
22735b81b6b3SRodney W. Grimes 		/* allow heads to settle */
22746182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 8);
22755b81b6b3SRodney W. Grimes 		fdc->state = RECALCOMPLETE;
22765b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
2277fb35bd37SJoerg Wunsch 
22785b81b6b3SRodney W. Grimes 	case RECALCOMPLETE:
22793a2f7427SDavid Greenman 		do {
22803a2f7427SDavid Greenman 			/*
2281dc5df763SJoerg Wunsch 			 * See SEEKCOMPLETE for a comment on this:
22823a2f7427SDavid Greenman 			 */
2283dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
2284246ed35dSJoerg Wunsch 				return (0); /* will return later */
2285dc5df763SJoerg Wunsch 			if(fdc->fdct == FDC_NE765
2286dc5df763SJoerg Wunsch 			   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
2287246ed35dSJoerg Wunsch 				return (0); /* hope for a real intr */
22883a2f7427SDavid Greenman 		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
22893a2f7427SDavid Greenman 		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
22905b81b6b3SRodney W. Grimes 		{
2291dc8603e3SJoerg Wunsch 			if(fdc->retry > 3)
2292dc8603e3SJoerg Wunsch 				/*
2293246ed35dSJoerg Wunsch 				 * A recalibrate from beyond cylinder 77
2294dc8603e3SJoerg Wunsch 				 * will "fail" due to the FDC limitations;
2295dc8603e3SJoerg Wunsch 				 * since people used to complain much about
2296dc8603e3SJoerg Wunsch 				 * the failure message, try not logging
2297dc8603e3SJoerg Wunsch 				 * this one if it seems to be the first
2298246ed35dSJoerg Wunsch 				 * time in a line.
2299dc8603e3SJoerg Wunsch 				 */
2300dc8603e3SJoerg Wunsch 				printf("fd%d: recal failed ST0 %b cyl %d\n",
2301dc8603e3SJoerg Wunsch 				       fdu, st0, NE7_ST0BITS, cyl);
23023a2f7427SDavid Greenman 			if(fdc->retry < 3) fdc->retry = 3;
23036182fdbdSPeter Wemm 			return (retrier(fdc));
23045b81b6b3SRodney W. Grimes 		}
23055b81b6b3SRodney W. Grimes 		fd->track = 0;
23065b81b6b3SRodney W. Grimes 		/* Seek (probably) necessary */
23075b81b6b3SRodney W. Grimes 		fdc->state = DOSEEK;
2308246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
2309fb35bd37SJoerg Wunsch 
23105b81b6b3SRodney W. Grimes 	case MOTORWAIT:
23115b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
23125b81b6b3SRodney W. Grimes 		{
23135b81b6b3SRodney W. Grimes 			return (0); /* time's not up yet */
23145b81b6b3SRodney W. Grimes 		}
23155c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
23165c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
23175c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
2318fb35bd37SJoerg Wunsch 		} else
2319fb35bd37SJoerg Wunsch 			fdc->state = DOSEEK;
2320246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
2321fb35bd37SJoerg Wunsch 
23225b81b6b3SRodney W. Grimes 	default:
2323b6e5f28eSPeter Wemm 		device_printf(fdc->fdc_dev, "unexpected FD int->");
232464860614SJoerg Wunsch 		if (fd_read_status(fdc) == 0)
2325a838d83dSBruce Evans 			printf("FDC status :%x %x %x %x %x %x %x   ",
23265b81b6b3SRodney W. Grimes 			       fdc->status[0],
23275b81b6b3SRodney W. Grimes 			       fdc->status[1],
23285b81b6b3SRodney W. Grimes 			       fdc->status[2],
23295b81b6b3SRodney W. Grimes 			       fdc->status[3],
23305b81b6b3SRodney W. Grimes 			       fdc->status[4],
23315b81b6b3SRodney W. Grimes 			       fdc->status[5],
23325b81b6b3SRodney W. Grimes 			       fdc->status[6] );
23333a2f7427SDavid Greenman 		else
2334dac0f2dbSJoerg Wunsch 			printf("No status available   ");
2335dac0f2dbSJoerg Wunsch 		if (fd_sense_int(fdc, &st0, &cyl) != 0)
2336dac0f2dbSJoerg Wunsch 		{
2337dac0f2dbSJoerg Wunsch 			printf("[controller is dead now]\n");
2338246ed35dSJoerg Wunsch 			return (0); /* will return later */
23395b81b6b3SRodney W. Grimes 		}
2340dac0f2dbSJoerg Wunsch 		printf("ST0 = %x, PCN = %x\n", st0, cyl);
2341246ed35dSJoerg Wunsch 		return (0);	/* will return later */
2342dac0f2dbSJoerg Wunsch 	}
2343246ed35dSJoerg Wunsch 	/* noone should ever get here */
23445b81b6b3SRodney W. Grimes }
23455b81b6b3SRodney W. Grimes 
2346aaf08d94SGarrett Wollman static int
23476182fdbdSPeter Wemm retrier(struct fdc_data *fdc)
23485b81b6b3SRodney W. Grimes {
23498177437dSPoul-Henning Kamp 	struct bio *bp;
23506182fdbdSPeter Wemm 	struct fd_data *fd;
23516182fdbdSPeter Wemm 	int fdu;
23525b81b6b3SRodney W. Grimes 
2353e93e63cbSBruce Evans 	bp = fdc->bp;
23545b81b6b3SRodney W. Grimes 
23556182fdbdSPeter Wemm 	/* XXX shouldn't this be cached somewhere?  */
23568177437dSPoul-Henning Kamp 	fdu = FDUNIT(minor(bp->bio_dev));
23576182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
23586182fdbdSPeter Wemm 	if (fd->options & FDOPT_NORETRY)
23593a2f7427SDavid Greenman 		goto fail;
23606182fdbdSPeter Wemm 
23616182fdbdSPeter Wemm 	switch (fdc->retry) {
23625b81b6b3SRodney W. Grimes 	case 0: case 1: case 2:
23635b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
23645b81b6b3SRodney W. Grimes 		break;
23655b81b6b3SRodney W. Grimes 	case 3: case 4: case 5:
23665b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
23675b81b6b3SRodney W. Grimes 		break;
23685b81b6b3SRodney W. Grimes 	case 6:
23695b81b6b3SRodney W. Grimes 		fdc->state = RESETCTLR;
23705b81b6b3SRodney W. Grimes 		break;
23715b81b6b3SRodney W. Grimes 	case 7:
23725b81b6b3SRodney W. Grimes 		break;
23735b81b6b3SRodney W. Grimes 	default:
23743a2f7427SDavid Greenman 	fail:
2375fb35bd37SJoerg Wunsch 		if ((fd->options & FDOPT_NOERRLOG) == 0) {
2376fb35bd37SJoerg Wunsch 			diskerr(bp, "hard error", fdc->fd->skip / DEV_BSIZE,
23773a2f7427SDavid Greenman 				(struct disklabel *)NULL);
2378fb35bd37SJoerg Wunsch 			if (fdc->flags & FDC_STAT_VALID) {
2379dc5df763SJoerg Wunsch 				printf(
2380a838d83dSBruce Evans 				" (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n",
2381dc5df763SJoerg Wunsch 				       fdc->status[0], NE7_ST0BITS,
2382dc5df763SJoerg Wunsch 				       fdc->status[1], NE7_ST1BITS,
2383dc5df763SJoerg Wunsch 				       fdc->status[2], NE7_ST2BITS,
2384dc5df763SJoerg Wunsch 				       fdc->status[3], fdc->status[4],
2385dc5df763SJoerg Wunsch 				       fdc->status[5]);
2386dc5df763SJoerg Wunsch 			}
2387dc5df763SJoerg Wunsch 			else
2388dc5df763SJoerg Wunsch 				printf(" (No status)\n");
23895b81b6b3SRodney W. Grimes 		}
23902995d110SJoerg Wunsch 		if ((fd->options & FDOPT_NOERROR) == 0) {
23918177437dSPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
23928177437dSPoul-Henning Kamp 			bp->bio_error = EIO;
2393fb35bd37SJoerg Wunsch 			bp->bio_resid = bp->bio_bcount - fdc->fd->skip;
2394fb35bd37SJoerg Wunsch 		} else
2395fb35bd37SJoerg Wunsch 			bp->bio_resid = 0;
2396e93e63cbSBruce Evans 		fdc->bp = NULL;
23975b81b6b3SRodney W. Grimes 		fdc->fd->skip = 0;
23985f830ea2SJoerg Wunsch 		device_unbusy(fd->dev);
2399a468031cSPoul-Henning Kamp 		biofinish(bp, &fdc->fd->device_stats, 0);
240092ed385aSRodney W. Grimes 		fdc->state = FINDWORK;
24015c1a1eaeSBruce Evans 		fdc->flags |= FDC_NEEDS_RESET;
24025b81b6b3SRodney W. Grimes 		fdc->fd = (fd_p) 0;
24035b81b6b3SRodney W. Grimes 		fdc->fdu = -1;
240492ed385aSRodney W. Grimes 		return (1);
24055b81b6b3SRodney W. Grimes 	}
24065b81b6b3SRodney W. Grimes 	fdc->retry++;
24075b81b6b3SRodney W. Grimes 	return (1);
24085b81b6b3SRodney W. Grimes }
24095b81b6b3SRodney W. Grimes 
24101fdb6e6cSPoul-Henning Kamp static void
24111fdb6e6cSPoul-Henning Kamp fdbiodone(struct bio *bp)
24121fdb6e6cSPoul-Henning Kamp {
24131fdb6e6cSPoul-Henning Kamp 	wakeup(bp);
24141fdb6e6cSPoul-Henning Kamp }
24151fdb6e6cSPoul-Henning Kamp 
2416b39c878eSAndrey A. Chernov static int
2417f664aeeeSJoerg Wunsch fdmisccmd(dev_t dev, u_int cmd, void *data)
2418b39c878eSAndrey A. Chernov {
2419b39c878eSAndrey A. Chernov  	fdu_t fdu;
2420b39c878eSAndrey A. Chernov  	fd_p fd;
24211fdb6e6cSPoul-Henning Kamp 	struct bio *bp;
2422f664aeeeSJoerg Wunsch 	struct fd_formb *finfo;
2423f664aeeeSJoerg Wunsch 	struct fdc_readid *idfield;
24243a2f7427SDavid Greenman 	size_t fdblk;
2425b39c878eSAndrey A. Chernov 
2426b39c878eSAndrey A. Chernov  	fdu = FDUNIT(minor(dev));
24276182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
24283a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2429f664aeeeSJoerg Wunsch 	finfo = (struct fd_formb *)data;
2430f664aeeeSJoerg Wunsch 	idfield = (struct fdc_readid *)data;
2431b39c878eSAndrey A. Chernov 
2432fb35bd37SJoerg Wunsch 	bp = malloc(sizeof(struct bio), M_TEMP, M_ZERO);
2433b39c878eSAndrey A. Chernov 
2434b39c878eSAndrey A. Chernov 	/*
2435f664aeeeSJoerg Wunsch 	 * Set up a bio request for fdstrategy().  bio_blkno is faked
2436f664aeeeSJoerg Wunsch 	 * so that fdstrategy() will seek to the the requested
2437f664aeeeSJoerg Wunsch 	 * cylinder, and use the desired head.  Since we are not
2438f664aeeeSJoerg Wunsch 	 * interested in bioqdisksort() munging with our faked bio
2439f664aeeeSJoerg Wunsch 	 * request, we mark it as being an ordered request.
2440b39c878eSAndrey A. Chernov 	 */
2441f664aeeeSJoerg Wunsch 	bp->bio_cmd = cmd;
2442f664aeeeSJoerg Wunsch 	if (cmd == BIO_FORMAT) {
2443f664aeeeSJoerg Wunsch 		bp->bio_blkno =
2444f664aeeeSJoerg Wunsch 		    (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) +
2445f664aeeeSJoerg Wunsch 		     finfo->head * fd->ft->sectrac) *
2446f664aeeeSJoerg Wunsch 		    fdblk / DEV_BSIZE;
2447f664aeeeSJoerg Wunsch 		bp->bio_bcount = sizeof(struct fd_idfield_data) *
2448f664aeeeSJoerg Wunsch 		    finfo->fd_formb_nsecs;
2449f664aeeeSJoerg Wunsch 	} else if (cmd == BIO_RDSECTID) {
2450f664aeeeSJoerg Wunsch 		bp->bio_blkno =
2451f664aeeeSJoerg Wunsch 		    (idfield->cyl * (fd->ft->sectrac * fd->ft->heads) +
2452f664aeeeSJoerg Wunsch 		     idfield->head * fd->ft->sectrac) *
2453f664aeeeSJoerg Wunsch 		    fdblk / DEV_BSIZE;
2454250300ebSJoerg Wunsch 		bp->bio_bcount = sizeof(struct fdc_readid);
2455f664aeeeSJoerg Wunsch 	} else
2456f664aeeeSJoerg Wunsch 		panic("wrong cmd in fdmisccmd()");
2457f664aeeeSJoerg Wunsch 	bp->bio_data = data;
2458250300ebSJoerg Wunsch 	bp->bio_dev = dev;
2459250300ebSJoerg Wunsch 	bp->bio_done = fdbiodone;
2460f664aeeeSJoerg Wunsch 	bp->bio_flags = BIO_ORDERED;
2461250300ebSJoerg Wunsch 
2462f5f7ba03SJordan K. Hubbard 	/*
2463fb35bd37SJoerg Wunsch 	 * Now run the command.  The wait loop is a version of bufwait()
2464fb35bd37SJoerg Wunsch 	 * adapted for struct bio instead of struct buf and specialized
2465fb35bd37SJoerg Wunsch 	 * for the current context.
2466f5f7ba03SJordan K. Hubbard 	 */
2467f664aeeeSJoerg Wunsch 	fdstrategy(bp);
2468f664aeeeSJoerg Wunsch 	while ((bp->bio_flags & BIO_DONE) == 0)
2469f664aeeeSJoerg Wunsch 		tsleep(bp, PRIBIO, "fdcmd", 0);
2470f664aeeeSJoerg Wunsch 
2471f664aeeeSJoerg Wunsch 	free(bp, M_TEMP);
2472f664aeeeSJoerg Wunsch 	return (bp->bio_flags & BIO_ERROR ? bp->bio_error : 0);
2473f664aeeeSJoerg Wunsch }
24745b81b6b3SRodney W. Grimes 
24753e425b96SJulian Elischer static int
2476b40ce416SJulian Elischer fdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
2477f5f7ba03SJordan K. Hubbard {
2478f664aeeeSJoerg Wunsch  	fdu_t fdu;
2479f664aeeeSJoerg Wunsch  	fd_p fd;
2480f5f7ba03SJordan K. Hubbard 	struct fd_type *fdt;
2481fb35bd37SJoerg Wunsch 	struct disklabel *lp;
24822995d110SJoerg Wunsch 	struct fdc_status *fsp;
2483250300ebSJoerg Wunsch 	struct fdc_readid *rid;
2484f664aeeeSJoerg Wunsch 	size_t fdblk;
2485fb35bd37SJoerg Wunsch 	int error;
2486f5f7ba03SJordan K. Hubbard 
2487f664aeeeSJoerg Wunsch  	fdu = FDUNIT(minor(dev));
2488f664aeeeSJoerg Wunsch  	fd = devclass_get_softc(fd_devclass, fdu);
2489fb35bd37SJoerg Wunsch 
2490fb35bd37SJoerg Wunsch 
24913a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2492fb35bd37SJoerg Wunsch 	error = 0;
2493f5f7ba03SJordan K. Hubbard 
24946182fdbdSPeter Wemm 	switch (cmd) {
2495f5f7ba03SJordan K. Hubbard 	case DIOCGDINFO:
2496fb35bd37SJoerg Wunsch 		lp = malloc(sizeof(*lp), M_TEMP, M_ZERO);
2497fb35bd37SJoerg Wunsch 		lp->d_secsize = fdblk;
24986182fdbdSPeter Wemm 		fdt = fd->ft;
2499fb35bd37SJoerg Wunsch 		lp->d_secpercyl = fdt->size / fdt->tracks;
2500fb35bd37SJoerg Wunsch 		lp->d_type = DTYPE_FLOPPY;
2501fb35bd37SJoerg Wunsch 		if (readdisklabel(dkmodpart(dev, RAW_PART), lp) != NULL)
2502f5f7ba03SJordan K. Hubbard 			error = EINVAL;
2503fb35bd37SJoerg Wunsch 		else
2504fb35bd37SJoerg Wunsch 			*(struct disklabel *)addr = *lp;
2505fb35bd37SJoerg Wunsch 		free(lp, M_TEMP);
2506f5f7ba03SJordan K. Hubbard 		break;
2507f5f7ba03SJordan K. Hubbard 
2508f5f7ba03SJordan K. Hubbard 	case DIOCSDINFO:
2509f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2510fb35bd37SJoerg Wunsch 			return (EBADF);
2511fb35bd37SJoerg Wunsch 		/*
2512fb35bd37SJoerg Wunsch 		 * XXX perhaps should call setdisklabel() to do error checking
2513fb35bd37SJoerg Wunsch 		 * although there is nowhere to "set" the result.  Perhaps
2514fb35bd37SJoerg Wunsch 		 * should always just fail.
2515fb35bd37SJoerg Wunsch 		 */
2516f5f7ba03SJordan K. Hubbard 		break;
2517f5f7ba03SJordan K. Hubbard 
2518f5f7ba03SJordan K. Hubbard 	case DIOCWLABEL:
2519f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2520fb35bd37SJoerg Wunsch 			return (EBADF);
2521f5f7ba03SJordan K. Hubbard 		break;
2522f5f7ba03SJordan K. Hubbard 
2523f5f7ba03SJordan K. Hubbard 	case DIOCWDINFO:
2524fb35bd37SJoerg Wunsch 		if ((flag & FWRITE) == 0)
2525fb35bd37SJoerg Wunsch 			return (EBADF);
2526fb35bd37SJoerg Wunsch 		lp = malloc(DEV_BSIZE, M_TEMP, M_ZERO);
2527fb35bd37SJoerg Wunsch 		error = setdisklabel(lp, (struct disklabel *)addr, (u_long)0);
2528fb35bd37SJoerg Wunsch 		if (error != 0)
2529fb35bd37SJoerg Wunsch 			error = writedisklabel(dev, lp);
2530fb35bd37SJoerg Wunsch 		free(lp, M_TEMP);
2531b39c878eSAndrey A. Chernov 		break;
2532f664aeeeSJoerg Wunsch 
2533b39c878eSAndrey A. Chernov 	case FD_FORM:
2534b39c878eSAndrey A. Chernov 		if ((flag & FWRITE) == 0)
2535fb35bd37SJoerg Wunsch 			return (EBADF);	/* must be opened for writing */
2536fb35bd37SJoerg Wunsch 		if (((struct fd_formb *)addr)->format_version !=
2537b39c878eSAndrey A. Chernov 		    FD_FORMAT_VERSION)
2538fb35bd37SJoerg Wunsch 			return (EINVAL); /* wrong version of formatting prog */
2539f664aeeeSJoerg Wunsch 		error = fdmisccmd(dev, BIO_FORMAT, addr);
2540b39c878eSAndrey A. Chernov 		break;
2541b39c878eSAndrey A. Chernov 
2542b39c878eSAndrey A. Chernov 	case FD_GTYPE:                  /* get drive type */
25433e425b96SJulian Elischer 		*(struct fd_type *)addr = *fd->ft;
2544f5f7ba03SJordan K. Hubbard 		break;
2545f5f7ba03SJordan K. Hubbard 
25463a2f7427SDavid Greenman 	case FD_STYPE:                  /* set drive type */
25473a2f7427SDavid Greenman 		/* this is considered harmful; only allow for superuser */
2548b40ce416SJulian Elischer 		if (suser_td(td) != 0)
2549fb35bd37SJoerg Wunsch 			return (EPERM);
25503e425b96SJulian Elischer 		*fd->ft = *(struct fd_type *)addr;
25513a2f7427SDavid Greenman 		break;
25523a2f7427SDavid Greenman 
25533a2f7427SDavid Greenman 	case FD_GOPTS:			/* get drive options */
25543e425b96SJulian Elischer 		*(int *)addr = fd->options;
25553a2f7427SDavid Greenman 		break;
25563a2f7427SDavid Greenman 
25573a2f7427SDavid Greenman 	case FD_SOPTS:			/* set drive options */
25583e425b96SJulian Elischer 		fd->options = *(int *)addr;
25593a2f7427SDavid Greenman 		break;
25603a2f7427SDavid Greenman 
2561f664aeeeSJoerg Wunsch #ifdef FDC_DEBUG
2562f664aeeeSJoerg Wunsch 	case FD_DEBUG:
25630e17a5bcSJoerg Wunsch 		if ((fd_debug != 0) != (*(int *)addr != 0)) {
25640e17a5bcSJoerg Wunsch 			fd_debug = (*(int *)addr != 0);
25650e17a5bcSJoerg Wunsch 			printf("fd%d: debugging turned %s\n",
25660e17a5bcSJoerg Wunsch 			    fd->fdu, fd_debug ? "on" : "off");
25670e17a5bcSJoerg Wunsch 		}
2568f664aeeeSJoerg Wunsch 		break;
2569f664aeeeSJoerg Wunsch #endif
2570f664aeeeSJoerg Wunsch 
25712995d110SJoerg Wunsch 	case FD_CLRERR:
2572b40ce416SJulian Elischer 		if (suser_td(td) != 0)
2573fb35bd37SJoerg Wunsch 			return (EPERM);
25742995d110SJoerg Wunsch 		fd->fdc->fdc_errs = 0;
25752995d110SJoerg Wunsch 		break;
25762995d110SJoerg Wunsch 
25772995d110SJoerg Wunsch 	case FD_GSTAT:
25782995d110SJoerg Wunsch 		fsp = (struct fdc_status *)addr;
25792995d110SJoerg Wunsch 		if ((fd->fdc->flags & FDC_STAT_VALID) == 0)
2580fb35bd37SJoerg Wunsch 			return (EINVAL);
25812995d110SJoerg Wunsch 		memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int));
25822995d110SJoerg Wunsch 		break;
25832995d110SJoerg Wunsch 
2584250300ebSJoerg Wunsch 	case FD_READID:
2585250300ebSJoerg Wunsch 		rid = (struct fdc_readid *)addr;
2586250300ebSJoerg Wunsch 		if (rid->cyl > MAX_CYLINDER || rid->head > MAX_HEAD)
2587fb35bd37SJoerg Wunsch 			return (EINVAL);
2588f664aeeeSJoerg Wunsch 		error = fdmisccmd(dev, BIO_RDSECTID, addr);
2589250300ebSJoerg Wunsch 		break;
2590250300ebSJoerg Wunsch 
2591f5f7ba03SJordan K. Hubbard 	default:
25923a2f7427SDavid Greenman 		error = ENOTTY;
2593f5f7ba03SJordan K. Hubbard 		break;
2594f5f7ba03SJordan K. Hubbard 	}
2595f5f7ba03SJordan K. Hubbard 	return (error);
2596f5f7ba03SJordan K. Hubbard }
2597