xref: /freebsd/sys/dev/fdc/fdc.c (revision 16e68fc605b84f5ab4d0d1a8231cc3e94c2dd281)
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  *
215b81b6b3SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
225b81b6b3SRodney W. Grimes  * modification, are permitted provided that the following conditions
235b81b6b3SRodney W. Grimes  * are met:
245b81b6b3SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
255b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
265b81b6b3SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
275b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
285b81b6b3SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
295b81b6b3SRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
305b81b6b3SRodney W. Grimes  *    must display the following acknowledgement:
315b81b6b3SRodney W. Grimes  *	This product includes software developed by the University of
325b81b6b3SRodney W. Grimes  *	California, Berkeley and its contributors.
335b81b6b3SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
345b81b6b3SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
355b81b6b3SRodney W. Grimes  *    without specific prior written permission.
365b81b6b3SRodney W. Grimes  *
375b81b6b3SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
385b81b6b3SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
395b81b6b3SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
405b81b6b3SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
415b81b6b3SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
425b81b6b3SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
435b81b6b3SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
445b81b6b3SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
455b81b6b3SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
465b81b6b3SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
475b81b6b3SRodney W. Grimes  * SUCH DAMAGE.
485b81b6b3SRodney W. Grimes  *
49dc4ff321SRodney W. Grimes  *	from:	@(#)fd.c	7.4 (Berkeley) 5/25/91
50c3aac50fSPeter Wemm  * $FreeBSD$
515b81b6b3SRodney W. Grimes  *
525b81b6b3SRodney W. Grimes  */
535b81b6b3SRodney W. Grimes 
545b81b6b3SRodney W. Grimes #include "fd.h"
55d2fb4892SJoerg Wunsch #include "opt_fdc.h"
565b81b6b3SRodney W. Grimes 
57b99f0a4aSAndrew Moore #if NFDC > 0
58b99f0a4aSAndrew Moore 
59b99f0a4aSAndrew Moore #include <sys/param.h>
60b99f0a4aSAndrew Moore #include <sys/systm.h>
61b99f0a4aSAndrew Moore #include <sys/kernel.h>
62b99f0a4aSAndrew Moore #include <sys/buf.h>
636182fdbdSPeter Wemm #include <sys/bus.h>
646182fdbdSPeter Wemm #include <sys/conf.h>
656182fdbdSPeter Wemm #include <sys/disklabel.h>
66b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h>
676182fdbdSPeter Wemm #include <sys/fcntl.h>
68b99f0a4aSAndrew Moore #include <sys/malloc.h>
696182fdbdSPeter Wemm #include <sys/module.h>
703a2f7427SDavid Greenman #include <sys/proc.h>
71b99f0a4aSAndrew Moore #include <sys/syslog.h>
726182fdbdSPeter Wemm 
736182fdbdSPeter Wemm #include <sys/bus.h>
746182fdbdSPeter Wemm #include <machine/bus.h>
756182fdbdSPeter Wemm #include <sys/rman.h>
766182fdbdSPeter Wemm 
776182fdbdSPeter Wemm #include <machine/clock.h>
786182fdbdSPeter Wemm #include <machine/ioctl_fd.h>
796182fdbdSPeter Wemm #include <machine/resource.h>
80dc5df763SJoerg Wunsch #include <machine/stdarg.h>
816182fdbdSPeter Wemm 
826182fdbdSPeter Wemm #include <isa/isavar.h>
83a97c75b7SDoug Rabson #include <isa/isareg.h>
84a97c75b7SDoug Rabson #include <isa/fdreg.h>
85a97c75b7SDoug Rabson #include <isa/fdc.h>
86a97c75b7SDoug Rabson #include <isa/rtc.h>
876182fdbdSPeter Wemm 
88edfdec19SPeter Wemm #ifdef FDC_YE
89edfdec19SPeter Wemm #undef FDC_YE
90edfdec19SPeter Wemm #warning "fix FDC_YE! - newbus casualty"
91edfdec19SPeter Wemm #endif
92edfdec19SPeter Wemm 
93b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */
94b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX
955b81b6b3SRodney W. Grimes 
960722d6abSJoerg Wunsch /* configuration flags */
970722d6abSJoerg Wunsch #define FDC_PRETEND_D0	(1 << 0)	/* pretend drive 0 to be there */
9869acd21dSWarner Losh #ifdef FDC_YE
9969acd21dSWarner Losh #define FDC_IS_PCMCIA  (1 << 1)		/* if successful probe, then it's
10069acd21dSWarner Losh 					   a PCMCIA device */
10169acd21dSWarner Losh #endif
1020722d6abSJoerg Wunsch 
1030722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */
1040722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED	0x1000
1050722d6abSJoerg Wunsch 
106dc5df763SJoerg Wunsch /* error returns for fd_cmd() */
107dc5df763SJoerg Wunsch #define FD_FAILED -1
108dc5df763SJoerg Wunsch #define FD_NOT_VALID -2
109dc5df763SJoerg Wunsch #define FDC_ERRMAX	100	/* do not log more */
110dc5df763SJoerg Wunsch 
111b39c878eSAndrey A. Chernov #define NUMTYPES 14
112b39c878eSAndrey A. Chernov #define NUMDENS  (NUMTYPES - 6)
1137ca0641bSAndrey A. Chernov 
1143a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */
115b99f0a4aSAndrew Moore #define F_TAPE_TYPE	0x020	/* bit for fd_types to indicate tape */
116b99f0a4aSAndrew Moore #define NO_TYPE		0	/* must match NO_TYPE in ft.c */
117b99f0a4aSAndrew Moore #define FD_1720         1
118b99f0a4aSAndrew Moore #define FD_1480         2
119b99f0a4aSAndrew Moore #define FD_1440         3
120b99f0a4aSAndrew Moore #define FD_1200         4
121b99f0a4aSAndrew Moore #define FD_820          5
122b99f0a4aSAndrew Moore #define FD_800          6
123b99f0a4aSAndrew Moore #define FD_720          7
124b99f0a4aSAndrew Moore #define FD_360          8
125ed2fa05eSAndrey A. Chernov 
126b99f0a4aSAndrew Moore #define FD_1480in5_25   9
127b99f0a4aSAndrew Moore #define FD_1440in5_25   10
128b99f0a4aSAndrew Moore #define FD_820in5_25    11
129b99f0a4aSAndrew Moore #define FD_800in5_25    12
130b99f0a4aSAndrew Moore #define FD_720in5_25    13
131b99f0a4aSAndrew Moore #define FD_360in5_25    14
132b99f0a4aSAndrew Moore 
1337ca0641bSAndrey A. Chernov 
1346f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] =
1355b81b6b3SRodney W. Grimes {
136126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
137126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
138126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
139126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
140126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
141126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
142126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
143b0568305SAndrey A. Chernov {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
144ed2fa05eSAndrey A. Chernov 
145126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
146126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
147126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
148126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
149126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
150126518a1SAndrey A. Chernov {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
1515b81b6b3SRodney W. Grimes };
1525b81b6b3SRodney W. Grimes 
153b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2		/* 2 floppies */
154dc16046fSJoerg Wunsch 
1555b81b6b3SRodney W. Grimes /***********************************************************************\
1565b81b6b3SRodney W. Grimes * Per controller structure.						*
1575b81b6b3SRodney W. Grimes \***********************************************************************/
1586182fdbdSPeter Wemm static devclass_t fdc_devclass;
1595b81b6b3SRodney W. Grimes 
1605b81b6b3SRodney W. Grimes /***********************************************************************\
1615b81b6b3SRodney W. Grimes * Per drive structure.							*
162b99f0a4aSAndrew Moore * N per controller  (DRVS_PER_CTLR)					*
1635b81b6b3SRodney W. Grimes \***********************************************************************/
1646182fdbdSPeter Wemm struct fd_data {
165b99f0a4aSAndrew Moore 	struct	fdc_data *fdc;	/* pointer to controller structure */
1665b81b6b3SRodney W. Grimes 	int	fdsu;		/* this units number on this controller */
1673a2f7427SDavid Greenman 	int	type;		/* Drive type (FD_1440...) */
1685b81b6b3SRodney W. Grimes 	struct	fd_type *ft;	/* pointer to the type descriptor */
1695b81b6b3SRodney W. Grimes 	int	flags;
1705b81b6b3SRodney W. Grimes #define	FD_OPEN		0x01	/* it's open		*/
1715b81b6b3SRodney W. Grimes #define	FD_ACTIVE	0x02	/* it's active		*/
1725b81b6b3SRodney W. Grimes #define	FD_MOTOR	0x04	/* motor should be on	*/
1735b81b6b3SRodney W. Grimes #define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
1745b81b6b3SRodney W. Grimes 	int	skip;
1755b81b6b3SRodney W. Grimes 	int	hddrv;
176dc5df763SJoerg Wunsch #define FD_NO_TRACK -2
1775b81b6b3SRodney W. Grimes 	int	track;		/* where we think the head is */
1783a2f7427SDavid Greenman 	int	options;	/* user configurable options, see ioctl_fd.h */
17902a19910SJustin T. Gibbs 	struct	callout_handle toffhandle;
18002a19910SJustin T. Gibbs 	struct	callout_handle tohandle;
181b2dfb1f9SJustin T. Gibbs 	struct	devstat device_stats;
1826182fdbdSPeter Wemm 	device_t dev;
1836182fdbdSPeter Wemm 	fdu_t	fdu;
1846182fdbdSPeter Wemm };
1856182fdbdSPeter Wemm static devclass_t fd_devclass;
1865b81b6b3SRodney W. Grimes 
1875b81b6b3SRodney W. Grimes /***********************************************************************\
1885b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used:		*
1895b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question	*
1905b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller		*
1915b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number					*
1925b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number				*
1935b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit)	*
1945b81b6b3SRodney W. Grimes \***********************************************************************/
195b99f0a4aSAndrew Moore 
19669acd21dSWarner Losh #ifdef FDC_YE
19769acd21dSWarner Losh #include "card.h"
19869acd21dSWarner Losh static int yeattach(struct isa_device *);
19969acd21dSWarner Losh #endif
20069acd21dSWarner Losh 
2013a2f7427SDavid Greenman /* needed for ft driver, thus exported */
2026182fdbdSPeter Wemm int in_fdc(struct fdc_data *);
2036182fdbdSPeter Wemm int out_fdc(struct fdc_data *, int);
2043a2f7427SDavid Greenman 
2053a2f7427SDavid Greenman /* internal functions */
2066182fdbdSPeter Wemm static	void fdc_add_device(device_t, const char *, int);
2076182fdbdSPeter Wemm static	void fdc_intr(void *);
2086182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int);
2093a2f7427SDavid Greenman #  define TURNON 1
2103a2f7427SDavid Greenman #  define TURNOFF 0
2113a2f7427SDavid Greenman static timeout_t fd_turnoff;
2123a2f7427SDavid Greenman static timeout_t fd_motor_on;
2136182fdbdSPeter Wemm static void fd_turnon(struct fd_data *);
2143a2f7427SDavid Greenman static void fdc_reset(fdc_p);
2156182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *);
2166182fdbdSPeter Wemm static void fdstart(struct fdc_data *);
2175c1a1eaeSBruce Evans static timeout_t fd_iotimeout;
2183a2f7427SDavid Greenman static timeout_t fd_pseudointr;
2196182fdbdSPeter Wemm static int fdstate(struct fdc_data *);
2206182fdbdSPeter Wemm static int retrier(struct fdc_data *);
2213a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *);
2223a2f7427SDavid Greenman 
223d66c374fSTor Egge static int enable_fifo(fdc_p fdc);
224d66c374fSTor Egge 
225d66c374fSTor Egge static int fifo_threshold = 8;	/* XXX: should be accessible via sysctl */
226d66c374fSTor Egge 
227aaf08d94SGarrett Wollman 
2285b81b6b3SRodney W. Grimes #define DEVIDLE		0
2295b81b6b3SRodney W. Grimes #define FINDWORK	1
2305b81b6b3SRodney W. Grimes #define	DOSEEK		2
2315b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 	3
2325b81b6b3SRodney W. Grimes #define	IOCOMPLETE	4
2335b81b6b3SRodney W. Grimes #define RECALCOMPLETE	5
2345b81b6b3SRodney W. Grimes #define	STARTRECAL	6
2355b81b6b3SRodney W. Grimes #define	RESETCTLR	7
2365b81b6b3SRodney W. Grimes #define	SEEKWAIT	8
2375b81b6b3SRodney W. Grimes #define	RECALWAIT	9
2385b81b6b3SRodney W. Grimes #define	MOTORWAIT	10
2395b81b6b3SRodney W. Grimes #define	IOTIMEDOUT	11
2405c1a1eaeSBruce Evans #define	RESETCOMPLETE	12
24169acd21dSWarner Losh #ifdef FDC_YE
24269acd21dSWarner Losh #define PIOREAD		13
24369acd21dSWarner Losh #endif
2445b81b6b3SRodney W. Grimes 
245d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
246cba2a7c6SBruce Evans static char const * const fdstates[] =
2475b81b6b3SRodney W. Grimes {
2485b81b6b3SRodney W. Grimes "DEVIDLE",
2495b81b6b3SRodney W. Grimes "FINDWORK",
2505b81b6b3SRodney W. Grimes "DOSEEK",
2515b81b6b3SRodney W. Grimes "SEEKCOMPLETE",
2525b81b6b3SRodney W. Grimes "IOCOMPLETE",
2535b81b6b3SRodney W. Grimes "RECALCOMPLETE",
2545b81b6b3SRodney W. Grimes "STARTRECAL",
2555b81b6b3SRodney W. Grimes "RESETCTLR",
2565b81b6b3SRodney W. Grimes "SEEKWAIT",
2575b81b6b3SRodney W. Grimes "RECALWAIT",
2585b81b6b3SRodney W. Grimes "MOTORWAIT",
2595c1a1eaeSBruce Evans "IOTIMEDOUT",
2605c1a1eaeSBruce Evans "RESETCOMPLETE",
26169acd21dSWarner Losh #ifdef FDC_YE
262ff9607b0SBruce Evans "PIOREAD",
26369acd21dSWarner Losh #endif
2645b81b6b3SRodney W. Grimes };
2655b81b6b3SRodney W. Grimes 
2663a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */
267cba2a7c6SBruce Evans static int volatile fd_debug = 0;
2685b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg)
2695b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
270d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */
2715b81b6b3SRodney W. Grimes #define TRACE0(arg)
2725b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2)
273d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */
2745b81b6b3SRodney W. Grimes 
27569acd21dSWarner Losh #ifdef FDC_YE
27669acd21dSWarner Losh #if NCARD > 0
27769acd21dSWarner Losh #include <sys/select.h>
278d17e4ee6SPeter Wemm #include <sys/module.h>
27969acd21dSWarner Losh #include <pccard/cardinfo.h>
28069acd21dSWarner Losh #include <pccard/driver.h>
28169acd21dSWarner Losh #include <pccard/slot.h>
28269acd21dSWarner Losh 
28369acd21dSWarner Losh /*
28469acd21dSWarner Losh  *	PC-Card (PCMCIA) specific code.
28569acd21dSWarner Losh  */
28669acd21dSWarner Losh static int yeinit(struct pccard_devinfo *);		/* init device */
28769acd21dSWarner Losh static void yeunload(struct pccard_devinfo *); 		/* Disable driver */
28869acd21dSWarner Losh static int yeintr(struct pccard_devinfo *); 		/* Interrupt handler */
28969acd21dSWarner Losh 
290d17e4ee6SPeter Wemm PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask);
29169acd21dSWarner Losh 
29269acd21dSWarner Losh /*
29369acd21dSWarner Losh  * this is the secret PIO data port (offset from base)
29469acd21dSWarner Losh  */
29569acd21dSWarner Losh #define FDC_YE_DATAPORT 6
29669acd21dSWarner Losh 
29769acd21dSWarner Losh /*
29869acd21dSWarner Losh  *	Initialize the device - called from Slot manager.
29969acd21dSWarner Losh  */
30069acd21dSWarner Losh static int yeinit(struct pccard_devinfo *devi)
30169acd21dSWarner Losh {
30269acd21dSWarner Losh 	fdc_p fdc = &fdc_data[devi->isahd.id_unit];
30369acd21dSWarner Losh 
30469acd21dSWarner Losh 	/* validate unit number. */
30569acd21dSWarner Losh 	if (devi->isahd.id_unit >= NFDC)
30669acd21dSWarner Losh 		return(ENODEV);
30769acd21dSWarner Losh 	fdc->baseport = devi->isahd.id_iobase;
30869acd21dSWarner Losh 	/*
30969acd21dSWarner Losh 	 * reset controller
31069acd21dSWarner Losh 	 */
31169acd21dSWarner Losh 	outb(fdc->baseport+FDOUT, 0);
31269acd21dSWarner Losh 	DELAY(100);
31369acd21dSWarner Losh 	outb(fdc->baseport+FDOUT, FDO_FRST);
31469acd21dSWarner Losh 
31569acd21dSWarner Losh 	/*
31669acd21dSWarner Losh 	 * wire into system
31769acd21dSWarner Losh 	 */
31869acd21dSWarner Losh 	if (yeattach(&devi->isahd) == 0)
31969acd21dSWarner Losh 		return(ENXIO);
32069acd21dSWarner Losh 
32169acd21dSWarner Losh 	return(0);
32269acd21dSWarner Losh }
32369acd21dSWarner Losh 
32469acd21dSWarner Losh /*
32569acd21dSWarner Losh  *	yeunload - unload the driver and clear the table.
32669acd21dSWarner Losh  *	XXX TODO:
32769acd21dSWarner Losh  *	This is usually called when the card is ejected, but
32869acd21dSWarner Losh  *	can be caused by a modunload of a controller driver.
32969acd21dSWarner Losh  *	The idea is to reset the driver's view of the device
33069acd21dSWarner Losh  *	and ensure that any driver entry points such as
33169acd21dSWarner Losh  *	read and write do not hang.
33269acd21dSWarner Losh  */
33369acd21dSWarner Losh static void yeunload(struct pccard_devinfo *devi)
33469acd21dSWarner Losh {
33569acd21dSWarner Losh 	if (fd_data[devi->isahd.id_unit].type == NO_TYPE)
33669acd21dSWarner Losh 		return;
33769acd21dSWarner Losh 
33869acd21dSWarner Losh 	/*
33969acd21dSWarner Losh 	 * this prevents Fdopen() and fdstrategy() from attempting
34069acd21dSWarner Losh 	 * to access unloaded controller
34169acd21dSWarner Losh 	 */
34269acd21dSWarner Losh 	fd_data[devi->isahd.id_unit].type = NO_TYPE;
34369acd21dSWarner Losh 
34469acd21dSWarner Losh 	printf("fdc%d: unload\n", devi->isahd.id_unit);
34569acd21dSWarner Losh }
34669acd21dSWarner Losh 
34769acd21dSWarner Losh /*
34869acd21dSWarner Losh  *	yeintr - Shared interrupt called from
34969acd21dSWarner Losh  *	front end of PC-Card handler.
35069acd21dSWarner Losh  */
35169acd21dSWarner Losh static int yeintr(struct pccard_devinfo *devi)
35269acd21dSWarner Losh {
35369acd21dSWarner Losh 	fdintr((fdcu_t)devi->isahd.id_unit);
35469acd21dSWarner Losh 	return(1);
35569acd21dSWarner Losh }
35669acd21dSWarner Losh #endif /* NCARD > 0 */
35769acd21dSWarner Losh #endif /* FDC_YE */
35869acd21dSWarner Losh 
35987f6c662SJulian Elischer static	d_open_t	Fdopen;	/* NOTE, not fdopen */
36087f6c662SJulian Elischer static	d_close_t	fdclose;
36187f6c662SJulian Elischer static	d_ioctl_t	fdioctl;
36287f6c662SJulian Elischer static	d_strategy_t	fdstrategy;
36387f6c662SJulian Elischer 
36487f6c662SJulian Elischer #define CDEV_MAJOR 9
36587f6c662SJulian Elischer #define BDEV_MAJOR 2
366f7ea2f55SJulian Elischer 
3674e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = {
3684e2f199eSPoul-Henning Kamp 	/* open */	Fdopen,
3694e2f199eSPoul-Henning Kamp 	/* close */	fdclose,
3704e2f199eSPoul-Henning Kamp 	/* read */	physread,
3714e2f199eSPoul-Henning Kamp 	/* write */	physwrite,
3724e2f199eSPoul-Henning Kamp 	/* ioctl */	fdioctl,
3734e2f199eSPoul-Henning Kamp 	/* stop */	nostop,
3744e2f199eSPoul-Henning Kamp 	/* reset */	noreset,
3754e2f199eSPoul-Henning Kamp 	/* devtotty */	nodevtotty,
3764e2f199eSPoul-Henning Kamp 	/* poll */	nopoll,
3774e2f199eSPoul-Henning Kamp 	/* mmap */	nommap,
3784e2f199eSPoul-Henning Kamp 	/* strategy */	fdstrategy,
3794e2f199eSPoul-Henning Kamp 	/* name */	"fd",
3804e2f199eSPoul-Henning Kamp 	/* parms */	noparms,
3814e2f199eSPoul-Henning Kamp 	/* maj */	CDEV_MAJOR,
3824e2f199eSPoul-Henning Kamp 	/* dump */	nodump,
3834e2f199eSPoul-Henning Kamp 	/* psize */	nopsize,
3844e2f199eSPoul-Henning Kamp 	/* flags */	D_DISK,
3854e2f199eSPoul-Henning Kamp 	/* maxio */	0,
3864e2f199eSPoul-Henning Kamp 	/* bmaj */	BDEV_MAJOR
3874e2f199eSPoul-Henning Kamp };
3884e2f199eSPoul-Henning Kamp 
389dc5df763SJoerg Wunsch static int
3906182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s)
391dc5df763SJoerg Wunsch {
3926182fdbdSPeter Wemm 	fdc->fdc_errs++;
39316b04b6aSJoerg Wunsch 	if (s) {
3946182fdbdSPeter Wemm 		if (fdc->fdc_errs < FDC_ERRMAX) {
3956182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
3966182fdbdSPeter Wemm 			printf("%s", s);
3976182fdbdSPeter Wemm 		} else if (fdc->fdc_errs == FDC_ERRMAX) {
3986182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
3996182fdbdSPeter Wemm 			printf("too many errors, not logging any more\n");
4006182fdbdSPeter Wemm 		}
40116b04b6aSJoerg Wunsch 	}
402dc5df763SJoerg Wunsch 
403dc5df763SJoerg Wunsch 	return FD_FAILED;
404dc5df763SJoerg Wunsch }
405dc5df763SJoerg Wunsch 
406dc5df763SJoerg Wunsch /*
407dc5df763SJoerg Wunsch  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
408dc5df763SJoerg Wunsch  * Unit number,
409dc5df763SJoerg Wunsch  * # of output bytes, output bytes as ints ...,
410dc5df763SJoerg Wunsch  * # of input bytes, input bytes as ints ...
411dc5df763SJoerg Wunsch  */
4126f4e0bebSPoul-Henning Kamp static int
4136182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...)
414dc5df763SJoerg Wunsch {
415dc5df763SJoerg Wunsch 	u_char cmd;
416dc5df763SJoerg Wunsch 	int n_in;
417dc5df763SJoerg Wunsch 	int n;
418dc5df763SJoerg Wunsch 	va_list ap;
419dc5df763SJoerg Wunsch 
420dc5df763SJoerg Wunsch 	va_start(ap, n_out);
421dc5df763SJoerg Wunsch 	cmd = (u_char)(va_arg(ap, int));
422dc5df763SJoerg Wunsch 	va_end(ap);
423dc5df763SJoerg Wunsch 	va_start(ap, n_out);
424dc5df763SJoerg Wunsch 	for (n = 0; n < n_out; n++)
425dc5df763SJoerg Wunsch 	{
4266182fdbdSPeter Wemm 		if (out_fdc(fdc, va_arg(ap, int)) < 0)
427dc5df763SJoerg Wunsch 		{
428dc5df763SJoerg Wunsch 			char msg[50];
4292127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
430dc5df763SJoerg Wunsch 				"cmd %x failed at out byte %d of %d\n",
431dc5df763SJoerg Wunsch 				cmd, n + 1, n_out);
4326182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
433dc5df763SJoerg Wunsch 		}
434dc5df763SJoerg Wunsch 	}
435dc5df763SJoerg Wunsch 	n_in = va_arg(ap, int);
436dc5df763SJoerg Wunsch 	for (n = 0; n < n_in; n++)
437dc5df763SJoerg Wunsch 	{
438dc5df763SJoerg Wunsch 		int *ptr = va_arg(ap, int *);
4396182fdbdSPeter Wemm 		if (fd_in(fdc, ptr) < 0)
440dc5df763SJoerg Wunsch 		{
441dc5df763SJoerg Wunsch 			char msg[50];
4422127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
443dc5df763SJoerg Wunsch 				"cmd %02x failed at in byte %d of %d\n",
444dc5df763SJoerg Wunsch 				cmd, n + 1, n_in);
4456182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
446dc5df763SJoerg Wunsch 		}
447dc5df763SJoerg Wunsch 	}
448dc5df763SJoerg Wunsch 
449dc5df763SJoerg Wunsch 	return 0;
450dc5df763SJoerg Wunsch }
451dc5df763SJoerg Wunsch 
4526f4e0bebSPoul-Henning Kamp static int
453d66c374fSTor Egge enable_fifo(fdc_p fdc)
454d66c374fSTor Egge {
455d66c374fSTor Egge 	int i, j;
456d66c374fSTor Egge 
457d66c374fSTor Egge 	if ((fdc->flags & FDC_HAS_FIFO) == 0) {
458d66c374fSTor Egge 
459d66c374fSTor Egge 		/*
460d66c374fSTor Egge 		 * XXX:
461d66c374fSTor Egge 		 * Cannot use fd_cmd the normal way here, since
462d66c374fSTor Egge 		 * this might be an invalid command. Thus we send the
463d66c374fSTor Egge 		 * first byte, and check for an early turn of data directon.
464d66c374fSTor Egge 		 */
465d66c374fSTor Egge 
4666182fdbdSPeter Wemm 		if (out_fdc(fdc, I8207X_CONFIGURE) < 0)
4676182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
468d66c374fSTor Egge 
469d66c374fSTor Egge 		/* If command is invalid, return */
470d66c374fSTor Egge 		j = 100000;
471d66c374fSTor Egge 		while ((i = inb(fdc->baseport + FDSTS) & (NE7_DIO | NE7_RQM))
472d66c374fSTor Egge 		       != NE7_RQM && j-- > 0)
473d66c374fSTor Egge 			if (i == (NE7_DIO | NE7_RQM)) {
474d66c374fSTor Egge 				fdc_reset(fdc);
475d66c374fSTor Egge 				return FD_FAILED;
476d66c374fSTor Egge 			}
477d66c374fSTor Egge 		if (j<0 ||
4786182fdbdSPeter Wemm 		    fd_cmd(fdc, 3,
479d66c374fSTor Egge 			   0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) {
480d66c374fSTor Egge 			fdc_reset(fdc);
4816182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
482d66c374fSTor Egge 		}
483d66c374fSTor Egge 		fdc->flags |= FDC_HAS_FIFO;
484d66c374fSTor Egge 		return 0;
485d66c374fSTor Egge 	}
4866182fdbdSPeter Wemm 	if (fd_cmd(fdc, 4,
487d66c374fSTor Egge 		   I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0)
4886182fdbdSPeter Wemm 		return fdc_err(fdc, "Re-enable FIFO failed\n");
489d66c374fSTor Egge 	return 0;
490d66c374fSTor Egge }
491d66c374fSTor Egge 
492d66c374fSTor Egge static int
493dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p)
494dc5df763SJoerg Wunsch {
495dc5df763SJoerg Wunsch 	int st3;
496dc5df763SJoerg Wunsch 
4976182fdbdSPeter Wemm 	if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
498dc5df763SJoerg Wunsch 	{
4996182fdbdSPeter Wemm 		return fdc_err(fdc, "Sense Drive Status failed\n");
500dc5df763SJoerg Wunsch 	}
501dc5df763SJoerg Wunsch 	if (st3p)
502dc5df763SJoerg Wunsch 		*st3p = st3;
503dc5df763SJoerg Wunsch 
504dc5df763SJoerg Wunsch 	return 0;
505dc5df763SJoerg Wunsch }
506dc5df763SJoerg Wunsch 
5076f4e0bebSPoul-Henning Kamp static int
508dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
509dc5df763SJoerg Wunsch {
5106182fdbdSPeter Wemm 	int cyl, st0, ret;
511dc5df763SJoerg Wunsch 
5126182fdbdSPeter Wemm 	ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0);
5136182fdbdSPeter Wemm 	if (ret) {
5146182fdbdSPeter Wemm 		(void)fdc_err(fdc,
515dc5df763SJoerg Wunsch 			      "sense intr err reading stat reg 0\n");
516dc5df763SJoerg Wunsch 		return ret;
517dc5df763SJoerg Wunsch 	}
518dc5df763SJoerg Wunsch 
519dc5df763SJoerg Wunsch 	if (st0p)
520dc5df763SJoerg Wunsch 		*st0p = st0;
521dc5df763SJoerg Wunsch 
5226182fdbdSPeter Wemm 	if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) {
523dc5df763SJoerg Wunsch 		/*
524dc5df763SJoerg Wunsch 		 * There doesn't seem to have been an interrupt.
525dc5df763SJoerg Wunsch 		 */
526dc5df763SJoerg Wunsch 		return FD_NOT_VALID;
527dc5df763SJoerg Wunsch 	}
528dc5df763SJoerg Wunsch 
5296182fdbdSPeter Wemm 	if (fd_in(fdc, &cyl) < 0) {
5306182fdbdSPeter Wemm 		return fdc_err(fdc, "can't get cyl num\n");
531dc5df763SJoerg Wunsch 	}
532dc5df763SJoerg Wunsch 
533dc5df763SJoerg Wunsch 	if (cylp)
534dc5df763SJoerg Wunsch 		*cylp = cyl;
535dc5df763SJoerg Wunsch 
536dc5df763SJoerg Wunsch 	return 0;
537dc5df763SJoerg Wunsch }
538dc5df763SJoerg Wunsch 
539dc5df763SJoerg Wunsch 
5406f4e0bebSPoul-Henning Kamp static int
541dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu)
542dc5df763SJoerg Wunsch {
543dc5df763SJoerg Wunsch 	int i, ret;
544b5e8ce9fSBruce Evans 
5456182fdbdSPeter Wemm 	for (i = 0; i < 7; i++) {
546b5e8ce9fSBruce Evans 		/*
547b5e8ce9fSBruce Evans 		 * XXX types are poorly chosen.  Only bytes can by read
548a838d83dSBruce Evans 		 * from the hardware, but fdc->status[] wants u_ints and
549b5e8ce9fSBruce Evans 		 * fd_in() gives ints.
550b5e8ce9fSBruce Evans 		 */
551b5e8ce9fSBruce Evans 		int status;
552b5e8ce9fSBruce Evans 
5536182fdbdSPeter Wemm 		ret = fd_in(fdc, &status);
554b5e8ce9fSBruce Evans 		fdc->status[i] = status;
555b5e8ce9fSBruce Evans 		if (ret != 0)
556dc5df763SJoerg Wunsch 			break;
557dc5df763SJoerg Wunsch 	}
558dc5df763SJoerg Wunsch 
559dc5df763SJoerg Wunsch 	if (ret == 0)
560dc5df763SJoerg Wunsch 		fdc->flags |= FDC_STAT_VALID;
561dc5df763SJoerg Wunsch 	else
562dc5df763SJoerg Wunsch 		fdc->flags &= ~FDC_STAT_VALID;
563dc5df763SJoerg Wunsch 
564dc5df763SJoerg Wunsch 	return ret;
565dc5df763SJoerg Wunsch }
566dc5df763SJoerg Wunsch 
5675b81b6b3SRodney W. Grimes /****************************************************************************/
5685b81b6b3SRodney W. Grimes /*                      autoconfiguration stuff                             */
5695b81b6b3SRodney W. Grimes /****************************************************************************/
570dc5df763SJoerg Wunsch 
57116e68fc6SPeter Wemm /*
57216e68fc6SPeter Wemm  * fdc controller section.
57316e68fc6SPeter Wemm  */
5743a2f7427SDavid Greenman static int
5756182fdbdSPeter Wemm fdc_probe(device_t dev)
5765b81b6b3SRodney W. Grimes {
5776182fdbdSPeter Wemm 	int	error, i, ic_type;
5786182fdbdSPeter Wemm 	struct	fdc_data *fdc;
5796182fdbdSPeter Wemm 	char	myname[8];	/* better be long enough */
5805b81b6b3SRodney W. Grimes 
581a97c75b7SDoug Rabson 	/* No pnp support */
582a97c75b7SDoug Rabson 	if (isa_get_vendorid(dev))
583a97c75b7SDoug Rabson 		return (ENXIO);
584a97c75b7SDoug Rabson 
5856182fdbdSPeter Wemm 	fdc = device_get_softc(dev);
5866182fdbdSPeter Wemm 	bzero(fdc, sizeof *fdc);
5876182fdbdSPeter Wemm 	fdc->fdc_dev = dev;
5886182fdbdSPeter Wemm 	fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0;
5896182fdbdSPeter Wemm 	fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0;
5906182fdbdSPeter Wemm 
5916182fdbdSPeter Wemm 	fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
5926182fdbdSPeter Wemm 					     &fdc->rid_ioport, 0ul, ~0ul,
5936182fdbdSPeter Wemm 					     IO_FDCSIZE, RF_ACTIVE);
5946182fdbdSPeter Wemm 	if (fdc->res_ioport == 0) {
5956182fdbdSPeter Wemm 		device_print_prettyname(dev);
5966182fdbdSPeter Wemm 		printf("cannot reserve I/O port range\n");
5976182fdbdSPeter Wemm 		error = ENXIO;
5986182fdbdSPeter Wemm 		goto out;
5996182fdbdSPeter Wemm 	}
6006182fdbdSPeter Wemm 	fdc->baseport = fdc->res_ioport->r_start;
6016182fdbdSPeter Wemm 
6026182fdbdSPeter Wemm 	fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ,
6036182fdbdSPeter Wemm 					  &fdc->rid_irq, 0ul, ~0ul, 1,
6046182fdbdSPeter Wemm 					  RF_ACTIVE);
6056182fdbdSPeter Wemm 	if (fdc->res_irq == 0) {
6066182fdbdSPeter Wemm 		device_print_prettyname(dev);
6076182fdbdSPeter Wemm 		printf("cannot reserve interrupt line\n");
6086182fdbdSPeter Wemm 		error = ENXIO;
6096182fdbdSPeter Wemm 		goto out;
6106182fdbdSPeter Wemm 	}
6116182fdbdSPeter Wemm 	fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ,
6126182fdbdSPeter Wemm 					  &fdc->rid_drq, 0ul, ~0ul, 1,
6136182fdbdSPeter Wemm 					  RF_ACTIVE);
6146182fdbdSPeter Wemm 	if (fdc->res_drq == 0) {
6156182fdbdSPeter Wemm 		device_print_prettyname(dev);
6166182fdbdSPeter Wemm 		printf("cannot reserve DMA request line\n");
6176182fdbdSPeter Wemm 		error = ENXIO;
6186182fdbdSPeter Wemm 		goto out;
6196182fdbdSPeter Wemm 	}
6206182fdbdSPeter Wemm 	fdc->dmachan = fdc->res_drq->r_start;
621566643e3SDoug Rabson 	error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq,
622566643e3SDoug Rabson 			       INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr);
6235b81b6b3SRodney W. Grimes 
62416111cedSAndrew Moore 	/* First - lets reset the floppy controller */
6256182fdbdSPeter Wemm 	outb(fdc->baseport + FDOUT, 0);
62616111cedSAndrew Moore 	DELAY(100);
6276182fdbdSPeter Wemm 	outb(fdc->baseport + FDOUT, FDO_FRST);
62816111cedSAndrew Moore 
6295b81b6b3SRodney W. Grimes 	/* see if it can handle a command */
6306182fdbdSPeter Wemm 	if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240),
6316182fdbdSPeter Wemm 		   NE7_SPEC_2(2, 0), 0)) {
6326182fdbdSPeter Wemm 		error = ENXIO;
6336182fdbdSPeter Wemm 		goto out;
6345b81b6b3SRodney W. Grimes 	}
6356182fdbdSPeter Wemm 
6366182fdbdSPeter Wemm 	if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) {
6376182fdbdSPeter Wemm 		ic_type = (u_char)ic_type;
6386182fdbdSPeter Wemm 		switch (ic_type) {
6396182fdbdSPeter Wemm 		case 0x80:
6406182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 765 or clone");
6416182fdbdSPeter Wemm 			fdc->fdct = FDC_NE765;
6426182fdbdSPeter Wemm 			break;
6436182fdbdSPeter Wemm 		case 0x81:
6446182fdbdSPeter Wemm 			device_set_desc(dev, "Intel 82077 or clone");
6456182fdbdSPeter Wemm 			fdc->fdct = FDC_I82077;
6466182fdbdSPeter Wemm 			break;
6476182fdbdSPeter Wemm 		case 0x90:
6486182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 72065B or clone");
6496182fdbdSPeter Wemm 			fdc->fdct = FDC_NE72065;
6506182fdbdSPeter Wemm 			break;
6516182fdbdSPeter Wemm 		default:
6526182fdbdSPeter Wemm 			device_set_desc(dev, "generic floppy controller");
6536182fdbdSPeter Wemm 			fdc->fdct = FDC_UNKNOWN;
6546182fdbdSPeter Wemm 			break;
6556182fdbdSPeter Wemm 		}
6566182fdbdSPeter Wemm 	}
6576182fdbdSPeter Wemm 
6586182fdbdSPeter Wemm 	snprintf(myname, sizeof(myname), "%s%d", device_get_name(dev),
6596182fdbdSPeter Wemm 		 device_get_unit(dev));
6606182fdbdSPeter Wemm 	for (i = resource_query_string(-1, "at", myname); i != -1;
6616182fdbdSPeter Wemm 	     i = resource_query_string(i, "at", myname))
6626182fdbdSPeter Wemm 		fdc_add_device(dev, resource_query_name(i),
6636182fdbdSPeter Wemm 			       resource_query_unit(i));
66469acd21dSWarner Losh #ifdef FDC_YE
66569acd21dSWarner Losh 	/*
66669acd21dSWarner Losh 	 * don't succeed on probe; wait
66769acd21dSWarner Losh 	 * for PCCARD subsystem to do it
66869acd21dSWarner Losh 	 */
66969acd21dSWarner Losh 	if (dev->id_flags & FDC_IS_PCMCIA)
67069acd21dSWarner Losh 		return(0);
67169acd21dSWarner Losh #endif
6726182fdbdSPeter Wemm 	return (0);
6736182fdbdSPeter Wemm 
6746182fdbdSPeter Wemm out:
6756182fdbdSPeter Wemm 	if (fdc->fdc_intr)
6766182fdbdSPeter Wemm 		BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq,
6776182fdbdSPeter Wemm 				  fdc->fdc_intr);
6786182fdbdSPeter Wemm 	if (fdc->res_irq != 0) {
6796182fdbdSPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
6806182fdbdSPeter Wemm 					fdc->res_irq);
6816182fdbdSPeter Wemm 		bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
6826182fdbdSPeter Wemm 				     fdc->res_irq);
6836182fdbdSPeter Wemm 	}
6846182fdbdSPeter Wemm 	if (fdc->res_ioport != 0) {
6856182fdbdSPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
6866182fdbdSPeter Wemm 					fdc->res_ioport);
6876182fdbdSPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
6886182fdbdSPeter Wemm 				     fdc->res_ioport);
6896182fdbdSPeter Wemm 	}
6906182fdbdSPeter Wemm 	if (fdc->res_drq != 0) {
6916182fdbdSPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
6926182fdbdSPeter Wemm 					fdc->res_drq);
6936182fdbdSPeter Wemm 		bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
6946182fdbdSPeter Wemm 				     fdc->res_drq);
6956182fdbdSPeter Wemm 	}
6966182fdbdSPeter Wemm 	return (error);
6975b81b6b3SRodney W. Grimes }
6985b81b6b3SRodney W. Grimes 
6995b81b6b3SRodney W. Grimes /*
7006182fdbdSPeter Wemm  * Aped dfr@freebsd.org's isa_add_device().
7015b81b6b3SRodney W. Grimes  */
7026182fdbdSPeter Wemm static void
7036182fdbdSPeter Wemm fdc_add_device(device_t dev, const char *name, int unit)
7045b81b6b3SRodney W. Grimes {
7056182fdbdSPeter Wemm 	int	disabled, *ivar;
7066182fdbdSPeter Wemm 	device_t child;
70792200632SGarrett Wollman 
7086182fdbdSPeter Wemm 	ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT);
7096182fdbdSPeter Wemm 	if (ivar == 0)
7106182fdbdSPeter Wemm 		return;
711a97c75b7SDoug Rabson 	if (resource_int_value(name, unit, "drive", ivar) != 0)
7126182fdbdSPeter Wemm 		*ivar = 0;
7136182fdbdSPeter Wemm 	child = device_add_child(dev, name, unit, ivar);
7146182fdbdSPeter Wemm 	if (child == 0)
7156182fdbdSPeter Wemm 		return;
716a97c75b7SDoug Rabson 	if (resource_int_value(name, unit, "disabled", &disabled) == 0
717a97c75b7SDoug Rabson 	    && disabled != 0)
7186182fdbdSPeter Wemm 		device_disable(child);
7196182fdbdSPeter Wemm }
7206182fdbdSPeter Wemm 
7216182fdbdSPeter Wemm static int
7226182fdbdSPeter Wemm fdc_attach(device_t dev)
7236182fdbdSPeter Wemm {
7246182fdbdSPeter Wemm 	struct	fdc_data *fdc = device_get_softc(dev);
7256182fdbdSPeter Wemm 	fdcu_t	fdcu = device_get_unit(dev);
7266182fdbdSPeter Wemm 
7275b81b6b3SRodney W. Grimes 	fdc->fdcu = fdcu;
7285b81b6b3SRodney W. Grimes 	fdc->flags |= FDC_ATTACHED;
7296182fdbdSPeter Wemm 
730100f78bbSSujal Patel 	/* Acquire the DMA channel forever, The driver will do the rest */
7316182fdbdSPeter Wemm 				/* XXX should integrate with rman */
732100f78bbSSujal Patel 	isa_dma_acquire(fdc->dmachan);
733dd87702aSBruce Evans 	isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */);
7345b81b6b3SRodney W. Grimes 	fdc->state = DEVIDLE;
7356182fdbdSPeter Wemm 
7363a2f7427SDavid Greenman 	/* reset controller, turn motor off, clear fdout mirror reg */
7373a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
73802a19910SJustin T. Gibbs 	bufq_init(&fdc->head);
7395b81b6b3SRodney W. Grimes 
7406182fdbdSPeter Wemm #ifdef FIFO_BEFORE_MOTORON
7416182fdbdSPeter Wemm 	/* Hmm, this doesn't work here - is set_motor() magic? -Peter */
7426182fdbdSPeter Wemm 	if (fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN
7436182fdbdSPeter Wemm 	    && enable_fifo(fdc) == 0) {
7446182fdbdSPeter Wemm 		device_print_prettyname(dev);
7456182fdbdSPeter Wemm 		printf("FIFO enabled, %d bytes threshold\n", fifo_threshold);
7466182fdbdSPeter Wemm 	}
7476182fdbdSPeter Wemm #endif
7486182fdbdSPeter Wemm 	/*
7496182fdbdSPeter Wemm 	 * Probe and attach any children as were configured above.
7506182fdbdSPeter Wemm 	 */
7516182fdbdSPeter Wemm 	return (bus_generic_attach(dev));
7526182fdbdSPeter Wemm }
7536182fdbdSPeter Wemm 
75415317dd8SMatthew N. Dodd static int
7556182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child)
7566182fdbdSPeter Wemm {
75715317dd8SMatthew N. Dodd 	int retval = 0;
75815317dd8SMatthew N. Dodd 
75915317dd8SMatthew N. Dodd 	retval += bus_print_child_header(me, child);
76015317dd8SMatthew N. Dodd 	retval += printf(" on %s drive %d\n", device_get_nameunit(me),
7616182fdbdSPeter Wemm 	       *(int *)device_get_ivars(child));
76215317dd8SMatthew N. Dodd 
76315317dd8SMatthew N. Dodd 	return (retval);
7646182fdbdSPeter Wemm }
7656182fdbdSPeter Wemm 
76616e68fc6SPeter Wemm static device_method_t fdc_methods[] = {
76716e68fc6SPeter Wemm 	/* Device interface */
76816e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fdc_probe),
76916e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fdc_attach),
77016e68fc6SPeter Wemm 	DEVMETHOD(device_detach,	bus_generic_detach),
77116e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
77216e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend),
77316e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume),
77416e68fc6SPeter Wemm 
77516e68fc6SPeter Wemm 	/* Bus interface */
77616e68fc6SPeter Wemm 	DEVMETHOD(bus_print_child,	fdc_print_child),
77716e68fc6SPeter Wemm 	/* Our children never use any other bus interface methods. */
77816e68fc6SPeter Wemm 
77916e68fc6SPeter Wemm 	{ 0, 0 }
78016e68fc6SPeter Wemm };
78116e68fc6SPeter Wemm 
78216e68fc6SPeter Wemm static driver_t fdc_driver = {
78316e68fc6SPeter Wemm 	"fdc",
78416e68fc6SPeter Wemm 	fdc_methods,
78516e68fc6SPeter Wemm 	sizeof(struct fdc_data)
78616e68fc6SPeter Wemm };
78716e68fc6SPeter Wemm 
78816e68fc6SPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0);
78916e68fc6SPeter Wemm 
79016e68fc6SPeter Wemm /******************************************************************/
79116e68fc6SPeter Wemm /*
79216e68fc6SPeter Wemm  * devices attached to the controller section.
79316e68fc6SPeter Wemm  */
7946182fdbdSPeter Wemm static int
7956182fdbdSPeter Wemm fd_probe(device_t dev)
7966182fdbdSPeter Wemm {
7976182fdbdSPeter Wemm 	int	i;
7986182fdbdSPeter Wemm 	u_int	fdt, st0, st3;
7996182fdbdSPeter Wemm 	struct	fd_data *fd;
8006182fdbdSPeter Wemm 	struct	fdc_data *fdc;
8016182fdbdSPeter Wemm 	fdsu_t	fdsu;
8026182fdbdSPeter Wemm #ifndef FIFO_BEFORE_MOTORON
8036182fdbdSPeter Wemm 	static int fd_fifo = 0;
8046182fdbdSPeter Wemm #endif
8056182fdbdSPeter Wemm 
8066182fdbdSPeter Wemm 	fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */
8076182fdbdSPeter Wemm 	fd = device_get_softc(dev);
8086182fdbdSPeter Wemm 	fdc = device_get_softc(device_get_parent(dev));
8096182fdbdSPeter Wemm 
8106182fdbdSPeter Wemm 	bzero(fd, sizeof *fd);
8116182fdbdSPeter Wemm 	fd->dev = dev;
8126182fdbdSPeter Wemm 	fd->fdc = fdc;
8136182fdbdSPeter Wemm 	fd->fdsu = fdsu;
8146182fdbdSPeter Wemm 	fd->fdu = device_get_unit(dev);
8156182fdbdSPeter Wemm 
816a97c75b7SDoug Rabson #ifdef __i386__
817b99f0a4aSAndrew Moore 	/* look up what bios thinks we have */
8186182fdbdSPeter Wemm 	switch (fd->fdu) {
8196182fdbdSPeter Wemm 	case 0:
8206182fdbdSPeter Wemm 		if (isa_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0)
8210722d6abSJoerg Wunsch 			fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
8220722d6abSJoerg Wunsch 		else
8230722d6abSJoerg Wunsch 			fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
824b99f0a4aSAndrew Moore 		break;
8256182fdbdSPeter Wemm 	case 1:
8266182fdbdSPeter Wemm 		fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
827dc5df763SJoerg Wunsch 		break;
828dc5df763SJoerg Wunsch 	default:
8296182fdbdSPeter Wemm 		fdt = RTCFDT_NONE;
830dc5df763SJoerg Wunsch 		break;
8316b7bd95bSJoerg Wunsch 	}
832a97c75b7SDoug Rabson #else
833a97c75b7SDoug Rabson 	fdt = RTCFDT_144M;	/* XXX probably */
834a97c75b7SDoug Rabson #endif
8356182fdbdSPeter Wemm 
8366182fdbdSPeter Wemm 	/* is there a unit? */
8376182fdbdSPeter Wemm 	if (fdt == RTCFDT_NONE)
8386182fdbdSPeter Wemm 		return (ENXIO);
8396182fdbdSPeter Wemm 
8406182fdbdSPeter Wemm 	/* select it */
8416182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNON);
8426182fdbdSPeter Wemm 	DELAY(1000000);	/* 1 sec */
8436182fdbdSPeter Wemm 
8446182fdbdSPeter Wemm #ifndef FIFO_BEFORE_MOTORON
8456182fdbdSPeter Wemm 	if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN
8466182fdbdSPeter Wemm 	    && enable_fifo(fdc) == 0) {
8476182fdbdSPeter Wemm 		device_print_prettyname(device_get_parent(dev));
8486182fdbdSPeter Wemm 		printf("FIFO enabled, %d bytes threshold\n", fifo_threshold);
849d66c374fSTor Egge 	}
8506182fdbdSPeter Wemm 	fd_fifo = 1;
8516182fdbdSPeter Wemm #endif
8526182fdbdSPeter Wemm 
8536182fdbdSPeter Wemm 	if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0)
8546182fdbdSPeter Wemm 	    && (st3 & NE7_ST3_T0)) {
855dc5df763SJoerg Wunsch 		/* if at track 0, first seek inwards */
856dc5df763SJoerg Wunsch 		/* seek some steps: */
8576182fdbdSPeter Wemm 		fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0);
858dc5df763SJoerg Wunsch 		DELAY(300000); /* ...wait a moment... */
8596182fdbdSPeter Wemm 		fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
860dc5df763SJoerg Wunsch 	}
861dc5df763SJoerg Wunsch 
862dc5df763SJoerg Wunsch 	/* If we're at track 0 first seek inwards. */
8636182fdbdSPeter Wemm 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
864dc5df763SJoerg Wunsch 		/* Seek some steps... */
8656182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
866dc5df763SJoerg Wunsch 			/* ...wait a moment... */
867dc5df763SJoerg Wunsch 			DELAY(300000);
868dc5df763SJoerg Wunsch 			/* make ctrlr happy: */
8696182fdbdSPeter Wemm 			fd_sense_int(fdc, 0, 0);
870dc5df763SJoerg Wunsch 		}
871dc5df763SJoerg Wunsch 	}
872dc5df763SJoerg Wunsch 
8736b7bd95bSJoerg Wunsch 	for (i = 0; i < 2; i++) {
8746b7bd95bSJoerg Wunsch 		/*
8756b7bd95bSJoerg Wunsch 		 * we must recalibrate twice, just in case the
8766b7bd95bSJoerg Wunsch 		 * heads have been beyond cylinder 76, since most
8776b7bd95bSJoerg Wunsch 		 * FDCs still barf when attempting to recalibrate
8786b7bd95bSJoerg Wunsch 		 * more than 77 steps
8796b7bd95bSJoerg Wunsch 		 */
880dc5df763SJoerg Wunsch 		/* go back to 0: */
8816182fdbdSPeter Wemm 		if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
8826b7bd95bSJoerg Wunsch 			/* a second being enough for full stroke seek*/
8836b7bd95bSJoerg Wunsch 			DELAY(i == 0 ? 1000000 : 300000);
8845b81b6b3SRodney W. Grimes 
8856b7bd95bSJoerg Wunsch 			/* anything responding? */
886dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
887dc5df763SJoerg Wunsch 			    (st0 & NE7_ST0_EC) == 0)
8886b7bd95bSJoerg Wunsch 				break; /* already probed succesfully */
8896b7bd95bSJoerg Wunsch 		}
890dc5df763SJoerg Wunsch 	}
8916b7bd95bSJoerg Wunsch 
8926182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNOFF);
8933a2f7427SDavid Greenman 
8943a2f7427SDavid Greenman 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
8956182fdbdSPeter Wemm 		return (ENXIO);
8965b81b6b3SRodney W. Grimes 
897dc5df763SJoerg Wunsch 	fd->track = FD_NO_TRACK;
898b99f0a4aSAndrew Moore 	fd->fdc = fdc;
899b99f0a4aSAndrew Moore 	fd->fdsu = fdsu;
9003a2f7427SDavid Greenman 	fd->options = 0;
90102a19910SJustin T. Gibbs 	callout_handle_init(&fd->toffhandle);
90202a19910SJustin T. Gibbs 	callout_handle_init(&fd->tohandle);
9035b81b6b3SRodney W. Grimes 
904b99f0a4aSAndrew Moore 	switch (fdt) {
9057ca0641bSAndrey A. Chernov 	case RTCFDT_12M:
9066182fdbdSPeter Wemm 		device_set_desc(dev, "1200-KB 5.25\" drive");
907b99f0a4aSAndrew Moore 		fd->type = FD_1200;
9087ca0641bSAndrey A. Chernov 		break;
9090722d6abSJoerg Wunsch 	case RTCFDT_144M | RTCFDT_144M_PRETENDED:
9106182fdbdSPeter Wemm 		device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive");
9110722d6abSJoerg Wunsch 		fdt = RTCFDT_144M;
9126182fdbdSPeter Wemm 		fd->type = FD_1440;
9137ca0641bSAndrey A. Chernov 	case RTCFDT_144M:
9146182fdbdSPeter Wemm 		device_set_desc(dev, "1440-KB 3.5\" drive");
915b99f0a4aSAndrew Moore 		fd->type = FD_1440;
9167ca0641bSAndrey A. Chernov 		break;
917290dd077SJoerg Wunsch 	case RTCFDT_288M:
91886a727d9SJoerg Wunsch 	case RTCFDT_288M_1:
9196182fdbdSPeter Wemm 		device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)");
920290dd077SJoerg Wunsch 		fd->type = FD_1440;
921290dd077SJoerg Wunsch 		break;
9227ca0641bSAndrey A. Chernov 	case RTCFDT_360K:
9236182fdbdSPeter Wemm 		device_set_desc(dev, "360-KB 5.25\" drive");
924b99f0a4aSAndrew Moore 		fd->type = FD_360;
9257ca0641bSAndrey A. Chernov 		break;
926ed2fa05eSAndrey A. Chernov 	case RTCFDT_720K:
9276182fdbdSPeter Wemm 		printf("720-KB 3.5\" drive");
928b99f0a4aSAndrew Moore 		fd->type = FD_720;
929ed2fa05eSAndrey A. Chernov 		break;
9307ca0641bSAndrey A. Chernov 	default:
9316182fdbdSPeter Wemm 		return (ENXIO);
9325b81b6b3SRodney W. Grimes 	}
9336182fdbdSPeter Wemm 	return (0);
9346182fdbdSPeter Wemm }
9356182fdbdSPeter Wemm 
9366182fdbdSPeter Wemm static int
9376182fdbdSPeter Wemm fd_attach(device_t dev)
9386182fdbdSPeter Wemm {
9396182fdbdSPeter Wemm 	struct	fd_data *fd;
940ada9bd8cSJulian Elischer #if 0
941ada9bd8cSJulian Elischer 	int	i;
942ada9bd8cSJulian Elischer 	int	mynor;
943ada9bd8cSJulian Elischer 	int	typemynor;
944ada9bd8cSJulian Elischer 	int	typesize;
945ada9bd8cSJulian Elischer #endif
9466182fdbdSPeter Wemm 
9476182fdbdSPeter Wemm 	fd = device_get_softc(dev);
9486182fdbdSPeter Wemm 
949ada9bd8cSJulian Elischer 	make_dev(&fd_cdevsw, (fd->fdu << 6),
950ada9bd8cSJulian Elischer 		UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fd->fdu);
951ada9bd8cSJulian Elischer 
952ada9bd8cSJulian Elischer #if 0
953ada9bd8cSJulian Elischer 	/* Other make_dev() go here. */
954ada9bd8cSJulian Elischer #endif
955ada9bd8cSJulian Elischer 
956671e2ceeSBruce Evans 	/*
957b2dfb1f9SJustin T. Gibbs 	 * Export the drive to the devstat interface.
958671e2ceeSBruce Evans 	 */
9596182fdbdSPeter Wemm 	devstat_add_entry(&fd->device_stats, device_get_name(dev),
9606182fdbdSPeter Wemm 			  device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS,
9612a888f93SKenneth D. Merry 			  DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER,
9622a888f93SKenneth D. Merry 			  DEVSTAT_PRIORITY_FD);
9636182fdbdSPeter Wemm 	return (0);
9645b81b6b3SRodney W. Grimes }
9655b81b6b3SRodney W. Grimes 
96616e68fc6SPeter Wemm static device_method_t fd_methods[] = {
96716e68fc6SPeter Wemm 	/* Device interface */
96816e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fd_probe),
96916e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fd_attach),
97016e68fc6SPeter Wemm 	DEVMETHOD(device_detach,	bus_generic_detach),
97116e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
97216e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend), /* XXX */
97316e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume), /* XXX */
97416e68fc6SPeter Wemm 
97516e68fc6SPeter Wemm 	{ 0, 0 }
97616e68fc6SPeter Wemm };
97716e68fc6SPeter Wemm 
97816e68fc6SPeter Wemm static driver_t fd_driver = {
97916e68fc6SPeter Wemm 	"fd",
98016e68fc6SPeter Wemm 	fd_methods,
98116e68fc6SPeter Wemm 	sizeof(struct fd_data)
98216e68fc6SPeter Wemm };
98316e68fc6SPeter Wemm 
98416e68fc6SPeter Wemm DEV_DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, fd_cdevsw, 0, 0);
98516e68fc6SPeter Wemm 
98616e68fc6SPeter Wemm /******************************************************************/
98716e68fc6SPeter Wemm 
98869acd21dSWarner Losh #ifdef FDC_YE
98969acd21dSWarner Losh /*
99069acd21dSWarner Losh  * this is a subset of fdattach() optimized for the Y-E Data
99169acd21dSWarner Losh  * PCMCIA floppy drive.
99269acd21dSWarner Losh  */
99369acd21dSWarner Losh static int yeattach(struct isa_device *dev)
99469acd21dSWarner Losh {
99569acd21dSWarner Losh 	fdcu_t  fdcu = dev->id_unit;
99669acd21dSWarner Losh 	fdc_p   fdc = fdc_data + fdcu;
99769acd21dSWarner Losh 	fdsu_t  fdsu = 0;               /* assume 1 drive per YE controller */
99869acd21dSWarner Losh 	fdu_t   fdu;
99969acd21dSWarner Losh 	fd_p    fd;
100069acd21dSWarner Losh 	int     st0, st3, i;
100169acd21dSWarner Losh 	fdc->fdcu = fdcu;
100269acd21dSWarner Losh 	/*
100369acd21dSWarner Losh 	 * the FDC_PCMCIA flag is used to to indicate special PIO is used
100469acd21dSWarner Losh 	 * instead of DMA
100569acd21dSWarner Losh 	 */
100669acd21dSWarner Losh 	fdc->flags = FDC_ATTACHED|FDC_PCMCIA;
100769acd21dSWarner Losh 	fdc->state = DEVIDLE;
100869acd21dSWarner Losh 	/* reset controller, turn motor off, clear fdout mirror reg */
100969acd21dSWarner Losh 	outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
101069acd21dSWarner Losh 	bufq_init(&fdc->head);
101169acd21dSWarner Losh 	/*
101269acd21dSWarner Losh 	 * assume 2 drives/ "normal" controller
101369acd21dSWarner Losh 	 */
101469acd21dSWarner Losh 	fdu = fdcu * 2;
101569acd21dSWarner Losh 	if (fdu >= NFD) {
101669acd21dSWarner Losh 		printf("fdu %d >= NFD\n",fdu);
101769acd21dSWarner Losh 		return(0);
101869acd21dSWarner Losh 	};
101969acd21dSWarner Losh 	fd = &fd_data[fdu];
102069acd21dSWarner Losh 
102169acd21dSWarner Losh 	set_motor(fdcu, fdsu, TURNON);
102269acd21dSWarner Losh 	DELAY(1000000); /* 1 sec */
102369acd21dSWarner Losh 	fdc->fdct = FDC_NE765;
102469acd21dSWarner Losh 
102569acd21dSWarner Losh 	if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) &&
102669acd21dSWarner Losh 		(st3 & NE7_ST3_T0)) {
102769acd21dSWarner Losh 		/* if at track 0, first seek inwards */
102869acd21dSWarner Losh 		/* seek some steps: */
102969acd21dSWarner Losh 		(void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0);
103069acd21dSWarner Losh 		DELAY(300000); /* ...wait a moment... */
103169acd21dSWarner Losh 		(void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
103269acd21dSWarner Losh 	}
103369acd21dSWarner Losh 
103469acd21dSWarner Losh 	/* If we're at track 0 first seek inwards. */
103569acd21dSWarner Losh 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
103669acd21dSWarner Losh 		/* Seek some steps... */
103769acd21dSWarner Losh 		if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
103869acd21dSWarner Losh 			/* ...wait a moment... */
103969acd21dSWarner Losh 			DELAY(300000);
104069acd21dSWarner Losh 			/* make ctrlr happy: */
104169acd21dSWarner Losh 			(void)fd_sense_int(fdc, 0, 0);
104269acd21dSWarner Losh 		}
104369acd21dSWarner Losh 	}
104469acd21dSWarner Losh 
104569acd21dSWarner Losh 	for(i = 0; i < 2; i++) {
104669acd21dSWarner Losh 		/*
104769acd21dSWarner Losh 		 * we must recalibrate twice, just in case the
104869acd21dSWarner Losh 		 * heads have been beyond cylinder 76, since most
104969acd21dSWarner Losh 		 * FDCs still barf when attempting to recalibrate
105069acd21dSWarner Losh 		 * more than 77 steps
105169acd21dSWarner Losh 		 */
105269acd21dSWarner Losh 		/* go back to 0: */
105369acd21dSWarner Losh 		if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
105469acd21dSWarner Losh 			/* a second being enough for full stroke seek*/
105569acd21dSWarner Losh 			DELAY(i == 0? 1000000: 300000);
105669acd21dSWarner Losh 
105769acd21dSWarner Losh 			/* anything responding? */
105869acd21dSWarner Losh 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
105969acd21dSWarner Losh 				(st0 & NE7_ST0_EC) == 0)
106069acd21dSWarner Losh 				break; /* already probed succesfully */
106169acd21dSWarner Losh 		}
106269acd21dSWarner Losh 	}
106369acd21dSWarner Losh 
106469acd21dSWarner Losh 	set_motor(fdcu, fdsu, TURNOFF);
106569acd21dSWarner Losh 
106669acd21dSWarner Losh 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
106769acd21dSWarner Losh 		return(0);
106869acd21dSWarner Losh 
106969acd21dSWarner Losh 	fd->track = FD_NO_TRACK;
107069acd21dSWarner Losh 	fd->fdc = fdc;
107169acd21dSWarner Losh 	fd->fdsu = fdsu;
107269acd21dSWarner Losh 	fd->options = 0;
107369acd21dSWarner Losh 	printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu);
107469acd21dSWarner Losh 	fd->type = FD_1440;
107569acd21dSWarner Losh 
107669acd21dSWarner Losh 	return (1);
107769acd21dSWarner Losh }
107869acd21dSWarner Losh #endif
107969acd21dSWarner Losh 
10805b81b6b3SRodney W. Grimes /****************************************************************************/
10815b81b6b3SRodney W. Grimes /*                            motor control stuff                           */
10825e235068SJordan K. Hubbard /*		remember to not deselect the drive we're working on         */
10835b81b6b3SRodney W. Grimes /****************************************************************************/
10843a2f7427SDavid Greenman static void
10856182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon)
10865b81b6b3SRodney W. Grimes {
10876182fdbdSPeter Wemm 	int fdout = fdc->fdout;
10883a2f7427SDavid Greenman 	int needspecify = 0;
10893a2f7427SDavid Greenman 
10903a2f7427SDavid Greenman 	if(turnon) {
10913a2f7427SDavid Greenman 		fdout &= ~FDO_FDSEL;
10923a2f7427SDavid Greenman 		fdout |= (FDO_MOEN0 << fdsu) + fdsu;
10933a2f7427SDavid Greenman 	} else
10943a2f7427SDavid Greenman 		fdout &= ~(FDO_MOEN0 << fdsu);
10953a2f7427SDavid Greenman 
10965e235068SJordan K. Hubbard 	if(!turnon
10975e235068SJordan K. Hubbard 	   && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0)
10985e235068SJordan K. Hubbard 		/* gonna turn off the last drive, put FDC to bed */
10995e235068SJordan K. Hubbard 		fdout &= ~ (FDO_FRST|FDO_FDMAEN);
11005e235068SJordan K. Hubbard 	else {
11013a2f7427SDavid Greenman 		/* make sure controller is selected and specified */
11023a2f7427SDavid Greenman 		if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0)
11033a2f7427SDavid Greenman 			needspecify = 1;
11043a2f7427SDavid Greenman 		fdout |= (FDO_FRST|FDO_FDMAEN);
11055b81b6b3SRodney W. Grimes 	}
11065b81b6b3SRodney W. Grimes 
11076182fdbdSPeter Wemm 	outb(fdc->baseport+FDOUT, fdout);
11086182fdbdSPeter Wemm 	fdc->fdout = fdout;
11093a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdout);
11103a2f7427SDavid Greenman 
11113a2f7427SDavid Greenman 	if (needspecify) {
1112dc8603e3SJoerg Wunsch 		/*
1113dc5df763SJoerg Wunsch 		 * XXX
1114dc8603e3SJoerg Wunsch 		 * special case: since we have just woken up the FDC
1115dc8603e3SJoerg Wunsch 		 * from its sleep, we silently assume the command will
1116dc8603e3SJoerg Wunsch 		 * be accepted, and do not test for a timeout
1117dc8603e3SJoerg Wunsch 		 */
11186182fdbdSPeter Wemm 		(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1119dc5df763SJoerg Wunsch 			     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1120dc5df763SJoerg Wunsch 			     0);
11216182fdbdSPeter Wemm 		if (fdc->flags & FDC_HAS_FIFO)
11226182fdbdSPeter Wemm 			(void) enable_fifo(fdc);
11233a2f7427SDavid Greenman 	}
11243a2f7427SDavid Greenman }
11253a2f7427SDavid Greenman 
1126381fe1aaSGarrett Wollman static void
11276182fdbdSPeter Wemm fd_turnoff(void *xfd)
11285b81b6b3SRodney W. Grimes {
1129f5f7ba03SJordan K. Hubbard 	int	s;
11306182fdbdSPeter Wemm 	fd_p fd = xfd;
1131dc16046fSJoerg Wunsch 
11326182fdbdSPeter Wemm 	TRACE1("[fd%d: turnoff]", fd->fdu);
11338335c1b8SBruce Evans 
11348335c1b8SBruce Evans 	/*
11358335c1b8SBruce Evans 	 * Don't turn off the motor yet if the drive is active.
11368335c1b8SBruce Evans 	 * XXX shouldn't even schedule turnoff until drive is inactive
11378335c1b8SBruce Evans 	 * and nothing is queued on it.
11388335c1b8SBruce Evans 	 */
11396182fdbdSPeter Wemm 	if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) {
11406182fdbdSPeter Wemm 		fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
11418335c1b8SBruce Evans 		return;
11428335c1b8SBruce Evans 	}
11438335c1b8SBruce Evans 
1144f5f7ba03SJordan K. Hubbard 	s = splbio();
11455b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR;
11466182fdbdSPeter Wemm 	set_motor(fd->fdc, fd->fdsu, TURNOFF);
1147f5f7ba03SJordan K. Hubbard 	splx(s);
11485b81b6b3SRodney W. Grimes }
11495b81b6b3SRodney W. Grimes 
11503a2f7427SDavid Greenman static void
11516182fdbdSPeter Wemm fd_motor_on(void *xfd)
11525b81b6b3SRodney W. Grimes {
1153f5f7ba03SJordan K. Hubbard 	int	s;
11546182fdbdSPeter Wemm 	fd_p fd = xfd;
1155f5f7ba03SJordan K. Hubbard 
1156f5f7ba03SJordan K. Hubbard 	s = splbio();
11575b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR_WAIT;
11585b81b6b3SRodney W. Grimes 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
11595b81b6b3SRodney W. Grimes 	{
11606182fdbdSPeter Wemm 		fdc_intr(fd->fdc);
11615b81b6b3SRodney W. Grimes 	}
1162f5f7ba03SJordan K. Hubbard 	splx(s);
11635b81b6b3SRodney W. Grimes }
11645b81b6b3SRodney W. Grimes 
11653a2f7427SDavid Greenman static void
11666182fdbdSPeter Wemm fd_turnon(fd_p fd)
11675b81b6b3SRodney W. Grimes {
11685b81b6b3SRodney W. Grimes 	if(!(fd->flags & FD_MOTOR))
11695b81b6b3SRodney W. Grimes 	{
11703a2f7427SDavid Greenman 		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
11716182fdbdSPeter Wemm 		set_motor(fd->fdc, fd->fdsu, TURNON);
11726182fdbdSPeter Wemm 		timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */
11735b81b6b3SRodney W. Grimes 	}
11745b81b6b3SRodney W. Grimes }
11755b81b6b3SRodney W. Grimes 
1176381fe1aaSGarrett Wollman static void
1177dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc)
11785b81b6b3SRodney W. Grimes {
11793a2f7427SDavid Greenman 	/* Try a reset, keep motor on */
11803a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
11813a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
11823a2f7427SDavid Greenman 	DELAY(100);
11833a2f7427SDavid Greenman 	/* enable FDC, but defer interrupts a moment */
11843a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN);
11853a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
11863a2f7427SDavid Greenman 	DELAY(100);
11873a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout);
11883a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout);
11893a2f7427SDavid Greenman 
1190dc5df763SJoerg Wunsch 	/* XXX after a reset, silently believe the FDC will accept commands */
11916182fdbdSPeter Wemm 	(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1192dc5df763SJoerg Wunsch 		     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1193dc5df763SJoerg Wunsch 		     0);
1194d66c374fSTor Egge 	if (fdc->flags & FDC_HAS_FIFO)
1195d66c374fSTor Egge 		(void) enable_fifo(fdc);
11965b81b6b3SRodney W. Grimes }
11975b81b6b3SRodney W. Grimes 
11985b81b6b3SRodney W. Grimes /****************************************************************************/
11995b81b6b3SRodney W. Grimes /*                             fdc in/out                                   */
12005b81b6b3SRodney W. Grimes /****************************************************************************/
12015b81b6b3SRodney W. Grimes int
12026182fdbdSPeter Wemm in_fdc(struct fdc_data *fdc)
12035b81b6b3SRodney W. Grimes {
12046182fdbdSPeter Wemm 	int baseport = fdc->baseport;
12055b81b6b3SRodney W. Grimes 	int i, j = 100000;
12063a2f7427SDavid Greenman 	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
12075b81b6b3SRodney W. Grimes 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1208dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
12096182fdbdSPeter Wemm 			return fdc_err(fdc, "ready for output in input\n");
12105b81b6b3SRodney W. Grimes 	if (j <= 0)
12116182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "input ready timeout\n": 0);
1212d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
12133a2f7427SDavid Greenman 	i = inb(baseport+FDDATA);
12143a2f7427SDavid Greenman 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
12155b81b6b3SRodney W. Grimes 	return(i);
1216d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
12173a2f7427SDavid Greenman 	return inb(baseport+FDDATA);
1218d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
12195b81b6b3SRodney W. Grimes }
12205b81b6b3SRodney W. Grimes 
1221dc5df763SJoerg Wunsch /*
1222dc5df763SJoerg Wunsch  * fd_in: Like in_fdc, but allows you to see if it worked.
1223dc5df763SJoerg Wunsch  */
1224b5e8ce9fSBruce Evans static int
12256182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr)
1226dc5df763SJoerg Wunsch {
12276182fdbdSPeter Wemm 	int baseport = fdc->baseport;
1228dc5df763SJoerg Wunsch 	int i, j = 100000;
1229dc5df763SJoerg Wunsch 	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
1230dc5df763SJoerg Wunsch 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1231dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
12326182fdbdSPeter Wemm 			return fdc_err(fdc, "ready for output in input\n");
1233dc5df763SJoerg Wunsch 	if (j <= 0)
12346182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "input ready timeout\n": 0);
1235d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
1236dc5df763SJoerg Wunsch 	i = inb(baseport+FDDATA);
1237dc5df763SJoerg Wunsch 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
1238dc5df763SJoerg Wunsch 	*ptr = i;
1239dc5df763SJoerg Wunsch 	return 0;
1240d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
1241dc5df763SJoerg Wunsch 	i = inb(baseport+FDDATA);
1242dc5df763SJoerg Wunsch 	if (ptr)
1243dc5df763SJoerg Wunsch 		*ptr = i;
1244dc5df763SJoerg Wunsch 	return 0;
1245d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
1246dc5df763SJoerg Wunsch }
1247dc5df763SJoerg Wunsch 
1248dc5df763SJoerg Wunsch int
12496182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x)
12505b81b6b3SRodney W. Grimes {
12516182fdbdSPeter Wemm 	int baseport = fdc->baseport;
12523b3837dbSRodney W. Grimes 	int i;
12535b81b6b3SRodney W. Grimes 
12543b3837dbSRodney W. Grimes 	/* Check that the direction bit is set */
12553b3837dbSRodney W. Grimes 	i = 100000;
12563a2f7427SDavid Greenman 	while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0);
12576182fdbdSPeter Wemm 	if (i <= 0) return fdc_err(fdc, "direction bit not set\n");
12583b3837dbSRodney W. Grimes 
12593b3837dbSRodney W. Grimes 	/* Check that the floppy controller is ready for a command */
12603b3837dbSRodney W. Grimes 	i = 100000;
12613a2f7427SDavid Greenman 	while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0);
126216b04b6aSJoerg Wunsch 	if (i <= 0)
12636182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "output ready timeout\n": 0);
12643b3837dbSRodney W. Grimes 
12653b3837dbSRodney W. Grimes 	/* Send the command and return */
12663a2f7427SDavid Greenman 	outb(baseport+FDDATA, x);
12673a2f7427SDavid Greenman 	TRACE1("[0x%x->FDDATA]", x);
12685b81b6b3SRodney W. Grimes 	return (0);
12695b81b6b3SRodney W. Grimes }
12705b81b6b3SRodney W. Grimes 
12715b81b6b3SRodney W. Grimes /****************************************************************************/
12725b81b6b3SRodney W. Grimes /*                           fdopen/fdclose                                 */
12735b81b6b3SRodney W. Grimes /****************************************************************************/
1274381fe1aaSGarrett Wollman int
1275671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p)
12765b81b6b3SRodney W. Grimes {
12775b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
127820a29168SAndrey A. Chernov 	int type = FDTYPE(minor(dev));
12796182fdbdSPeter Wemm 	fd_p	fd;
1280b99f0a4aSAndrew Moore 	fdc_p	fdc;
12815b81b6b3SRodney W. Grimes 
12829465bf44SPoul-Henning Kamp 	dev->si_bsize_phys = DEV_BSIZE;
12837012bab9SJulian Elischer 	dev->si_bsize_best = BLKDEV_IOSIZE;
12849465bf44SPoul-Henning Kamp 	dev->si_bsize_max = MAXBSIZE;
12855b81b6b3SRodney W. Grimes 	/* check bounds */
12866182fdbdSPeter Wemm 	if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0)
1287b99f0a4aSAndrew Moore 		return (ENXIO);
12886182fdbdSPeter Wemm 	fdc = fd->fdc;
12896182fdbdSPeter Wemm 	if ((fdc == NULL) || (fd->type == NO_TYPE))
1290b99f0a4aSAndrew Moore 		return (ENXIO);
1291b99f0a4aSAndrew Moore 	if (type > NUMDENS)
1292b99f0a4aSAndrew Moore 		return (ENXIO);
12937ca0641bSAndrey A. Chernov 	if (type == 0)
12946182fdbdSPeter Wemm 		type = fd->type;
12957ca0641bSAndrey A. Chernov 	else {
12963e425b96SJulian Elischer 		/*
12973e425b96SJulian Elischer 		 * For each type of basic drive, make sure we are trying
12983e425b96SJulian Elischer 		 * to open a type it can do,
12993e425b96SJulian Elischer 		 */
13006182fdbdSPeter Wemm 		if (type != fd->type) {
13016182fdbdSPeter Wemm 			switch (fd->type) {
13027ca0641bSAndrey A. Chernov 			case FD_360:
13037ca0641bSAndrey A. Chernov 				return (ENXIO);
1304ed2fa05eSAndrey A. Chernov 			case FD_720:
1305b39c878eSAndrey A. Chernov 				if (   type != FD_820
1306b39c878eSAndrey A. Chernov 				    && type != FD_800
1307ed2fa05eSAndrey A. Chernov 				   )
1308ed2fa05eSAndrey A. Chernov 					return (ENXIO);
1309ed2fa05eSAndrey A. Chernov 				break;
13107ca0641bSAndrey A. Chernov 			case FD_1200:
1311b39c878eSAndrey A. Chernov 				switch (type) {
1312b39c878eSAndrey A. Chernov 				case FD_1480:
1313b39c878eSAndrey A. Chernov 					type = FD_1480in5_25;
1314fa4700b4SAndrey A. Chernov 					break;
13157ca0641bSAndrey A. Chernov 				case FD_1440:
1316b39c878eSAndrey A. Chernov 					type = FD_1440in5_25;
1317b39c878eSAndrey A. Chernov 					break;
1318b39c878eSAndrey A. Chernov 				case FD_820:
1319b39c878eSAndrey A. Chernov 					type = FD_820in5_25;
1320b39c878eSAndrey A. Chernov 					break;
1321b39c878eSAndrey A. Chernov 				case FD_800:
1322b39c878eSAndrey A. Chernov 					type = FD_800in5_25;
1323b39c878eSAndrey A. Chernov 					break;
1324b39c878eSAndrey A. Chernov 				case FD_720:
1325b39c878eSAndrey A. Chernov 					type = FD_720in5_25;
1326b39c878eSAndrey A. Chernov 					break;
1327b39c878eSAndrey A. Chernov 				case FD_360:
1328b39c878eSAndrey A. Chernov 					type = FD_360in5_25;
1329b39c878eSAndrey A. Chernov 					break;
1330b39c878eSAndrey A. Chernov 				default:
1331b39c878eSAndrey A. Chernov 					return(ENXIO);
1332b39c878eSAndrey A. Chernov 				}
1333b39c878eSAndrey A. Chernov 				break;
1334b39c878eSAndrey A. Chernov 			case FD_1440:
1335b39c878eSAndrey A. Chernov 				if (   type != FD_1720
1336b39c878eSAndrey A. Chernov 				    && type != FD_1480
1337ed2fa05eSAndrey A. Chernov 				    && type != FD_1200
1338b39c878eSAndrey A. Chernov 				    && type != FD_820
1339b39c878eSAndrey A. Chernov 				    && type != FD_800
1340b39c878eSAndrey A. Chernov 				    && type != FD_720
13417ca0641bSAndrey A. Chernov 				    )
1342dffff499SAndrey A. Chernov 					return(ENXIO);
1343fa4700b4SAndrey A. Chernov 				break;
13447ca0641bSAndrey A. Chernov 			}
13457ca0641bSAndrey A. Chernov 		}
1346fa4700b4SAndrey A. Chernov 	}
13476182fdbdSPeter Wemm 	fd->ft = fd_types + type - 1;
13486182fdbdSPeter Wemm 	fd->flags |= FD_OPEN;
13496182fdbdSPeter Wemm 	device_busy(fd->dev);
13506182fdbdSPeter Wemm 	device_busy(fd->fdc->fdc_dev);
13515b81b6b3SRodney W. Grimes 	return 0;
13525b81b6b3SRodney W. Grimes }
13535b81b6b3SRodney W. Grimes 
1354381fe1aaSGarrett Wollman int
1355671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p)
13565b81b6b3SRodney W. Grimes {
13575b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
13586182fdbdSPeter Wemm 	struct fd_data *fd;
1359b99f0a4aSAndrew Moore 
13606182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
13616182fdbdSPeter Wemm 	fd->flags &= ~FD_OPEN;
13626182fdbdSPeter Wemm 	fd->options &= ~FDOPT_NORETRY;
1363dc16046fSJoerg Wunsch 
13645b81b6b3SRodney W. Grimes 	return (0);
13655b81b6b3SRodney W. Grimes }
13665b81b6b3SRodney W. Grimes 
13673a2f7427SDavid Greenman /****************************************************************************/
13683a2f7427SDavid Greenman /*                               fdstrategy                                 */
13693a2f7427SDavid Greenman /****************************************************************************/
13703a2f7427SDavid Greenman void
13713a2f7427SDavid Greenman fdstrategy(struct buf *bp)
13723a2f7427SDavid Greenman {
1373bb6382faSJoerg Wunsch 	unsigned nblocks, blknum, cando;
13743a2f7427SDavid Greenman  	int	s;
13753a2f7427SDavid Greenman  	fdu_t	fdu;
13763a2f7427SDavid Greenman  	fdc_p	fdc;
13773a2f7427SDavid Greenman  	fd_p	fd;
13783a2f7427SDavid Greenman 	size_t	fdblk;
13793a2f7427SDavid Greenman 
13803a2f7427SDavid Greenman  	fdu = FDUNIT(minor(bp->b_dev));
13816182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
13826182fdbdSPeter Wemm 	if (fd == 0)
13836182fdbdSPeter Wemm 		panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)",
13846182fdbdSPeter Wemm 		      (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev));
13853a2f7427SDavid Greenman 	fdc = fd->fdc;
138669acd21dSWarner Losh #ifdef FDC_YE
138769acd21dSWarner Losh 	if (fd->type == NO_TYPE) {
138869acd21dSWarner Losh 		bp->b_error = ENXIO;
138969acd21dSWarner Losh 		bp->b_flags |= B_ERROR;
139069acd21dSWarner Losh 		/*
139169acd21dSWarner Losh 		 * I _refuse_ to use a goto
139269acd21dSWarner Losh 		 */
139369acd21dSWarner Losh 		biodone(bp);
139469acd21dSWarner Losh 		return;
139569acd21dSWarner Losh 	};
139669acd21dSWarner Losh #endif
13973a2f7427SDavid Greenman 
1398d3628763SRodney W. Grimes 	fdblk = 128 << (fd->ft->secsize);
13993a2f7427SDavid Greenman 	if (!(bp->b_flags & B_FORMAT)) {
14006182fdbdSPeter Wemm 		if (bp->b_blkno < 0) {
1401dc5df763SJoerg Wunsch 			printf(
14026a0e6f42SRodney W. Grimes 		"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
1403702c623aSPoul-Henning Kamp 			       fdu, (u_long)bp->b_blkno, bp->b_bcount);
14043a2f7427SDavid Greenman 			bp->b_error = EINVAL;
14053a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
14063a2f7427SDavid Greenman 			goto bad;
14073a2f7427SDavid Greenman 		}
14083a2f7427SDavid Greenman 		if ((bp->b_bcount % fdblk) != 0) {
14093a2f7427SDavid Greenman 			bp->b_error = EINVAL;
14103a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
14113a2f7427SDavid Greenman 			goto bad;
14123a2f7427SDavid Greenman 		}
14133a2f7427SDavid Greenman 	}
14143a2f7427SDavid Greenman 
14153a2f7427SDavid Greenman 	/*
14163a2f7427SDavid Greenman 	 * Set up block calculations.
14173a2f7427SDavid Greenman 	 */
1418bb6382faSJoerg Wunsch 	if (bp->b_blkno > 20000000) {
1419bb6382faSJoerg Wunsch 		/*
1420bb6382faSJoerg Wunsch 		 * Reject unreasonably high block number, prevent the
1421bb6382faSJoerg Wunsch 		 * multiplication below from overflowing.
1422bb6382faSJoerg Wunsch 		 */
1423bb6382faSJoerg Wunsch 		bp->b_error = EINVAL;
14243a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
14253a2f7427SDavid Greenman 		goto bad;
14263a2f7427SDavid Greenman 	}
1427bb6382faSJoerg Wunsch 	blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk;
1428bb6382faSJoerg Wunsch  	nblocks = fd->ft->size;
1429bb6382faSJoerg Wunsch 	bp->b_resid = 0;
1430bb6382faSJoerg Wunsch 	if (blknum + (bp->b_bcount / fdblk) > nblocks) {
1431bb6382faSJoerg Wunsch 		if (blknum <= nblocks) {
1432bb6382faSJoerg Wunsch 			cando = (nblocks - blknum) * fdblk;
1433bb6382faSJoerg Wunsch 			bp->b_resid = bp->b_bcount - cando;
1434bb6382faSJoerg Wunsch 			if (cando == 0)
1435bb6382faSJoerg Wunsch 				goto bad;	/* not actually bad but EOF */
1436bb6382faSJoerg Wunsch 		} else {
1437bb6382faSJoerg Wunsch 			bp->b_error = EINVAL;
1438bb6382faSJoerg Wunsch 			bp->b_flags |= B_ERROR;
1439bb6382faSJoerg Wunsch 			goto bad;
1440bb6382faSJoerg Wunsch 		}
1441bb6382faSJoerg Wunsch 	}
144227513ca7SBruce Evans  	bp->b_pblkno = bp->b_blkno;
14433a2f7427SDavid Greenman 	s = splbio();
144402a19910SJustin T. Gibbs 	bufqdisksort(&fdc->head, bp);
14456182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */
1446b2dfb1f9SJustin T. Gibbs 
1447b2dfb1f9SJustin T. Gibbs 	/* Tell devstat we are starting on the transaction */
1448b2dfb1f9SJustin T. Gibbs 	devstat_start_transaction(&fd->device_stats);
1449b2dfb1f9SJustin T. Gibbs 
14506182fdbdSPeter Wemm 	fdstart(fdc);
14513a2f7427SDavid Greenman 	splx(s);
14523a2f7427SDavid Greenman 	return;
14533a2f7427SDavid Greenman 
14543a2f7427SDavid Greenman bad:
14553a2f7427SDavid Greenman 	biodone(bp);
14563a2f7427SDavid Greenman }
14573a2f7427SDavid Greenman 
14585b81b6b3SRodney W. Grimes /***************************************************************\
14595b81b6b3SRodney W. Grimes *				fdstart				*
14605b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy	*
14615b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command	*
14625b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more*
14635b81b6b3SRodney W. Grimes * work to do and picks up what we just added.			*
14645b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it	*
14655b81b6b3SRodney W. Grimes * will pick up our work when the present work completes		*
14665b81b6b3SRodney W. Grimes \***************************************************************/
1467381fe1aaSGarrett Wollman static void
14686182fdbdSPeter Wemm fdstart(struct fdc_data *fdc)
14695b81b6b3SRodney W. Grimes {
14705b81b6b3SRodney W. Grimes 	int s;
14715b81b6b3SRodney W. Grimes 
14725b81b6b3SRodney W. Grimes 	s = splbio();
14736182fdbdSPeter Wemm 	if(fdc->state == DEVIDLE)
14745b81b6b3SRodney W. Grimes 	{
14756182fdbdSPeter Wemm 		fdc_intr(fdc);
14765b81b6b3SRodney W. Grimes 	}
14775b81b6b3SRodney W. Grimes 	splx(s);
14785b81b6b3SRodney W. Grimes }
14795b81b6b3SRodney W. Grimes 
1480381fe1aaSGarrett Wollman static void
14816182fdbdSPeter Wemm fd_iotimeout(void *xfdc)
14825b81b6b3SRodney W. Grimes {
14835c1a1eaeSBruce Evans  	fdc_p fdc;
1484f5f7ba03SJordan K. Hubbard 	int s;
14855b81b6b3SRodney W. Grimes 
14866182fdbdSPeter Wemm 	fdc = xfdc;
14875c1a1eaeSBruce Evans 	TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
14885b81b6b3SRodney W. Grimes 
14893a2f7427SDavid Greenman 	/*
14903a2f7427SDavid Greenman 	 * Due to IBM's brain-dead design, the FDC has a faked ready
14913a2f7427SDavid Greenman 	 * signal, hardwired to ready == true. Thus, any command
14923a2f7427SDavid Greenman 	 * issued if there's no diskette in the drive will _never_
14933a2f7427SDavid Greenman 	 * complete, and must be aborted by resetting the FDC.
14943a2f7427SDavid Greenman 	 * Many thanks, Big Blue!
14955c1a1eaeSBruce Evans 	 * The FDC must not be reset directly, since that would
14965c1a1eaeSBruce Evans 	 * interfere with the state machine.  Instead, pretend that
14975c1a1eaeSBruce Evans 	 * the command completed but was invalid.  The state machine
14985c1a1eaeSBruce Evans 	 * will reset the FDC and retry once.
14993a2f7427SDavid Greenman 	 */
15003a2f7427SDavid Greenman 	s = splbio();
15015c1a1eaeSBruce Evans 	fdc->status[0] = NE7_ST0_IC_IV;
15025c1a1eaeSBruce Evans 	fdc->flags &= ~FDC_STAT_VALID;
15035c1a1eaeSBruce Evans 	fdc->state = IOTIMEDOUT;
15046182fdbdSPeter Wemm 	fdc_intr(fdc);
1505f5f7ba03SJordan K. Hubbard 	splx(s);
15065b81b6b3SRodney W. Grimes }
15075b81b6b3SRodney W. Grimes 
15085b81b6b3SRodney W. Grimes /* just ensure it has the right spl */
1509381fe1aaSGarrett Wollman static void
15106182fdbdSPeter Wemm fd_pseudointr(void *xfdc)
15115b81b6b3SRodney W. Grimes {
15125b81b6b3SRodney W. Grimes 	int	s;
15133a2f7427SDavid Greenman 
15145b81b6b3SRodney W. Grimes 	s = splbio();
15156182fdbdSPeter Wemm 	fdc_intr(xfdc);
15165b81b6b3SRodney W. Grimes 	splx(s);
15175b81b6b3SRodney W. Grimes }
15185b81b6b3SRodney W. Grimes 
15195b81b6b3SRodney W. Grimes /***********************************************************************\
15205b81b6b3SRodney W. Grimes *                                 fdintr				*
15215b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0			*
15225b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO 						*
15235b81b6b3SRodney W. Grimes \***********************************************************************/
1524fe310de8SBruce Evans static void
15256182fdbdSPeter Wemm fdc_intr(void *xfdc)
15265b81b6b3SRodney W. Grimes {
15276182fdbdSPeter Wemm 	fdc_p fdc = xfdc;
15286182fdbdSPeter Wemm 	while(fdstate(fdc))
1529381fe1aaSGarrett Wollman 		;
15305b81b6b3SRodney W. Grimes }
15315b81b6b3SRodney W. Grimes 
153269acd21dSWarner Losh #ifdef FDC_YE
153369acd21dSWarner Losh /*
153469acd21dSWarner Losh  * magic pseudo-DMA initialization for YE FDC. Sets count and
153569acd21dSWarner Losh  * direction
153669acd21dSWarner Losh  */
153769acd21dSWarner Losh #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \
153869acd21dSWarner Losh 	outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f)))
153969acd21dSWarner Losh 
154069acd21dSWarner Losh /*
154169acd21dSWarner Losh  * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy
154269acd21dSWarner Losh  */
154369acd21dSWarner Losh static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count)
154469acd21dSWarner Losh {
154569acd21dSWarner Losh 	u_char *cptr = (u_char *)addr;
154669acd21dSWarner Losh 	fdc_p fdc = &fdc_data[fdcu];
154769acd21dSWarner Losh 	int io = fdc->baseport;
154869acd21dSWarner Losh 
154969acd21dSWarner Losh 	if (flags & B_READ) {
155069acd21dSWarner Losh 		if (fdc->state != PIOREAD) {
155169acd21dSWarner Losh 			fdc->state = PIOREAD;
155269acd21dSWarner Losh 			return(0);
155369acd21dSWarner Losh 		};
155469acd21dSWarner Losh 		SET_BCDR(0,count,io);
155569acd21dSWarner Losh 		insb(io+FDC_YE_DATAPORT,cptr,count);
155669acd21dSWarner Losh 	} else {
155769acd21dSWarner Losh 		outsb(io+FDC_YE_DATAPORT,cptr,count);
155869acd21dSWarner Losh 		SET_BCDR(0,count,io);
155969acd21dSWarner Losh 	};
156069acd21dSWarner Losh 	return(1);
156169acd21dSWarner Losh }
156269acd21dSWarner Losh #endif /* FDC_YE */
156369acd21dSWarner Losh 
15645b81b6b3SRodney W. Grimes /***********************************************************************\
15655b81b6b3SRodney W. Grimes * The controller state machine.						*
15665b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly	*
15675b81b6b3SRodney W. Grimes \***********************************************************************/
15683a2f7427SDavid Greenman static int
15696182fdbdSPeter Wemm fdstate(fdc_p fdc)
15705b81b6b3SRodney W. Grimes {
15715c1a1eaeSBruce Evans 	int read, format, head, i, sec = 0, sectrac, st0, cyl, st3;
1572bb6382faSJoerg Wunsch 	unsigned blknum = 0, b_cylinder = 0;
15735b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc->fdu;
15745b81b6b3SRodney W. Grimes 	fd_p fd;
157517542807SPoul-Henning Kamp 	register struct buf *bp;
1576b39c878eSAndrey A. Chernov 	struct fd_formb *finfo = NULL;
15773a2f7427SDavid Greenman 	size_t fdblk;
15785b81b6b3SRodney W. Grimes 
1579e93e63cbSBruce Evans 	bp = fdc->bp;
1580e93e63cbSBruce Evans 	if (bp == NULL) {
158102a19910SJustin T. Gibbs 		bp = bufq_first(&fdc->head);
1582e93e63cbSBruce Evans 		if (bp != NULL) {
1583e93e63cbSBruce Evans 			bufq_remove(&fdc->head, bp);
1584e93e63cbSBruce Evans 			fdc->bp = bp;
1585e93e63cbSBruce Evans 		}
1586e93e63cbSBruce Evans 	}
1587e93e63cbSBruce Evans 	if (bp == NULL) {
15885b81b6b3SRodney W. Grimes 		/***********************************************\
15895b81b6b3SRodney W. Grimes 		* nothing left for this controller to do	*
15905b81b6b3SRodney W. Grimes 		* Force into the IDLE state,			*
15915b81b6b3SRodney W. Grimes 		\***********************************************/
15925b81b6b3SRodney W. Grimes 		fdc->state = DEVIDLE;
15936182fdbdSPeter Wemm 		if (fdc->fd) {
15946182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
15956182fdbdSPeter Wemm 			printf("unexpected valid fd pointer\n");
15965b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
15975b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
15985b81b6b3SRodney W. Grimes 		}
15996182fdbdSPeter Wemm 		TRACE1("[fdc%d IDLE]", fdc->fdcu);
16005b81b6b3SRodney W. Grimes  		return (0);
16015b81b6b3SRodney W. Grimes 	}
16025b81b6b3SRodney W. Grimes 	fdu = FDUNIT(minor(bp->b_dev));
16036182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
16043a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
16056182fdbdSPeter Wemm 	if (fdc->fd && (fd != fdc->fd)) {
16066182fdbdSPeter Wemm 		device_print_prettyname(fd->dev);
16076182fdbdSPeter Wemm 		printf("confused fd pointers\n");
16085b81b6b3SRodney W. Grimes 	}
16095b81b6b3SRodney W. Grimes 	read = bp->b_flags & B_READ;
1610b39c878eSAndrey A. Chernov 	format = bp->b_flags & B_FORMAT;
1611bb6382faSJoerg Wunsch 	if (format) {
1612ab3f7469SPoul-Henning Kamp 		finfo = (struct fd_formb *)bp->b_data;
1613bb6382faSJoerg Wunsch 		fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1614bb6382faSJoerg Wunsch 			- (char *)finfo;
1615bb6382faSJoerg Wunsch 	}
1616bb6382faSJoerg Wunsch 	if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) {
16173e425b96SJulian Elischer 		blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk +
1618bb6382faSJoerg Wunsch 			fd->skip/fdblk;
1619bb6382faSJoerg Wunsch 		b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1620bb6382faSJoerg Wunsch 	}
16215b81b6b3SRodney W. Grimes 	TRACE1("fd%d", fdu);
16225b81b6b3SRodney W. Grimes 	TRACE1("[%s]", fdstates[fdc->state]);
16235b81b6b3SRodney W. Grimes 	TRACE1("(0x%x)", fd->flags);
16246182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle);
16256182fdbdSPeter Wemm 	fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
16265b81b6b3SRodney W. Grimes 	switch (fdc->state)
16275b81b6b3SRodney W. Grimes 	{
16285b81b6b3SRodney W. Grimes 	case DEVIDLE:
16295b81b6b3SRodney W. Grimes 	case FINDWORK:	/* we have found new work */
16305b81b6b3SRodney W. Grimes 		fdc->retry = 0;
16315b81b6b3SRodney W. Grimes 		fd->skip = 0;
16325b81b6b3SRodney W. Grimes 		fdc->fd = fd;
16335b81b6b3SRodney W. Grimes 		fdc->fdu = fdu;
16343a2f7427SDavid Greenman 		outb(fdc->baseport+FDCTL, fd->ft->trans);
16353a2f7427SDavid Greenman 		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
16365b81b6b3SRodney W. Grimes 		/*******************************************************\
16375b81b6b3SRodney W. Grimes 		* If the next drive has a motor startup pending, then	*
1638dc733423SDag-Erling Smørgrav 		* it will start up in its own good time		*
16395b81b6b3SRodney W. Grimes 		\*******************************************************/
16406182fdbdSPeter Wemm 		if(fd->flags & FD_MOTOR_WAIT) {
16415b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
16425b81b6b3SRodney W. Grimes 			return (0); /* come back later */
16435b81b6b3SRodney W. Grimes 		}
16445b81b6b3SRodney W. Grimes 		/*******************************************************\
16455b81b6b3SRodney W. Grimes 		* Maybe if it's not starting, it SHOULD be starting	*
16465b81b6b3SRodney W. Grimes 		\*******************************************************/
16475b81b6b3SRodney W. Grimes 		if (!(fd->flags & FD_MOTOR))
16485b81b6b3SRodney W. Grimes 		{
16495b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
16506182fdbdSPeter Wemm 			fd_turnon(fd);
16515b81b6b3SRodney W. Grimes 			return (0);
16525b81b6b3SRodney W. Grimes 		}
16535b81b6b3SRodney W. Grimes 		else	/* at least make sure we are selected */
16545b81b6b3SRodney W. Grimes 		{
16556182fdbdSPeter Wemm 			set_motor(fdc, fd->fdsu, TURNON);
16565b81b6b3SRodney W. Grimes 		}
16575c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
16585c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
16595c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
16605c1a1eaeSBruce Evans 		} else
16615e235068SJordan K. Hubbard 			fdc->state = DOSEEK;
16625b81b6b3SRodney W. Grimes 		break;
16635b81b6b3SRodney W. Grimes 	case DOSEEK:
1664bb6382faSJoerg Wunsch 		if (b_cylinder == (unsigned)fd->track)
16655b81b6b3SRodney W. Grimes 		{
16665b81b6b3SRodney W. Grimes 			fdc->state = SEEKCOMPLETE;
16675b81b6b3SRodney W. Grimes 			break;
16685b81b6b3SRodney W. Grimes 		}
16696182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK,
1670bb6382faSJoerg Wunsch 			   fd->fdsu, b_cylinder * fd->ft->steptrac,
1671dc5df763SJoerg Wunsch 			   0))
1672dc8603e3SJoerg Wunsch 		{
1673dc8603e3SJoerg Wunsch 			/*
1674dc8603e3SJoerg Wunsch 			 * seek command not accepted, looks like
1675dc8603e3SJoerg Wunsch 			 * the FDC went off to the Saints...
1676dc8603e3SJoerg Wunsch 			 */
1677dc8603e3SJoerg Wunsch 			fdc->retry = 6;	/* try a reset */
16786182fdbdSPeter Wemm 			return(retrier(fdc));
1679dc8603e3SJoerg Wunsch 		}
1680dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
16815b81b6b3SRodney W. Grimes 		fdc->state = SEEKWAIT;
16825b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
16835b81b6b3SRodney W. Grimes 	case SEEKWAIT:
16845b81b6b3SRodney W. Grimes 		/* allow heads to settle */
16856182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 16);
16865b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
16875b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
16885b81b6b3SRodney W. Grimes 	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
16895b81b6b3SRodney W. Grimes 		/* Make sure seek really happened*/
16906182fdbdSPeter Wemm 		if(fd->track == FD_NO_TRACK) {
1691bb6382faSJoerg Wunsch 			int descyl = b_cylinder * fd->ft->steptrac;
16923a2f7427SDavid Greenman 			do {
16933a2f7427SDavid Greenman 				/*
1694dc5df763SJoerg Wunsch 				 * This might be a "ready changed" interrupt,
1695dc5df763SJoerg Wunsch 				 * which cannot really happen since the
1696dc5df763SJoerg Wunsch 				 * RDY pin is hardwired to + 5 volts.  This
1697dc5df763SJoerg Wunsch 				 * generally indicates a "bouncing" intr
1698dc5df763SJoerg Wunsch 				 * line, so do one of the following:
1699dc5df763SJoerg Wunsch 				 *
1700dc5df763SJoerg Wunsch 				 * When running on an enhanced FDC that is
1701dc5df763SJoerg Wunsch 				 * known to not go stuck after responding
1702dc5df763SJoerg Wunsch 				 * with INVALID, fetch all interrupt states
1703dc5df763SJoerg Wunsch 				 * until seeing either an INVALID or a
1704dc5df763SJoerg Wunsch 				 * real interrupt condition.
1705dc5df763SJoerg Wunsch 				 *
1706dc5df763SJoerg Wunsch 				 * When running on a dumb old NE765, give
1707dc5df763SJoerg Wunsch 				 * up immediately.  The controller will
1708dc5df763SJoerg Wunsch 				 * provide up to four dummy RC interrupt
1709dc5df763SJoerg Wunsch 				 * conditions right after reset (for the
1710dc5df763SJoerg Wunsch 				 * corresponding four drives), so this is
1711dc5df763SJoerg Wunsch 				 * our only chance to get notice that it
1712dc5df763SJoerg Wunsch 				 * was not the FDC that caused the interrupt.
17133a2f7427SDavid Greenman 				 */
1714dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, &cyl)
1715dc5df763SJoerg Wunsch 				    == FD_NOT_VALID)
1716dc5df763SJoerg Wunsch 					return 0;
1717dc5df763SJoerg Wunsch 				if(fdc->fdct == FDC_NE765
1718dc5df763SJoerg Wunsch 				   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1719dc5df763SJoerg Wunsch 					return 0; /* hope for a real intr */
17203a2f7427SDavid Greenman 			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1721dc5df763SJoerg Wunsch 
17226182fdbdSPeter Wemm 			if (0 == descyl) {
1723dc5df763SJoerg Wunsch 				int failed = 0;
17243a2f7427SDavid Greenman 				/*
17253a2f7427SDavid Greenman 				 * seek to cyl 0 requested; make sure we are
17263a2f7427SDavid Greenman 				 * really there
17273a2f7427SDavid Greenman 				 */
1728dc5df763SJoerg Wunsch 				if (fd_sense_drive_status(fdc, &st3))
1729dc5df763SJoerg Wunsch 					failed = 1;
17303a2f7427SDavid Greenman 				if ((st3 & NE7_ST3_T0) == 0) {
17313a2f7427SDavid Greenman 					printf(
17323a2f7427SDavid Greenman 		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
17333a2f7427SDavid Greenman 					       fdu, st3, NE7_ST3BITS);
1734dc5df763SJoerg Wunsch 					failed = 1;
1735dc5df763SJoerg Wunsch 				}
1736dc5df763SJoerg Wunsch 
17376182fdbdSPeter Wemm 				if (failed) {
17383a2f7427SDavid Greenman 					if(fdc->retry < 3)
17393a2f7427SDavid Greenman 						fdc->retry = 3;
17406182fdbdSPeter Wemm 					return (retrier(fdc));
17413a2f7427SDavid Greenman 				}
17423a2f7427SDavid Greenman 			}
1743dc5df763SJoerg Wunsch 
17446182fdbdSPeter Wemm 			if (cyl != descyl) {
17453a2f7427SDavid Greenman 				printf(
17463a2f7427SDavid Greenman 		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
17472d9d0204SRodney W. Grimes 				       fdu, descyl, cyl, st0);
1748e5d7d243SBruce Evans 				if (fdc->retry < 3)
1749e5d7d243SBruce Evans 					fdc->retry = 3;
17506182fdbdSPeter Wemm 				return (retrier(fdc));
17515b81b6b3SRodney W. Grimes 			}
17525b81b6b3SRodney W. Grimes 		}
17535b81b6b3SRodney W. Grimes 
1754bb6382faSJoerg Wunsch 		fd->track = b_cylinder;
175569acd21dSWarner Losh #ifdef FDC_YE
175669acd21dSWarner Losh 		if (!(fdc->flags & FDC_PCMCIA))
175769acd21dSWarner Losh #endif
1758ab3f7469SPoul-Henning Kamp 			isa_dmastart(bp->b_flags, bp->b_data+fd->skip,
17593a2f7427SDavid Greenman 				format ? bp->b_bcount : fdblk, fdc->dmachan);
17605b81b6b3SRodney W. Grimes 		sectrac = fd->ft->sectrac;
17615b81b6b3SRodney W. Grimes 		sec = blknum %  (sectrac * fd->ft->heads);
17625b81b6b3SRodney W. Grimes 		head = sec / sectrac;
17635b81b6b3SRodney W. Grimes 		sec = sec % sectrac + 1;
17643a2f7427SDavid Greenman 		fd->hddrv = ((head&1)<<2)+fdu;
17653a2f7427SDavid Greenman 
17663a2f7427SDavid Greenman 		if(format || !read)
17673a2f7427SDavid Greenman 		{
17683a2f7427SDavid Greenman 			/* make sure the drive is writable */
1769dc5df763SJoerg Wunsch 			if(fd_sense_drive_status(fdc, &st3) != 0)
1770dc8603e3SJoerg Wunsch 			{
1771dc8603e3SJoerg Wunsch 				/* stuck controller? */
17725c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
17735c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
17745c1a1eaeSBruce Evans 					    fdc->dmachan);
1775dc8603e3SJoerg Wunsch 				fdc->retry = 6;	/* reset the beast */
17766182fdbdSPeter Wemm 				return (retrier(fdc));
1777dc8603e3SJoerg Wunsch 			}
17783a2f7427SDavid Greenman 			if(st3 & NE7_ST3_WP)
17793a2f7427SDavid Greenman 			{
17803a2f7427SDavid Greenman 				/*
17813a2f7427SDavid Greenman 				 * XXX YES! this is ugly.
17823a2f7427SDavid Greenman 				 * in order to force the current operation
17833a2f7427SDavid Greenman 				 * to fail, we will have to fake an FDC
17843a2f7427SDavid Greenman 				 * error - all error handling is done
17853a2f7427SDavid Greenman 				 * by the retrier()
17863a2f7427SDavid Greenman 				 */
17873a2f7427SDavid Greenman 				fdc->status[0] = NE7_ST0_IC_AT;
17883a2f7427SDavid Greenman 				fdc->status[1] = NE7_ST1_NW;
17893a2f7427SDavid Greenman 				fdc->status[2] = 0;
17903a2f7427SDavid Greenman 				fdc->status[3] = fd->track;
17913a2f7427SDavid Greenman 				fdc->status[4] = head;
17923a2f7427SDavid Greenman 				fdc->status[5] = sec;
17933a2f7427SDavid Greenman 				fdc->retry = 8;	/* break out immediately */
17943a2f7427SDavid Greenman 				fdc->state = IOTIMEDOUT; /* not really... */
17953a2f7427SDavid Greenman 				return (1);
17963a2f7427SDavid Greenman 			}
17973a2f7427SDavid Greenman 		}
17985b81b6b3SRodney W. Grimes 
17996182fdbdSPeter Wemm 		if (format) {
180069acd21dSWarner Losh #ifdef FDC_YE
180169acd21dSWarner Losh 			if (fdc->flags & FDC_PCMCIA)
180269acd21dSWarner Losh 				(void)fdcpio(fdcu,bp->b_flags,
180369acd21dSWarner Losh 					bp->b_data+fd->skip,
180469acd21dSWarner Losh 					bp->b_bcount);
180569acd21dSWarner Losh #endif
1806b39c878eSAndrey A. Chernov 			/* formatting */
18076182fdbdSPeter Wemm 			if(fd_cmd(fdc, 6,  NE7CMD_FORMAT, head << 2 | fdu,
1808dc5df763SJoerg Wunsch 				  finfo->fd_formb_secshift,
1809dc5df763SJoerg Wunsch 				  finfo->fd_formb_nsecs,
1810dc5df763SJoerg Wunsch 				  finfo->fd_formb_gaplen,
18116182fdbdSPeter Wemm 				  finfo->fd_formb_fillbyte, 0)) {
1812dc8603e3SJoerg Wunsch 				/* controller fell over */
18135c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18145c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
18155c1a1eaeSBruce Evans 					    fdc->dmachan);
1816dc8603e3SJoerg Wunsch 				fdc->retry = 6;
18176182fdbdSPeter Wemm 				return (retrier(fdc));
1818dc8603e3SJoerg Wunsch 			}
18196182fdbdSPeter Wemm 		} else {
182069acd21dSWarner Losh #ifdef FDC_YE
182169acd21dSWarner Losh 			if (fdc->flags & FDC_PCMCIA) {
182269acd21dSWarner Losh 				/*
182369acd21dSWarner Losh 				 * this seems to be necessary even when
182469acd21dSWarner Losh 				 * reading data
182569acd21dSWarner Losh 				 */
182669acd21dSWarner Losh 				SET_BCDR(1,fdblk,fdc->baseport);
182769acd21dSWarner Losh 
182869acd21dSWarner Losh 				/*
182969acd21dSWarner Losh 				 * perform the write pseudo-DMA before
183069acd21dSWarner Losh 				 * the WRITE command is sent
183169acd21dSWarner Losh 				 */
183269acd21dSWarner Losh 				if (!read)
183369acd21dSWarner Losh 					(void)fdcpio(fdcu,bp->b_flags,
183469acd21dSWarner Losh 					    bp->b_data+fd->skip,
183569acd21dSWarner Losh 					    fdblk);
183669acd21dSWarner Losh 			}
183769acd21dSWarner Losh #endif
18386182fdbdSPeter Wemm 			if (fd_cmd(fdc, 9,
1839dc5df763SJoerg Wunsch 				   (read ? NE7CMD_READ : NE7CMD_WRITE),
1840dc5df763SJoerg Wunsch 				   head << 2 | fdu,  /* head & unit */
1841dc5df763SJoerg Wunsch 				   fd->track,        /* track */
1842dc5df763SJoerg Wunsch 				   head,
1843dc5df763SJoerg Wunsch 				   sec,              /* sector + 1 */
1844dc5df763SJoerg Wunsch 				   fd->ft->secsize,  /* sector size */
1845dc5df763SJoerg Wunsch 				   sectrac,          /* sectors/track */
1846dc5df763SJoerg Wunsch 				   fd->ft->gap,      /* gap size */
1847dc5df763SJoerg Wunsch 				   fd->ft->datalen,  /* data length */
18486182fdbdSPeter Wemm 				   0)) {
1849dc8603e3SJoerg Wunsch 				/* the beast is sleeping again */
18505c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18515c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
18525c1a1eaeSBruce Evans 					    fdc->dmachan);
1853dc8603e3SJoerg Wunsch 				fdc->retry = 6;
18546182fdbdSPeter Wemm 				return (retrier(fdc));
18555b81b6b3SRodney W. Grimes 			}
1856b39c878eSAndrey A. Chernov 		}
185769acd21dSWarner Losh #ifdef FDC_YE
185869acd21dSWarner Losh 		if (fdc->flags & FDC_PCMCIA)
185969acd21dSWarner Losh 			/*
186069acd21dSWarner Losh 			 * if this is a read, then simply await interrupt
186169acd21dSWarner Losh 			 * before performing PIO
186269acd21dSWarner Losh 			 */
186369acd21dSWarner Losh 			if (read && !fdcpio(fdcu,bp->b_flags,
186469acd21dSWarner Losh 			    bp->b_data+fd->skip,fdblk)) {
186569acd21dSWarner Losh 				fd->tohandle = timeout(fd_iotimeout,
186669acd21dSWarner Losh 					(caddr_t)fdcu, hz);
186769acd21dSWarner Losh 				return(0);      /* will return later */
186869acd21dSWarner Losh 			};
186969acd21dSWarner Losh 
187069acd21dSWarner Losh 		/*
187169acd21dSWarner Losh 		 * write (or format) operation will fall through and
187269acd21dSWarner Losh 		 * await completion interrupt
187369acd21dSWarner Losh 		 */
187469acd21dSWarner Losh #endif
18755b81b6b3SRodney W. Grimes 		fdc->state = IOCOMPLETE;
18766182fdbdSPeter Wemm 		fd->tohandle = timeout(fd_iotimeout, fdc, hz);
18775b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
187869acd21dSWarner Losh #ifdef FDC_YE
187969acd21dSWarner Losh 	case PIOREAD:
188069acd21dSWarner Losh 		/*
188169acd21dSWarner Losh 		 * actually perform the PIO read.  The IOCOMPLETE case
188269acd21dSWarner Losh 		 * removes the timeout for us.
188369acd21dSWarner Losh 		 */
188469acd21dSWarner Losh 		(void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk);
188569acd21dSWarner Losh 		fdc->state = IOCOMPLETE;
188669acd21dSWarner Losh 		/* FALLTHROUGH */
188769acd21dSWarner Losh #endif
18885b81b6b3SRodney W. Grimes 	case IOCOMPLETE: /* IO DONE, post-analyze */
18896182fdbdSPeter Wemm 		untimeout(fd_iotimeout, fdc, fd->tohandle);
1890dc5df763SJoerg Wunsch 
18916182fdbdSPeter Wemm 		if (fd_read_status(fdc, fd->fdsu)) {
18925c1a1eaeSBruce Evans 			isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18935c1a1eaeSBruce Evans 				    format ? bp->b_bcount : fdblk,
18945c1a1eaeSBruce Evans 				    fdc->dmachan);
1895dc5df763SJoerg Wunsch 			if (fdc->retry < 6)
1896dc5df763SJoerg Wunsch 				fdc->retry = 6;	/* force a reset */
18976182fdbdSPeter Wemm 			return (retrier(fdc));
18985b81b6b3SRodney W. Grimes   		}
1899dc5df763SJoerg Wunsch 
19003a2f7427SDavid Greenman 		fdc->state = IOTIMEDOUT;
1901dc5df763SJoerg Wunsch 
19023a2f7427SDavid Greenman 		/* FALLTHROUGH */
1903dc5df763SJoerg Wunsch 
19043a2f7427SDavid Greenman 	case IOTIMEDOUT:
190569acd21dSWarner Losh #ifdef FDC_YE
190669acd21dSWarner Losh 		if (!(fdc->flags & FDC_PCMCIA))
190769acd21dSWarner Losh #endif
1908ab3f7469SPoul-Henning Kamp 			isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
19093a2f7427SDavid Greenman 				format ? bp->b_bcount : fdblk, fdc->dmachan);
19106182fdbdSPeter Wemm 		if (fdc->status[0] & NE7_ST0_IC) {
19113a2f7427SDavid Greenman                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
19123a2f7427SDavid Greenman 			    && fdc->status[1] & NE7_ST1_OR) {
1913b39c878eSAndrey A. Chernov                                 /*
19143a2f7427SDavid Greenman 				 * DMA overrun. Someone hogged the bus
19153a2f7427SDavid Greenman 				 * and didn't release it in time for the
19163a2f7427SDavid Greenman 				 * next FDC transfer.
19173a2f7427SDavid Greenman 				 * Just restart it, don't increment retry
19183a2f7427SDavid Greenman 				 * count. (vak)
1919b39c878eSAndrey A. Chernov                                  */
1920b39c878eSAndrey A. Chernov                                 fdc->state = SEEKCOMPLETE;
1921b39c878eSAndrey A. Chernov                                 return (1);
1922b39c878eSAndrey A. Chernov                         }
19233a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
19243a2f7427SDavid Greenman 				&& fdc->retry < 6)
19253a2f7427SDavid Greenman 				fdc->retry = 6;	/* force a reset */
19263a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
19273a2f7427SDavid Greenman 				&& fdc->status[2] & NE7_ST2_WC
19283a2f7427SDavid Greenman 				&& fdc->retry < 3)
19293a2f7427SDavid Greenman 				fdc->retry = 3;	/* force recalibrate */
19306182fdbdSPeter Wemm 			return (retrier(fdc));
19315b81b6b3SRodney W. Grimes 		}
19325b81b6b3SRodney W. Grimes 		/* All OK */
19333a2f7427SDavid Greenman 		fd->skip += fdblk;
19346182fdbdSPeter Wemm 		if (!format && fd->skip < bp->b_bcount - bp->b_resid) {
19355b81b6b3SRodney W. Grimes 			/* set up next transfer */
19365b81b6b3SRodney W. Grimes 			fdc->state = DOSEEK;
19376182fdbdSPeter Wemm 		} else {
19385b81b6b3SRodney W. Grimes 			/* ALL DONE */
19395b81b6b3SRodney W. Grimes 			fd->skip = 0;
1940e93e63cbSBruce Evans 			fdc->bp = NULL;
1941b2dfb1f9SJustin T. Gibbs 			/* Tell devstat we have finished with the transaction */
1942b2dfb1f9SJustin T. Gibbs 			devstat_end_transaction(&fd->device_stats,
1943b2dfb1f9SJustin T. Gibbs 						bp->b_bcount - bp->b_resid,
1944b2dfb1f9SJustin T. Gibbs 						DEVSTAT_TAG_NONE,
1945b2dfb1f9SJustin T. Gibbs 						(bp->b_flags & B_READ) ?
1946b2dfb1f9SJustin T. Gibbs 						DEVSTAT_READ : DEVSTAT_WRITE);
19475b81b6b3SRodney W. Grimes 			biodone(bp);
19485b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
19495b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
19505b81b6b3SRodney W. Grimes 			fdc->state = FINDWORK;
19515b81b6b3SRodney W. Grimes 		}
19525b81b6b3SRodney W. Grimes 		return (1);
19535b81b6b3SRodney W. Grimes 	case RESETCTLR:
19543a2f7427SDavid Greenman 		fdc_reset(fdc);
19555b81b6b3SRodney W. Grimes 		fdc->retry++;
19565c1a1eaeSBruce Evans 		fdc->state = RESETCOMPLETE;
19575c1a1eaeSBruce Evans 		return (0);
19585c1a1eaeSBruce Evans 	case RESETCOMPLETE:
19595c1a1eaeSBruce Evans 		/*
19605c1a1eaeSBruce Evans 		 * Discard all the results from the reset so that they
19615c1a1eaeSBruce Evans 		 * can't cause an unexpected interrupt later.
19625c1a1eaeSBruce Evans 		 */
19630e317d05SJoerg Wunsch 		for (i = 0; i < 4; i++)
19640e317d05SJoerg Wunsch 			(void)fd_sense_int(fdc, &st0, &cyl);
19655c1a1eaeSBruce Evans 		fdc->state = STARTRECAL;
19665c1a1eaeSBruce Evans 		/* Fall through. */
19675c1a1eaeSBruce Evans 	case STARTRECAL:
19686182fdbdSPeter Wemm 		if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) {
1969dc8603e3SJoerg Wunsch 			/* arrgl */
1970dc8603e3SJoerg Wunsch 			fdc->retry = 6;
19716182fdbdSPeter Wemm 			return (retrier(fdc));
1972dc8603e3SJoerg Wunsch 		}
19735b81b6b3SRodney W. Grimes 		fdc->state = RECALWAIT;
19745b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
19755b81b6b3SRodney W. Grimes 	case RECALWAIT:
19765b81b6b3SRodney W. Grimes 		/* allow heads to settle */
19776182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 8);
19785b81b6b3SRodney W. Grimes 		fdc->state = RECALCOMPLETE;
19795b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
19805b81b6b3SRodney W. Grimes 	case RECALCOMPLETE:
19813a2f7427SDavid Greenman 		do {
19823a2f7427SDavid Greenman 			/*
1983dc5df763SJoerg Wunsch 			 * See SEEKCOMPLETE for a comment on this:
19843a2f7427SDavid Greenman 			 */
1985dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
1986dc5df763SJoerg Wunsch 				return 0;
1987dc5df763SJoerg Wunsch 			if(fdc->fdct == FDC_NE765
1988dc5df763SJoerg Wunsch 			   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1989dc5df763SJoerg Wunsch 				return 0; /* hope for a real intr */
19903a2f7427SDavid Greenman 		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
19913a2f7427SDavid Greenman 		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
19925b81b6b3SRodney W. Grimes 		{
1993dc8603e3SJoerg Wunsch 			if(fdc->retry > 3)
1994dc8603e3SJoerg Wunsch 				/*
1995dc8603e3SJoerg Wunsch 				 * a recalibrate from beyond cylinder 77
1996dc8603e3SJoerg Wunsch 				 * will "fail" due to the FDC limitations;
1997dc8603e3SJoerg Wunsch 				 * since people used to complain much about
1998dc8603e3SJoerg Wunsch 				 * the failure message, try not logging
1999dc8603e3SJoerg Wunsch 				 * this one if it seems to be the first
2000dc8603e3SJoerg Wunsch 				 * time in a line
2001dc8603e3SJoerg Wunsch 				 */
2002dc8603e3SJoerg Wunsch 				printf("fd%d: recal failed ST0 %b cyl %d\n",
2003dc8603e3SJoerg Wunsch 				       fdu, st0, NE7_ST0BITS, cyl);
20043a2f7427SDavid Greenman 			if(fdc->retry < 3) fdc->retry = 3;
20056182fdbdSPeter Wemm 			return (retrier(fdc));
20065b81b6b3SRodney W. Grimes 		}
20075b81b6b3SRodney W. Grimes 		fd->track = 0;
20085b81b6b3SRodney W. Grimes 		/* Seek (probably) necessary */
20095b81b6b3SRodney W. Grimes 		fdc->state = DOSEEK;
20105b81b6b3SRodney W. Grimes 		return (1);	/* will return immediatly */
20115b81b6b3SRodney W. Grimes 	case MOTORWAIT:
20125b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
20135b81b6b3SRodney W. Grimes 		{
20145b81b6b3SRodney W. Grimes 			return (0); /* time's not up yet */
20155b81b6b3SRodney W. Grimes 		}
20165c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
20175c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
20185c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
20195c1a1eaeSBruce Evans 		} else {
20205e235068SJordan K. Hubbard 			/*
20215c1a1eaeSBruce Evans 			 * If all motors were off, then the controller was
20225c1a1eaeSBruce Evans 			 * reset, so it has lost track of the current
20235c1a1eaeSBruce Evans 			 * cylinder.  Recalibrate to handle this case.
2024f86e4077SBruce Evans 			 * But first, discard the results of the reset.
20255e235068SJordan K. Hubbard 			 */
2026f86e4077SBruce Evans 			fdc->state = RESETCOMPLETE;
20275c1a1eaeSBruce Evans 		}
20285b81b6b3SRodney W. Grimes 		return (1);	/* will return immediatly */
20295b81b6b3SRodney W. Grimes 	default:
20306182fdbdSPeter Wemm 		device_print_prettyname(fdc->fdc_dev);
20316182fdbdSPeter Wemm 		printf("unexpected FD int->");
2032dc5df763SJoerg Wunsch 		if (fd_read_status(fdc, fd->fdsu) == 0)
2033a838d83dSBruce Evans 			printf("FDC status :%x %x %x %x %x %x %x   ",
20345b81b6b3SRodney W. Grimes 			       fdc->status[0],
20355b81b6b3SRodney W. Grimes 			       fdc->status[1],
20365b81b6b3SRodney W. Grimes 			       fdc->status[2],
20375b81b6b3SRodney W. Grimes 			       fdc->status[3],
20385b81b6b3SRodney W. Grimes 			       fdc->status[4],
20395b81b6b3SRodney W. Grimes 			       fdc->status[5],
20405b81b6b3SRodney W. Grimes 			       fdc->status[6] );
20413a2f7427SDavid Greenman 		else
2042dac0f2dbSJoerg Wunsch 			printf("No status available   ");
2043dac0f2dbSJoerg Wunsch 		if (fd_sense_int(fdc, &st0, &cyl) != 0)
2044dac0f2dbSJoerg Wunsch 		{
2045dac0f2dbSJoerg Wunsch 			printf("[controller is dead now]\n");
20465b81b6b3SRodney W. Grimes 			return (0);
20475b81b6b3SRodney W. Grimes 		}
2048dac0f2dbSJoerg Wunsch 		printf("ST0 = %x, PCN = %x\n", st0, cyl);
2049dac0f2dbSJoerg Wunsch 		return (0);
2050dac0f2dbSJoerg Wunsch 	}
2051dac0f2dbSJoerg Wunsch 	/*XXX confusing: some branches return immediately, others end up here*/
20525b81b6b3SRodney W. Grimes 	return (1); /* Come back immediatly to new state */
20535b81b6b3SRodney W. Grimes }
20545b81b6b3SRodney W. Grimes 
2055aaf08d94SGarrett Wollman static int
20566182fdbdSPeter Wemm retrier(struct fdc_data *fdc)
20575b81b6b3SRodney W. Grimes {
205817542807SPoul-Henning Kamp 	register struct buf *bp;
20596182fdbdSPeter Wemm 	struct fd_data *fd;
20606182fdbdSPeter Wemm 	int fdu;
20615b81b6b3SRodney W. Grimes 
2062e93e63cbSBruce Evans 	bp = fdc->bp;
20635b81b6b3SRodney W. Grimes 
20646182fdbdSPeter Wemm 	/* XXX shouldn't this be cached somewhere?  */
20656182fdbdSPeter Wemm 	fdu = FDUNIT(minor(bp->b_dev));
20666182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
20676182fdbdSPeter Wemm 	if (fd->options & FDOPT_NORETRY)
20683a2f7427SDavid Greenman 		goto fail;
20696182fdbdSPeter Wemm 
20706182fdbdSPeter Wemm 	switch (fdc->retry) {
20715b81b6b3SRodney W. Grimes 	case 0: case 1: case 2:
20725b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
20735b81b6b3SRodney W. Grimes 		break;
20745b81b6b3SRodney W. Grimes 	case 3: case 4: case 5:
20755b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
20765b81b6b3SRodney W. Grimes 		break;
20775b81b6b3SRodney W. Grimes 	case 6:
20785b81b6b3SRodney W. Grimes 		fdc->state = RESETCTLR;
20795b81b6b3SRodney W. Grimes 		break;
20805b81b6b3SRodney W. Grimes 	case 7:
20815b81b6b3SRodney W. Grimes 		break;
20825b81b6b3SRodney W. Grimes 	default:
20833a2f7427SDavid Greenman 	fail:
20845b81b6b3SRodney W. Grimes 		{
20857ca0641bSAndrey A. Chernov 			dev_t sav_b_dev = bp->b_dev;
20867ca0641bSAndrey A. Chernov 			/* Trick diskerr */
20873a2f7427SDavid Greenman 			bp->b_dev = makedev(major(bp->b_dev),
20883a2f7427SDavid Greenman 				    (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART);
208992ed385aSRodney W. Grimes 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
20903a2f7427SDavid Greenman 				fdc->fd->skip / DEV_BSIZE,
20913a2f7427SDavid Greenman 				(struct disklabel *)NULL);
20927ca0641bSAndrey A. Chernov 			bp->b_dev = sav_b_dev;
2093dc5df763SJoerg Wunsch 			if (fdc->flags & FDC_STAT_VALID)
2094dc5df763SJoerg Wunsch 			{
2095dc5df763SJoerg Wunsch 				printf(
2096a838d83dSBruce Evans 			" (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n",
2097dc5df763SJoerg Wunsch 				       fdc->status[0], NE7_ST0BITS,
2098dc5df763SJoerg Wunsch 				       fdc->status[1], NE7_ST1BITS,
2099dc5df763SJoerg Wunsch 				       fdc->status[2], NE7_ST2BITS,
2100dc5df763SJoerg Wunsch 				       fdc->status[3], fdc->status[4],
2101dc5df763SJoerg Wunsch 				       fdc->status[5]);
2102dc5df763SJoerg Wunsch 			}
2103dc5df763SJoerg Wunsch 			else
2104dc5df763SJoerg Wunsch 				printf(" (No status)\n");
21055b81b6b3SRodney W. Grimes 		}
21065b81b6b3SRodney W. Grimes 		bp->b_flags |= B_ERROR;
21075b81b6b3SRodney W. Grimes 		bp->b_error = EIO;
2108bb6382faSJoerg Wunsch 		bp->b_resid += bp->b_bcount - fdc->fd->skip;
2109e93e63cbSBruce Evans 		fdc->bp = NULL;
2110b2dfb1f9SJustin T. Gibbs 
2111b2dfb1f9SJustin T. Gibbs 		/* Tell devstat we have finished with the transaction */
21129f02ad60SJustin T. Gibbs 		devstat_end_transaction(&fdc->fd->device_stats,
2113b2dfb1f9SJustin T. Gibbs 					bp->b_bcount - bp->b_resid,
2114b2dfb1f9SJustin T. Gibbs 					DEVSTAT_TAG_NONE,
2115b2dfb1f9SJustin T. Gibbs 					(bp->b_flags & B_READ) ? DEVSTAT_READ :
2116b2dfb1f9SJustin T. Gibbs 								 DEVSTAT_WRITE);
21175b81b6b3SRodney W. Grimes 		fdc->fd->skip = 0;
21185b81b6b3SRodney W. Grimes 		biodone(bp);
211992ed385aSRodney W. Grimes 		fdc->state = FINDWORK;
21205c1a1eaeSBruce Evans 		fdc->flags |= FDC_NEEDS_RESET;
21215b81b6b3SRodney W. Grimes 		fdc->fd = (fd_p) 0;
21225b81b6b3SRodney W. Grimes 		fdc->fdu = -1;
212392ed385aSRodney W. Grimes 		return (1);
21245b81b6b3SRodney W. Grimes 	}
21255b81b6b3SRodney W. Grimes 	fdc->retry++;
21265b81b6b3SRodney W. Grimes 	return (1);
21275b81b6b3SRodney W. Grimes }
21285b81b6b3SRodney W. Grimes 
2129b39c878eSAndrey A. Chernov static int
2130b39c878eSAndrey A. Chernov fdformat(dev, finfo, p)
2131b39c878eSAndrey A. Chernov 	dev_t dev;
2132b39c878eSAndrey A. Chernov 	struct fd_formb *finfo;
2133b39c878eSAndrey A. Chernov 	struct proc *p;
2134b39c878eSAndrey A. Chernov {
2135b39c878eSAndrey A. Chernov  	fdu_t	fdu;
2136b39c878eSAndrey A. Chernov  	fd_p	fd;
2137b39c878eSAndrey A. Chernov 
2138b39c878eSAndrey A. Chernov 	struct buf *bp;
2139b39c878eSAndrey A. Chernov 	int rv = 0, s;
21403a2f7427SDavid Greenman 	size_t fdblk;
2141b39c878eSAndrey A. Chernov 
2142b39c878eSAndrey A. Chernov  	fdu	= FDUNIT(minor(dev));
21436182fdbdSPeter Wemm 	fd	= devclass_get_softc(fd_devclass, fdu);
21443a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2145b39c878eSAndrey A. Chernov 
2146b39c878eSAndrey A. Chernov 	/* set up a buffer header for fdstrategy() */
2147b39c878eSAndrey A. Chernov 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
2148b39c878eSAndrey A. Chernov 	if(bp == 0)
2149b39c878eSAndrey A. Chernov 		return ENOBUFS;
215082f5379bSJoerg Wunsch 	/*
215182f5379bSJoerg Wunsch 	 * keep the process from being swapped
215282f5379bSJoerg Wunsch 	 */
21532ada239cSPeter Wemm 	PHOLD(p);
2154b39c878eSAndrey A. Chernov 	bzero((void *)bp, sizeof(struct buf));
215567812eacSKirk McKusick 	BUF_LOCKINIT(bp);
215667812eacSKirk McKusick 	BUF_LOCK(bp, LK_EXCLUSIVE);
215767812eacSKirk McKusick 	bp->b_flags = B_PHYS | B_FORMAT;
2158b39c878eSAndrey A. Chernov 
2159b39c878eSAndrey A. Chernov 	/*
2160b39c878eSAndrey A. Chernov 	 * calculate a fake blkno, so fdstrategy() would initiate a
2161b39c878eSAndrey A. Chernov 	 * seek to the requested cylinder
2162b39c878eSAndrey A. Chernov 	 */
2163b39c878eSAndrey A. Chernov 	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
21643a2f7427SDavid Greenman 		+ finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
2165b39c878eSAndrey A. Chernov 
2166b39c878eSAndrey A. Chernov 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
2167ab3f7469SPoul-Henning Kamp 	bp->b_data = (caddr_t)finfo;
2168b39c878eSAndrey A. Chernov 
2169b39c878eSAndrey A. Chernov 	/* now do the format */
21703e425b96SJulian Elischer 	bp->b_dev = dev;
217149ff4debSPoul-Henning Kamp 	BUF_STRATEGY(bp, 0);
2172b39c878eSAndrey A. Chernov 
2173b39c878eSAndrey A. Chernov 	/* ...and wait for it to complete */
2174b39c878eSAndrey A. Chernov 	s = splbio();
21756182fdbdSPeter Wemm 	while(!(bp->b_flags & B_DONE)) {
21765e235068SJordan K. Hubbard 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
2177b39c878eSAndrey A. Chernov 		if (rv == EWOULDBLOCK)
2178b39c878eSAndrey A. Chernov 			break;
2179b39c878eSAndrey A. Chernov 	}
2180b39c878eSAndrey A. Chernov 	splx(s);
2181b39c878eSAndrey A. Chernov 
218282f5379bSJoerg Wunsch 	if (rv == EWOULDBLOCK) {
2183b39c878eSAndrey A. Chernov 		/* timed out */
2184b39c878eSAndrey A. Chernov 		rv = EIO;
218582f5379bSJoerg Wunsch 		biodone(bp);
218682f5379bSJoerg Wunsch 	}
21873a2f7427SDavid Greenman 	if (bp->b_flags & B_ERROR)
21883a2f7427SDavid Greenman 		rv = bp->b_error;
218982f5379bSJoerg Wunsch 	/*
219082f5379bSJoerg Wunsch 	 * allow the process to be swapped
219182f5379bSJoerg Wunsch 	 */
21922ada239cSPeter Wemm 	PRELE(p);
219367812eacSKirk McKusick 	BUF_UNLOCK(bp);
219467812eacSKirk McKusick 	BUF_LOCKFREE(bp);
2195b39c878eSAndrey A. Chernov 	free(bp, M_TEMP);
2196b39c878eSAndrey A. Chernov 	return rv;
2197b39c878eSAndrey A. Chernov }
2198b39c878eSAndrey A. Chernov 
2199f5f7ba03SJordan K. Hubbard /*
2200671e2ceeSBruce Evans  * TODO: don't allocate buffer on stack.
2201f5f7ba03SJordan K. Hubbard  */
22025b81b6b3SRodney W. Grimes 
22033e425b96SJulian Elischer static int
2204b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p)
2205f5f7ba03SJordan K. Hubbard 	dev_t dev;
2206ecbb00a2SDoug Rabson 	u_long cmd;
2207f5f7ba03SJordan K. Hubbard 	caddr_t addr;
2208f5f7ba03SJordan K. Hubbard 	int flag;
2209b39c878eSAndrey A. Chernov 	struct proc *p;
2210f5f7ba03SJordan K. Hubbard {
22113a2f7427SDavid Greenman  	fdu_t	fdu = FDUNIT(minor(dev));
22126182fdbdSPeter Wemm  	fd_p	fd = devclass_get_softc(fd_devclass, fdu);
22133a2f7427SDavid Greenman 	size_t fdblk;
22143a2f7427SDavid Greenman 
2215f5f7ba03SJordan K. Hubbard 	struct fd_type *fdt;
2216f5f7ba03SJordan K. Hubbard 	struct disklabel *dl;
2217f5f7ba03SJordan K. Hubbard 	char buffer[DEV_BSIZE];
22183a2f7427SDavid Greenman 	int error = 0;
2219f5f7ba03SJordan K. Hubbard 
22203a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2221f5f7ba03SJordan K. Hubbard 
22226182fdbdSPeter Wemm 	switch (cmd) {
2223f5f7ba03SJordan K. Hubbard 	case DIOCGDINFO:
2224f5f7ba03SJordan K. Hubbard 		bzero(buffer, sizeof (buffer));
2225f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)buffer;
22263a2f7427SDavid Greenman 		dl->d_secsize = fdblk;
22276182fdbdSPeter Wemm 		fdt = fd->ft;
2228f5f7ba03SJordan K. Hubbard 		dl->d_secpercyl = fdt->size / fdt->tracks;
2229f5f7ba03SJordan K. Hubbard 		dl->d_type = DTYPE_FLOPPY;
2230f5f7ba03SJordan K. Hubbard 
223149ff4debSPoul-Henning Kamp 		if (readdisklabel(dkmodpart(dev, RAW_PART), dl)
2232191e1a59SBruce Evans 		    == NULL)
2233f5f7ba03SJordan K. Hubbard 			error = 0;
2234f5f7ba03SJordan K. Hubbard 		else
2235f5f7ba03SJordan K. Hubbard 			error = EINVAL;
2236f5f7ba03SJordan K. Hubbard 
2237f5f7ba03SJordan K. Hubbard 		*(struct disklabel *)addr = *dl;
2238f5f7ba03SJordan K. Hubbard 		break;
2239f5f7ba03SJordan K. Hubbard 
2240f5f7ba03SJordan K. Hubbard 	case DIOCSDINFO:
2241f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2242f5f7ba03SJordan K. Hubbard 			error = EBADF;
2243f5f7ba03SJordan K. Hubbard 		break;
2244f5f7ba03SJordan K. Hubbard 
2245f5f7ba03SJordan K. Hubbard 	case DIOCWLABEL:
2246f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2247f5f7ba03SJordan K. Hubbard 			error = EBADF;
2248f5f7ba03SJordan K. Hubbard 		break;
2249f5f7ba03SJordan K. Hubbard 
2250f5f7ba03SJordan K. Hubbard 	case DIOCWDINFO:
22516182fdbdSPeter Wemm 		if ((flag & FWRITE) == 0) {
2252f5f7ba03SJordan K. Hubbard 			error = EBADF;
2253f5f7ba03SJordan K. Hubbard 			break;
2254f5f7ba03SJordan K. Hubbard 		}
2255f5f7ba03SJordan K. Hubbard 
2256f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)addr;
2257f5f7ba03SJordan K. Hubbard 
2258191e1a59SBruce Evans 		if ((error = setdisklabel((struct disklabel *)buffer, dl,
2259191e1a59SBruce Evans 					  (u_long)0)) != 0)
2260f5f7ba03SJordan K. Hubbard 			break;
2261f5f7ba03SJordan K. Hubbard 
226249ff4debSPoul-Henning Kamp 		error = writedisklabel(dev, (struct disklabel *)buffer);
2263b39c878eSAndrey A. Chernov 		break;
2264b39c878eSAndrey A. Chernov 	case FD_FORM:
2265b39c878eSAndrey A. Chernov 		if ((flag & FWRITE) == 0)
2266b39c878eSAndrey A. Chernov 			error = EBADF;	/* must be opened for writing */
2267b39c878eSAndrey A. Chernov 		else if (((struct fd_formb *)addr)->format_version !=
2268b39c878eSAndrey A. Chernov 			FD_FORMAT_VERSION)
2269b39c878eSAndrey A. Chernov 			error = EINVAL;	/* wrong version of formatting prog */
2270b39c878eSAndrey A. Chernov 		else
2271b39c878eSAndrey A. Chernov 			error = fdformat(dev, (struct fd_formb *)addr, p);
2272b39c878eSAndrey A. Chernov 		break;
2273b39c878eSAndrey A. Chernov 
2274b39c878eSAndrey A. Chernov 	case FD_GTYPE:                  /* get drive type */
22753e425b96SJulian Elischer 		*(struct fd_type *)addr = *fd->ft;
2276f5f7ba03SJordan K. Hubbard 		break;
2277f5f7ba03SJordan K. Hubbard 
22783a2f7427SDavid Greenman 	case FD_STYPE:                  /* set drive type */
22793a2f7427SDavid Greenman 		/* this is considered harmful; only allow for superuser */
2280f711d546SPoul-Henning Kamp 		if (suser(p) != 0)
22813a2f7427SDavid Greenman 			return EPERM;
22823e425b96SJulian Elischer 		*fd->ft = *(struct fd_type *)addr;
22833a2f7427SDavid Greenman 		break;
22843a2f7427SDavid Greenman 
22853a2f7427SDavid Greenman 	case FD_GOPTS:			/* get drive options */
22863e425b96SJulian Elischer 		*(int *)addr = fd->options;
22873a2f7427SDavid Greenman 		break;
22883a2f7427SDavid Greenman 
22893a2f7427SDavid Greenman 	case FD_SOPTS:			/* set drive options */
22903e425b96SJulian Elischer 		fd->options = *(int *)addr;
22913a2f7427SDavid Greenman 		break;
22923a2f7427SDavid Greenman 
2293f5f7ba03SJordan K. Hubbard 	default:
22943a2f7427SDavid Greenman 		error = ENOTTY;
2295f5f7ba03SJordan K. Hubbard 		break;
2296f5f7ba03SJordan K. Hubbard 	}
2297f5f7ba03SJordan K. Hubbard 	return (error);
2298f5f7ba03SJordan K. Hubbard }
2299f5f7ba03SJordan K. Hubbard 
23006182fdbdSPeter Wemm #endif /* NFDC > 0 */
23013e425b96SJulian Elischer 
23023a2f7427SDavid Greenman /*
23033a2f7427SDavid Greenman  * Hello emacs, these are the
23043a2f7427SDavid Greenman  * Local Variables:
23053a2f7427SDavid Greenman  *  c-indent-level:               8
23063a2f7427SDavid Greenman  *  c-continued-statement-offset: 8
23073a2f7427SDavid Greenman  *  c-continued-brace-offset:     0
23083a2f7427SDavid Greenman  *  c-brace-offset:              -8
23093a2f7427SDavid Greenman  *  c-brace-imaginary-offset:     0
23103a2f7427SDavid Greenman  *  c-argdecl-indent:             8
23113a2f7427SDavid Greenman  *  c-label-offset:              -8
23123a2f7427SDavid Greenman  *  c++-hanging-braces:           1
23133a2f7427SDavid Greenman  *  c++-access-specifier-offset: -8
23143a2f7427SDavid Greenman  *  c++-empty-arglist-indent:     8
23153a2f7427SDavid Greenman  *  c++-friend-offset:            0
23163a2f7427SDavid Greenman  * End:
23173a2f7427SDavid Greenman  */
2318