xref: /freebsd/sys/dev/fdc/fdc.c (revision 246ed35d55139bbc97f5fb1d2132fb62031be1ad)
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;
148246ed35dSJoerg Wunsch 	int	fdout;		/* mirror of the w/o digital output reg */
149246ed35dSJoerg Wunsch 	u_int	status[7];	/* copy of the registers */
150246ed35dSJoerg Wunsch 	enum	fdc_type fdct;	/* chip version of FDC */
151246ed35dSJoerg Wunsch 	int	fdc_errs;	/* number of logged errors */
152246ed35dSJoerg Wunsch 	int	dma_overruns;	/* number of DMA overruns */
153246ed35dSJoerg Wunsch 	struct	bio_queue_head head;
154246ed35dSJoerg Wunsch 	struct	bio *bp;	/* active buffer */
155246ed35dSJoerg Wunsch 	struct	resource *res_ioport, *res_ctl, *res_irq, *res_drq;
156246ed35dSJoerg Wunsch 	int	rid_ioport, rid_ctl, rid_irq, rid_drq;
157246ed35dSJoerg Wunsch 	int	port_off;
158246ed35dSJoerg Wunsch 	bus_space_tag_t portt;
159246ed35dSJoerg Wunsch 	bus_space_handle_t porth;
160246ed35dSJoerg Wunsch 	bus_space_tag_t ctlt;
161246ed35dSJoerg Wunsch 	bus_space_handle_t ctlh;
162246ed35dSJoerg Wunsch 	void	*fdc_intr;
163246ed35dSJoerg Wunsch 	struct	device *fdc_dev;
164246ed35dSJoerg Wunsch 	void	(*fdctl_wr)(struct fdc_data *fdc, u_int8_t v);
165246ed35dSJoerg Wunsch };
166246ed35dSJoerg Wunsch 
167246ed35dSJoerg Wunsch typedef int	fdu_t;
168246ed35dSJoerg Wunsch typedef int	fdcu_t;
169246ed35dSJoerg Wunsch typedef int	fdsu_t;
170246ed35dSJoerg Wunsch typedef	struct fd_data *fd_p;
171246ed35dSJoerg Wunsch typedef struct fdc_data *fdc_p;
172246ed35dSJoerg Wunsch typedef enum fdc_type fdc_t;
173246ed35dSJoerg Wunsch 
174246ed35dSJoerg Wunsch #define FDUNIT(s)	(((s) >> 6) & 3)
175246ed35dSJoerg Wunsch #define FDTYPE(s)	((s) & 0x3f)
176246ed35dSJoerg Wunsch 
177246ed35dSJoerg Wunsch /*
178246ed35dSJoerg Wunsch  * fdc maintains a set (1!) of ivars per child of each controller.
179246ed35dSJoerg Wunsch  */
180246ed35dSJoerg Wunsch enum fdc_device_ivars {
181246ed35dSJoerg Wunsch 	FDC_IVAR_FDUNIT,
182246ed35dSJoerg Wunsch };
183246ed35dSJoerg Wunsch 
184246ed35dSJoerg Wunsch /*
185246ed35dSJoerg Wunsch  * Simple access macros for the ivars.
186246ed35dSJoerg Wunsch  */
187246ed35dSJoerg Wunsch #define FDC_ACCESSOR(A, B, T)						\
188246ed35dSJoerg Wunsch static __inline T fdc_get_ ## A(device_t dev)				\
189246ed35dSJoerg Wunsch {									\
190246ed35dSJoerg Wunsch 	uintptr_t v;							\
191246ed35dSJoerg Wunsch 	BUS_READ_IVAR(device_get_parent(dev), dev, FDC_IVAR_ ## B, &v);	\
192246ed35dSJoerg Wunsch 	return (T) v;							\
193246ed35dSJoerg Wunsch }
194246ed35dSJoerg Wunsch FDC_ACCESSOR(fdunit,	FDUNIT,	int)
195246ed35dSJoerg Wunsch 
1960722d6abSJoerg Wunsch /* configuration flags */
1970722d6abSJoerg Wunsch #define FDC_PRETEND_D0	(1 << 0)	/* pretend drive 0 to be there */
198e34c71eaSJoerg Wunsch #define FDC_NO_FIFO	(1 << 2)	/* do not enable FIFO  */
1990722d6abSJoerg Wunsch 
2000722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */
2010722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED	0x1000
2020722d6abSJoerg Wunsch 
203dc5df763SJoerg Wunsch /* error returns for fd_cmd() */
204dc5df763SJoerg Wunsch #define FD_FAILED -1
205dc5df763SJoerg Wunsch #define FD_NOT_VALID -2
206dc5df763SJoerg Wunsch #define FDC_ERRMAX	100	/* do not log more */
2073fef646eSJoerg Wunsch /*
2083fef646eSJoerg Wunsch  * Stop retrying after this many DMA overruns.  Since each retry takes
20983edbfa5SJoerg Wunsch  * one revolution, with 300 rpm., 25 retries take approximately 5
2103fef646eSJoerg Wunsch  * seconds which the read attempt will block in case the DMA overrun
2113fef646eSJoerg Wunsch  * is persistent.
2123fef646eSJoerg Wunsch  */
2133fef646eSJoerg Wunsch #define FDC_DMAOV_MAX	25
214dc5df763SJoerg Wunsch 
2156fb89845SKATO Takenori #define NUMTYPES 17
2166fb89845SKATO Takenori #define NUMDENS  (NUMTYPES - 7)
2177ca0641bSAndrey A. Chernov 
218246ed35dSJoerg Wunsch #define NO_TYPE		0
219b99f0a4aSAndrew Moore #define FD_1720         1
220b99f0a4aSAndrew Moore #define FD_1480         2
221b99f0a4aSAndrew Moore #define FD_1440         3
222b99f0a4aSAndrew Moore #define FD_1200         4
223b99f0a4aSAndrew Moore #define FD_820          5
224b99f0a4aSAndrew Moore #define FD_800          6
225b99f0a4aSAndrew Moore #define FD_720          7
226b99f0a4aSAndrew Moore #define FD_360          8
2276fb89845SKATO Takenori #define FD_640          9
2286fb89845SKATO Takenori #define FD_1232         10
229ed2fa05eSAndrey A. Chernov 
2306fb89845SKATO Takenori #define FD_1480in5_25   11
2316fb89845SKATO Takenori #define FD_1440in5_25   12
2326fb89845SKATO Takenori #define FD_820in5_25    13
2336fb89845SKATO Takenori #define FD_800in5_25    14
2346fb89845SKATO Takenori #define FD_720in5_25    15
2356fb89845SKATO Takenori #define FD_360in5_25    16
2366fb89845SKATO Takenori #define FD_640in5_25    17
237b99f0a4aSAndrew Moore 
238f664aeeeSJoerg Wunsch #define BIO_RDSECTID	BIO_CMD1
2397ca0641bSAndrey A. Chernov 
2406f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] =
2415b81b6b3SRodney W. Grimes {
242126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
243126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
244126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
245126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
246126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
247126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
248126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
249b0568305SAndrey A. Chernov {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
2506fb89845SKATO Takenori {  8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /*  640K in DD 5.25in */
2516fb89845SKATO Takenori {  8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */
252ed2fa05eSAndrey A. Chernov 
253126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
254126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
255126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
256126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
257126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
258126518a1SAndrey A. Chernov {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
2596fb89845SKATO Takenori {  8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /*  640K in HD 5.25in */
2605b81b6b3SRodney W. Grimes };
2615b81b6b3SRodney W. Grimes 
262b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2		/* 2 floppies */
263dc16046fSJoerg Wunsch 
264f8ce7dd5SJoerg Wunsch #define MAX_SEC_SIZE	(128 << 3)
26564860614SJoerg Wunsch #define MAX_CYLINDER	85	/* some people really stress their drives
26664860614SJoerg Wunsch 				 * up to cyl 82 */
267250300ebSJoerg Wunsch #define MAX_HEAD	1
268f8ce7dd5SJoerg Wunsch 
2696182fdbdSPeter Wemm static devclass_t fdc_devclass;
2705b81b6b3SRodney W. Grimes 
271246ed35dSJoerg Wunsch /*
272246ed35dSJoerg Wunsch  * Per drive structure (softc).
273246ed35dSJoerg Wunsch  */
2746182fdbdSPeter Wemm struct fd_data {
275b99f0a4aSAndrew Moore 	struct	fdc_data *fdc;	/* pointer to controller structure */
2765b81b6b3SRodney W. Grimes 	int	fdsu;		/* this units number on this controller */
2773a2f7427SDavid Greenman 	int	type;		/* Drive type (FD_1440...) */
2785b81b6b3SRodney W. Grimes 	struct	fd_type *ft;	/* pointer to the type descriptor */
2795b81b6b3SRodney W. Grimes 	int	flags;
2805b81b6b3SRodney W. Grimes #define	FD_OPEN		0x01	/* it's open		*/
2815b81b6b3SRodney W. Grimes #define	FD_ACTIVE	0x02	/* it's active		*/
2825b81b6b3SRodney W. Grimes #define	FD_MOTOR	0x04	/* motor should be on	*/
2835b81b6b3SRodney W. Grimes #define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
2845b81b6b3SRodney W. Grimes 	int	skip;
2855b81b6b3SRodney W. Grimes 	int	hddrv;
286dc5df763SJoerg Wunsch #define FD_NO_TRACK -2
2875b81b6b3SRodney W. Grimes 	int	track;		/* where we think the head is */
2883a2f7427SDavid Greenman 	int	options;	/* user configurable options, see ioctl_fd.h */
28902a19910SJustin T. Gibbs 	struct	callout_handle toffhandle;
29002a19910SJustin T. Gibbs 	struct	callout_handle tohandle;
291b2dfb1f9SJustin T. Gibbs 	struct	devstat device_stats;
292e219897aSJoerg Wunsch 	eventhandler_tag clonetag;
293e219897aSJoerg Wunsch 	dev_t	masterdev;
29460444853SJoerg Wunsch #define NCLONEDEVS	10	/* must match the table below */
29560444853SJoerg Wunsch 	dev_t	clonedevs[NCLONEDEVS];
2966182fdbdSPeter Wemm 	device_t dev;
2976182fdbdSPeter Wemm 	fdu_t	fdu;
2986182fdbdSPeter Wemm };
29937286586SPeter Wemm 
30037286586SPeter Wemm struct fdc_ivars {
30137286586SPeter Wemm 	int	fdunit;
30237286586SPeter Wemm };
3036182fdbdSPeter Wemm static devclass_t fd_devclass;
3045b81b6b3SRodney W. Grimes 
305246ed35dSJoerg Wunsch /*
306246ed35dSJoerg Wunsch  * Throughout this file the following conventions will be used:
307246ed35dSJoerg Wunsch  *
308246ed35dSJoerg Wunsch  * fd is a pointer to the fd_data struct for the drive in question
309246ed35dSJoerg Wunsch  * fdc is a pointer to the fdc_data struct for the controller
310246ed35dSJoerg Wunsch  * fdu is the floppy drive unit number
311246ed35dSJoerg Wunsch  * fdcu is the floppy controller unit number
312246ed35dSJoerg Wunsch  * fdsu is the floppy drive unit number on that controller. (sub-unit)
313246ed35dSJoerg Wunsch  */
314b99f0a4aSAndrew Moore 
315246ed35dSJoerg Wunsch /*
316246ed35dSJoerg Wunsch  * Function declarations, same (chaotic) order as they appear in the
317246ed35dSJoerg Wunsch  * file.  Re-ordering is too late now, it would only obfuscate the
318246ed35dSJoerg Wunsch  * diffs against old and offspring versions (like the PC98 one).
319246ed35dSJoerg Wunsch  *
320246ed35dSJoerg Wunsch  * Anyone adding functions here, please keep this sequence the same
321246ed35dSJoerg Wunsch  * as below -- makes locating a particular function in the body much
322246ed35dSJoerg Wunsch  * easier.
323246ed35dSJoerg Wunsch  */
324246ed35dSJoerg Wunsch static void fdout_wr(fdc_p, u_int8_t);
325246ed35dSJoerg Wunsch static u_int8_t fdsts_rd(fdc_p);
326246ed35dSJoerg Wunsch static void fddata_wr(fdc_p, u_int8_t);
327246ed35dSJoerg Wunsch static u_int8_t fddata_rd(fdc_p);
328246ed35dSJoerg Wunsch static void fdctl_wr_isa(fdc_p, u_int8_t);
329246ed35dSJoerg Wunsch #if NCARD > 0
330246ed35dSJoerg Wunsch static void fdctl_wr_pcmcia(fdc_p, u_int8_t);
331246ed35dSJoerg Wunsch #endif
332246ed35dSJoerg Wunsch #if 0
333246ed35dSJoerg Wunsch static u_int8_t fdin_rd(fdc_p);
334246ed35dSJoerg Wunsch #endif
335246ed35dSJoerg Wunsch static int fdc_err(struct fdc_data *, const char *);
336246ed35dSJoerg Wunsch static int fd_cmd(struct fdc_data *, int, ...);
337246ed35dSJoerg Wunsch static int enable_fifo(fdc_p fdc);
338246ed35dSJoerg Wunsch static int fd_sense_drive_status(fdc_p, int *);
339246ed35dSJoerg Wunsch static int fd_sense_int(fdc_p, int *, int *);
340246ed35dSJoerg Wunsch static int fd_read_status(fdc_p);
341246ed35dSJoerg Wunsch static int fdc_alloc_resources(struct fdc_data *);
342246ed35dSJoerg Wunsch static void fdc_release_resources(struct fdc_data *);
343246ed35dSJoerg Wunsch static int fdc_read_ivar(device_t, device_t, int, uintptr_t *);
344246ed35dSJoerg Wunsch static int fdc_probe(device_t);
345246ed35dSJoerg Wunsch #if NCARD > 0
346246ed35dSJoerg Wunsch static int fdc_pccard_probe(device_t);
347246ed35dSJoerg Wunsch #endif
348246ed35dSJoerg Wunsch static int fdc_detach(device_t dev);
349246ed35dSJoerg Wunsch static void fdc_add_child(device_t, const char *, int);
350246ed35dSJoerg Wunsch static int fdc_attach(device_t);
351246ed35dSJoerg Wunsch static int fdc_print_child(device_t, device_t);
352246ed35dSJoerg Wunsch static void fd_clone (void *, char *, int, dev_t *);
353246ed35dSJoerg Wunsch static int fd_probe(device_t);
354246ed35dSJoerg Wunsch static int fd_attach(device_t);
355246ed35dSJoerg Wunsch static int fd_detach(device_t);
3566182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int);
3573a2f7427SDavid Greenman #  define TURNON 1
3583a2f7427SDavid Greenman #  define TURNOFF 0
3593a2f7427SDavid Greenman static timeout_t fd_turnoff;
3603a2f7427SDavid Greenman static timeout_t fd_motor_on;
3616182fdbdSPeter Wemm static void fd_turnon(struct fd_data *);
3623a2f7427SDavid Greenman static void fdc_reset(fdc_p);
3636182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *);
36480909a7dSJoerg Wunsch static int out_fdc(struct fdc_data *, int);
365246ed35dSJoerg Wunsch /*
366246ed35dSJoerg Wunsch  * The open function is named Fdopen() to avoid confusion with fdopen()
367246ed35dSJoerg Wunsch  * in fd(4).  The difference is now only meaningful for debuggers.
368246ed35dSJoerg Wunsch  */
369246ed35dSJoerg Wunsch static	d_open_t	Fdopen;
370246ed35dSJoerg Wunsch static	d_close_t	fdclose;
371246ed35dSJoerg Wunsch static	d_strategy_t	fdstrategy;
3726182fdbdSPeter Wemm static void fdstart(struct fdc_data *);
3735c1a1eaeSBruce Evans static timeout_t fd_iotimeout;
3743a2f7427SDavid Greenman static timeout_t fd_pseudointr;
375246ed35dSJoerg Wunsch static driver_intr_t fdc_intr;
376246ed35dSJoerg Wunsch static int fdcpio(fdc_p, long, caddr_t, u_int);
3776182fdbdSPeter Wemm static int fdstate(struct fdc_data *);
3786182fdbdSPeter Wemm static int retrier(struct fdc_data *);
379246ed35dSJoerg Wunsch static void fdbiodone(struct bio *);
380f664aeeeSJoerg Wunsch static int fdmisccmd(dev_t, u_int, void *);
381246ed35dSJoerg Wunsch static	d_ioctl_t	fdioctl;
382d66c374fSTor Egge 
383d66c374fSTor Egge static int fifo_threshold = 8;	/* XXX: should be accessible via sysctl */
384d66c374fSTor Egge 
385d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
3863a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */
387cba2a7c6SBruce Evans static int volatile fd_debug = 0;
3880e17a5bcSJoerg Wunsch #define TRACE0(arg) do { if (fd_debug) printf(arg); } while (0)
3890e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { if (fd_debug) printf(arg1, arg2); } while (0)
390d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */
3910e17a5bcSJoerg Wunsch #define TRACE0(arg) do { } while (0)
3920e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { } while (0)
393d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */
3945b81b6b3SRodney W. Grimes 
395246ed35dSJoerg Wunsch /*
396246ed35dSJoerg Wunsch  * Bus space handling (access to low-level IO).
397246ed35dSJoerg Wunsch  */
398427ccf00SDoug Rabson static void
399427ccf00SDoug Rabson fdout_wr(fdc_p fdc, u_int8_t v)
400427ccf00SDoug Rabson {
401427ccf00SDoug Rabson 	bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v);
402427ccf00SDoug Rabson }
403427ccf00SDoug Rabson 
404427ccf00SDoug Rabson static u_int8_t
405427ccf00SDoug Rabson fdsts_rd(fdc_p fdc)
406427ccf00SDoug Rabson {
407427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off);
408427ccf00SDoug Rabson }
409427ccf00SDoug Rabson 
410427ccf00SDoug Rabson static void
411427ccf00SDoug Rabson fddata_wr(fdc_p fdc, u_int8_t v)
412427ccf00SDoug Rabson {
413427ccf00SDoug Rabson 	bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v);
414427ccf00SDoug Rabson }
415427ccf00SDoug Rabson 
416427ccf00SDoug Rabson static u_int8_t
417427ccf00SDoug Rabson fddata_rd(fdc_p fdc)
418427ccf00SDoug Rabson {
419427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off);
420427ccf00SDoug Rabson }
421427ccf00SDoug Rabson 
422427ccf00SDoug Rabson static void
4235f830ea2SJoerg Wunsch fdctl_wr_isa(fdc_p fdc, u_int8_t v)
424427ccf00SDoug Rabson {
425427ccf00SDoug Rabson 	bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v);
426427ccf00SDoug Rabson }
427427ccf00SDoug Rabson 
42858c9d623SPeter Wemm #if NCARD > 0
4295f830ea2SJoerg Wunsch static void
4305f830ea2SJoerg Wunsch fdctl_wr_pcmcia(fdc_p fdc, u_int8_t v)
4315f830ea2SJoerg Wunsch {
4325f830ea2SJoerg Wunsch 	bus_space_write_1(fdc->portt, fdc->porth, FDCTL+fdc->port_off, v);
4335f830ea2SJoerg Wunsch }
43458c9d623SPeter Wemm #endif
4355f830ea2SJoerg Wunsch 
436427ccf00SDoug Rabson #if 0
437427ccf00SDoug Rabson 
438427ccf00SDoug Rabson static u_int8_t
439427ccf00SDoug Rabson fdin_rd(fdc_p fdc)
440427ccf00SDoug Rabson {
441427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDIN);
442427ccf00SDoug Rabson }
443427ccf00SDoug Rabson 
444427ccf00SDoug Rabson #endif
445427ccf00SDoug Rabson 
44687f6c662SJulian Elischer #define CDEV_MAJOR 9
4474e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = {
4484e2f199eSPoul-Henning Kamp 	/* open */	Fdopen,
4494e2f199eSPoul-Henning Kamp 	/* close */	fdclose,
4504e2f199eSPoul-Henning Kamp 	/* read */	physread,
4514e2f199eSPoul-Henning Kamp 	/* write */	physwrite,
4524e2f199eSPoul-Henning Kamp 	/* ioctl */	fdioctl,
4534e2f199eSPoul-Henning Kamp 	/* poll */	nopoll,
4544e2f199eSPoul-Henning Kamp 	/* mmap */	nommap,
4554e2f199eSPoul-Henning Kamp 	/* strategy */	fdstrategy,
4564e2f199eSPoul-Henning Kamp 	/* name */	"fd",
4574e2f199eSPoul-Henning Kamp 	/* maj */	CDEV_MAJOR,
4584e2f199eSPoul-Henning Kamp 	/* dump */	nodump,
4594e2f199eSPoul-Henning Kamp 	/* psize */	nopsize,
4604e2f199eSPoul-Henning Kamp 	/* flags */	D_DISK,
4614e2f199eSPoul-Henning Kamp };
4624e2f199eSPoul-Henning Kamp 
463246ed35dSJoerg Wunsch /*
464246ed35dSJoerg Wunsch  * Auxiliary functions.  Well, some only.  Others are scattered
465246ed35dSJoerg Wunsch  * throughout the entire file.
466246ed35dSJoerg Wunsch  */
467dc5df763SJoerg Wunsch static int
4686182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s)
469dc5df763SJoerg Wunsch {
4706182fdbdSPeter Wemm 	fdc->fdc_errs++;
47116b04b6aSJoerg Wunsch 	if (s) {
472b6e5f28eSPeter Wemm 		if (fdc->fdc_errs < FDC_ERRMAX)
473b6e5f28eSPeter Wemm 			device_printf(fdc->fdc_dev, "%s", s);
474b6e5f28eSPeter Wemm 		else if (fdc->fdc_errs == FDC_ERRMAX)
475b6e5f28eSPeter Wemm 			device_printf(fdc->fdc_dev, "too many errors, not "
476b6e5f28eSPeter Wemm 						    "logging any more\n");
47716b04b6aSJoerg Wunsch 	}
478dc5df763SJoerg Wunsch 
479dc5df763SJoerg Wunsch 	return FD_FAILED;
480dc5df763SJoerg Wunsch }
481dc5df763SJoerg Wunsch 
482dc5df763SJoerg Wunsch /*
483dc5df763SJoerg Wunsch  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
484dc5df763SJoerg Wunsch  * Unit number,
485dc5df763SJoerg Wunsch  * # of output bytes, output bytes as ints ...,
486dc5df763SJoerg Wunsch  * # of input bytes, input bytes as ints ...
487dc5df763SJoerg Wunsch  */
4886f4e0bebSPoul-Henning Kamp static int
4896182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...)
490dc5df763SJoerg Wunsch {
491dc5df763SJoerg Wunsch 	u_char cmd;
492dc5df763SJoerg Wunsch 	int n_in;
493dc5df763SJoerg Wunsch 	int n;
494dc5df763SJoerg Wunsch 	va_list ap;
495dc5df763SJoerg Wunsch 
496dc5df763SJoerg Wunsch 	va_start(ap, n_out);
497dc5df763SJoerg Wunsch 	cmd = (u_char)(va_arg(ap, int));
498dc5df763SJoerg Wunsch 	va_end(ap);
499dc5df763SJoerg Wunsch 	va_start(ap, n_out);
500dc5df763SJoerg Wunsch 	for (n = 0; n < n_out; n++)
501dc5df763SJoerg Wunsch 	{
5026182fdbdSPeter Wemm 		if (out_fdc(fdc, va_arg(ap, int)) < 0)
503dc5df763SJoerg Wunsch 		{
504dc5df763SJoerg Wunsch 			char msg[50];
5052127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
506dc5df763SJoerg Wunsch 				"cmd %x failed at out byte %d of %d\n",
507dc5df763SJoerg Wunsch 				cmd, n + 1, n_out);
5086182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
509dc5df763SJoerg Wunsch 		}
510dc5df763SJoerg Wunsch 	}
511dc5df763SJoerg Wunsch 	n_in = va_arg(ap, int);
512dc5df763SJoerg Wunsch 	for (n = 0; n < n_in; n++)
513dc5df763SJoerg Wunsch 	{
514dc5df763SJoerg Wunsch 		int *ptr = va_arg(ap, int *);
5156182fdbdSPeter Wemm 		if (fd_in(fdc, ptr) < 0)
516dc5df763SJoerg Wunsch 		{
517dc5df763SJoerg Wunsch 			char msg[50];
5182127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
519dc5df763SJoerg Wunsch 				"cmd %02x failed at in byte %d of %d\n",
520dc5df763SJoerg Wunsch 				cmd, n + 1, n_in);
5216182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
522dc5df763SJoerg Wunsch 		}
523dc5df763SJoerg Wunsch 	}
524dc5df763SJoerg Wunsch 
525dc5df763SJoerg Wunsch 	return 0;
526dc5df763SJoerg Wunsch }
527dc5df763SJoerg Wunsch 
5286f4e0bebSPoul-Henning Kamp static int
529d66c374fSTor Egge enable_fifo(fdc_p fdc)
530d66c374fSTor Egge {
531d66c374fSTor Egge 	int i, j;
532d66c374fSTor Egge 
533d66c374fSTor Egge 	if ((fdc->flags & FDC_HAS_FIFO) == 0) {
534d66c374fSTor Egge 
535d66c374fSTor Egge 		/*
536d66c374fSTor Egge 		 * Cannot use fd_cmd the normal way here, since
537d66c374fSTor Egge 		 * this might be an invalid command. Thus we send the
538d66c374fSTor Egge 		 * first byte, and check for an early turn of data directon.
539d66c374fSTor Egge 		 */
540d66c374fSTor Egge 
5416182fdbdSPeter Wemm 		if (out_fdc(fdc, I8207X_CONFIGURE) < 0)
5426182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
543d66c374fSTor Egge 
544d66c374fSTor Egge 		/* If command is invalid, return */
545d66c374fSTor Egge 		j = 100000;
546427ccf00SDoug Rabson 		while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM))
547d66c374fSTor Egge 		       != NE7_RQM && j-- > 0)
548d66c374fSTor Egge 			if (i == (NE7_DIO | NE7_RQM)) {
549d66c374fSTor Egge 				fdc_reset(fdc);
550d66c374fSTor Egge 				return FD_FAILED;
551d66c374fSTor Egge 			}
552d66c374fSTor Egge 		if (j<0 ||
5536182fdbdSPeter Wemm 		    fd_cmd(fdc, 3,
554d66c374fSTor Egge 			   0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) {
555d66c374fSTor Egge 			fdc_reset(fdc);
5566182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
557d66c374fSTor Egge 		}
558d66c374fSTor Egge 		fdc->flags |= FDC_HAS_FIFO;
559d66c374fSTor Egge 		return 0;
560d66c374fSTor Egge 	}
5616182fdbdSPeter Wemm 	if (fd_cmd(fdc, 4,
562d66c374fSTor Egge 		   I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0)
5636182fdbdSPeter Wemm 		return fdc_err(fdc, "Re-enable FIFO failed\n");
564d66c374fSTor Egge 	return 0;
565d66c374fSTor Egge }
566d66c374fSTor Egge 
567d66c374fSTor Egge static int
568dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p)
569dc5df763SJoerg Wunsch {
570dc5df763SJoerg Wunsch 	int st3;
571dc5df763SJoerg Wunsch 
5726182fdbdSPeter Wemm 	if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
573dc5df763SJoerg Wunsch 	{
5746182fdbdSPeter Wemm 		return fdc_err(fdc, "Sense Drive Status failed\n");
575dc5df763SJoerg Wunsch 	}
576dc5df763SJoerg Wunsch 	if (st3p)
577dc5df763SJoerg Wunsch 		*st3p = st3;
578dc5df763SJoerg Wunsch 
579dc5df763SJoerg Wunsch 	return 0;
580dc5df763SJoerg Wunsch }
581dc5df763SJoerg Wunsch 
5826f4e0bebSPoul-Henning Kamp static int
583dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
584dc5df763SJoerg Wunsch {
5856182fdbdSPeter Wemm 	int cyl, st0, ret;
586dc5df763SJoerg Wunsch 
5876182fdbdSPeter Wemm 	ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0);
5886182fdbdSPeter Wemm 	if (ret) {
5896182fdbdSPeter Wemm 		(void)fdc_err(fdc,
590dc5df763SJoerg Wunsch 			      "sense intr err reading stat reg 0\n");
591dc5df763SJoerg Wunsch 		return ret;
592dc5df763SJoerg Wunsch 	}
593dc5df763SJoerg Wunsch 
594dc5df763SJoerg Wunsch 	if (st0p)
595dc5df763SJoerg Wunsch 		*st0p = st0;
596dc5df763SJoerg Wunsch 
5976182fdbdSPeter Wemm 	if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) {
598dc5df763SJoerg Wunsch 		/*
599dc5df763SJoerg Wunsch 		 * There doesn't seem to have been an interrupt.
600dc5df763SJoerg Wunsch 		 */
601dc5df763SJoerg Wunsch 		return FD_NOT_VALID;
602dc5df763SJoerg Wunsch 	}
603dc5df763SJoerg Wunsch 
6046182fdbdSPeter Wemm 	if (fd_in(fdc, &cyl) < 0) {
6056182fdbdSPeter Wemm 		return fdc_err(fdc, "can't get cyl num\n");
606dc5df763SJoerg Wunsch 	}
607dc5df763SJoerg Wunsch 
608dc5df763SJoerg Wunsch 	if (cylp)
609dc5df763SJoerg Wunsch 		*cylp = cyl;
610dc5df763SJoerg Wunsch 
611dc5df763SJoerg Wunsch 	return 0;
612dc5df763SJoerg Wunsch }
613dc5df763SJoerg Wunsch 
614dc5df763SJoerg Wunsch 
6156f4e0bebSPoul-Henning Kamp static int
61664860614SJoerg Wunsch fd_read_status(fdc_p fdc)
617dc5df763SJoerg Wunsch {
618dc5df763SJoerg Wunsch 	int i, ret;
619b5e8ce9fSBruce Evans 
620246ed35dSJoerg Wunsch 	for (i = ret = 0; i < 7; i++) {
621b5e8ce9fSBruce Evans 		/*
62264860614SJoerg Wunsch 		 * XXX types are poorly chosen.  Only bytes can be read
623a838d83dSBruce Evans 		 * from the hardware, but fdc->status[] wants u_ints and
624b5e8ce9fSBruce Evans 		 * fd_in() gives ints.
625b5e8ce9fSBruce Evans 		 */
626b5e8ce9fSBruce Evans 		int status;
627b5e8ce9fSBruce Evans 
6286182fdbdSPeter Wemm 		ret = fd_in(fdc, &status);
629b5e8ce9fSBruce Evans 		fdc->status[i] = status;
630b5e8ce9fSBruce Evans 		if (ret != 0)
631dc5df763SJoerg Wunsch 			break;
632dc5df763SJoerg Wunsch 	}
633dc5df763SJoerg Wunsch 
634dc5df763SJoerg Wunsch 	if (ret == 0)
635dc5df763SJoerg Wunsch 		fdc->flags |= FDC_STAT_VALID;
636dc5df763SJoerg Wunsch 	else
637dc5df763SJoerg Wunsch 		fdc->flags &= ~FDC_STAT_VALID;
638dc5df763SJoerg Wunsch 
639dc5df763SJoerg Wunsch 	return ret;
640dc5df763SJoerg Wunsch }
641dc5df763SJoerg Wunsch 
6423a2f7427SDavid Greenman static int
64337286586SPeter Wemm fdc_alloc_resources(struct fdc_data *fdc)
6445b81b6b3SRodney W. Grimes {
64537286586SPeter Wemm 	device_t dev;
646e219897aSJoerg Wunsch 	int ispnp, ispcmcia, nports;
6475b81b6b3SRodney W. Grimes 
64837286586SPeter Wemm 	dev = fdc->fdc_dev;
6495f830ea2SJoerg Wunsch 	ispnp = (fdc->flags & FDC_ISPNP) != 0;
6505f830ea2SJoerg Wunsch 	ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0;
6516182fdbdSPeter Wemm 	fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0;
6526182fdbdSPeter Wemm 	fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0;
6536182fdbdSPeter Wemm 
654b6e5f28eSPeter Wemm 	/*
6555f830ea2SJoerg Wunsch 	 * On standard ISA, we don't just use an 8 port range
6565f830ea2SJoerg Wunsch 	 * (e.g. 0x3f0-0x3f7) since that covers an IDE control
6575f830ea2SJoerg Wunsch 	 * register at 0x3f6.
6585f830ea2SJoerg Wunsch 	 *
659b6e5f28eSPeter Wemm 	 * Isn't PC hardware wonderful.
6605f830ea2SJoerg Wunsch 	 *
6615f830ea2SJoerg Wunsch 	 * The Y-E Data PCMCIA FDC doesn't have this problem, it
6625f830ea2SJoerg Wunsch 	 * uses the register with offset 6 for pseudo-DMA, and the
6635f830ea2SJoerg Wunsch 	 * one with offset 7 as control register.
664b6e5f28eSPeter Wemm 	 */
665e219897aSJoerg Wunsch 	nports = ispcmcia ? 8 : (ispnp ? 1 : 6);
6666182fdbdSPeter Wemm 	fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
6676182fdbdSPeter Wemm 					     &fdc->rid_ioport, 0ul, ~0ul,
668e219897aSJoerg Wunsch 					     nports, RF_ACTIVE);
6696182fdbdSPeter Wemm 	if (fdc->res_ioport == 0) {
670e219897aSJoerg Wunsch 		device_printf(dev, "cannot reserve I/O port range (%d ports)\n",
671e219897aSJoerg Wunsch 			      nports);
67237286586SPeter Wemm 		return ENXIO;
6736182fdbdSPeter Wemm 	}
674427ccf00SDoug Rabson 	fdc->portt = rman_get_bustag(fdc->res_ioport);
675427ccf00SDoug Rabson 	fdc->porth = rman_get_bushandle(fdc->res_ioport);
676427ccf00SDoug Rabson 
6775f830ea2SJoerg Wunsch 	if (!ispcmcia) {
678427ccf00SDoug Rabson 		/*
6795f830ea2SJoerg Wunsch 		 * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7
6805f830ea2SJoerg Wunsch 		 * and some at 0x3f0-0x3f5,0x3f7. We detect the former
6815f830ea2SJoerg Wunsch 		 * by checking the size and adjust the port address
6825f830ea2SJoerg Wunsch 		 * accordingly.
683b6e5f28eSPeter Wemm 		 */
684b6e5f28eSPeter Wemm 		if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4)
685b6e5f28eSPeter Wemm 			fdc->port_off = -2;
686b6e5f28eSPeter Wemm 
687b6e5f28eSPeter Wemm 		/*
6885f830ea2SJoerg Wunsch 		 * Register the control port range as rid 1 if it
6895f830ea2SJoerg Wunsch 		 * isn't there already. Most PnP BIOSen will have
6905f830ea2SJoerg Wunsch 		 * already done this but non-PnP configurations don't.
691a2639a18SPeter Wemm 		 *
6925f830ea2SJoerg Wunsch 		 * And some (!!) report 0x3f2-0x3f5 and completely
6935f830ea2SJoerg Wunsch 		 * leave out the control register!  It seems that some
6945f830ea2SJoerg Wunsch 		 * non-antique controller chips have a different
6955f830ea2SJoerg Wunsch 		 * method of programming the transfer speed which
6965f830ea2SJoerg Wunsch 		 * doesn't require the control register, but it's
6975f830ea2SJoerg Wunsch 		 * mighty bogus as the chip still responds to the
6985f830ea2SJoerg Wunsch 		 * address for the control register.
699427ccf00SDoug Rabson 		 */
700b6e5f28eSPeter Wemm 		if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) {
701b9da888fSPeter Wemm 			u_long ctlstart;
702b9da888fSPeter Wemm 
703b6e5f28eSPeter Wemm 			/* Find the control port, usually 0x3f7 */
7045f830ea2SJoerg Wunsch 			ctlstart = rman_get_start(fdc->res_ioport) +
7055f830ea2SJoerg Wunsch 				fdc->port_off + 7;
706b6e5f28eSPeter Wemm 
707b6e5f28eSPeter Wemm 			bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1);
708b9da888fSPeter Wemm 		}
709b6e5f28eSPeter Wemm 
710b6e5f28eSPeter Wemm 		/*
711b6e5f28eSPeter Wemm 		 * Now (finally!) allocate the control port.
712b6e5f28eSPeter Wemm 		 */
713427ccf00SDoug Rabson 		fdc->rid_ctl = 1;
7145f830ea2SJoerg Wunsch 		fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT,
7155f830ea2SJoerg Wunsch 						  &fdc->rid_ctl,
716b6e5f28eSPeter Wemm 						  0ul, ~0ul, 1, RF_ACTIVE);
717427ccf00SDoug Rabson 		if (fdc->res_ctl == 0) {
7185f830ea2SJoerg Wunsch 			device_printf(dev,
719e219897aSJoerg Wunsch 		"cannot reserve control I/O port range (control port)\n");
72037286586SPeter Wemm 			return ENXIO;
721427ccf00SDoug Rabson 		}
722427ccf00SDoug Rabson 		fdc->ctlt = rman_get_bustag(fdc->res_ctl);
723427ccf00SDoug Rabson 		fdc->ctlh = rman_get_bushandle(fdc->res_ctl);
7245f830ea2SJoerg Wunsch 	}
7256182fdbdSPeter Wemm 
7266182fdbdSPeter Wemm 	fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ,
7276182fdbdSPeter Wemm 					  &fdc->rid_irq, 0ul, ~0ul, 1,
7286182fdbdSPeter Wemm 					  RF_ACTIVE);
7296182fdbdSPeter Wemm 	if (fdc->res_irq == 0) {
730427ccf00SDoug Rabson 		device_printf(dev, "cannot reserve interrupt line\n");
73137286586SPeter Wemm 		return ENXIO;
7326182fdbdSPeter Wemm 	}
7335f830ea2SJoerg Wunsch 
7345f830ea2SJoerg Wunsch 	if ((fdc->flags & FDC_NODMA) == 0) {
7356182fdbdSPeter Wemm 		fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ,
7366182fdbdSPeter Wemm 						  &fdc->rid_drq, 0ul, ~0ul, 1,
7376182fdbdSPeter Wemm 						  RF_ACTIVE);
7386182fdbdSPeter Wemm 		if (fdc->res_drq == 0) {
739427ccf00SDoug Rabson 			device_printf(dev, "cannot reserve DMA request line\n");
74037286586SPeter Wemm 			return ENXIO;
7416182fdbdSPeter Wemm 		}
7426182fdbdSPeter Wemm 		fdc->dmachan = fdc->res_drq->r_start;
7435f830ea2SJoerg Wunsch 	}
74437286586SPeter Wemm 
74537286586SPeter Wemm 	return 0;
74637286586SPeter Wemm }
74737286586SPeter Wemm 
74837286586SPeter Wemm static void
74937286586SPeter Wemm fdc_release_resources(struct fdc_data *fdc)
75037286586SPeter Wemm {
75137286586SPeter Wemm 	device_t dev;
75237286586SPeter Wemm 
75337286586SPeter Wemm 	dev = fdc->fdc_dev;
75437286586SPeter Wemm 	if (fdc->res_irq != 0) {
75537286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
75637286586SPeter Wemm 					fdc->res_irq);
75737286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
75837286586SPeter Wemm 				     fdc->res_irq);
75937286586SPeter Wemm 	}
76037286586SPeter Wemm 	if (fdc->res_ctl != 0) {
76137286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
76237286586SPeter Wemm 					fdc->res_ctl);
76337286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
76437286586SPeter Wemm 				     fdc->res_ctl);
76537286586SPeter Wemm 	}
76637286586SPeter Wemm 	if (fdc->res_ioport != 0) {
76737286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
76837286586SPeter Wemm 					fdc->res_ioport);
76937286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
77037286586SPeter Wemm 				     fdc->res_ioport);
77137286586SPeter Wemm 	}
77237286586SPeter Wemm 	if (fdc->res_drq != 0) {
77337286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
77437286586SPeter Wemm 					fdc->res_drq);
77537286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
77637286586SPeter Wemm 				     fdc->res_drq);
77737286586SPeter Wemm 	}
77837286586SPeter Wemm }
77937286586SPeter Wemm 
780246ed35dSJoerg Wunsch /*
781246ed35dSJoerg Wunsch  * Configuration/initialization stuff, per controller.
782246ed35dSJoerg Wunsch  */
78337286586SPeter Wemm 
78437286586SPeter Wemm static struct isa_pnp_id fdc_ids[] = {
78537286586SPeter Wemm 	{0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */
78637286586SPeter Wemm 	{0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */
78737286586SPeter Wemm 	{0}
78837286586SPeter Wemm };
78937286586SPeter Wemm 
79037286586SPeter Wemm static int
7913aae7b16SBruce Evans fdc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
79237286586SPeter Wemm {
79337286586SPeter Wemm 	struct fdc_ivars *ivars = device_get_ivars(child);
79437286586SPeter Wemm 
79537286586SPeter Wemm 	switch (which) {
79637286586SPeter Wemm 	case FDC_IVAR_FDUNIT:
79737286586SPeter Wemm 		*result = ivars->fdunit;
79837286586SPeter Wemm 		break;
79937286586SPeter Wemm 	default:
80037286586SPeter Wemm 		return ENOENT;
80137286586SPeter Wemm 	}
80237286586SPeter Wemm 	return 0;
80337286586SPeter Wemm }
80437286586SPeter Wemm 
80537286586SPeter Wemm static int
80637286586SPeter Wemm fdc_probe(device_t dev)
80737286586SPeter Wemm {
80837286586SPeter Wemm 	int	error, ic_type;
80937286586SPeter Wemm 	struct	fdc_data *fdc;
81037286586SPeter Wemm 
81137286586SPeter Wemm 	fdc = device_get_softc(dev);
81237286586SPeter Wemm 	bzero(fdc, sizeof *fdc);
81337286586SPeter Wemm 	fdc->fdc_dev = dev;
8145f830ea2SJoerg Wunsch 	fdc->fdctl_wr = fdctl_wr_isa;
81537286586SPeter Wemm 
81637286586SPeter Wemm 	/* Check pnp ids */
81737286586SPeter Wemm 	error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids);
81837286586SPeter Wemm 	if (error == ENXIO)
81937286586SPeter Wemm 		return ENXIO;
8205f830ea2SJoerg Wunsch 	if (error == 0)
8215f830ea2SJoerg Wunsch 		fdc->flags |= FDC_ISPNP;
82237286586SPeter Wemm 
82337286586SPeter Wemm 	/* Attempt to allocate our resources for the duration of the probe */
82437286586SPeter Wemm 	error = fdc_alloc_resources(fdc);
82537286586SPeter Wemm 	if (error)
82637286586SPeter Wemm 		goto out;
8275b81b6b3SRodney W. Grimes 
82816111cedSAndrew Moore 	/* First - lets reset the floppy controller */
829427ccf00SDoug Rabson 	fdout_wr(fdc, 0);
83016111cedSAndrew Moore 	DELAY(100);
831427ccf00SDoug Rabson 	fdout_wr(fdc, FDO_FRST);
83216111cedSAndrew Moore 
8335b81b6b3SRodney W. Grimes 	/* see if it can handle a command */
8346182fdbdSPeter Wemm 	if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240),
8356182fdbdSPeter Wemm 		   NE7_SPEC_2(2, 0), 0)) {
8366182fdbdSPeter Wemm 		error = ENXIO;
8376182fdbdSPeter Wemm 		goto out;
8385b81b6b3SRodney W. Grimes 	}
8396182fdbdSPeter Wemm 
8406182fdbdSPeter Wemm 	if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) {
8416182fdbdSPeter Wemm 		ic_type = (u_char)ic_type;
8426182fdbdSPeter Wemm 		switch (ic_type) {
8436182fdbdSPeter Wemm 		case 0x80:
8446182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 765 or clone");
8456182fdbdSPeter Wemm 			fdc->fdct = FDC_NE765;
8466182fdbdSPeter Wemm 			break;
8476182fdbdSPeter Wemm 		case 0x81:
8486182fdbdSPeter Wemm 			device_set_desc(dev, "Intel 82077 or clone");
8496182fdbdSPeter Wemm 			fdc->fdct = FDC_I82077;
8506182fdbdSPeter Wemm 			break;
8516182fdbdSPeter Wemm 		case 0x90:
8526182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 72065B or clone");
8536182fdbdSPeter Wemm 			fdc->fdct = FDC_NE72065;
8546182fdbdSPeter Wemm 			break;
8556182fdbdSPeter Wemm 		default:
8566182fdbdSPeter Wemm 			device_set_desc(dev, "generic floppy controller");
8576182fdbdSPeter Wemm 			fdc->fdct = FDC_UNKNOWN;
8586182fdbdSPeter Wemm 			break;
8596182fdbdSPeter Wemm 		}
8606182fdbdSPeter Wemm 	}
8616182fdbdSPeter Wemm 
8626182fdbdSPeter Wemm out:
86337286586SPeter Wemm 	fdc_release_resources(fdc);
8646182fdbdSPeter Wemm 	return (error);
8655b81b6b3SRodney W. Grimes }
8665b81b6b3SRodney W. Grimes 
8675f830ea2SJoerg Wunsch #if NCARD > 0
8685f830ea2SJoerg Wunsch 
8695f830ea2SJoerg Wunsch static int
8705f830ea2SJoerg Wunsch fdc_pccard_probe(device_t dev)
8715f830ea2SJoerg Wunsch {
8725f830ea2SJoerg Wunsch 	int	error;
8735f830ea2SJoerg Wunsch 	struct	fdc_data *fdc;
8745f830ea2SJoerg Wunsch 
8755f830ea2SJoerg Wunsch 	fdc = device_get_softc(dev);
8765f830ea2SJoerg Wunsch 	bzero(fdc, sizeof *fdc);
8775f830ea2SJoerg Wunsch 	fdc->fdc_dev = dev;
8785f830ea2SJoerg Wunsch 	fdc->fdctl_wr = fdctl_wr_pcmcia;
8795f830ea2SJoerg Wunsch 
8805f830ea2SJoerg Wunsch 	fdc->flags |= FDC_ISPCMCIA | FDC_NODMA;
8815f830ea2SJoerg Wunsch 
8825f830ea2SJoerg Wunsch 	/* Attempt to allocate our resources for the duration of the probe */
8835f830ea2SJoerg Wunsch 	error = fdc_alloc_resources(fdc);
8845f830ea2SJoerg Wunsch 	if (error)
8855f830ea2SJoerg Wunsch 		goto out;
8865f830ea2SJoerg Wunsch 
8875f830ea2SJoerg Wunsch 	/* First - lets reset the floppy controller */
8885f830ea2SJoerg Wunsch 	fdout_wr(fdc, 0);
8895f830ea2SJoerg Wunsch 	DELAY(100);
8905f830ea2SJoerg Wunsch 	fdout_wr(fdc, FDO_FRST);
8915f830ea2SJoerg Wunsch 
8925f830ea2SJoerg Wunsch 	/* see if it can handle a command */
8935f830ea2SJoerg Wunsch 	if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240),
8945f830ea2SJoerg Wunsch 		   NE7_SPEC_2(2, 0), 0)) {
8955f830ea2SJoerg Wunsch 		error = ENXIO;
8965f830ea2SJoerg Wunsch 		goto out;
8975f830ea2SJoerg Wunsch 	}
8985f830ea2SJoerg Wunsch 
8995f830ea2SJoerg Wunsch 	device_set_desc(dev, "Y-E Data PCMCIA floppy");
9005f830ea2SJoerg Wunsch 	fdc->fdct = FDC_NE765;
9015f830ea2SJoerg Wunsch 
9025f830ea2SJoerg Wunsch out:
9035f830ea2SJoerg Wunsch 	fdc_release_resources(fdc);
9045f830ea2SJoerg Wunsch 	return (error);
9055f830ea2SJoerg Wunsch }
9065f830ea2SJoerg Wunsch 
907e219897aSJoerg Wunsch #endif /* NCARD > 0 */
908e219897aSJoerg Wunsch 
9095f830ea2SJoerg Wunsch static int
910e219897aSJoerg Wunsch fdc_detach(device_t dev)
9115f830ea2SJoerg Wunsch {
9125f830ea2SJoerg Wunsch 	struct	fdc_data *fdc;
9135f830ea2SJoerg Wunsch 	int	error;
9145f830ea2SJoerg Wunsch 
9155f830ea2SJoerg Wunsch 	fdc = device_get_softc(dev);
9165f830ea2SJoerg Wunsch 
9175f830ea2SJoerg Wunsch 	/* have our children detached first */
9185f830ea2SJoerg Wunsch 	if ((error = bus_generic_detach(dev)))
9195f830ea2SJoerg Wunsch 		return (error);
9205f830ea2SJoerg Wunsch 
921e219897aSJoerg Wunsch 	/* reset controller, turn motor off */
922e219897aSJoerg Wunsch 	fdout_wr(fdc, 0);
923e219897aSJoerg Wunsch 
924e219897aSJoerg Wunsch 	if ((fdc->flags & FDC_NODMA) == 0)
925e219897aSJoerg Wunsch 		isa_dma_release(fdc->dmachan);
926e219897aSJoerg Wunsch 
9275f830ea2SJoerg Wunsch 	if ((fdc->flags & FDC_ATTACHED) == 0) {
9285f830ea2SJoerg Wunsch 		device_printf(dev, "already unloaded\n");
9295f830ea2SJoerg Wunsch 		return (0);
9305f830ea2SJoerg Wunsch 	}
9315f830ea2SJoerg Wunsch 	fdc->flags &= ~FDC_ATTACHED;
9325f830ea2SJoerg Wunsch 
9335f830ea2SJoerg Wunsch 	BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq,
9345f830ea2SJoerg Wunsch 			  fdc->fdc_intr);
9355f830ea2SJoerg Wunsch 	fdc_release_resources(fdc);
9365f830ea2SJoerg Wunsch 	device_printf(dev, "unload\n");
9375f830ea2SJoerg Wunsch 	return (0);
9385f830ea2SJoerg Wunsch }
9395f830ea2SJoerg Wunsch 
9405b81b6b3SRodney W. Grimes /*
94137286586SPeter Wemm  * Add a child device to the fdc controller.  It will then be probed etc.
9425b81b6b3SRodney W. Grimes  */
9436182fdbdSPeter Wemm static void
94437286586SPeter Wemm fdc_add_child(device_t dev, const char *name, int unit)
9455b81b6b3SRodney W. Grimes {
94637286586SPeter Wemm 	int	disabled;
94737286586SPeter Wemm 	struct fdc_ivars *ivar;
9486182fdbdSPeter Wemm 	device_t child;
94992200632SGarrett Wollman 
9507cc0979fSDavid Malone 	ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT | M_ZERO);
95137286586SPeter Wemm 	if (ivar == NULL)
9526182fdbdSPeter Wemm 		return;
95337286586SPeter Wemm 	if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0)
95437286586SPeter Wemm 		ivar->fdunit = 0;
955fe0d4089SMatthew N. Dodd 	child = device_add_child(dev, name, unit);
95637286586SPeter Wemm 	if (child == NULL)
9576182fdbdSPeter Wemm 		return;
95837286586SPeter Wemm 	device_set_ivars(child, ivar);
959a97c75b7SDoug Rabson 	if (resource_int_value(name, unit, "disabled", &disabled) == 0
960a97c75b7SDoug Rabson 	    && disabled != 0)
9616182fdbdSPeter Wemm 		device_disable(child);
9626182fdbdSPeter Wemm }
9636182fdbdSPeter Wemm 
9646182fdbdSPeter Wemm static int
9656182fdbdSPeter Wemm fdc_attach(device_t dev)
9666182fdbdSPeter Wemm {
96737286586SPeter Wemm 	struct	fdc_data *fdc;
9682398f0cdSPeter Wemm 	const char *name, *dname;
9695d54fe91SJoerg Wunsch 	int	i, error, dunit;
970427ccf00SDoug Rabson 
97137286586SPeter Wemm 	fdc = device_get_softc(dev);
97237286586SPeter Wemm 	error = fdc_alloc_resources(fdc);
97337286586SPeter Wemm 	if (error) {
974f8ce7dd5SJoerg Wunsch 		device_printf(dev, "cannot re-acquire resources\n");
97537286586SPeter Wemm 		return error;
97637286586SPeter Wemm 	}
97737286586SPeter Wemm 	error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq,
9783c36743eSMark Murray 			       INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc,
9793c36743eSMark Murray 			       &fdc->fdc_intr);
98037286586SPeter Wemm 	if (error) {
98137286586SPeter Wemm 		device_printf(dev, "cannot setup interrupt\n");
98237286586SPeter Wemm 		return error;
98337286586SPeter Wemm 	}
98437286586SPeter Wemm 	fdc->fdcu = device_get_unit(dev);
985fb35bd37SJoerg Wunsch 	fdc->flags |= FDC_ATTACHED | FDC_NEEDS_RESET;
9866182fdbdSPeter Wemm 
9875f830ea2SJoerg Wunsch 	if ((fdc->flags & FDC_NODMA) == 0) {
988f8ce7dd5SJoerg Wunsch 		/*
989f8ce7dd5SJoerg Wunsch 		 * Acquire the DMA channel forever, the driver will do
990f8ce7dd5SJoerg Wunsch 		 * the rest
991f8ce7dd5SJoerg Wunsch 		 * XXX should integrate with rman
992f8ce7dd5SJoerg Wunsch 		 */
993100f78bbSSujal Patel 		isa_dma_acquire(fdc->dmachan);
994f8ce7dd5SJoerg Wunsch 		isa_dmainit(fdc->dmachan, MAX_SEC_SIZE);
9955f830ea2SJoerg Wunsch 	}
9965b81b6b3SRodney W. Grimes 	fdc->state = DEVIDLE;
9976182fdbdSPeter Wemm 
9983a2f7427SDavid Greenman 	/* reset controller, turn motor off, clear fdout mirror reg */
99964860614SJoerg Wunsch 	fdout_wr(fdc, fdc->fdout = 0);
10008177437dSPoul-Henning Kamp 	bioq_init(&fdc->head);
10015b81b6b3SRodney W. Grimes 
10026182fdbdSPeter Wemm 	/*
100337286586SPeter Wemm 	 * Probe and attach any children.  We should probably detect
100437286586SPeter Wemm 	 * devices from the BIOS unless overridden.
10056182fdbdSPeter Wemm 	 */
1006ada54f9eSPeter Wemm 	name = device_get_nameunit(dev);
10072398f0cdSPeter Wemm 	i = 0;
10082398f0cdSPeter Wemm 	while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0)
10092398f0cdSPeter Wemm 		fdc_add_child(dev, dname, dunit);
101037286586SPeter Wemm 
101160444853SJoerg Wunsch 	if ((error = bus_generic_attach(dev)) != 0)
101260444853SJoerg Wunsch 		return (error);
101360444853SJoerg Wunsch 
101460444853SJoerg Wunsch 	return (0);
10156182fdbdSPeter Wemm }
10166182fdbdSPeter Wemm 
101715317dd8SMatthew N. Dodd static int
10186182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child)
10196182fdbdSPeter Wemm {
102015317dd8SMatthew N. Dodd 	int retval = 0;
102115317dd8SMatthew N. Dodd 
102215317dd8SMatthew N. Dodd 	retval += bus_print_child_header(me, child);
102315317dd8SMatthew N. Dodd 	retval += printf(" on %s drive %d\n", device_get_nameunit(me),
102437286586SPeter Wemm 	       fdc_get_fdunit(child));
102515317dd8SMatthew N. Dodd 
102615317dd8SMatthew N. Dodd 	return (retval);
10276182fdbdSPeter Wemm }
10286182fdbdSPeter Wemm 
102916e68fc6SPeter Wemm static device_method_t fdc_methods[] = {
103016e68fc6SPeter Wemm 	/* Device interface */
103116e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fdc_probe),
103216e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fdc_attach),
1033e219897aSJoerg Wunsch 	DEVMETHOD(device_detach,	fdc_detach),
103416e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
103516e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend),
103616e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume),
103716e68fc6SPeter Wemm 
103816e68fc6SPeter Wemm 	/* Bus interface */
103916e68fc6SPeter Wemm 	DEVMETHOD(bus_print_child,	fdc_print_child),
104037286586SPeter Wemm 	DEVMETHOD(bus_read_ivar,	fdc_read_ivar),
104116e68fc6SPeter Wemm 	/* Our children never use any other bus interface methods. */
104216e68fc6SPeter Wemm 
104316e68fc6SPeter Wemm 	{ 0, 0 }
104416e68fc6SPeter Wemm };
104516e68fc6SPeter Wemm 
104616e68fc6SPeter Wemm static driver_t fdc_driver = {
104716e68fc6SPeter Wemm 	"fdc",
104816e68fc6SPeter Wemm 	fdc_methods,
104916e68fc6SPeter Wemm 	sizeof(struct fdc_data)
105016e68fc6SPeter Wemm };
105116e68fc6SPeter Wemm 
105216e68fc6SPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0);
105316e68fc6SPeter Wemm 
10545f830ea2SJoerg Wunsch #if NCARD > 0
10555f830ea2SJoerg Wunsch 
10565f830ea2SJoerg Wunsch static device_method_t fdc_pccard_methods[] = {
10575f830ea2SJoerg Wunsch 	/* Device interface */
10585f830ea2SJoerg Wunsch 	DEVMETHOD(device_probe,		fdc_pccard_probe),
10595f830ea2SJoerg Wunsch 	DEVMETHOD(device_attach,	fdc_attach),
1060e219897aSJoerg Wunsch 	DEVMETHOD(device_detach,	fdc_detach),
10615f830ea2SJoerg Wunsch 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
10625f830ea2SJoerg Wunsch 	DEVMETHOD(device_suspend,	bus_generic_suspend),
10635f830ea2SJoerg Wunsch 	DEVMETHOD(device_resume,	bus_generic_resume),
10645f830ea2SJoerg Wunsch 
10655f830ea2SJoerg Wunsch 	/* Bus interface */
10665f830ea2SJoerg Wunsch 	DEVMETHOD(bus_print_child,	fdc_print_child),
10675f830ea2SJoerg Wunsch 	DEVMETHOD(bus_read_ivar,	fdc_read_ivar),
10685f830ea2SJoerg Wunsch 	/* Our children never use any other bus interface methods. */
10695f830ea2SJoerg Wunsch 
10705f830ea2SJoerg Wunsch 	{ 0, 0 }
10715f830ea2SJoerg Wunsch };
10725f830ea2SJoerg Wunsch 
10735f830ea2SJoerg Wunsch static driver_t fdc_pccard_driver = {
10745f830ea2SJoerg Wunsch 	"fdc",
10755f830ea2SJoerg Wunsch 	fdc_pccard_methods,
10765f830ea2SJoerg Wunsch 	sizeof(struct fdc_data)
10775f830ea2SJoerg Wunsch };
10785f830ea2SJoerg Wunsch 
10795f830ea2SJoerg Wunsch DRIVER_MODULE(fdc, pccard, fdc_pccard_driver, fdc_devclass, 0, 0);
10805f830ea2SJoerg Wunsch 
10815f830ea2SJoerg Wunsch #endif /* NCARD > 0 */
10825f830ea2SJoerg Wunsch 
10833f54a085SPoul-Henning Kamp static struct {
10843f54a085SPoul-Henning Kamp 	char *match;
10853f54a085SPoul-Henning Kamp 	int minor;
10863f54a085SPoul-Henning Kamp 	int link;
10873f54a085SPoul-Henning Kamp } fd_suffix[] = {
108860444853SJoerg Wunsch 	/*
108960444853SJoerg Wunsch 	 * Genuine clone devices must come first, and their number must
109060444853SJoerg Wunsch 	 * match NCLONEDEVS above.
109160444853SJoerg Wunsch 	 */
10923f54a085SPoul-Henning Kamp 	{ ".1720",	1,	0 },
10933f54a085SPoul-Henning Kamp 	{ ".1480",	2,	0 },
10943f54a085SPoul-Henning Kamp 	{ ".1440",	3,	0 },
10953f54a085SPoul-Henning Kamp 	{ ".1200",	4,	0 },
10963f54a085SPoul-Henning Kamp 	{ ".820",	5,	0 },
10973f54a085SPoul-Henning Kamp 	{ ".800",	6,	0 },
10983f54a085SPoul-Henning Kamp 	{ ".720",	7,	0 },
10993f54a085SPoul-Henning Kamp 	{ ".360",	8,	0 },
11003f54a085SPoul-Henning Kamp 	{ ".640",	9,	0 },
11013f54a085SPoul-Henning Kamp 	{ ".1232",	10,	0 },
110260444853SJoerg Wunsch 	{ "a",		0,	1 },
110360444853SJoerg Wunsch 	{ "b",		0,	1 },
110460444853SJoerg Wunsch 	{ "c",		0,	1 },
110560444853SJoerg Wunsch 	{ "d",		0,	1 },
110660444853SJoerg Wunsch 	{ "e",		0,	1 },
110760444853SJoerg Wunsch 	{ "f",		0,	1 },
110860444853SJoerg Wunsch 	{ "g",		0,	1 },
110960444853SJoerg Wunsch 	{ "h",		0,	1 },
11103f54a085SPoul-Henning Kamp 	{ 0, 0 }
11113f54a085SPoul-Henning Kamp };
1112246ed35dSJoerg Wunsch 
11133f54a085SPoul-Henning Kamp static void
111480909a7dSJoerg Wunsch fd_clone(void *arg, char *name, int namelen, dev_t *dev)
11153f54a085SPoul-Henning Kamp {
111660444853SJoerg Wunsch 	struct	fd_data *fd;
11173f54a085SPoul-Henning Kamp 	int u, d, i;
11183f54a085SPoul-Henning Kamp 	char *n;
11193f54a085SPoul-Henning Kamp 
112060444853SJoerg Wunsch 	fd = (struct fd_data *)arg;
11213f54a085SPoul-Henning Kamp 	if (*dev != NODEV)
11223f54a085SPoul-Henning Kamp 		return;
1123db901281SPoul-Henning Kamp 	if (dev_stdclone(name, &n, "fd", &u) != 2)
11243f54a085SPoul-Henning Kamp 		return;
11253f54a085SPoul-Henning Kamp 	for (i = 0; ; i++) {
11263f54a085SPoul-Henning Kamp 		if (fd_suffix[i].match == NULL)
11273f54a085SPoul-Henning Kamp 			return;
11283f54a085SPoul-Henning Kamp 		if (strcmp(n, fd_suffix[i].match))
11293f54a085SPoul-Henning Kamp 			continue;
11303f54a085SPoul-Henning Kamp 		d = fd_suffix[i].minor;
11313f54a085SPoul-Henning Kamp 		break;
11323f54a085SPoul-Henning Kamp 	}
11333f54a085SPoul-Henning Kamp 	if (fd_suffix[i].link == 0) {
11343f54a085SPoul-Henning Kamp 		*dev = make_dev(&fd_cdevsw, (u << 6) + d,
11353f54a085SPoul-Henning Kamp 			UID_ROOT, GID_OPERATOR, 0640, name);
113660444853SJoerg Wunsch 		fd->clonedevs[i] = *dev;
11373f54a085SPoul-Henning Kamp 	} else {
113860444853SJoerg Wunsch 		*dev = make_dev_alias(fd->masterdev, name);
11393f54a085SPoul-Henning Kamp 	}
11403f54a085SPoul-Henning Kamp }
11413f54a085SPoul-Henning Kamp 
114216e68fc6SPeter Wemm /*
1143246ed35dSJoerg Wunsch  * Configuration/initialization, per drive.
114416e68fc6SPeter Wemm  */
11456182fdbdSPeter Wemm static int
11466182fdbdSPeter Wemm fd_probe(device_t dev)
11476182fdbdSPeter Wemm {
11486182fdbdSPeter Wemm 	int	i;
11496182fdbdSPeter Wemm 	u_int	fdt, st0, st3;
11506182fdbdSPeter Wemm 	struct	fd_data *fd;
11516182fdbdSPeter Wemm 	struct	fdc_data *fdc;
11526182fdbdSPeter Wemm 	fdsu_t	fdsu;
11536182fdbdSPeter Wemm 	static int fd_fifo = 0;
11546182fdbdSPeter Wemm 
11556182fdbdSPeter Wemm 	fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */
11566182fdbdSPeter Wemm 	fd = device_get_softc(dev);
11576182fdbdSPeter Wemm 	fdc = device_get_softc(device_get_parent(dev));
11586182fdbdSPeter Wemm 
11596182fdbdSPeter Wemm 	bzero(fd, sizeof *fd);
11606182fdbdSPeter Wemm 	fd->dev = dev;
11616182fdbdSPeter Wemm 	fd->fdc = fdc;
11626182fdbdSPeter Wemm 	fd->fdsu = fdsu;
11636182fdbdSPeter Wemm 	fd->fdu = device_get_unit(dev);
11646182fdbdSPeter Wemm 
1165a97c75b7SDoug Rabson #ifdef __i386__
1166b99f0a4aSAndrew Moore 	/* look up what bios thinks we have */
11676182fdbdSPeter Wemm 	switch (fd->fdu) {
11686182fdbdSPeter Wemm 	case 0:
11695f830ea2SJoerg Wunsch 		if ((fdc->flags & FDC_ISPCMCIA))
11705f830ea2SJoerg Wunsch 			fdt = RTCFDT_144M;
11715f830ea2SJoerg Wunsch 		else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0)
11720722d6abSJoerg Wunsch 			fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
11730722d6abSJoerg Wunsch 		else
11740722d6abSJoerg Wunsch 			fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
1175b99f0a4aSAndrew Moore 		break;
11766182fdbdSPeter Wemm 	case 1:
11776182fdbdSPeter Wemm 		fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
1178dc5df763SJoerg Wunsch 		break;
1179dc5df763SJoerg Wunsch 	default:
11806182fdbdSPeter Wemm 		fdt = RTCFDT_NONE;
1181dc5df763SJoerg Wunsch 		break;
11826b7bd95bSJoerg Wunsch 	}
1183a97c75b7SDoug Rabson #else
1184a97c75b7SDoug Rabson 	fdt = RTCFDT_144M;	/* XXX probably */
1185a97c75b7SDoug Rabson #endif
11866182fdbdSPeter Wemm 
11876182fdbdSPeter Wemm 	/* is there a unit? */
11886182fdbdSPeter Wemm 	if (fdt == RTCFDT_NONE)
11896182fdbdSPeter Wemm 		return (ENXIO);
11906182fdbdSPeter Wemm 
11916182fdbdSPeter Wemm 	/* select it */
11926182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNON);
1193fb35bd37SJoerg Wunsch 	fdc_reset(fdc);		/* XXX reset, then unreset, etc. */
11946182fdbdSPeter Wemm 	DELAY(1000000);	/* 1 sec */
11956182fdbdSPeter Wemm 
11968de0675cSPeter Wemm 	/* XXX This doesn't work before the first set_motor() */
11976182fdbdSPeter Wemm 	if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN
1198e34c71eaSJoerg Wunsch 	    && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0
11996182fdbdSPeter Wemm 	    && enable_fifo(fdc) == 0) {
1200b6e5f28eSPeter Wemm 		device_printf(device_get_parent(dev),
1201b6e5f28eSPeter Wemm 		    "FIFO enabled, %d bytes threshold\n", fifo_threshold);
1202d66c374fSTor Egge 	}
12036182fdbdSPeter Wemm 	fd_fifo = 1;
12046182fdbdSPeter Wemm 
12056182fdbdSPeter Wemm 	if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0)
12066182fdbdSPeter Wemm 	    && (st3 & NE7_ST3_T0)) {
1207dc5df763SJoerg Wunsch 		/* if at track 0, first seek inwards */
1208dc5df763SJoerg Wunsch 		/* seek some steps: */
12096182fdbdSPeter Wemm 		fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0);
1210dc5df763SJoerg Wunsch 		DELAY(300000); /* ...wait a moment... */
12116182fdbdSPeter Wemm 		fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
1212dc5df763SJoerg Wunsch 	}
1213dc5df763SJoerg Wunsch 
1214dc5df763SJoerg Wunsch 	/* If we're at track 0 first seek inwards. */
12156182fdbdSPeter Wemm 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
1216dc5df763SJoerg Wunsch 		/* Seek some steps... */
12176182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
1218dc5df763SJoerg Wunsch 			/* ...wait a moment... */
1219dc5df763SJoerg Wunsch 			DELAY(300000);
1220dc5df763SJoerg Wunsch 			/* make ctrlr happy: */
12216182fdbdSPeter Wemm 			fd_sense_int(fdc, 0, 0);
1222dc5df763SJoerg Wunsch 		}
1223dc5df763SJoerg Wunsch 	}
1224dc5df763SJoerg Wunsch 
12256b7bd95bSJoerg Wunsch 	for (i = 0; i < 2; i++) {
12266b7bd95bSJoerg Wunsch 		/*
12276b7bd95bSJoerg Wunsch 		 * we must recalibrate twice, just in case the
12286b7bd95bSJoerg Wunsch 		 * heads have been beyond cylinder 76, since most
12296b7bd95bSJoerg Wunsch 		 * FDCs still barf when attempting to recalibrate
12306b7bd95bSJoerg Wunsch 		 * more than 77 steps
12316b7bd95bSJoerg Wunsch 		 */
1232dc5df763SJoerg Wunsch 		/* go back to 0: */
12336182fdbdSPeter Wemm 		if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
12346b7bd95bSJoerg Wunsch 			/* a second being enough for full stroke seek*/
12356b7bd95bSJoerg Wunsch 			DELAY(i == 0 ? 1000000 : 300000);
12365b81b6b3SRodney W. Grimes 
12376b7bd95bSJoerg Wunsch 			/* anything responding? */
1238dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
1239dc5df763SJoerg Wunsch 			    (st0 & NE7_ST0_EC) == 0)
12406b7bd95bSJoerg Wunsch 				break; /* already probed succesfully */
12416b7bd95bSJoerg Wunsch 		}
1242dc5df763SJoerg Wunsch 	}
12436b7bd95bSJoerg Wunsch 
12446182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNOFF);
12453a2f7427SDavid Greenman 
12463a2f7427SDavid Greenman 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
12476182fdbdSPeter Wemm 		return (ENXIO);
12485b81b6b3SRodney W. Grimes 
1249dc5df763SJoerg Wunsch 	fd->track = FD_NO_TRACK;
1250b99f0a4aSAndrew Moore 	fd->fdc = fdc;
1251b99f0a4aSAndrew Moore 	fd->fdsu = fdsu;
12523a2f7427SDavid Greenman 	fd->options = 0;
125302a19910SJustin T. Gibbs 	callout_handle_init(&fd->toffhandle);
125402a19910SJustin T. Gibbs 	callout_handle_init(&fd->tohandle);
12555b81b6b3SRodney W. Grimes 
1256b99f0a4aSAndrew Moore 	switch (fdt) {
12577ca0641bSAndrey A. Chernov 	case RTCFDT_12M:
12586182fdbdSPeter Wemm 		device_set_desc(dev, "1200-KB 5.25\" drive");
1259b99f0a4aSAndrew Moore 		fd->type = FD_1200;
12607ca0641bSAndrey A. Chernov 		break;
12610722d6abSJoerg Wunsch 	case RTCFDT_144M | RTCFDT_144M_PRETENDED:
12626182fdbdSPeter Wemm 		device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive");
12630722d6abSJoerg Wunsch 		fdt = RTCFDT_144M;
12646182fdbdSPeter Wemm 		fd->type = FD_1440;
12657ca0641bSAndrey A. Chernov 	case RTCFDT_144M:
12666182fdbdSPeter Wemm 		device_set_desc(dev, "1440-KB 3.5\" drive");
1267b99f0a4aSAndrew Moore 		fd->type = FD_1440;
12687ca0641bSAndrey A. Chernov 		break;
1269290dd077SJoerg Wunsch 	case RTCFDT_288M:
127086a727d9SJoerg Wunsch 	case RTCFDT_288M_1:
12716182fdbdSPeter Wemm 		device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)");
1272290dd077SJoerg Wunsch 		fd->type = FD_1440;
1273290dd077SJoerg Wunsch 		break;
12747ca0641bSAndrey A. Chernov 	case RTCFDT_360K:
12756182fdbdSPeter Wemm 		device_set_desc(dev, "360-KB 5.25\" drive");
1276b99f0a4aSAndrew Moore 		fd->type = FD_360;
12777ca0641bSAndrey A. Chernov 		break;
1278ed2fa05eSAndrey A. Chernov 	case RTCFDT_720K:
12796182fdbdSPeter Wemm 		printf("720-KB 3.5\" drive");
1280b99f0a4aSAndrew Moore 		fd->type = FD_720;
1281ed2fa05eSAndrey A. Chernov 		break;
12827ca0641bSAndrey A. Chernov 	default:
12836182fdbdSPeter Wemm 		return (ENXIO);
12845b81b6b3SRodney W. Grimes 	}
12856182fdbdSPeter Wemm 	return (0);
12866182fdbdSPeter Wemm }
12876182fdbdSPeter Wemm 
12886182fdbdSPeter Wemm static int
12896182fdbdSPeter Wemm fd_attach(device_t dev)
12906182fdbdSPeter Wemm {
12916182fdbdSPeter Wemm 	struct	fd_data *fd;
129264860614SJoerg Wunsch 	static	int cdevsw_add_done;
129360444853SJoerg Wunsch 	int i;
12946182fdbdSPeter Wemm 
129502211baeSNick Hibma 	if (!cdevsw_add_done) {
1296475ad603SPeter Wemm 		cdevsw_add(&fd_cdevsw);	/* XXX */
129764860614SJoerg Wunsch 		cdevsw_add_done = 1;
129802211baeSNick Hibma 	}
129964860614SJoerg Wunsch 	fd = device_get_softc(dev);
130060444853SJoerg Wunsch 	fd->clonetag = EVENTHANDLER_REGISTER(dev_clone, fd_clone, fd, 1000);
13015f431174SJoerg Wunsch 	fd->masterdev = make_dev(&fd_cdevsw, fd->fdu << 6,
13023f54a085SPoul-Henning Kamp 				 UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu);
130360444853SJoerg Wunsch 	for (i = 0; i < NCLONEDEVS; i++)
130460444853SJoerg Wunsch 		fd->clonedevs[i] = NODEV;
13056182fdbdSPeter Wemm 	devstat_add_entry(&fd->device_stats, device_get_name(dev),
1306f8ce7dd5SJoerg Wunsch 			  device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS,
13072a888f93SKenneth D. Merry 			  DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER,
13082a888f93SKenneth D. Merry 			  DEVSTAT_PRIORITY_FD);
13096182fdbdSPeter Wemm 	return (0);
13105b81b6b3SRodney W. Grimes }
13115b81b6b3SRodney W. Grimes 
13125f830ea2SJoerg Wunsch static int
13135f830ea2SJoerg Wunsch fd_detach(device_t dev)
13145f830ea2SJoerg Wunsch {
13155f830ea2SJoerg Wunsch 	struct	fd_data *fd;
131660444853SJoerg Wunsch 	int i;
13175f830ea2SJoerg Wunsch 
13185f830ea2SJoerg Wunsch 	fd = device_get_softc(dev);
131960444853SJoerg Wunsch 	untimeout(fd_turnoff, fd, fd->toffhandle);
1320e219897aSJoerg Wunsch 	devstat_remove_entry(&fd->device_stats);
1321e219897aSJoerg Wunsch 	destroy_dev(fd->masterdev);
132260444853SJoerg Wunsch 	for (i = 0; i < NCLONEDEVS; i++)
132360444853SJoerg Wunsch 		if (fd->clonedevs[i] != NODEV)
132460444853SJoerg Wunsch 			destroy_dev(fd->clonedevs[i]);
1325e219897aSJoerg Wunsch 	cdevsw_remove(&fd_cdevsw);
1326e219897aSJoerg Wunsch 	EVENTHANDLER_DEREGISTER(dev_clone, fd->clonetag);
13275f830ea2SJoerg Wunsch 
13285f830ea2SJoerg Wunsch 	return (0);
13295f830ea2SJoerg Wunsch }
13305f830ea2SJoerg Wunsch 
133116e68fc6SPeter Wemm static device_method_t fd_methods[] = {
133216e68fc6SPeter Wemm 	/* Device interface */
133316e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fd_probe),
133416e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fd_attach),
13355f830ea2SJoerg Wunsch 	DEVMETHOD(device_detach,	fd_detach),
133616e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
133716e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend), /* XXX */
133816e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume), /* XXX */
133916e68fc6SPeter Wemm 
134016e68fc6SPeter Wemm 	{ 0, 0 }
134116e68fc6SPeter Wemm };
134216e68fc6SPeter Wemm 
134316e68fc6SPeter Wemm static driver_t fd_driver = {
134416e68fc6SPeter Wemm 	"fd",
134516e68fc6SPeter Wemm 	fd_methods,
134616e68fc6SPeter Wemm 	sizeof(struct fd_data)
134716e68fc6SPeter Wemm };
134816e68fc6SPeter Wemm 
1349475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0);
135016e68fc6SPeter Wemm 
1351246ed35dSJoerg Wunsch /*
1352246ed35dSJoerg Wunsch  * More auxiliary functions.
1353246ed35dSJoerg Wunsch  */
1354246ed35dSJoerg Wunsch /*
1355246ed35dSJoerg Wunsch  * Motor control stuff.
1356246ed35dSJoerg Wunsch  * Remember to not deselect the drive we're working on.
1357246ed35dSJoerg Wunsch  */
13583a2f7427SDavid Greenman static void
13596182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon)
13605b81b6b3SRodney W. Grimes {
1361fb35bd37SJoerg Wunsch 	int fdout;
13623a2f7427SDavid Greenman 
1363fb35bd37SJoerg Wunsch 	fdout = fdc->fdout;
13643a2f7427SDavid Greenman 	if (turnon) {
13653a2f7427SDavid Greenman 		fdout &= ~FDO_FDSEL;
1366fb35bd37SJoerg Wunsch 		fdout |= (FDO_MOEN0 << fdsu) | FDO_FDMAEN | FDO_FRST | fdsu;
13673a2f7427SDavid Greenman 	} else
13683a2f7427SDavid Greenman 		fdout &= ~(FDO_MOEN0 << fdsu);
13696182fdbdSPeter Wemm 	fdc->fdout = fdout;
1370fb35bd37SJoerg Wunsch 	fdout_wr(fdc, fdout);
13713a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdout);
13723a2f7427SDavid Greenman }
13733a2f7427SDavid Greenman 
1374381fe1aaSGarrett Wollman static void
13756182fdbdSPeter Wemm fd_turnoff(void *xfd)
13765b81b6b3SRodney W. Grimes {
1377f5f7ba03SJordan K. Hubbard 	int	s;
13786182fdbdSPeter Wemm 	fd_p fd = xfd;
1379dc16046fSJoerg Wunsch 
13806182fdbdSPeter Wemm 	TRACE1("[fd%d: turnoff]", fd->fdu);
13818335c1b8SBruce Evans 
13825f830ea2SJoerg Wunsch 	s = splbio();
13838335c1b8SBruce Evans 	/*
13848335c1b8SBruce Evans 	 * Don't turn off the motor yet if the drive is active.
13855f830ea2SJoerg Wunsch 	 *
13865f830ea2SJoerg Wunsch 	 * If we got here, this could only mean we missed an interrupt.
13875f830ea2SJoerg Wunsch 	 * This can e. g. happen on the Y-E Date PCMCIA floppy controller
13885f830ea2SJoerg Wunsch 	 * after a controller reset.  Just schedule a pseudo-interrupt
13895f830ea2SJoerg Wunsch 	 * so the state machine gets re-entered.
13908335c1b8SBruce Evans 	 */
13916182fdbdSPeter Wemm 	if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) {
13925f830ea2SJoerg Wunsch 		fdc_intr(fd->fdc);
13935f830ea2SJoerg Wunsch 		splx(s);
13948335c1b8SBruce Evans 		return;
13958335c1b8SBruce Evans 	}
13968335c1b8SBruce Evans 
13975b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR;
13986182fdbdSPeter Wemm 	set_motor(fd->fdc, fd->fdsu, TURNOFF);
1399f5f7ba03SJordan K. Hubbard 	splx(s);
14005b81b6b3SRodney W. Grimes }
14015b81b6b3SRodney W. Grimes 
14023a2f7427SDavid Greenman static void
14036182fdbdSPeter Wemm fd_motor_on(void *xfd)
14045b81b6b3SRodney W. Grimes {
1405f5f7ba03SJordan K. Hubbard 	int	s;
14066182fdbdSPeter Wemm 	fd_p fd = xfd;
1407f5f7ba03SJordan K. Hubbard 
1408f5f7ba03SJordan K. Hubbard 	s = splbio();
14095b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR_WAIT;
14105b81b6b3SRodney W. Grimes 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
14115b81b6b3SRodney W. Grimes 	{
14126182fdbdSPeter Wemm 		fdc_intr(fd->fdc);
14135b81b6b3SRodney W. Grimes 	}
1414f5f7ba03SJordan K. Hubbard 	splx(s);
14155b81b6b3SRodney W. Grimes }
14165b81b6b3SRodney W. Grimes 
14173a2f7427SDavid Greenman static void
14186182fdbdSPeter Wemm fd_turnon(fd_p fd)
14195b81b6b3SRodney W. Grimes {
14205b81b6b3SRodney W. Grimes 	if(!(fd->flags & FD_MOTOR))
14215b81b6b3SRodney W. Grimes 	{
14223a2f7427SDavid Greenman 		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
14236182fdbdSPeter Wemm 		set_motor(fd->fdc, fd->fdsu, TURNON);
14246182fdbdSPeter Wemm 		timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */
14255b81b6b3SRodney W. Grimes 	}
14265b81b6b3SRodney W. Grimes }
14275b81b6b3SRodney W. Grimes 
1428381fe1aaSGarrett Wollman static void
1429dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc)
14305b81b6b3SRodney W. Grimes {
14313a2f7427SDavid Greenman 	/* Try a reset, keep motor on */
1432427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
14333a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
14343a2f7427SDavid Greenman 	DELAY(100);
14353a2f7427SDavid Greenman 	/* enable FDC, but defer interrupts a moment */
1436427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN);
14373a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
14383a2f7427SDavid Greenman 	DELAY(100);
1439427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout);
14403a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout);
14413a2f7427SDavid Greenman 
144264860614SJoerg Wunsch 	/* XXX after a reset, silently believe the FDC will accept commands */
14436182fdbdSPeter Wemm 	(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1444dc5df763SJoerg Wunsch 		     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1445dc5df763SJoerg Wunsch 		     0);
1446d66c374fSTor Egge 	if (fdc->flags & FDC_HAS_FIFO)
1447d66c374fSTor Egge 		(void) enable_fifo(fdc);
14485b81b6b3SRodney W. Grimes }
14495b81b6b3SRodney W. Grimes 
1450246ed35dSJoerg Wunsch /*
1451246ed35dSJoerg Wunsch  * FDC IO functions, take care of the main status register, timeout
1452246ed35dSJoerg Wunsch  * in case the desired status bits are never set.
1453246ed35dSJoerg Wunsch  */
1454b5e8ce9fSBruce Evans static int
14556182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr)
1456dc5df763SJoerg Wunsch {
1457dc5df763SJoerg Wunsch 	int i, j = 100000;
1458427ccf00SDoug Rabson 	while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM))
1459dc5df763SJoerg Wunsch 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1460dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
14616182fdbdSPeter Wemm 			return fdc_err(fdc, "ready for output in input\n");
1462dc5df763SJoerg Wunsch 	if (j <= 0)
14636182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "input ready timeout\n": 0);
1464d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
1465427ccf00SDoug Rabson 	i = fddata_rd(fdc);
1466dc5df763SJoerg Wunsch 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
1467dc5df763SJoerg Wunsch 	*ptr = i;
1468dc5df763SJoerg Wunsch 	return 0;
1469d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
1470427ccf00SDoug Rabson 	i = fddata_rd(fdc);
1471dc5df763SJoerg Wunsch 	if (ptr)
1472dc5df763SJoerg Wunsch 		*ptr = i;
1473dc5df763SJoerg Wunsch 	return 0;
1474d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
1475dc5df763SJoerg Wunsch }
1476dc5df763SJoerg Wunsch 
1477dc5df763SJoerg Wunsch int
14786182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x)
14795b81b6b3SRodney W. Grimes {
14803b3837dbSRodney W. Grimes 	int i;
14815b81b6b3SRodney W. Grimes 
14823b3837dbSRodney W. Grimes 	/* Check that the direction bit is set */
14833b3837dbSRodney W. Grimes 	i = 100000;
1484427ccf00SDoug Rabson 	while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0);
14856182fdbdSPeter Wemm 	if (i <= 0) return fdc_err(fdc, "direction bit not set\n");
14863b3837dbSRodney W. Grimes 
14873b3837dbSRodney W. Grimes 	/* Check that the floppy controller is ready for a command */
14883b3837dbSRodney W. Grimes 	i = 100000;
1489427ccf00SDoug Rabson 	while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0);
149016b04b6aSJoerg Wunsch 	if (i <= 0)
14916182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "output ready timeout\n": 0);
14923b3837dbSRodney W. Grimes 
14933b3837dbSRodney W. Grimes 	/* Send the command and return */
1494427ccf00SDoug Rabson 	fddata_wr(fdc, x);
14953a2f7427SDavid Greenman 	TRACE1("[0x%x->FDDATA]", x);
14965b81b6b3SRodney W. Grimes 	return (0);
14975b81b6b3SRodney W. Grimes }
14985b81b6b3SRodney W. Grimes 
1499246ed35dSJoerg Wunsch /*
1500246ed35dSJoerg Wunsch  * Block device driver interface functions (interspersed with even more
1501246ed35dSJoerg Wunsch  * auxiliary functions).
1502246ed35dSJoerg Wunsch  */
1503381fe1aaSGarrett Wollman int
1504671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p)
15055b81b6b3SRodney W. Grimes {
15065b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
150720a29168SAndrey A. Chernov 	int type = FDTYPE(minor(dev));
15086182fdbdSPeter Wemm 	fd_p	fd;
1509b99f0a4aSAndrew Moore 	fdc_p	fdc;
15105b81b6b3SRodney W. Grimes 
15115b81b6b3SRodney W. Grimes 	/* check bounds */
15126182fdbdSPeter Wemm 	if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0)
1513b99f0a4aSAndrew Moore 		return (ENXIO);
15146182fdbdSPeter Wemm 	fdc = fd->fdc;
15156182fdbdSPeter Wemm 	if ((fdc == NULL) || (fd->type == NO_TYPE))
1516b99f0a4aSAndrew Moore 		return (ENXIO);
1517b99f0a4aSAndrew Moore 	if (type > NUMDENS)
1518b99f0a4aSAndrew Moore 		return (ENXIO);
15197ca0641bSAndrey A. Chernov 	if (type == 0)
15206182fdbdSPeter Wemm 		type = fd->type;
15217ca0641bSAndrey A. Chernov 	else {
15223e425b96SJulian Elischer 		/*
15233e425b96SJulian Elischer 		 * For each type of basic drive, make sure we are trying
15243e425b96SJulian Elischer 		 * to open a type it can do,
15253e425b96SJulian Elischer 		 */
15266182fdbdSPeter Wemm 		if (type != fd->type) {
15276182fdbdSPeter Wemm 			switch (fd->type) {
15287ca0641bSAndrey A. Chernov 			case FD_360:
15297ca0641bSAndrey A. Chernov 				return (ENXIO);
1530ed2fa05eSAndrey A. Chernov 			case FD_720:
1531b39c878eSAndrey A. Chernov 				if (   type != FD_820
1532b39c878eSAndrey A. Chernov 				    && type != FD_800
15336fb89845SKATO Takenori 				    && type != FD_640
1534ed2fa05eSAndrey A. Chernov 				   )
1535ed2fa05eSAndrey A. Chernov 					return (ENXIO);
1536ed2fa05eSAndrey A. Chernov 				break;
15377ca0641bSAndrey A. Chernov 			case FD_1200:
1538b39c878eSAndrey A. Chernov 				switch (type) {
1539b39c878eSAndrey A. Chernov 				case FD_1480:
1540b39c878eSAndrey A. Chernov 					type = FD_1480in5_25;
1541fa4700b4SAndrey A. Chernov 					break;
15427ca0641bSAndrey A. Chernov 				case FD_1440:
1543b39c878eSAndrey A. Chernov 					type = FD_1440in5_25;
1544b39c878eSAndrey A. Chernov 					break;
15456fb89845SKATO Takenori 				case FD_1232:
15466fb89845SKATO Takenori 					break;
1547b39c878eSAndrey A. Chernov 				case FD_820:
1548b39c878eSAndrey A. Chernov 					type = FD_820in5_25;
1549b39c878eSAndrey A. Chernov 					break;
1550b39c878eSAndrey A. Chernov 				case FD_800:
1551b39c878eSAndrey A. Chernov 					type = FD_800in5_25;
1552b39c878eSAndrey A. Chernov 					break;
1553b39c878eSAndrey A. Chernov 				case FD_720:
1554b39c878eSAndrey A. Chernov 					type = FD_720in5_25;
1555b39c878eSAndrey A. Chernov 					break;
15566fb89845SKATO Takenori 				case FD_640:
15576fb89845SKATO Takenori 					type = FD_640in5_25;
15586fb89845SKATO Takenori 					break;
1559b39c878eSAndrey A. Chernov 				case FD_360:
1560b39c878eSAndrey A. Chernov 					type = FD_360in5_25;
1561b39c878eSAndrey A. Chernov 					break;
1562b39c878eSAndrey A. Chernov 				default:
1563b39c878eSAndrey A. Chernov 					return(ENXIO);
1564b39c878eSAndrey A. Chernov 				}
1565b39c878eSAndrey A. Chernov 				break;
1566b39c878eSAndrey A. Chernov 			case FD_1440:
1567b39c878eSAndrey A. Chernov 				if (   type != FD_1720
1568b39c878eSAndrey A. Chernov 				    && type != FD_1480
1569ed2fa05eSAndrey A. Chernov 				    && type != FD_1200
1570b39c878eSAndrey A. Chernov 				    && type != FD_820
1571b39c878eSAndrey A. Chernov 				    && type != FD_800
1572b39c878eSAndrey A. Chernov 				    && type != FD_720
15736fb89845SKATO Takenori 				    && type != FD_640
15747ca0641bSAndrey A. Chernov 				    )
1575dffff499SAndrey A. Chernov 					return(ENXIO);
1576fa4700b4SAndrey A. Chernov 				break;
15777ca0641bSAndrey A. Chernov 			}
15787ca0641bSAndrey A. Chernov 		}
1579fa4700b4SAndrey A. Chernov 	}
15806182fdbdSPeter Wemm 	fd->ft = fd_types + type - 1;
15816182fdbdSPeter Wemm 	fd->flags |= FD_OPEN;
15823fef646eSJoerg Wunsch 	/*
15833fef646eSJoerg Wunsch 	 * Clearing the DMA overrun counter at open time is a bit messy.
15843fef646eSJoerg Wunsch 	 * Since we're only managing one counter per controller, opening
15853fef646eSJoerg Wunsch 	 * the second drive could mess it up.  Anyway, if the DMA overrun
15863fef646eSJoerg Wunsch 	 * condition is really persistent, it will eventually time out
15873fef646eSJoerg Wunsch 	 * still.  OTOH, clearing it here will ensure we'll at least start
15883fef646eSJoerg Wunsch 	 * trying again after a previous (maybe even long ago) failure.
15893fef646eSJoerg Wunsch 	 * Also, this is merely a stop-gap measure only that should not
15903fef646eSJoerg Wunsch 	 * happen during normal operation, so we can tolerate it to be a
15913fef646eSJoerg Wunsch 	 * bit sloppy about this.
15923fef646eSJoerg Wunsch 	 */
15933fef646eSJoerg Wunsch 	fdc->dma_overruns = 0;
15945f830ea2SJoerg Wunsch 
15955b81b6b3SRodney W. Grimes 	return 0;
15965b81b6b3SRodney W. Grimes }
15975b81b6b3SRodney W. Grimes 
1598381fe1aaSGarrett Wollman int
1599671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p)
16005b81b6b3SRodney W. Grimes {
16015b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
16026182fdbdSPeter Wemm 	struct fd_data *fd;
1603b99f0a4aSAndrew Moore 
16046182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
16056182fdbdSPeter Wemm 	fd->flags &= ~FD_OPEN;
16062995d110SJoerg Wunsch 	fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR);
1607dc16046fSJoerg Wunsch 
16085b81b6b3SRodney W. Grimes 	return (0);
16095b81b6b3SRodney W. Grimes }
16105b81b6b3SRodney W. Grimes 
16113a2f7427SDavid Greenman void
16128177437dSPoul-Henning Kamp fdstrategy(struct bio *bp)
16133a2f7427SDavid Greenman {
1614fb35bd37SJoerg Wunsch 	long blknum, nblocks;
16153a2f7427SDavid Greenman  	int	s;
16163a2f7427SDavid Greenman  	fdu_t	fdu;
16173a2f7427SDavid Greenman  	fdc_p	fdc;
16183a2f7427SDavid Greenman  	fd_p	fd;
16193a2f7427SDavid Greenman 	size_t	fdblk;
16203a2f7427SDavid Greenman 
16218177437dSPoul-Henning Kamp  	fdu = FDUNIT(minor(bp->bio_dev));
16226182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
16236182fdbdSPeter Wemm 	if (fd == 0)
16246182fdbdSPeter Wemm 		panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)",
16258177437dSPoul-Henning Kamp 		      (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev));
16263a2f7427SDavid Greenman 	fdc = fd->fdc;
162769acd21dSWarner Losh 	if (fd->type == NO_TYPE) {
16288177437dSPoul-Henning Kamp 		bp->bio_error = ENXIO;
16298177437dSPoul-Henning Kamp 		bp->bio_flags |= BIO_ERROR;
16303b178206SWarner Losh 		goto bad;
163164860614SJoerg Wunsch 	}
1632d3628763SRodney W. Grimes 	fdblk = 128 << (fd->ft->secsize);
1633f664aeeeSJoerg Wunsch 	if (bp->bio_cmd != BIO_FORMAT && bp->bio_cmd != BIO_RDSECTID) {
16348177437dSPoul-Henning Kamp 		if (bp->bio_blkno < 0) {
1635dc5df763SJoerg Wunsch 			printf(
16366a0e6f42SRodney W. Grimes 		"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
16378177437dSPoul-Henning Kamp 			       fdu, (u_long)bp->bio_blkno, bp->bio_bcount);
16388177437dSPoul-Henning Kamp 			bp->bio_error = EINVAL;
16398177437dSPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
16403a2f7427SDavid Greenman 			goto bad;
16413a2f7427SDavid Greenman 		}
16428177437dSPoul-Henning Kamp 		if ((bp->bio_bcount % fdblk) != 0) {
16438177437dSPoul-Henning Kamp 			bp->bio_error = EINVAL;
16448177437dSPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
16453a2f7427SDavid Greenman 			goto bad;
16463a2f7427SDavid Greenman 		}
16473a2f7427SDavid Greenman 	}
16483a2f7427SDavid Greenman 
16493a2f7427SDavid Greenman 	/*
16503a2f7427SDavid Greenman 	 * Set up block calculations.
16513a2f7427SDavid Greenman 	 */
16528177437dSPoul-Henning Kamp 	if (bp->bio_blkno > 20000000) {
1653bb6382faSJoerg Wunsch 		/*
1654bb6382faSJoerg Wunsch 		 * Reject unreasonably high block number, prevent the
1655bb6382faSJoerg Wunsch 		 * multiplication below from overflowing.
1656bb6382faSJoerg Wunsch 		 */
16578177437dSPoul-Henning Kamp 		bp->bio_error = EINVAL;
16588177437dSPoul-Henning Kamp 		bp->bio_flags |= BIO_ERROR;
16593a2f7427SDavid Greenman 		goto bad;
16603a2f7427SDavid Greenman 	}
1661fb35bd37SJoerg Wunsch 	blknum = bp->bio_blkno * DEV_BSIZE / fdblk;
1662bb6382faSJoerg Wunsch  	nblocks = fd->ft->size;
1663fb35bd37SJoerg Wunsch 	if (blknum + bp->bio_bcount / fdblk > nblocks) {
1664fb35bd37SJoerg Wunsch 		if (blknum >= nblocks) {
1665fb35bd37SJoerg Wunsch 			if (bp->bio_cmd == BIO_READ)
1666fb35bd37SJoerg Wunsch 				bp->bio_resid = bp->bio_bcount;
1667fb35bd37SJoerg Wunsch 			else {
1668fb35bd37SJoerg Wunsch 				bp->bio_error = ENOSPC;
16698177437dSPoul-Henning Kamp 				bp->bio_flags |= BIO_ERROR;
1670bb6382faSJoerg Wunsch 			}
1671fb35bd37SJoerg Wunsch 			goto bad;	/* not always bad, but EOF */
1672fb35bd37SJoerg Wunsch 		}
1673fb35bd37SJoerg Wunsch 		bp->bio_bcount = (nblocks - blknum) * fdblk;
1674bb6382faSJoerg Wunsch 	}
16758177437dSPoul-Henning Kamp  	bp->bio_pblkno = bp->bio_blkno;
16763a2f7427SDavid Greenman 	s = splbio();
16778177437dSPoul-Henning Kamp 	bioqdisksort(&fdc->head, bp);
16786182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */
1679b2dfb1f9SJustin T. Gibbs 	devstat_start_transaction(&fd->device_stats);
16805f830ea2SJoerg Wunsch 	device_busy(fd->dev);
16816182fdbdSPeter Wemm 	fdstart(fdc);
16823a2f7427SDavid Greenman 	splx(s);
16833a2f7427SDavid Greenman 	return;
16843a2f7427SDavid Greenman 
16853a2f7427SDavid Greenman bad:
16863a2f7427SDavid Greenman 	biodone(bp);
16873a2f7427SDavid Greenman }
16883a2f7427SDavid Greenman 
1689246ed35dSJoerg Wunsch /*
1690246ed35dSJoerg Wunsch  * fdstart
1691246ed35dSJoerg Wunsch  *
1692246ed35dSJoerg Wunsch  * We have just queued something.  If the controller is not busy
1693246ed35dSJoerg Wunsch  * then simulate the case where it has just finished a command
1694246ed35dSJoerg Wunsch  * So that it (the interrupt routine) looks on the queue for more
1695246ed35dSJoerg Wunsch  * work to do and picks up what we just added.
1696246ed35dSJoerg Wunsch  *
1697246ed35dSJoerg Wunsch  * If the controller is already busy, we need do nothing, as it
1698246ed35dSJoerg Wunsch  * will pick up our work when the present work completes.
1699246ed35dSJoerg Wunsch  */
1700381fe1aaSGarrett Wollman static void
17016182fdbdSPeter Wemm fdstart(struct fdc_data *fdc)
17025b81b6b3SRodney W. Grimes {
17035b81b6b3SRodney W. Grimes 	int s;
17045b81b6b3SRodney W. Grimes 
17055b81b6b3SRodney W. Grimes 	s = splbio();
17066182fdbdSPeter Wemm 	if(fdc->state == DEVIDLE)
17075b81b6b3SRodney W. Grimes 	{
17086182fdbdSPeter Wemm 		fdc_intr(fdc);
17095b81b6b3SRodney W. Grimes 	}
17105b81b6b3SRodney W. Grimes 	splx(s);
17115b81b6b3SRodney W. Grimes }
17125b81b6b3SRodney W. Grimes 
1713381fe1aaSGarrett Wollman static void
17146182fdbdSPeter Wemm fd_iotimeout(void *xfdc)
17155b81b6b3SRodney W. Grimes {
17165c1a1eaeSBruce Evans  	fdc_p fdc;
1717f5f7ba03SJordan K. Hubbard 	int s;
17185b81b6b3SRodney W. Grimes 
17196182fdbdSPeter Wemm 	fdc = xfdc;
17205c1a1eaeSBruce Evans 	TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
17215b81b6b3SRodney W. Grimes 
17223a2f7427SDavid Greenman 	/*
17233a2f7427SDavid Greenman 	 * Due to IBM's brain-dead design, the FDC has a faked ready
17243a2f7427SDavid Greenman 	 * signal, hardwired to ready == true. Thus, any command
17253a2f7427SDavid Greenman 	 * issued if there's no diskette in the drive will _never_
17263a2f7427SDavid Greenman 	 * complete, and must be aborted by resetting the FDC.
17273a2f7427SDavid Greenman 	 * Many thanks, Big Blue!
17285c1a1eaeSBruce Evans 	 * The FDC must not be reset directly, since that would
17295c1a1eaeSBruce Evans 	 * interfere with the state machine.  Instead, pretend that
17305c1a1eaeSBruce Evans 	 * the command completed but was invalid.  The state machine
17315c1a1eaeSBruce Evans 	 * will reset the FDC and retry once.
17323a2f7427SDavid Greenman 	 */
17333a2f7427SDavid Greenman 	s = splbio();
17345c1a1eaeSBruce Evans 	fdc->status[0] = NE7_ST0_IC_IV;
17355c1a1eaeSBruce Evans 	fdc->flags &= ~FDC_STAT_VALID;
17365c1a1eaeSBruce Evans 	fdc->state = IOTIMEDOUT;
17376182fdbdSPeter Wemm 	fdc_intr(fdc);
1738f5f7ba03SJordan K. Hubbard 	splx(s);
17395b81b6b3SRodney W. Grimes }
17405b81b6b3SRodney W. Grimes 
1741246ed35dSJoerg Wunsch /* Just ensure it has the right spl. */
1742381fe1aaSGarrett Wollman static void
17436182fdbdSPeter Wemm fd_pseudointr(void *xfdc)
17445b81b6b3SRodney W. Grimes {
17455b81b6b3SRodney W. Grimes 	int	s;
17463a2f7427SDavid Greenman 
17475b81b6b3SRodney W. Grimes 	s = splbio();
17486182fdbdSPeter Wemm 	fdc_intr(xfdc);
17495b81b6b3SRodney W. Grimes 	splx(s);
17505b81b6b3SRodney W. Grimes }
17515b81b6b3SRodney W. Grimes 
1752246ed35dSJoerg Wunsch /*
1753246ed35dSJoerg Wunsch  * fdc_intr
1754246ed35dSJoerg Wunsch  *
1755246ed35dSJoerg Wunsch  * Keep calling the state machine until it returns a 0.
1756246ed35dSJoerg Wunsch  * Always called at splbio.
1757246ed35dSJoerg Wunsch  */
1758fe310de8SBruce Evans static void
17596182fdbdSPeter Wemm fdc_intr(void *xfdc)
17605b81b6b3SRodney W. Grimes {
17616182fdbdSPeter Wemm 	fdc_p fdc = xfdc;
17626182fdbdSPeter Wemm 	while(fdstate(fdc))
1763381fe1aaSGarrett Wollman 		;
17645b81b6b3SRodney W. Grimes }
17655b81b6b3SRodney W. Grimes 
176669acd21dSWarner Losh /*
1767246ed35dSJoerg Wunsch  * Magic pseudo-DMA initialization for YE FDC. Sets count and
1768246ed35dSJoerg Wunsch  * direction.
176969acd21dSWarner Losh  */
17703b178206SWarner Losh #define SET_BCDR(fdc,wr,cnt,port) \
17713b178206SWarner Losh 	bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port,	 \
17723b178206SWarner Losh 	    ((cnt)-1) & 0xff);						 \
17733b178206SWarner Losh 	bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \
17743b178206SWarner Losh 	    ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f)));
177569acd21dSWarner Losh 
177669acd21dSWarner Losh /*
1777246ed35dSJoerg Wunsch  * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy.
177869acd21dSWarner Losh  */
17794c34deeeSJoerg Wunsch static int
17804c34deeeSJoerg Wunsch fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count)
178169acd21dSWarner Losh {
178269acd21dSWarner Losh 	u_char *cptr = (u_char *)addr;
178369acd21dSWarner Losh 
178421144e3bSPoul-Henning Kamp 	if (flags == BIO_READ) {
178569acd21dSWarner Losh 		if (fdc->state != PIOREAD) {
178669acd21dSWarner Losh 			fdc->state = PIOREAD;
178769acd21dSWarner Losh 			return(0);
178864860614SJoerg Wunsch 		}
17893b178206SWarner Losh 		SET_BCDR(fdc, 0, count, 0);
17903b178206SWarner Losh 		bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off +
17913b178206SWarner Losh 		    FDC_YE_DATAPORT, cptr, count);
179269acd21dSWarner Losh 	} else {
17933b178206SWarner Losh 		bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off +
17943b178206SWarner Losh 		    FDC_YE_DATAPORT, cptr, count);
17953b178206SWarner Losh 		SET_BCDR(fdc, 0, count, 0);
179664860614SJoerg Wunsch 	}
179769acd21dSWarner Losh 	return(1);
179869acd21dSWarner Losh }
179969acd21dSWarner Losh 
1800246ed35dSJoerg Wunsch /*
1801246ed35dSJoerg Wunsch  * The controller state machine.
1802246ed35dSJoerg Wunsch  *
1803246ed35dSJoerg Wunsch  * If it returns a non zero value, it should be called again immediately.
1804246ed35dSJoerg Wunsch  */
18053a2f7427SDavid Greenman static int
18066182fdbdSPeter Wemm fdstate(fdc_p fdc)
18075b81b6b3SRodney W. Grimes {
180864860614SJoerg Wunsch 	struct fdc_readid *idp;
1809fb35bd37SJoerg Wunsch 	int read, format, rdsectid, cylinder, head, i, sec = 0, sectrac;
1810fb35bd37SJoerg Wunsch 	int st0, cyl, st3, idf;
1811fb35bd37SJoerg Wunsch 	unsigned long blknum;
18125b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc->fdu;
18135b81b6b3SRodney W. Grimes 	fd_p fd;
18148177437dSPoul-Henning Kamp 	register struct bio *bp;
1815b39c878eSAndrey A. Chernov 	struct fd_formb *finfo = NULL;
18163a2f7427SDavid Greenman 	size_t fdblk;
18175b81b6b3SRodney W. Grimes 
1818e93e63cbSBruce Evans 	bp = fdc->bp;
1819e93e63cbSBruce Evans 	if (bp == NULL) {
18208177437dSPoul-Henning Kamp 		bp = bioq_first(&fdc->head);
1821e93e63cbSBruce Evans 		if (bp != NULL) {
18228177437dSPoul-Henning Kamp 			bioq_remove(&fdc->head, bp);
1823e93e63cbSBruce Evans 			fdc->bp = bp;
1824e93e63cbSBruce Evans 		}
1825e93e63cbSBruce Evans 	}
1826e93e63cbSBruce Evans 	if (bp == NULL) {
1827246ed35dSJoerg Wunsch 		/*
1828246ed35dSJoerg Wunsch 		 * Nothing left for this controller to do,
1829246ed35dSJoerg Wunsch 		 * force into the IDLE state.
1830246ed35dSJoerg Wunsch 		 */
18315b81b6b3SRodney W. Grimes 		fdc->state = DEVIDLE;
18326182fdbdSPeter Wemm 		if (fdc->fd) {
1833b6e5f28eSPeter Wemm 			device_printf(fdc->fdc_dev,
1834b6e5f28eSPeter Wemm 			    "unexpected valid fd pointer\n");
18355b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
18365b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
18375b81b6b3SRodney W. Grimes 		}
18386182fdbdSPeter Wemm 		TRACE1("[fdc%d IDLE]", fdc->fdcu);
18395b81b6b3SRodney W. Grimes  		return (0);
18405b81b6b3SRodney W. Grimes 	}
18418177437dSPoul-Henning Kamp 	fdu = FDUNIT(minor(bp->bio_dev));
18426182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
18433a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
1844b6e5f28eSPeter Wemm 	if (fdc->fd && (fd != fdc->fd))
1845b6e5f28eSPeter Wemm 		device_printf(fd->dev, "confused fd pointers\n");
18468177437dSPoul-Henning Kamp 	read = bp->bio_cmd == BIO_READ;
184756a23089SPoul-Henning Kamp 	if (read)
184856a23089SPoul-Henning Kamp 		idf = ISADMA_READ;
184956a23089SPoul-Henning Kamp 	else
185056a23089SPoul-Henning Kamp 		idf = ISADMA_WRITE;
185164860614SJoerg Wunsch 	format = bp->bio_cmd == BIO_FORMAT;
1852f664aeeeSJoerg Wunsch 	rdsectid = bp->bio_cmd == BIO_RDSECTID;
1853fb35bd37SJoerg Wunsch 	if (format)
18548177437dSPoul-Henning Kamp 		finfo = (struct fd_formb *)bp->bio_data;
18555b81b6b3SRodney W. Grimes 	TRACE1("fd%d", fdu);
18565b81b6b3SRodney W. Grimes 	TRACE1("[%s]", fdstates[fdc->state]);
18575b81b6b3SRodney W. Grimes 	TRACE1("(0x%x)", fd->flags);
18586182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle);
18596182fdbdSPeter Wemm 	fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
18605b81b6b3SRodney W. Grimes 	switch (fdc->state)
18615b81b6b3SRodney W. Grimes 	{
18625b81b6b3SRodney W. Grimes 	case DEVIDLE:
18635b81b6b3SRodney W. Grimes 	case FINDWORK:	/* we have found new work */
18645b81b6b3SRodney W. Grimes 		fdc->retry = 0;
18655b81b6b3SRodney W. Grimes 		fd->skip = 0;
18665b81b6b3SRodney W. Grimes 		fdc->fd = fd;
18675b81b6b3SRodney W. Grimes 		fdc->fdu = fdu;
18685f830ea2SJoerg Wunsch 		fdc->fdctl_wr(fdc, fd->ft->trans);
18693a2f7427SDavid Greenman 		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
1870246ed35dSJoerg Wunsch 		/*
1871246ed35dSJoerg Wunsch 		 * If the next drive has a motor startup pending, then
1872246ed35dSJoerg Wunsch 		 * it will start up in its own good time.
1873246ed35dSJoerg Wunsch 		 */
18746182fdbdSPeter Wemm 		if(fd->flags & FD_MOTOR_WAIT) {
18755b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
1876246ed35dSJoerg Wunsch 			return (0); /* will return later */
18775b81b6b3SRodney W. Grimes 		}
1878246ed35dSJoerg Wunsch 		/*
1879246ed35dSJoerg Wunsch 		 * Maybe if it's not starting, it SHOULD be starting.
1880246ed35dSJoerg Wunsch 		 */
18815b81b6b3SRodney W. Grimes 		if (!(fd->flags & FD_MOTOR))
18825b81b6b3SRodney W. Grimes 		{
18835b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
18846182fdbdSPeter Wemm 			fd_turnon(fd);
1885246ed35dSJoerg Wunsch 			return (0); /* will return later */
18865b81b6b3SRodney W. Grimes 		}
18875b81b6b3SRodney W. Grimes 		else	/* at least make sure we are selected */
18885b81b6b3SRodney W. Grimes 		{
18896182fdbdSPeter Wemm 			set_motor(fdc, fd->fdsu, TURNON);
18905b81b6b3SRodney W. Grimes 		}
18915c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
18925c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
18935c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
18945c1a1eaeSBruce Evans 		} else
18955e235068SJordan K. Hubbard 			fdc->state = DOSEEK;
1896246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
1897fb35bd37SJoerg Wunsch 
18985b81b6b3SRodney W. Grimes 	case DOSEEK:
1899fb35bd37SJoerg Wunsch 		blknum = bp->bio_pblkno + fd->skip / fdblk;
1900fb35bd37SJoerg Wunsch 		cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1901fb35bd37SJoerg Wunsch 		if (cylinder == fd->track)
19025b81b6b3SRodney W. Grimes 		{
19035b81b6b3SRodney W. Grimes 			fdc->state = SEEKCOMPLETE;
1904246ed35dSJoerg Wunsch 			return (1); /* will return immediately */
19055b81b6b3SRodney W. Grimes 		}
19066182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK,
1907fb35bd37SJoerg Wunsch 			   fd->fdsu, cylinder * fd->ft->steptrac,
1908dc5df763SJoerg Wunsch 			   0))
1909dc8603e3SJoerg Wunsch 		{
1910dc8603e3SJoerg Wunsch 			/*
1911246ed35dSJoerg Wunsch 			 * Seek command not accepted, looks like
1912dc8603e3SJoerg Wunsch 			 * the FDC went off to the Saints...
1913dc8603e3SJoerg Wunsch 			 */
1914dc8603e3SJoerg Wunsch 			fdc->retry = 6;	/* try a reset */
19156182fdbdSPeter Wemm 			return(retrier(fdc));
1916dc8603e3SJoerg Wunsch 		}
1917dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
19185b81b6b3SRodney W. Grimes 		fdc->state = SEEKWAIT;
19195b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
1920fb35bd37SJoerg Wunsch 
19215b81b6b3SRodney W. Grimes 	case SEEKWAIT:
19225b81b6b3SRodney W. Grimes 		/* allow heads to settle */
19236182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 16);
19245b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
19255b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
1926fb35bd37SJoerg Wunsch 
1927246ed35dSJoerg Wunsch 	case SEEKCOMPLETE : /* seek done, start DMA */
1928fb35bd37SJoerg Wunsch 		blknum = bp->bio_pblkno + fd->skip / fdblk;
1929fb35bd37SJoerg Wunsch 		cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1930fb35bd37SJoerg Wunsch 
1931246ed35dSJoerg Wunsch 		/* Make sure seek really happened. */
19326182fdbdSPeter Wemm 		if(fd->track == FD_NO_TRACK) {
1933fb35bd37SJoerg Wunsch 			int descyl = cylinder * fd->ft->steptrac;
19343a2f7427SDavid Greenman 			do {
19353a2f7427SDavid Greenman 				/*
1936dc5df763SJoerg Wunsch 				 * This might be a "ready changed" interrupt,
1937dc5df763SJoerg Wunsch 				 * which cannot really happen since the
1938dc5df763SJoerg Wunsch 				 * RDY pin is hardwired to + 5 volts.  This
1939dc5df763SJoerg Wunsch 				 * generally indicates a "bouncing" intr
1940dc5df763SJoerg Wunsch 				 * line, so do one of the following:
1941dc5df763SJoerg Wunsch 				 *
1942dc5df763SJoerg Wunsch 				 * When running on an enhanced FDC that is
1943dc5df763SJoerg Wunsch 				 * known to not go stuck after responding
1944dc5df763SJoerg Wunsch 				 * with INVALID, fetch all interrupt states
1945dc5df763SJoerg Wunsch 				 * until seeing either an INVALID or a
1946dc5df763SJoerg Wunsch 				 * real interrupt condition.
1947dc5df763SJoerg Wunsch 				 *
1948dc5df763SJoerg Wunsch 				 * When running on a dumb old NE765, give
1949dc5df763SJoerg Wunsch 				 * up immediately.  The controller will
1950dc5df763SJoerg Wunsch 				 * provide up to four dummy RC interrupt
1951dc5df763SJoerg Wunsch 				 * conditions right after reset (for the
1952dc5df763SJoerg Wunsch 				 * corresponding four drives), so this is
1953dc5df763SJoerg Wunsch 				 * our only chance to get notice that it
1954dc5df763SJoerg Wunsch 				 * was not the FDC that caused the interrupt.
19553a2f7427SDavid Greenman 				 */
1956dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, &cyl)
1957dc5df763SJoerg Wunsch 				    == FD_NOT_VALID)
1958246ed35dSJoerg Wunsch 					return (0); /* will return later */
1959dc5df763SJoerg Wunsch 				if(fdc->fdct == FDC_NE765
1960dc5df763SJoerg Wunsch 				   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1961246ed35dSJoerg Wunsch 					return (0); /* hope for a real intr */
19623a2f7427SDavid Greenman 			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1963dc5df763SJoerg Wunsch 
19646182fdbdSPeter Wemm 			if (0 == descyl) {
1965dc5df763SJoerg Wunsch 				int failed = 0;
19663a2f7427SDavid Greenman 				/*
19673a2f7427SDavid Greenman 				 * seek to cyl 0 requested; make sure we are
19683a2f7427SDavid Greenman 				 * really there
19693a2f7427SDavid Greenman 				 */
1970dc5df763SJoerg Wunsch 				if (fd_sense_drive_status(fdc, &st3))
1971dc5df763SJoerg Wunsch 					failed = 1;
19723a2f7427SDavid Greenman 				if ((st3 & NE7_ST3_T0) == 0) {
19733a2f7427SDavid Greenman 					printf(
19743a2f7427SDavid Greenman 		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
19753a2f7427SDavid Greenman 					       fdu, st3, NE7_ST3BITS);
1976dc5df763SJoerg Wunsch 					failed = 1;
1977dc5df763SJoerg Wunsch 				}
1978dc5df763SJoerg Wunsch 
19796182fdbdSPeter Wemm 				if (failed) {
19803a2f7427SDavid Greenman 					if(fdc->retry < 3)
19813a2f7427SDavid Greenman 						fdc->retry = 3;
19826182fdbdSPeter Wemm 					return (retrier(fdc));
19833a2f7427SDavid Greenman 				}
19843a2f7427SDavid Greenman 			}
1985dc5df763SJoerg Wunsch 
19866182fdbdSPeter Wemm 			if (cyl != descyl) {
19873a2f7427SDavid Greenman 				printf(
19883a2f7427SDavid Greenman 		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
19892d9d0204SRodney W. Grimes 				       fdu, descyl, cyl, st0);
1990e5d7d243SBruce Evans 				if (fdc->retry < 3)
1991e5d7d243SBruce Evans 					fdc->retry = 3;
19926182fdbdSPeter Wemm 				return (retrier(fdc));
19935b81b6b3SRodney W. Grimes 			}
19945b81b6b3SRodney W. Grimes 		}
19955b81b6b3SRodney W. Grimes 
1996fb35bd37SJoerg Wunsch 		fd->track = cylinder;
1997fb35bd37SJoerg Wunsch 		if (format)
1998fb35bd37SJoerg Wunsch 			fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1999fb35bd37SJoerg Wunsch 			    - (char *)finfo;
2000250300ebSJoerg Wunsch 		if (!rdsectid && !(fdc->flags & FDC_NODMA))
20018177437dSPoul-Henning Kamp 			isa_dmastart(idf, bp->bio_data+fd->skip,
20028177437dSPoul-Henning Kamp 				format ? bp->bio_bcount : fdblk, fdc->dmachan);
2003fb35bd37SJoerg Wunsch 		blknum = bp->bio_pblkno + fd->skip / fdblk;
20045b81b6b3SRodney W. Grimes 		sectrac = fd->ft->sectrac;
20055b81b6b3SRodney W. Grimes 		sec = blknum %  (sectrac * fd->ft->heads);
20065b81b6b3SRodney W. Grimes 		head = sec / sectrac;
20075b81b6b3SRodney W. Grimes 		sec = sec % sectrac + 1;
20083a2f7427SDavid Greenman 		fd->hddrv = ((head&1)<<2)+fdu;
20093a2f7427SDavid Greenman 
2010250300ebSJoerg Wunsch 		if(format || !(read || rdsectid))
20113a2f7427SDavid Greenman 		{
20123a2f7427SDavid Greenman 			/* make sure the drive is writable */
2013dc5df763SJoerg Wunsch 			if(fd_sense_drive_status(fdc, &st3) != 0)
2014dc8603e3SJoerg Wunsch 			{
2015dc8603e3SJoerg Wunsch 				/* stuck controller? */
20165f830ea2SJoerg Wunsch 				if (!(fdc->flags & FDC_NODMA))
201756a23089SPoul-Henning Kamp 					isa_dmadone(idf,
20188177437dSPoul-Henning Kamp 						    bp->bio_data + fd->skip,
20198177437dSPoul-Henning Kamp 						    format ? bp->bio_bcount : fdblk,
20205c1a1eaeSBruce Evans 						    fdc->dmachan);
2021dc8603e3SJoerg Wunsch 				fdc->retry = 6;	/* reset the beast */
20226182fdbdSPeter Wemm 				return (retrier(fdc));
2023dc8603e3SJoerg Wunsch 			}
20243a2f7427SDavid Greenman 			if(st3 & NE7_ST3_WP)
20253a2f7427SDavid Greenman 			{
20263a2f7427SDavid Greenman 				/*
20273a2f7427SDavid Greenman 				 * XXX YES! this is ugly.
20283a2f7427SDavid Greenman 				 * in order to force the current operation
20293a2f7427SDavid Greenman 				 * to fail, we will have to fake an FDC
20303a2f7427SDavid Greenman 				 * error - all error handling is done
20313a2f7427SDavid Greenman 				 * by the retrier()
20323a2f7427SDavid Greenman 				 */
20333a2f7427SDavid Greenman 				fdc->status[0] = NE7_ST0_IC_AT;
20343a2f7427SDavid Greenman 				fdc->status[1] = NE7_ST1_NW;
20353a2f7427SDavid Greenman 				fdc->status[2] = 0;
20363a2f7427SDavid Greenman 				fdc->status[3] = fd->track;
20373a2f7427SDavid Greenman 				fdc->status[4] = head;
20383a2f7427SDavid Greenman 				fdc->status[5] = sec;
20393a2f7427SDavid Greenman 				fdc->retry = 8;	/* break out immediately */
20403a2f7427SDavid Greenman 				fdc->state = IOTIMEDOUT; /* not really... */
2041246ed35dSJoerg Wunsch 				return (1); /* will return immediately */
20423a2f7427SDavid Greenman 			}
20433a2f7427SDavid Greenman 		}
20445b81b6b3SRodney W. Grimes 
20456182fdbdSPeter Wemm 		if (format) {
20465f830ea2SJoerg Wunsch 			if (fdc->flags & FDC_NODMA) {
20475f830ea2SJoerg Wunsch 				/*
20485f830ea2SJoerg Wunsch 				 * This seems to be necessary for
20495f830ea2SJoerg Wunsch 				 * whatever obscure reason; if we omit
20505f830ea2SJoerg Wunsch 				 * it, we end up filling the sector ID
20515f830ea2SJoerg Wunsch 				 * fields of the newly formatted track
20525f830ea2SJoerg Wunsch 				 * entirely with garbage, causing
20535f830ea2SJoerg Wunsch 				 * `wrong cylinder' errors all over
20545f830ea2SJoerg Wunsch 				 * the place when trying to read them
20555f830ea2SJoerg Wunsch 				 * back.
20565f830ea2SJoerg Wunsch 				 *
20575f830ea2SJoerg Wunsch 				 * Umpf.
20585f830ea2SJoerg Wunsch 				 */
20598177437dSPoul-Henning Kamp 				SET_BCDR(fdc, 1, bp->bio_bcount, 0);
20605f830ea2SJoerg Wunsch 
20618177437dSPoul-Henning Kamp 				(void)fdcpio(fdc,bp->bio_cmd,
20628177437dSPoul-Henning Kamp 					bp->bio_data+fd->skip,
20638177437dSPoul-Henning Kamp 					bp->bio_bcount);
20645f830ea2SJoerg Wunsch 
20655f830ea2SJoerg Wunsch 			}
2066b39c878eSAndrey A. Chernov 			/* formatting */
20676182fdbdSPeter Wemm 			if(fd_cmd(fdc, 6,  NE7CMD_FORMAT, head << 2 | fdu,
2068dc5df763SJoerg Wunsch 				  finfo->fd_formb_secshift,
2069dc5df763SJoerg Wunsch 				  finfo->fd_formb_nsecs,
2070dc5df763SJoerg Wunsch 				  finfo->fd_formb_gaplen,
20716182fdbdSPeter Wemm 				  finfo->fd_formb_fillbyte, 0)) {
2072dc8603e3SJoerg Wunsch 				/* controller fell over */
20735f830ea2SJoerg Wunsch 				if (!(fdc->flags & FDC_NODMA))
207456a23089SPoul-Henning Kamp 					isa_dmadone(idf,
20758177437dSPoul-Henning Kamp 						    bp->bio_data + fd->skip,
20768177437dSPoul-Henning Kamp 						    format ? bp->bio_bcount : fdblk,
20775c1a1eaeSBruce Evans 						    fdc->dmachan);
2078dc8603e3SJoerg Wunsch 				fdc->retry = 6;
20796182fdbdSPeter Wemm 				return (retrier(fdc));
2080dc8603e3SJoerg Wunsch 			}
2081250300ebSJoerg Wunsch 		} else if (rdsectid) {
2082250300ebSJoerg Wunsch 			if (fd_cmd(fdc, 2, NE7CMD_READID, head << 2 | fdu, 0)) {
2083250300ebSJoerg Wunsch 				/* controller jamming */
2084250300ebSJoerg Wunsch 				fdc->retry = 6;
2085250300ebSJoerg Wunsch 				return (retrier(fdc));
2086250300ebSJoerg Wunsch 			}
20876182fdbdSPeter Wemm 		} else {
2088250300ebSJoerg Wunsch 			/* read or write operation */
20893b178206SWarner Losh 			if (fdc->flags & FDC_NODMA) {
209069acd21dSWarner Losh 				/*
2091246ed35dSJoerg Wunsch 				 * This seems to be necessary even when
2092246ed35dSJoerg Wunsch 				 * reading data.
209369acd21dSWarner Losh 				 */
20943b178206SWarner Losh 				SET_BCDR(fdc, 1, fdblk, 0);
209569acd21dSWarner Losh 
209669acd21dSWarner Losh 				/*
2097246ed35dSJoerg Wunsch 				 * Perform the write pseudo-DMA before
2098246ed35dSJoerg Wunsch 				 * the WRITE command is sent.
209969acd21dSWarner Losh 				 */
210069acd21dSWarner Losh 				if (!read)
21018177437dSPoul-Henning Kamp 					(void)fdcpio(fdc,bp->bio_cmd,
21028177437dSPoul-Henning Kamp 					    bp->bio_data+fd->skip,
210369acd21dSWarner Losh 					    fdblk);
210469acd21dSWarner Losh 			}
21056182fdbdSPeter Wemm 			if (fd_cmd(fdc, 9,
2106dc5df763SJoerg Wunsch 				   (read ? NE7CMD_READ : NE7CMD_WRITE),
2107dc5df763SJoerg Wunsch 				   head << 2 | fdu,  /* head & unit */
2108dc5df763SJoerg Wunsch 				   fd->track,        /* track */
2109dc5df763SJoerg Wunsch 				   head,
2110dc5df763SJoerg Wunsch 				   sec,              /* sector + 1 */
2111dc5df763SJoerg Wunsch 				   fd->ft->secsize,  /* sector size */
2112dc5df763SJoerg Wunsch 				   sectrac,          /* sectors/track */
2113dc5df763SJoerg Wunsch 				   fd->ft->gap,      /* gap size */
2114dc5df763SJoerg Wunsch 				   fd->ft->datalen,  /* data length */
21156182fdbdSPeter Wemm 				   0)) {
2116dc8603e3SJoerg Wunsch 				/* the beast is sleeping again */
21175f830ea2SJoerg Wunsch 				if (!(fdc->flags & FDC_NODMA))
211856a23089SPoul-Henning Kamp 					isa_dmadone(idf,
21198177437dSPoul-Henning Kamp 						    bp->bio_data + fd->skip,
21208177437dSPoul-Henning Kamp 						    format ? bp->bio_bcount : fdblk,
21215c1a1eaeSBruce Evans 						    fdc->dmachan);
2122dc8603e3SJoerg Wunsch 				fdc->retry = 6;
21236182fdbdSPeter Wemm 				return (retrier(fdc));
21245b81b6b3SRodney W. Grimes 			}
2125b39c878eSAndrey A. Chernov 		}
2126250300ebSJoerg Wunsch 		if (!rdsectid && (fdc->flags & FDC_NODMA))
212769acd21dSWarner Losh 			/*
2128246ed35dSJoerg Wunsch 			 * If this is a read, then simply await interrupt
2129246ed35dSJoerg Wunsch 			 * before performing PIO.
213069acd21dSWarner Losh 			 */
21318177437dSPoul-Henning Kamp 			if (read && !fdcpio(fdc,bp->bio_cmd,
21328177437dSPoul-Henning Kamp 			    bp->bio_data+fd->skip,fdblk)) {
21333b178206SWarner Losh 				fd->tohandle = timeout(fd_iotimeout, fdc, hz);
213469acd21dSWarner Losh 				return(0);      /* will return later */
213564860614SJoerg Wunsch 			}
213669acd21dSWarner Losh 
213769acd21dSWarner Losh 		/*
2138246ed35dSJoerg Wunsch 		 * Write (or format) operation will fall through and
2139246ed35dSJoerg Wunsch 		 * await completion interrupt.
214069acd21dSWarner Losh 		 */
21415b81b6b3SRodney W. Grimes 		fdc->state = IOCOMPLETE;
21426182fdbdSPeter Wemm 		fd->tohandle = timeout(fd_iotimeout, fdc, hz);
21435b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
2144fb35bd37SJoerg Wunsch 
214569acd21dSWarner Losh 	case PIOREAD:
214669acd21dSWarner Losh 		/*
2147246ed35dSJoerg Wunsch 		 * Actually perform the PIO read.  The IOCOMPLETE case
214869acd21dSWarner Losh 		 * removes the timeout for us.
214969acd21dSWarner Losh 		 */
21508177437dSPoul-Henning Kamp 		(void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk);
215169acd21dSWarner Losh 		fdc->state = IOCOMPLETE;
215269acd21dSWarner Losh 		/* FALLTHROUGH */
2153246ed35dSJoerg Wunsch 	case IOCOMPLETE: /* IO done, post-analyze */
21546182fdbdSPeter Wemm 		untimeout(fd_iotimeout, fdc, fd->tohandle);
2155dc5df763SJoerg Wunsch 
215664860614SJoerg Wunsch 		if (fd_read_status(fdc)) {
2157250300ebSJoerg Wunsch 			if (!rdsectid && !(fdc->flags & FDC_NODMA))
21588177437dSPoul-Henning Kamp 				isa_dmadone(idf, bp->bio_data + fd->skip,
21598177437dSPoul-Henning Kamp 					    format ? bp->bio_bcount : fdblk,
21605c1a1eaeSBruce Evans 					    fdc->dmachan);
2161dc5df763SJoerg Wunsch 			if (fdc->retry < 6)
2162dc5df763SJoerg Wunsch 				fdc->retry = 6;	/* force a reset */
21636182fdbdSPeter Wemm 			return (retrier(fdc));
21645b81b6b3SRodney W. Grimes   		}
2165dc5df763SJoerg Wunsch 
21663a2f7427SDavid Greenman 		fdc->state = IOTIMEDOUT;
2167dc5df763SJoerg Wunsch 
21683a2f7427SDavid Greenman 		/* FALLTHROUGH */
21693a2f7427SDavid Greenman 	case IOTIMEDOUT:
2170250300ebSJoerg Wunsch 		if (!rdsectid && !(fdc->flags & FDC_NODMA))
21718177437dSPoul-Henning Kamp 			isa_dmadone(idf, bp->bio_data + fd->skip,
21728177437dSPoul-Henning Kamp 				format ? bp->bio_bcount : fdblk, fdc->dmachan);
21736182fdbdSPeter Wemm 		if (fdc->status[0] & NE7_ST0_IC) {
21743a2f7427SDavid Greenman                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
21753a2f7427SDavid Greenman 			    && fdc->status[1] & NE7_ST1_OR) {
2176b39c878eSAndrey A. Chernov                                 /*
21773fef646eSJoerg Wunsch 				 * DMA overrun. Someone hogged the bus and
21783fef646eSJoerg Wunsch 				 * didn't release it in time for the next
21793fef646eSJoerg Wunsch 				 * FDC transfer.
21803fef646eSJoerg Wunsch 				 *
21813fef646eSJoerg Wunsch 				 * We normally restart this without bumping
21823fef646eSJoerg Wunsch 				 * the retry counter.  However, in case
21833fef646eSJoerg Wunsch 				 * something is seriously messed up (like
21843fef646eSJoerg Wunsch 				 * broken hardware), we rather limit the
21853fef646eSJoerg Wunsch 				 * number of retries so the IO operation
21863fef646eSJoerg Wunsch 				 * doesn't block indefinately.
2187b39c878eSAndrey A. Chernov 				 */
21883fef646eSJoerg Wunsch 				if (fdc->dma_overruns++ < FDC_DMAOV_MAX) {
2189b39c878eSAndrey A. Chernov 					fdc->state = SEEKCOMPLETE;
2190246ed35dSJoerg Wunsch 					return (1);/* will return immediately */
21913fef646eSJoerg Wunsch 				} /* else fall through */
2192b39c878eSAndrey A. Chernov                         }
21933fef646eSJoerg Wunsch 			if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
21943a2f7427SDavid Greenman 				&& fdc->retry < 6)
21953a2f7427SDavid Greenman 				fdc->retry = 6;	/* force a reset */
21963a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
21973a2f7427SDavid Greenman 				&& fdc->status[2] & NE7_ST2_WC
21983a2f7427SDavid Greenman 				&& fdc->retry < 3)
21993a2f7427SDavid Greenman 				fdc->retry = 3;	/* force recalibrate */
22006182fdbdSPeter Wemm 			return (retrier(fdc));
22015b81b6b3SRodney W. Grimes 		}
22025b81b6b3SRodney W. Grimes 		/* All OK */
2203250300ebSJoerg Wunsch 		if (rdsectid) {
2204250300ebSJoerg Wunsch 			/* copy out ID field contents */
220564860614SJoerg Wunsch 			idp = (struct fdc_readid *)bp->bio_data;
2206250300ebSJoerg Wunsch 			idp->cyl = fdc->status[3];
2207250300ebSJoerg Wunsch 			idp->head = fdc->status[4];
2208250300ebSJoerg Wunsch 			idp->sec = fdc->status[5];
2209250300ebSJoerg Wunsch 			idp->secshift = fdc->status[6];
2210250300ebSJoerg Wunsch 		}
22113fef646eSJoerg Wunsch 		/* Operation successful, retry DMA overruns again next time. */
22123fef646eSJoerg Wunsch 		fdc->dma_overruns = 0;
22133a2f7427SDavid Greenman 		fd->skip += fdblk;
2214fb35bd37SJoerg Wunsch 		if (!rdsectid && !format && fd->skip < bp->bio_bcount) {
22155b81b6b3SRodney W. Grimes 			/* set up next transfer */
22165b81b6b3SRodney W. Grimes 			fdc->state = DOSEEK;
22176182fdbdSPeter Wemm 		} else {
22185b81b6b3SRodney W. Grimes 			/* ALL DONE */
22195b81b6b3SRodney W. Grimes 			fd->skip = 0;
2220fb35bd37SJoerg Wunsch 			bp->bio_resid = 0;
2221e93e63cbSBruce Evans 			fdc->bp = NULL;
22225f830ea2SJoerg Wunsch 			device_unbusy(fd->dev);
2223a468031cSPoul-Henning Kamp 			biofinish(bp, &fd->device_stats, 0);
22245b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
22255b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
22265b81b6b3SRodney W. Grimes 			fdc->state = FINDWORK;
22275b81b6b3SRodney W. Grimes 		}
2228246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
2229fb35bd37SJoerg Wunsch 
22305b81b6b3SRodney W. Grimes 	case RESETCTLR:
22313a2f7427SDavid Greenman 		fdc_reset(fdc);
22325b81b6b3SRodney W. Grimes 		fdc->retry++;
22335c1a1eaeSBruce Evans 		fdc->state = RESETCOMPLETE;
2234246ed35dSJoerg Wunsch 		return (0);	/* will return later */
2235fb35bd37SJoerg Wunsch 
22365c1a1eaeSBruce Evans 	case RESETCOMPLETE:
22375c1a1eaeSBruce Evans 		/*
22385c1a1eaeSBruce Evans 		 * Discard all the results from the reset so that they
22395c1a1eaeSBruce Evans 		 * can't cause an unexpected interrupt later.
22405c1a1eaeSBruce Evans 		 */
22410e317d05SJoerg Wunsch 		for (i = 0; i < 4; i++)
22420e317d05SJoerg Wunsch 			(void)fd_sense_int(fdc, &st0, &cyl);
22435c1a1eaeSBruce Evans 		fdc->state = STARTRECAL;
2244fb35bd37SJoerg Wunsch 		/* FALLTHROUGH */
22455c1a1eaeSBruce Evans 	case STARTRECAL:
22466182fdbdSPeter Wemm 		if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) {
2247dc8603e3SJoerg Wunsch 			/* arrgl */
2248dc8603e3SJoerg Wunsch 			fdc->retry = 6;
22496182fdbdSPeter Wemm 			return (retrier(fdc));
2250dc8603e3SJoerg Wunsch 		}
22515b81b6b3SRodney W. Grimes 		fdc->state = RECALWAIT;
22525b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
2253fb35bd37SJoerg Wunsch 
22545b81b6b3SRodney W. Grimes 	case RECALWAIT:
22555b81b6b3SRodney W. Grimes 		/* allow heads to settle */
22566182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 8);
22575b81b6b3SRodney W. Grimes 		fdc->state = RECALCOMPLETE;
22585b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
2259fb35bd37SJoerg Wunsch 
22605b81b6b3SRodney W. Grimes 	case RECALCOMPLETE:
22613a2f7427SDavid Greenman 		do {
22623a2f7427SDavid Greenman 			/*
2263dc5df763SJoerg Wunsch 			 * See SEEKCOMPLETE for a comment on this:
22643a2f7427SDavid Greenman 			 */
2265dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
2266246ed35dSJoerg Wunsch 				return (0); /* will return later */
2267dc5df763SJoerg Wunsch 			if(fdc->fdct == FDC_NE765
2268dc5df763SJoerg Wunsch 			   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
2269246ed35dSJoerg Wunsch 				return (0); /* hope for a real intr */
22703a2f7427SDavid Greenman 		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
22713a2f7427SDavid Greenman 		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
22725b81b6b3SRodney W. Grimes 		{
2273dc8603e3SJoerg Wunsch 			if(fdc->retry > 3)
2274dc8603e3SJoerg Wunsch 				/*
2275246ed35dSJoerg Wunsch 				 * A recalibrate from beyond cylinder 77
2276dc8603e3SJoerg Wunsch 				 * will "fail" due to the FDC limitations;
2277dc8603e3SJoerg Wunsch 				 * since people used to complain much about
2278dc8603e3SJoerg Wunsch 				 * the failure message, try not logging
2279dc8603e3SJoerg Wunsch 				 * this one if it seems to be the first
2280246ed35dSJoerg Wunsch 				 * time in a line.
2281dc8603e3SJoerg Wunsch 				 */
2282dc8603e3SJoerg Wunsch 				printf("fd%d: recal failed ST0 %b cyl %d\n",
2283dc8603e3SJoerg Wunsch 				       fdu, st0, NE7_ST0BITS, cyl);
22843a2f7427SDavid Greenman 			if(fdc->retry < 3) fdc->retry = 3;
22856182fdbdSPeter Wemm 			return (retrier(fdc));
22865b81b6b3SRodney W. Grimes 		}
22875b81b6b3SRodney W. Grimes 		fd->track = 0;
22885b81b6b3SRodney W. Grimes 		/* Seek (probably) necessary */
22895b81b6b3SRodney W. Grimes 		fdc->state = DOSEEK;
2290246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
2291fb35bd37SJoerg Wunsch 
22925b81b6b3SRodney W. Grimes 	case MOTORWAIT:
22935b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
22945b81b6b3SRodney W. Grimes 		{
22955b81b6b3SRodney W. Grimes 			return (0); /* time's not up yet */
22965b81b6b3SRodney W. Grimes 		}
22975c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
22985c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
22995c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
2300fb35bd37SJoerg Wunsch 		} else
2301fb35bd37SJoerg Wunsch 			fdc->state = DOSEEK;
2302246ed35dSJoerg Wunsch 		return (1);	/* will return immediately */
2303fb35bd37SJoerg Wunsch 
23045b81b6b3SRodney W. Grimes 	default:
2305b6e5f28eSPeter Wemm 		device_printf(fdc->fdc_dev, "unexpected FD int->");
230664860614SJoerg Wunsch 		if (fd_read_status(fdc) == 0)
2307a838d83dSBruce Evans 			printf("FDC status :%x %x %x %x %x %x %x   ",
23085b81b6b3SRodney W. Grimes 			       fdc->status[0],
23095b81b6b3SRodney W. Grimes 			       fdc->status[1],
23105b81b6b3SRodney W. Grimes 			       fdc->status[2],
23115b81b6b3SRodney W. Grimes 			       fdc->status[3],
23125b81b6b3SRodney W. Grimes 			       fdc->status[4],
23135b81b6b3SRodney W. Grimes 			       fdc->status[5],
23145b81b6b3SRodney W. Grimes 			       fdc->status[6] );
23153a2f7427SDavid Greenman 		else
2316dac0f2dbSJoerg Wunsch 			printf("No status available   ");
2317dac0f2dbSJoerg Wunsch 		if (fd_sense_int(fdc, &st0, &cyl) != 0)
2318dac0f2dbSJoerg Wunsch 		{
2319dac0f2dbSJoerg Wunsch 			printf("[controller is dead now]\n");
2320246ed35dSJoerg Wunsch 			return (0); /* will return later */
23215b81b6b3SRodney W. Grimes 		}
2322dac0f2dbSJoerg Wunsch 		printf("ST0 = %x, PCN = %x\n", st0, cyl);
2323246ed35dSJoerg Wunsch 		return (0);	/* will return later */
2324dac0f2dbSJoerg Wunsch 	}
2325246ed35dSJoerg Wunsch 	/* noone should ever get here */
23265b81b6b3SRodney W. Grimes }
23275b81b6b3SRodney W. Grimes 
2328aaf08d94SGarrett Wollman static int
23296182fdbdSPeter Wemm retrier(struct fdc_data *fdc)
23305b81b6b3SRodney W. Grimes {
23318177437dSPoul-Henning Kamp 	struct bio *bp;
23326182fdbdSPeter Wemm 	struct fd_data *fd;
23336182fdbdSPeter Wemm 	int fdu;
23345b81b6b3SRodney W. Grimes 
2335e93e63cbSBruce Evans 	bp = fdc->bp;
23365b81b6b3SRodney W. Grimes 
23376182fdbdSPeter Wemm 	/* XXX shouldn't this be cached somewhere?  */
23388177437dSPoul-Henning Kamp 	fdu = FDUNIT(minor(bp->bio_dev));
23396182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
23406182fdbdSPeter Wemm 	if (fd->options & FDOPT_NORETRY)
23413a2f7427SDavid Greenman 		goto fail;
23426182fdbdSPeter Wemm 
23436182fdbdSPeter Wemm 	switch (fdc->retry) {
23445b81b6b3SRodney W. Grimes 	case 0: case 1: case 2:
23455b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
23465b81b6b3SRodney W. Grimes 		break;
23475b81b6b3SRodney W. Grimes 	case 3: case 4: case 5:
23485b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
23495b81b6b3SRodney W. Grimes 		break;
23505b81b6b3SRodney W. Grimes 	case 6:
23515b81b6b3SRodney W. Grimes 		fdc->state = RESETCTLR;
23525b81b6b3SRodney W. Grimes 		break;
23535b81b6b3SRodney W. Grimes 	case 7:
23545b81b6b3SRodney W. Grimes 		break;
23555b81b6b3SRodney W. Grimes 	default:
23563a2f7427SDavid Greenman 	fail:
2357fb35bd37SJoerg Wunsch 		if ((fd->options & FDOPT_NOERRLOG) == 0) {
2358fb35bd37SJoerg Wunsch 			diskerr(bp, "hard error", fdc->fd->skip / DEV_BSIZE,
23593a2f7427SDavid Greenman 				(struct disklabel *)NULL);
2360fb35bd37SJoerg Wunsch 			if (fdc->flags & FDC_STAT_VALID) {
2361dc5df763SJoerg Wunsch 				printf(
2362a838d83dSBruce Evans 				" (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n",
2363dc5df763SJoerg Wunsch 				       fdc->status[0], NE7_ST0BITS,
2364dc5df763SJoerg Wunsch 				       fdc->status[1], NE7_ST1BITS,
2365dc5df763SJoerg Wunsch 				       fdc->status[2], NE7_ST2BITS,
2366dc5df763SJoerg Wunsch 				       fdc->status[3], fdc->status[4],
2367dc5df763SJoerg Wunsch 				       fdc->status[5]);
2368dc5df763SJoerg Wunsch 			}
2369dc5df763SJoerg Wunsch 			else
2370dc5df763SJoerg Wunsch 				printf(" (No status)\n");
23715b81b6b3SRodney W. Grimes 		}
23722995d110SJoerg Wunsch 		if ((fd->options & FDOPT_NOERROR) == 0) {
23738177437dSPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
23748177437dSPoul-Henning Kamp 			bp->bio_error = EIO;
2375fb35bd37SJoerg Wunsch 			bp->bio_resid = bp->bio_bcount - fdc->fd->skip;
2376fb35bd37SJoerg Wunsch 		} else
2377fb35bd37SJoerg Wunsch 			bp->bio_resid = 0;
2378e93e63cbSBruce Evans 		fdc->bp = NULL;
23795b81b6b3SRodney W. Grimes 		fdc->fd->skip = 0;
23805f830ea2SJoerg Wunsch 		device_unbusy(fd->dev);
2381a468031cSPoul-Henning Kamp 		biofinish(bp, &fdc->fd->device_stats, 0);
238292ed385aSRodney W. Grimes 		fdc->state = FINDWORK;
23835c1a1eaeSBruce Evans 		fdc->flags |= FDC_NEEDS_RESET;
23845b81b6b3SRodney W. Grimes 		fdc->fd = (fd_p) 0;
23855b81b6b3SRodney W. Grimes 		fdc->fdu = -1;
238692ed385aSRodney W. Grimes 		return (1);
23875b81b6b3SRodney W. Grimes 	}
23885b81b6b3SRodney W. Grimes 	fdc->retry++;
23895b81b6b3SRodney W. Grimes 	return (1);
23905b81b6b3SRodney W. Grimes }
23915b81b6b3SRodney W. Grimes 
23921fdb6e6cSPoul-Henning Kamp static void
23931fdb6e6cSPoul-Henning Kamp fdbiodone(struct bio *bp)
23941fdb6e6cSPoul-Henning Kamp {
23951fdb6e6cSPoul-Henning Kamp 	wakeup(bp);
23961fdb6e6cSPoul-Henning Kamp }
23971fdb6e6cSPoul-Henning Kamp 
2398b39c878eSAndrey A. Chernov static int
2399f664aeeeSJoerg Wunsch fdmisccmd(dev_t dev, u_int cmd, void *data)
2400b39c878eSAndrey A. Chernov {
2401b39c878eSAndrey A. Chernov  	fdu_t fdu;
2402b39c878eSAndrey A. Chernov  	fd_p fd;
24031fdb6e6cSPoul-Henning Kamp 	struct bio *bp;
2404f664aeeeSJoerg Wunsch 	struct fd_formb *finfo;
2405f664aeeeSJoerg Wunsch 	struct fdc_readid *idfield;
24063a2f7427SDavid Greenman 	size_t fdblk;
2407b39c878eSAndrey A. Chernov 
2408b39c878eSAndrey A. Chernov  	fdu = FDUNIT(minor(dev));
24096182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
24103a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2411f664aeeeSJoerg Wunsch 	finfo = (struct fd_formb *)data;
2412f664aeeeSJoerg Wunsch 	idfield = (struct fdc_readid *)data;
2413b39c878eSAndrey A. Chernov 
2414fb35bd37SJoerg Wunsch 	bp = malloc(sizeof(struct bio), M_TEMP, M_ZERO);
2415b39c878eSAndrey A. Chernov 
2416b39c878eSAndrey A. Chernov 	/*
2417f664aeeeSJoerg Wunsch 	 * Set up a bio request for fdstrategy().  bio_blkno is faked
2418f664aeeeSJoerg Wunsch 	 * so that fdstrategy() will seek to the the requested
2419f664aeeeSJoerg Wunsch 	 * cylinder, and use the desired head.  Since we are not
2420f664aeeeSJoerg Wunsch 	 * interested in bioqdisksort() munging with our faked bio
2421f664aeeeSJoerg Wunsch 	 * request, we mark it as being an ordered request.
2422b39c878eSAndrey A. Chernov 	 */
2423f664aeeeSJoerg Wunsch 	bp->bio_cmd = cmd;
2424f664aeeeSJoerg Wunsch 	if (cmd == BIO_FORMAT) {
2425f664aeeeSJoerg Wunsch 		bp->bio_blkno =
2426f664aeeeSJoerg Wunsch 		    (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) +
2427f664aeeeSJoerg Wunsch 		     finfo->head * fd->ft->sectrac) *
2428f664aeeeSJoerg Wunsch 		    fdblk / DEV_BSIZE;
2429f664aeeeSJoerg Wunsch 		bp->bio_bcount = sizeof(struct fd_idfield_data) *
2430f664aeeeSJoerg Wunsch 		    finfo->fd_formb_nsecs;
2431f664aeeeSJoerg Wunsch 	} else if (cmd == BIO_RDSECTID) {
2432f664aeeeSJoerg Wunsch 		bp->bio_blkno =
2433f664aeeeSJoerg Wunsch 		    (idfield->cyl * (fd->ft->sectrac * fd->ft->heads) +
2434f664aeeeSJoerg Wunsch 		     idfield->head * fd->ft->sectrac) *
2435f664aeeeSJoerg Wunsch 		    fdblk / DEV_BSIZE;
2436250300ebSJoerg Wunsch 		bp->bio_bcount = sizeof(struct fdc_readid);
2437f664aeeeSJoerg Wunsch 	} else
2438f664aeeeSJoerg Wunsch 		panic("wrong cmd in fdmisccmd()");
2439f664aeeeSJoerg Wunsch 	bp->bio_data = data;
2440250300ebSJoerg Wunsch 	bp->bio_dev = dev;
2441250300ebSJoerg Wunsch 	bp->bio_done = fdbiodone;
2442f664aeeeSJoerg Wunsch 	bp->bio_flags = BIO_ORDERED;
2443250300ebSJoerg Wunsch 
2444f5f7ba03SJordan K. Hubbard 	/*
2445fb35bd37SJoerg Wunsch 	 * Now run the command.  The wait loop is a version of bufwait()
2446fb35bd37SJoerg Wunsch 	 * adapted for struct bio instead of struct buf and specialized
2447fb35bd37SJoerg Wunsch 	 * for the current context.
2448f5f7ba03SJordan K. Hubbard 	 */
2449f664aeeeSJoerg Wunsch 	fdstrategy(bp);
2450f664aeeeSJoerg Wunsch 	while ((bp->bio_flags & BIO_DONE) == 0)
2451f664aeeeSJoerg Wunsch 		tsleep(bp, PRIBIO, "fdcmd", 0);
2452f664aeeeSJoerg Wunsch 
2453f664aeeeSJoerg Wunsch 	free(bp, M_TEMP);
2454f664aeeeSJoerg Wunsch 	return (bp->bio_flags & BIO_ERROR ? bp->bio_error : 0);
2455f664aeeeSJoerg Wunsch }
24565b81b6b3SRodney W. Grimes 
24573e425b96SJulian Elischer static int
24584c34deeeSJoerg Wunsch fdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
2459f5f7ba03SJordan K. Hubbard {
2460f664aeeeSJoerg Wunsch  	fdu_t fdu;
2461f664aeeeSJoerg Wunsch  	fd_p fd;
2462f5f7ba03SJordan K. Hubbard 	struct fd_type *fdt;
2463fb35bd37SJoerg Wunsch 	struct disklabel *lp;
24642995d110SJoerg Wunsch 	struct fdc_status *fsp;
2465250300ebSJoerg Wunsch 	struct fdc_readid *rid;
2466f664aeeeSJoerg Wunsch 	size_t fdblk;
2467fb35bd37SJoerg Wunsch 	int error;
2468f5f7ba03SJordan K. Hubbard 
2469f664aeeeSJoerg Wunsch  	fdu = FDUNIT(minor(dev));
2470f664aeeeSJoerg Wunsch  	fd = devclass_get_softc(fd_devclass, fdu);
2471fb35bd37SJoerg Wunsch 
2472fb35bd37SJoerg Wunsch 
24733a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2474fb35bd37SJoerg Wunsch 	error = 0;
2475f5f7ba03SJordan K. Hubbard 
24766182fdbdSPeter Wemm 	switch (cmd) {
2477f5f7ba03SJordan K. Hubbard 	case DIOCGDINFO:
2478fb35bd37SJoerg Wunsch 		lp = malloc(sizeof(*lp), M_TEMP, M_ZERO);
2479fb35bd37SJoerg Wunsch 		lp->d_secsize = fdblk;
24806182fdbdSPeter Wemm 		fdt = fd->ft;
2481fb35bd37SJoerg Wunsch 		lp->d_secpercyl = fdt->size / fdt->tracks;
2482fb35bd37SJoerg Wunsch 		lp->d_type = DTYPE_FLOPPY;
2483fb35bd37SJoerg Wunsch 		if (readdisklabel(dkmodpart(dev, RAW_PART), lp) != NULL)
2484f5f7ba03SJordan K. Hubbard 			error = EINVAL;
2485fb35bd37SJoerg Wunsch 		else
2486fb35bd37SJoerg Wunsch 			*(struct disklabel *)addr = *lp;
2487fb35bd37SJoerg Wunsch 		free(lp, M_TEMP);
2488f5f7ba03SJordan K. Hubbard 		break;
2489f5f7ba03SJordan K. Hubbard 
2490f5f7ba03SJordan K. Hubbard 	case DIOCSDINFO:
2491f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2492fb35bd37SJoerg Wunsch 			return (EBADF);
2493fb35bd37SJoerg Wunsch 		/*
2494fb35bd37SJoerg Wunsch 		 * XXX perhaps should call setdisklabel() to do error checking
2495fb35bd37SJoerg Wunsch 		 * although there is nowhere to "set" the result.  Perhaps
2496fb35bd37SJoerg Wunsch 		 * should always just fail.
2497fb35bd37SJoerg Wunsch 		 */
2498f5f7ba03SJordan K. Hubbard 		break;
2499f5f7ba03SJordan K. Hubbard 
2500f5f7ba03SJordan K. Hubbard 	case DIOCWLABEL:
2501f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2502fb35bd37SJoerg Wunsch 			return (EBADF);
2503f5f7ba03SJordan K. Hubbard 		break;
2504f5f7ba03SJordan K. Hubbard 
2505f5f7ba03SJordan K. Hubbard 	case DIOCWDINFO:
2506fb35bd37SJoerg Wunsch 		if ((flag & FWRITE) == 0)
2507fb35bd37SJoerg Wunsch 			return (EBADF);
2508fb35bd37SJoerg Wunsch 		lp = malloc(DEV_BSIZE, M_TEMP, M_ZERO);
2509fb35bd37SJoerg Wunsch 		error = setdisklabel(lp, (struct disklabel *)addr, (u_long)0);
2510fb35bd37SJoerg Wunsch 		if (error != 0)
2511fb35bd37SJoerg Wunsch 			error = writedisklabel(dev, lp);
2512fb35bd37SJoerg Wunsch 		free(lp, M_TEMP);
2513b39c878eSAndrey A. Chernov 		break;
2514f664aeeeSJoerg Wunsch 
2515b39c878eSAndrey A. Chernov 	case FD_FORM:
2516b39c878eSAndrey A. Chernov 		if ((flag & FWRITE) == 0)
2517fb35bd37SJoerg Wunsch 			return (EBADF);	/* must be opened for writing */
2518fb35bd37SJoerg Wunsch 		if (((struct fd_formb *)addr)->format_version !=
2519b39c878eSAndrey A. Chernov 		    FD_FORMAT_VERSION)
2520fb35bd37SJoerg Wunsch 			return (EINVAL); /* wrong version of formatting prog */
2521f664aeeeSJoerg Wunsch 		error = fdmisccmd(dev, BIO_FORMAT, addr);
2522b39c878eSAndrey A. Chernov 		break;
2523b39c878eSAndrey A. Chernov 
2524b39c878eSAndrey A. Chernov 	case FD_GTYPE:                  /* get drive type */
25253e425b96SJulian Elischer 		*(struct fd_type *)addr = *fd->ft;
2526f5f7ba03SJordan K. Hubbard 		break;
2527f5f7ba03SJordan K. Hubbard 
25283a2f7427SDavid Greenman 	case FD_STYPE:                  /* set drive type */
25293a2f7427SDavid Greenman 		/* this is considered harmful; only allow for superuser */
2530f711d546SPoul-Henning Kamp 		if (suser(p) != 0)
2531fb35bd37SJoerg Wunsch 			return (EPERM);
25323e425b96SJulian Elischer 		*fd->ft = *(struct fd_type *)addr;
25333a2f7427SDavid Greenman 		break;
25343a2f7427SDavid Greenman 
25353a2f7427SDavid Greenman 	case FD_GOPTS:			/* get drive options */
25363e425b96SJulian Elischer 		*(int *)addr = fd->options;
25373a2f7427SDavid Greenman 		break;
25383a2f7427SDavid Greenman 
25393a2f7427SDavid Greenman 	case FD_SOPTS:			/* set drive options */
25403e425b96SJulian Elischer 		fd->options = *(int *)addr;
25413a2f7427SDavid Greenman 		break;
25423a2f7427SDavid Greenman 
2543f664aeeeSJoerg Wunsch #ifdef FDC_DEBUG
2544f664aeeeSJoerg Wunsch 	case FD_DEBUG:
25450e17a5bcSJoerg Wunsch 		if ((fd_debug != 0) != (*(int *)addr != 0)) {
25460e17a5bcSJoerg Wunsch 			fd_debug = (*(int *)addr != 0);
25470e17a5bcSJoerg Wunsch 			printf("fd%d: debugging turned %s\n",
25480e17a5bcSJoerg Wunsch 			    fd->fdu, fd_debug ? "on" : "off");
25490e17a5bcSJoerg Wunsch 		}
2550f664aeeeSJoerg Wunsch 		break;
2551f664aeeeSJoerg Wunsch #endif
2552f664aeeeSJoerg Wunsch 
25532995d110SJoerg Wunsch 	case FD_CLRERR:
25542995d110SJoerg Wunsch 		if (suser(p) != 0)
2555fb35bd37SJoerg Wunsch 			return (EPERM);
25562995d110SJoerg Wunsch 		fd->fdc->fdc_errs = 0;
25572995d110SJoerg Wunsch 		break;
25582995d110SJoerg Wunsch 
25592995d110SJoerg Wunsch 	case FD_GSTAT:
25602995d110SJoerg Wunsch 		fsp = (struct fdc_status *)addr;
25612995d110SJoerg Wunsch 		if ((fd->fdc->flags & FDC_STAT_VALID) == 0)
2562fb35bd37SJoerg Wunsch 			return (EINVAL);
25632995d110SJoerg Wunsch 		memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int));
25642995d110SJoerg Wunsch 		break;
25652995d110SJoerg Wunsch 
2566250300ebSJoerg Wunsch 	case FD_READID:
2567250300ebSJoerg Wunsch 		rid = (struct fdc_readid *)addr;
2568250300ebSJoerg Wunsch 		if (rid->cyl > MAX_CYLINDER || rid->head > MAX_HEAD)
2569fb35bd37SJoerg Wunsch 			return (EINVAL);
2570f664aeeeSJoerg Wunsch 		error = fdmisccmd(dev, BIO_RDSECTID, addr);
2571250300ebSJoerg Wunsch 		break;
2572250300ebSJoerg Wunsch 
2573f5f7ba03SJordan K. Hubbard 	default:
25743a2f7427SDavid Greenman 		error = ENOTTY;
2575f5f7ba03SJordan K. Hubbard 		break;
2576f5f7ba03SJordan K. Hubbard 	}
2577f5f7ba03SJordan K. Hubbard 	return (error);
2578f5f7ba03SJordan K. Hubbard }
2579