xref: /freebsd/sys/dev/fdc/fdc.c (revision 49ff4debd3d4c155448b2b4e3b95b17d9eb575ed)
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
5049ff4debSPoul-Henning Kamp  *	$Id: fd.c,v 1.151 1999/07/29 11:27:33 joerg Exp $
515b81b6b3SRodney W. Grimes  *
525b81b6b3SRodney W. Grimes  */
535b81b6b3SRodney W. Grimes 
545b81b6b3SRodney W. Grimes #include "fd.h"
557b778b5eSEivind Eklund #include "opt_devfs.h"
56d2fb4892SJoerg Wunsch #include "opt_fdc.h"
575b81b6b3SRodney W. Grimes 
58b99f0a4aSAndrew Moore #if NFDC > 0
59b99f0a4aSAndrew Moore 
60b99f0a4aSAndrew Moore #include <sys/param.h>
61b99f0a4aSAndrew Moore #include <sys/systm.h>
62b99f0a4aSAndrew Moore #include <sys/kernel.h>
63b99f0a4aSAndrew Moore #include <sys/buf.h>
646182fdbdSPeter Wemm #include <sys/bus.h>
656182fdbdSPeter Wemm #include <sys/conf.h>
666182fdbdSPeter Wemm #include <sys/disklabel.h>
67b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h>
686182fdbdSPeter Wemm #include <sys/fcntl.h>
69b99f0a4aSAndrew Moore #include <sys/malloc.h>
706182fdbdSPeter Wemm #include <sys/module.h>
713a2f7427SDavid Greenman #include <sys/proc.h>
72b99f0a4aSAndrew Moore #include <sys/syslog.h>
736182fdbdSPeter Wemm 
746182fdbdSPeter Wemm #include <sys/bus.h>
756182fdbdSPeter Wemm #include <machine/bus.h>
766182fdbdSPeter Wemm #include <sys/rman.h>
776182fdbdSPeter Wemm 
786182fdbdSPeter Wemm #include <machine/clock.h>
796182fdbdSPeter Wemm #include <machine/ioctl_fd.h>
806182fdbdSPeter Wemm #include <machine/resource.h>
81dc5df763SJoerg Wunsch #include <machine/stdarg.h>
826182fdbdSPeter Wemm 
838af5d536SJulian Elischer #ifdef	DEVFS
848af5d536SJulian Elischer #include <sys/devfsext.h>
853e425b96SJulian Elischer #endif	/* DEVFS */
865b81b6b3SRodney W. Grimes 
876182fdbdSPeter Wemm #include <isa/isavar.h>
88a97c75b7SDoug Rabson #include <isa/isareg.h>
89a97c75b7SDoug Rabson #include <isa/fdreg.h>
90a97c75b7SDoug Rabson #include <isa/fdc.h>
91a97c75b7SDoug Rabson #include <isa/rtc.h>
926182fdbdSPeter Wemm 
93edfdec19SPeter Wemm #ifdef FDC_YE
94edfdec19SPeter Wemm #undef FDC_YE
95edfdec19SPeter Wemm #warning "fix FDC_YE! - newbus casualty"
96edfdec19SPeter Wemm #endif
97edfdec19SPeter Wemm 
98b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */
99b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX
1005b81b6b3SRodney W. Grimes 
1010722d6abSJoerg Wunsch /* configuration flags */
1020722d6abSJoerg Wunsch #define FDC_PRETEND_D0	(1 << 0)	/* pretend drive 0 to be there */
10369acd21dSWarner Losh #ifdef FDC_YE
10469acd21dSWarner Losh #define FDC_IS_PCMCIA  (1 << 1)		/* if successful probe, then it's
10569acd21dSWarner Losh 					   a PCMCIA device */
10669acd21dSWarner Losh #endif
1070722d6abSJoerg Wunsch 
1080722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */
1090722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED	0x1000
1100722d6abSJoerg Wunsch 
111dc5df763SJoerg Wunsch /* error returns for fd_cmd() */
112dc5df763SJoerg Wunsch #define FD_FAILED -1
113dc5df763SJoerg Wunsch #define FD_NOT_VALID -2
114dc5df763SJoerg Wunsch #define FDC_ERRMAX	100	/* do not log more */
115dc5df763SJoerg Wunsch 
116b39c878eSAndrey A. Chernov #define NUMTYPES 14
117b39c878eSAndrey A. Chernov #define NUMDENS  (NUMTYPES - 6)
1187ca0641bSAndrey A. Chernov 
1193a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */
120b99f0a4aSAndrew Moore #define F_TAPE_TYPE	0x020	/* bit for fd_types to indicate tape */
121b99f0a4aSAndrew Moore #define NO_TYPE		0	/* must match NO_TYPE in ft.c */
122b99f0a4aSAndrew Moore #define FD_1720         1
123b99f0a4aSAndrew Moore #define FD_1480         2
124b99f0a4aSAndrew Moore #define FD_1440         3
125b99f0a4aSAndrew Moore #define FD_1200         4
126b99f0a4aSAndrew Moore #define FD_820          5
127b99f0a4aSAndrew Moore #define FD_800          6
128b99f0a4aSAndrew Moore #define FD_720          7
129b99f0a4aSAndrew Moore #define FD_360          8
130ed2fa05eSAndrey A. Chernov 
131b99f0a4aSAndrew Moore #define FD_1480in5_25   9
132b99f0a4aSAndrew Moore #define FD_1440in5_25   10
133b99f0a4aSAndrew Moore #define FD_820in5_25    11
134b99f0a4aSAndrew Moore #define FD_800in5_25    12
135b99f0a4aSAndrew Moore #define FD_720in5_25    13
136b99f0a4aSAndrew Moore #define FD_360in5_25    14
137b99f0a4aSAndrew Moore 
1387ca0641bSAndrey A. Chernov 
1396f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] =
1405b81b6b3SRodney W. Grimes {
141126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
142126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
143126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
144126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
145126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
146126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
147126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
148b0568305SAndrey A. Chernov {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
149ed2fa05eSAndrey A. Chernov 
150126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
151126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
152126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
153126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
154126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
155126518a1SAndrey A. Chernov {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
1565b81b6b3SRodney W. Grimes };
1575b81b6b3SRodney W. Grimes 
158b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2		/* 2 floppies */
159dc16046fSJoerg Wunsch 
1605b81b6b3SRodney W. Grimes /***********************************************************************\
1615b81b6b3SRodney W. Grimes * Per controller structure.						*
1625b81b6b3SRodney W. Grimes \***********************************************************************/
1636182fdbdSPeter Wemm static devclass_t fdc_devclass;
1645b81b6b3SRodney W. Grimes 
1655b81b6b3SRodney W. Grimes /***********************************************************************\
1665b81b6b3SRodney W. Grimes * Per drive structure.							*
167b99f0a4aSAndrew Moore * N per controller  (DRVS_PER_CTLR)					*
1685b81b6b3SRodney W. Grimes \***********************************************************************/
1696182fdbdSPeter Wemm struct fd_data {
170b99f0a4aSAndrew Moore 	struct	fdc_data *fdc;	/* pointer to controller structure */
1715b81b6b3SRodney W. Grimes 	int	fdsu;		/* this units number on this controller */
1723a2f7427SDavid Greenman 	int	type;		/* Drive type (FD_1440...) */
1735b81b6b3SRodney W. Grimes 	struct	fd_type *ft;	/* pointer to the type descriptor */
1745b81b6b3SRodney W. Grimes 	int	flags;
1755b81b6b3SRodney W. Grimes #define	FD_OPEN		0x01	/* it's open		*/
1765b81b6b3SRodney W. Grimes #define	FD_ACTIVE	0x02	/* it's active		*/
1775b81b6b3SRodney W. Grimes #define	FD_MOTOR	0x04	/* motor should be on	*/
1785b81b6b3SRodney W. Grimes #define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
1795b81b6b3SRodney W. Grimes 	int	skip;
1805b81b6b3SRodney W. Grimes 	int	hddrv;
181dc5df763SJoerg Wunsch #define FD_NO_TRACK -2
1825b81b6b3SRodney W. Grimes 	int	track;		/* where we think the head is */
1833a2f7427SDavid Greenman 	int	options;	/* user configurable options, see ioctl_fd.h */
18402a19910SJustin T. Gibbs 	struct	callout_handle toffhandle;
18502a19910SJustin T. Gibbs 	struct	callout_handle tohandle;
186b2dfb1f9SJustin T. Gibbs 	struct	devstat device_stats;
18787f6c662SJulian Elischer #ifdef DEVFS
18821519754SBruce Evans 	void	*bdevs[1 + NUMDENS + MAXPARTITIONS];
18921519754SBruce Evans 	void	*cdevs[1 + NUMDENS + MAXPARTITIONS];
19087f6c662SJulian Elischer #endif
1916182fdbdSPeter Wemm 	device_t dev;
1926182fdbdSPeter Wemm 	fdu_t	fdu;
1936182fdbdSPeter Wemm };
1946182fdbdSPeter Wemm static devclass_t fd_devclass;
1955b81b6b3SRodney W. Grimes 
1965b81b6b3SRodney W. Grimes /***********************************************************************\
1975b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used:		*
1985b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question	*
1995b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller		*
2005b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number					*
2015b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number				*
2025b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit)	*
2035b81b6b3SRodney W. Grimes \***********************************************************************/
204b99f0a4aSAndrew Moore 
20569acd21dSWarner Losh #ifdef FDC_YE
20669acd21dSWarner Losh #include "card.h"
20769acd21dSWarner Losh static int yeattach(struct isa_device *);
20869acd21dSWarner Losh #endif
20969acd21dSWarner Losh 
2103a2f7427SDavid Greenman /* needed for ft driver, thus exported */
2116182fdbdSPeter Wemm int in_fdc(struct fdc_data *);
2126182fdbdSPeter Wemm int out_fdc(struct fdc_data *, int);
2133a2f7427SDavid Greenman 
2143a2f7427SDavid Greenman /* internal functions */
2156182fdbdSPeter Wemm static	void fdc_add_device(device_t, const char *, int);
2166182fdbdSPeter Wemm static	void fdc_intr(void *);
2176182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int);
2183a2f7427SDavid Greenman #  define TURNON 1
2193a2f7427SDavid Greenman #  define TURNOFF 0
2203a2f7427SDavid Greenman static timeout_t fd_turnoff;
2213a2f7427SDavid Greenman static timeout_t fd_motor_on;
2226182fdbdSPeter Wemm static void fd_turnon(struct fd_data *);
2233a2f7427SDavid Greenman static void fdc_reset(fdc_p);
2246182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *);
2256182fdbdSPeter Wemm static void fdstart(struct fdc_data *);
2265c1a1eaeSBruce Evans static timeout_t fd_iotimeout;
2273a2f7427SDavid Greenman static timeout_t fd_pseudointr;
2286182fdbdSPeter Wemm static int fdstate(struct fdc_data *);
2296182fdbdSPeter Wemm static int retrier(struct fdc_data *);
2303a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *);
2313a2f7427SDavid Greenman 
232d66c374fSTor Egge static int enable_fifo(fdc_p fdc);
233d66c374fSTor Egge 
234d66c374fSTor Egge static int fifo_threshold = 8;	/* XXX: should be accessible via sysctl */
235d66c374fSTor Egge 
236aaf08d94SGarrett Wollman 
2375b81b6b3SRodney W. Grimes #define DEVIDLE		0
2385b81b6b3SRodney W. Grimes #define FINDWORK	1
2395b81b6b3SRodney W. Grimes #define	DOSEEK		2
2405b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 	3
2415b81b6b3SRodney W. Grimes #define	IOCOMPLETE	4
2425b81b6b3SRodney W. Grimes #define RECALCOMPLETE	5
2435b81b6b3SRodney W. Grimes #define	STARTRECAL	6
2445b81b6b3SRodney W. Grimes #define	RESETCTLR	7
2455b81b6b3SRodney W. Grimes #define	SEEKWAIT	8
2465b81b6b3SRodney W. Grimes #define	RECALWAIT	9
2475b81b6b3SRodney W. Grimes #define	MOTORWAIT	10
2485b81b6b3SRodney W. Grimes #define	IOTIMEDOUT	11
2495c1a1eaeSBruce Evans #define	RESETCOMPLETE	12
25069acd21dSWarner Losh #ifdef FDC_YE
25169acd21dSWarner Losh #define PIOREAD		13
25269acd21dSWarner Losh #endif
2535b81b6b3SRodney W. Grimes 
254d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
255cba2a7c6SBruce Evans static char const * const fdstates[] =
2565b81b6b3SRodney W. Grimes {
2575b81b6b3SRodney W. Grimes "DEVIDLE",
2585b81b6b3SRodney W. Grimes "FINDWORK",
2595b81b6b3SRodney W. Grimes "DOSEEK",
2605b81b6b3SRodney W. Grimes "SEEKCOMPLETE",
2615b81b6b3SRodney W. Grimes "IOCOMPLETE",
2625b81b6b3SRodney W. Grimes "RECALCOMPLETE",
2635b81b6b3SRodney W. Grimes "STARTRECAL",
2645b81b6b3SRodney W. Grimes "RESETCTLR",
2655b81b6b3SRodney W. Grimes "SEEKWAIT",
2665b81b6b3SRodney W. Grimes "RECALWAIT",
2675b81b6b3SRodney W. Grimes "MOTORWAIT",
2685c1a1eaeSBruce Evans "IOTIMEDOUT",
2695c1a1eaeSBruce Evans "RESETCOMPLETE",
27069acd21dSWarner Losh #ifdef FDC_YE
271ff9607b0SBruce Evans "PIOREAD",
27269acd21dSWarner Losh #endif
2735b81b6b3SRodney W. Grimes };
2745b81b6b3SRodney W. Grimes 
2753a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */
276cba2a7c6SBruce Evans static int volatile fd_debug = 0;
2775b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg)
2785b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
279d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */
2805b81b6b3SRodney W. Grimes #define TRACE0(arg)
2815b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2)
282d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */
2835b81b6b3SRodney W. Grimes 
28469acd21dSWarner Losh #ifdef FDC_YE
28569acd21dSWarner Losh #if NCARD > 0
28669acd21dSWarner Losh #include <sys/select.h>
287d17e4ee6SPeter Wemm #include <sys/module.h>
28869acd21dSWarner Losh #include <pccard/cardinfo.h>
28969acd21dSWarner Losh #include <pccard/driver.h>
29069acd21dSWarner Losh #include <pccard/slot.h>
29169acd21dSWarner Losh 
29269acd21dSWarner Losh /*
29369acd21dSWarner Losh  *	PC-Card (PCMCIA) specific code.
29469acd21dSWarner Losh  */
29569acd21dSWarner Losh static int yeinit(struct pccard_devinfo *);		/* init device */
29669acd21dSWarner Losh static void yeunload(struct pccard_devinfo *); 		/* Disable driver */
29769acd21dSWarner Losh static int yeintr(struct pccard_devinfo *); 		/* Interrupt handler */
29869acd21dSWarner Losh 
299d17e4ee6SPeter Wemm PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask);
30069acd21dSWarner Losh 
30169acd21dSWarner Losh /*
30269acd21dSWarner Losh  * this is the secret PIO data port (offset from base)
30369acd21dSWarner Losh  */
30469acd21dSWarner Losh #define FDC_YE_DATAPORT 6
30569acd21dSWarner Losh 
30669acd21dSWarner Losh /*
30769acd21dSWarner Losh  *	Initialize the device - called from Slot manager.
30869acd21dSWarner Losh  */
30969acd21dSWarner Losh static int yeinit(struct pccard_devinfo *devi)
31069acd21dSWarner Losh {
31169acd21dSWarner Losh 	fdc_p fdc = &fdc_data[devi->isahd.id_unit];
31269acd21dSWarner Losh 
31369acd21dSWarner Losh 	/* validate unit number. */
31469acd21dSWarner Losh 	if (devi->isahd.id_unit >= NFDC)
31569acd21dSWarner Losh 		return(ENODEV);
31669acd21dSWarner Losh 	fdc->baseport = devi->isahd.id_iobase;
31769acd21dSWarner Losh 	/*
31869acd21dSWarner Losh 	 * reset controller
31969acd21dSWarner Losh 	 */
32069acd21dSWarner Losh 	outb(fdc->baseport+FDOUT, 0);
32169acd21dSWarner Losh 	DELAY(100);
32269acd21dSWarner Losh 	outb(fdc->baseport+FDOUT, FDO_FRST);
32369acd21dSWarner Losh 
32469acd21dSWarner Losh 	/*
32569acd21dSWarner Losh 	 * wire into system
32669acd21dSWarner Losh 	 */
32769acd21dSWarner Losh 	if (yeattach(&devi->isahd) == 0)
32869acd21dSWarner Losh 		return(ENXIO);
32969acd21dSWarner Losh 
33069acd21dSWarner Losh 	return(0);
33169acd21dSWarner Losh }
33269acd21dSWarner Losh 
33369acd21dSWarner Losh /*
33469acd21dSWarner Losh  *	yeunload - unload the driver and clear the table.
33569acd21dSWarner Losh  *	XXX TODO:
33669acd21dSWarner Losh  *	This is usually called when the card is ejected, but
33769acd21dSWarner Losh  *	can be caused by a modunload of a controller driver.
33869acd21dSWarner Losh  *	The idea is to reset the driver's view of the device
33969acd21dSWarner Losh  *	and ensure that any driver entry points such as
34069acd21dSWarner Losh  *	read and write do not hang.
34169acd21dSWarner Losh  */
34269acd21dSWarner Losh static void yeunload(struct pccard_devinfo *devi)
34369acd21dSWarner Losh {
34469acd21dSWarner Losh 	if (fd_data[devi->isahd.id_unit].type == NO_TYPE)
34569acd21dSWarner Losh 		return;
34669acd21dSWarner Losh 
34769acd21dSWarner Losh 	/*
34869acd21dSWarner Losh 	 * this prevents Fdopen() and fdstrategy() from attempting
34969acd21dSWarner Losh 	 * to access unloaded controller
35069acd21dSWarner Losh 	 */
35169acd21dSWarner Losh 	fd_data[devi->isahd.id_unit].type = NO_TYPE;
35269acd21dSWarner Losh 
35369acd21dSWarner Losh 	printf("fdc%d: unload\n", devi->isahd.id_unit);
35469acd21dSWarner Losh }
35569acd21dSWarner Losh 
35669acd21dSWarner Losh /*
35769acd21dSWarner Losh  *	yeintr - Shared interrupt called from
35869acd21dSWarner Losh  *	front end of PC-Card handler.
35969acd21dSWarner Losh  */
36069acd21dSWarner Losh static int yeintr(struct pccard_devinfo *devi)
36169acd21dSWarner Losh {
36269acd21dSWarner Losh 	fdintr((fdcu_t)devi->isahd.id_unit);
36369acd21dSWarner Losh 	return(1);
36469acd21dSWarner Losh }
36569acd21dSWarner Losh #endif /* NCARD > 0 */
36669acd21dSWarner Losh #endif /* FDC_YE */
36769acd21dSWarner Losh 
36887f6c662SJulian Elischer static	d_open_t	Fdopen;	/* NOTE, not fdopen */
36987f6c662SJulian Elischer static	d_close_t	fdclose;
37087f6c662SJulian Elischer static	d_ioctl_t	fdioctl;
37187f6c662SJulian Elischer static	d_strategy_t	fdstrategy;
37287f6c662SJulian Elischer 
37387f6c662SJulian Elischer #define CDEV_MAJOR 9
37487f6c662SJulian Elischer #define BDEV_MAJOR 2
375f7ea2f55SJulian Elischer 
3764e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = {
3774e2f199eSPoul-Henning Kamp 	/* open */	Fdopen,
3784e2f199eSPoul-Henning Kamp 	/* close */	fdclose,
3794e2f199eSPoul-Henning Kamp 	/* read */	physread,
3804e2f199eSPoul-Henning Kamp 	/* write */	physwrite,
3814e2f199eSPoul-Henning Kamp 	/* ioctl */	fdioctl,
3824e2f199eSPoul-Henning Kamp 	/* stop */	nostop,
3834e2f199eSPoul-Henning Kamp 	/* reset */	noreset,
3844e2f199eSPoul-Henning Kamp 	/* devtotty */	nodevtotty,
3854e2f199eSPoul-Henning Kamp 	/* poll */	nopoll,
3864e2f199eSPoul-Henning Kamp 	/* mmap */	nommap,
3874e2f199eSPoul-Henning Kamp 	/* strategy */	fdstrategy,
3884e2f199eSPoul-Henning Kamp 	/* name */	"fd",
3894e2f199eSPoul-Henning Kamp 	/* parms */	noparms,
3904e2f199eSPoul-Henning Kamp 	/* maj */	CDEV_MAJOR,
3914e2f199eSPoul-Henning Kamp 	/* dump */	nodump,
3924e2f199eSPoul-Henning Kamp 	/* psize */	nopsize,
3934e2f199eSPoul-Henning Kamp 	/* flags */	D_DISK,
3944e2f199eSPoul-Henning Kamp 	/* maxio */	0,
3954e2f199eSPoul-Henning Kamp 	/* bmaj */	BDEV_MAJOR
3964e2f199eSPoul-Henning Kamp };
3974e2f199eSPoul-Henning Kamp 
398dc5df763SJoerg Wunsch static int
3996182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s)
400dc5df763SJoerg Wunsch {
4016182fdbdSPeter Wemm 	fdc->fdc_errs++;
40216b04b6aSJoerg Wunsch 	if (s) {
4036182fdbdSPeter Wemm 		if (fdc->fdc_errs < FDC_ERRMAX) {
4046182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
4056182fdbdSPeter Wemm 			printf("%s", s);
4066182fdbdSPeter Wemm 		} else if (fdc->fdc_errs == FDC_ERRMAX) {
4076182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
4086182fdbdSPeter Wemm 			printf("too many errors, not logging any more\n");
4096182fdbdSPeter Wemm 		}
41016b04b6aSJoerg Wunsch 	}
411dc5df763SJoerg Wunsch 
412dc5df763SJoerg Wunsch 	return FD_FAILED;
413dc5df763SJoerg Wunsch }
414dc5df763SJoerg Wunsch 
415dc5df763SJoerg Wunsch /*
416dc5df763SJoerg Wunsch  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
417dc5df763SJoerg Wunsch  * Unit number,
418dc5df763SJoerg Wunsch  * # of output bytes, output bytes as ints ...,
419dc5df763SJoerg Wunsch  * # of input bytes, input bytes as ints ...
420dc5df763SJoerg Wunsch  */
4216f4e0bebSPoul-Henning Kamp static int
4226182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...)
423dc5df763SJoerg Wunsch {
424dc5df763SJoerg Wunsch 	u_char cmd;
425dc5df763SJoerg Wunsch 	int n_in;
426dc5df763SJoerg Wunsch 	int n;
427dc5df763SJoerg Wunsch 	va_list ap;
428dc5df763SJoerg Wunsch 
429dc5df763SJoerg Wunsch 	va_start(ap, n_out);
430dc5df763SJoerg Wunsch 	cmd = (u_char)(va_arg(ap, int));
431dc5df763SJoerg Wunsch 	va_end(ap);
432dc5df763SJoerg Wunsch 	va_start(ap, n_out);
433dc5df763SJoerg Wunsch 	for (n = 0; n < n_out; n++)
434dc5df763SJoerg Wunsch 	{
4356182fdbdSPeter Wemm 		if (out_fdc(fdc, va_arg(ap, int)) < 0)
436dc5df763SJoerg Wunsch 		{
437dc5df763SJoerg Wunsch 			char msg[50];
4382127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
439dc5df763SJoerg Wunsch 				"cmd %x failed at out byte %d of %d\n",
440dc5df763SJoerg Wunsch 				cmd, n + 1, n_out);
4416182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
442dc5df763SJoerg Wunsch 		}
443dc5df763SJoerg Wunsch 	}
444dc5df763SJoerg Wunsch 	n_in = va_arg(ap, int);
445dc5df763SJoerg Wunsch 	for (n = 0; n < n_in; n++)
446dc5df763SJoerg Wunsch 	{
447dc5df763SJoerg Wunsch 		int *ptr = va_arg(ap, int *);
4486182fdbdSPeter Wemm 		if (fd_in(fdc, ptr) < 0)
449dc5df763SJoerg Wunsch 		{
450dc5df763SJoerg Wunsch 			char msg[50];
4512127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
452dc5df763SJoerg Wunsch 				"cmd %02x failed at in byte %d of %d\n",
453dc5df763SJoerg Wunsch 				cmd, n + 1, n_in);
4546182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
455dc5df763SJoerg Wunsch 		}
456dc5df763SJoerg Wunsch 	}
457dc5df763SJoerg Wunsch 
458dc5df763SJoerg Wunsch 	return 0;
459dc5df763SJoerg Wunsch }
460dc5df763SJoerg Wunsch 
4616f4e0bebSPoul-Henning Kamp static int
462d66c374fSTor Egge enable_fifo(fdc_p fdc)
463d66c374fSTor Egge {
464d66c374fSTor Egge 	int i, j;
465d66c374fSTor Egge 
466d66c374fSTor Egge 	if ((fdc->flags & FDC_HAS_FIFO) == 0) {
467d66c374fSTor Egge 
468d66c374fSTor Egge 		/*
469d66c374fSTor Egge 		 * XXX:
470d66c374fSTor Egge 		 * Cannot use fd_cmd the normal way here, since
471d66c374fSTor Egge 		 * this might be an invalid command. Thus we send the
472d66c374fSTor Egge 		 * first byte, and check for an early turn of data directon.
473d66c374fSTor Egge 		 */
474d66c374fSTor Egge 
4756182fdbdSPeter Wemm 		if (out_fdc(fdc, I8207X_CONFIGURE) < 0)
4766182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
477d66c374fSTor Egge 
478d66c374fSTor Egge 		/* If command is invalid, return */
479d66c374fSTor Egge 		j = 100000;
480d66c374fSTor Egge 		while ((i = inb(fdc->baseport + FDSTS) & (NE7_DIO | NE7_RQM))
481d66c374fSTor Egge 		       != NE7_RQM && j-- > 0)
482d66c374fSTor Egge 			if (i == (NE7_DIO | NE7_RQM)) {
483d66c374fSTor Egge 				fdc_reset(fdc);
484d66c374fSTor Egge 				return FD_FAILED;
485d66c374fSTor Egge 			}
486d66c374fSTor Egge 		if (j<0 ||
4876182fdbdSPeter Wemm 		    fd_cmd(fdc, 3,
488d66c374fSTor Egge 			   0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) {
489d66c374fSTor Egge 			fdc_reset(fdc);
4906182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
491d66c374fSTor Egge 		}
492d66c374fSTor Egge 		fdc->flags |= FDC_HAS_FIFO;
493d66c374fSTor Egge 		return 0;
494d66c374fSTor Egge 	}
4956182fdbdSPeter Wemm 	if (fd_cmd(fdc, 4,
496d66c374fSTor Egge 		   I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0)
4976182fdbdSPeter Wemm 		return fdc_err(fdc, "Re-enable FIFO failed\n");
498d66c374fSTor Egge 	return 0;
499d66c374fSTor Egge }
500d66c374fSTor Egge 
501d66c374fSTor Egge static int
502dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p)
503dc5df763SJoerg Wunsch {
504dc5df763SJoerg Wunsch 	int st3;
505dc5df763SJoerg Wunsch 
5066182fdbdSPeter Wemm 	if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
507dc5df763SJoerg Wunsch 	{
5086182fdbdSPeter Wemm 		return fdc_err(fdc, "Sense Drive Status failed\n");
509dc5df763SJoerg Wunsch 	}
510dc5df763SJoerg Wunsch 	if (st3p)
511dc5df763SJoerg Wunsch 		*st3p = st3;
512dc5df763SJoerg Wunsch 
513dc5df763SJoerg Wunsch 	return 0;
514dc5df763SJoerg Wunsch }
515dc5df763SJoerg Wunsch 
5166f4e0bebSPoul-Henning Kamp static int
517dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
518dc5df763SJoerg Wunsch {
5196182fdbdSPeter Wemm 	int cyl, st0, ret;
520dc5df763SJoerg Wunsch 
5216182fdbdSPeter Wemm 	ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0);
5226182fdbdSPeter Wemm 	if (ret) {
5236182fdbdSPeter Wemm 		(void)fdc_err(fdc,
524dc5df763SJoerg Wunsch 			      "sense intr err reading stat reg 0\n");
525dc5df763SJoerg Wunsch 		return ret;
526dc5df763SJoerg Wunsch 	}
527dc5df763SJoerg Wunsch 
528dc5df763SJoerg Wunsch 	if (st0p)
529dc5df763SJoerg Wunsch 		*st0p = st0;
530dc5df763SJoerg Wunsch 
5316182fdbdSPeter Wemm 	if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) {
532dc5df763SJoerg Wunsch 		/*
533dc5df763SJoerg Wunsch 		 * There doesn't seem to have been an interrupt.
534dc5df763SJoerg Wunsch 		 */
535dc5df763SJoerg Wunsch 		return FD_NOT_VALID;
536dc5df763SJoerg Wunsch 	}
537dc5df763SJoerg Wunsch 
5386182fdbdSPeter Wemm 	if (fd_in(fdc, &cyl) < 0) {
5396182fdbdSPeter Wemm 		return fdc_err(fdc, "can't get cyl num\n");
540dc5df763SJoerg Wunsch 	}
541dc5df763SJoerg Wunsch 
542dc5df763SJoerg Wunsch 	if (cylp)
543dc5df763SJoerg Wunsch 		*cylp = cyl;
544dc5df763SJoerg Wunsch 
545dc5df763SJoerg Wunsch 	return 0;
546dc5df763SJoerg Wunsch }
547dc5df763SJoerg Wunsch 
548dc5df763SJoerg Wunsch 
5496f4e0bebSPoul-Henning Kamp static int
550dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu)
551dc5df763SJoerg Wunsch {
552dc5df763SJoerg Wunsch 	int i, ret;
553b5e8ce9fSBruce Evans 
5546182fdbdSPeter Wemm 	for (i = 0; i < 7; i++) {
555b5e8ce9fSBruce Evans 		/*
556b5e8ce9fSBruce Evans 		 * XXX types are poorly chosen.  Only bytes can by read
557a838d83dSBruce Evans 		 * from the hardware, but fdc->status[] wants u_ints and
558b5e8ce9fSBruce Evans 		 * fd_in() gives ints.
559b5e8ce9fSBruce Evans 		 */
560b5e8ce9fSBruce Evans 		int status;
561b5e8ce9fSBruce Evans 
5626182fdbdSPeter Wemm 		ret = fd_in(fdc, &status);
563b5e8ce9fSBruce Evans 		fdc->status[i] = status;
564b5e8ce9fSBruce Evans 		if (ret != 0)
565dc5df763SJoerg Wunsch 			break;
566dc5df763SJoerg Wunsch 	}
567dc5df763SJoerg Wunsch 
568dc5df763SJoerg Wunsch 	if (ret == 0)
569dc5df763SJoerg Wunsch 		fdc->flags |= FDC_STAT_VALID;
570dc5df763SJoerg Wunsch 	else
571dc5df763SJoerg Wunsch 		fdc->flags &= ~FDC_STAT_VALID;
572dc5df763SJoerg Wunsch 
573dc5df763SJoerg Wunsch 	return ret;
574dc5df763SJoerg Wunsch }
575dc5df763SJoerg Wunsch 
5765b81b6b3SRodney W. Grimes /****************************************************************************/
5775b81b6b3SRodney W. Grimes /*                      autoconfiguration stuff                             */
5785b81b6b3SRodney W. Grimes /****************************************************************************/
579dc5df763SJoerg Wunsch 
5803a2f7427SDavid Greenman static int
5816182fdbdSPeter Wemm fdc_probe(device_t dev)
5825b81b6b3SRodney W. Grimes {
5836182fdbdSPeter Wemm 	int	error, i, ic_type;
5846182fdbdSPeter Wemm 	struct	fdc_data *fdc;
5856182fdbdSPeter Wemm 	char	myname[8];	/* better be long enough */
5865b81b6b3SRodney W. Grimes 
587a97c75b7SDoug Rabson 	/* No pnp support */
588a97c75b7SDoug Rabson 	if (isa_get_vendorid(dev))
589a97c75b7SDoug Rabson 		return (ENXIO);
590a97c75b7SDoug Rabson 
5916182fdbdSPeter Wemm 	fdc = device_get_softc(dev);
5926182fdbdSPeter Wemm 	bzero(fdc, sizeof *fdc);
5936182fdbdSPeter Wemm 	fdc->fdc_dev = dev;
5946182fdbdSPeter Wemm 	fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0;
5956182fdbdSPeter Wemm 	fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0;
5966182fdbdSPeter Wemm 
5976182fdbdSPeter Wemm 	fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
5986182fdbdSPeter Wemm 					     &fdc->rid_ioport, 0ul, ~0ul,
5996182fdbdSPeter Wemm 					     IO_FDCSIZE, RF_ACTIVE);
6006182fdbdSPeter Wemm 	if (fdc->res_ioport == 0) {
6016182fdbdSPeter Wemm 		device_print_prettyname(dev);
6026182fdbdSPeter Wemm 		printf("cannot reserve I/O port range\n");
6036182fdbdSPeter Wemm 		error = ENXIO;
6046182fdbdSPeter Wemm 		goto out;
6056182fdbdSPeter Wemm 	}
6066182fdbdSPeter Wemm 	fdc->baseport = fdc->res_ioport->r_start;
6076182fdbdSPeter Wemm 
6086182fdbdSPeter Wemm 	fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ,
6096182fdbdSPeter Wemm 					  &fdc->rid_irq, 0ul, ~0ul, 1,
6106182fdbdSPeter Wemm 					  RF_ACTIVE);
6116182fdbdSPeter Wemm 	if (fdc->res_irq == 0) {
6126182fdbdSPeter Wemm 		device_print_prettyname(dev);
6136182fdbdSPeter Wemm 		printf("cannot reserve interrupt line\n");
6146182fdbdSPeter Wemm 		error = ENXIO;
6156182fdbdSPeter Wemm 		goto out;
6166182fdbdSPeter Wemm 	}
6176182fdbdSPeter Wemm 	fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ,
6186182fdbdSPeter Wemm 					  &fdc->rid_drq, 0ul, ~0ul, 1,
6196182fdbdSPeter Wemm 					  RF_ACTIVE);
6206182fdbdSPeter Wemm 	if (fdc->res_drq == 0) {
6216182fdbdSPeter Wemm 		device_print_prettyname(dev);
6226182fdbdSPeter Wemm 		printf("cannot reserve DMA request line\n");
6236182fdbdSPeter Wemm 		error = ENXIO;
6246182fdbdSPeter Wemm 		goto out;
6256182fdbdSPeter Wemm 	}
6266182fdbdSPeter Wemm 	fdc->dmachan = fdc->res_drq->r_start;
627566643e3SDoug Rabson 	error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq,
628566643e3SDoug Rabson 			       INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr);
6295b81b6b3SRodney W. Grimes 
63016111cedSAndrew Moore 	/* First - lets reset the floppy controller */
6316182fdbdSPeter Wemm 	outb(fdc->baseport + FDOUT, 0);
63216111cedSAndrew Moore 	DELAY(100);
6336182fdbdSPeter Wemm 	outb(fdc->baseport + FDOUT, FDO_FRST);
63416111cedSAndrew Moore 
6355b81b6b3SRodney W. Grimes 	/* see if it can handle a command */
6366182fdbdSPeter Wemm 	if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240),
6376182fdbdSPeter Wemm 		   NE7_SPEC_2(2, 0), 0)) {
6386182fdbdSPeter Wemm 		error = ENXIO;
6396182fdbdSPeter Wemm 		goto out;
6405b81b6b3SRodney W. Grimes 	}
6416182fdbdSPeter Wemm 
6426182fdbdSPeter Wemm 	if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) {
6436182fdbdSPeter Wemm 		ic_type = (u_char)ic_type;
6446182fdbdSPeter Wemm 		switch (ic_type) {
6456182fdbdSPeter Wemm 		case 0x80:
6466182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 765 or clone");
6476182fdbdSPeter Wemm 			fdc->fdct = FDC_NE765;
6486182fdbdSPeter Wemm 			break;
6496182fdbdSPeter Wemm 		case 0x81:
6506182fdbdSPeter Wemm 			device_set_desc(dev, "Intel 82077 or clone");
6516182fdbdSPeter Wemm 			fdc->fdct = FDC_I82077;
6526182fdbdSPeter Wemm 			break;
6536182fdbdSPeter Wemm 		case 0x90:
6546182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 72065B or clone");
6556182fdbdSPeter Wemm 			fdc->fdct = FDC_NE72065;
6566182fdbdSPeter Wemm 			break;
6576182fdbdSPeter Wemm 		default:
6586182fdbdSPeter Wemm 			device_set_desc(dev, "generic floppy controller");
6596182fdbdSPeter Wemm 			fdc->fdct = FDC_UNKNOWN;
6606182fdbdSPeter Wemm 			break;
6616182fdbdSPeter Wemm 		}
6626182fdbdSPeter Wemm 	}
6636182fdbdSPeter Wemm 
6646182fdbdSPeter Wemm 	snprintf(myname, sizeof(myname), "%s%d", device_get_name(dev),
6656182fdbdSPeter Wemm 		 device_get_unit(dev));
6666182fdbdSPeter Wemm 	for (i = resource_query_string(-1, "at", myname); i != -1;
6676182fdbdSPeter Wemm 	     i = resource_query_string(i, "at", myname))
6686182fdbdSPeter Wemm 		fdc_add_device(dev, resource_query_name(i),
6696182fdbdSPeter Wemm 			       resource_query_unit(i));
67069acd21dSWarner Losh #ifdef FDC_YE
67169acd21dSWarner Losh 	/*
67269acd21dSWarner Losh 	 * don't succeed on probe; wait
67369acd21dSWarner Losh 	 * for PCCARD subsystem to do it
67469acd21dSWarner Losh 	 */
67569acd21dSWarner Losh 	if (dev->id_flags & FDC_IS_PCMCIA)
67669acd21dSWarner Losh 		return(0);
67769acd21dSWarner Losh #endif
6786182fdbdSPeter Wemm 	return (0);
6796182fdbdSPeter Wemm 
6806182fdbdSPeter Wemm out:
6816182fdbdSPeter Wemm 	if (fdc->fdc_intr)
6826182fdbdSPeter Wemm 		BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq,
6836182fdbdSPeter Wemm 				  fdc->fdc_intr);
6846182fdbdSPeter Wemm 	if (fdc->res_irq != 0) {
6856182fdbdSPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
6866182fdbdSPeter Wemm 					fdc->res_irq);
6876182fdbdSPeter Wemm 		bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
6886182fdbdSPeter Wemm 				     fdc->res_irq);
6896182fdbdSPeter Wemm 	}
6906182fdbdSPeter Wemm 	if (fdc->res_ioport != 0) {
6916182fdbdSPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
6926182fdbdSPeter Wemm 					fdc->res_ioport);
6936182fdbdSPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
6946182fdbdSPeter Wemm 				     fdc->res_ioport);
6956182fdbdSPeter Wemm 	}
6966182fdbdSPeter Wemm 	if (fdc->res_drq != 0) {
6976182fdbdSPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
6986182fdbdSPeter Wemm 					fdc->res_drq);
6996182fdbdSPeter Wemm 		bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
7006182fdbdSPeter Wemm 				     fdc->res_drq);
7016182fdbdSPeter Wemm 	}
7026182fdbdSPeter Wemm 	return (error);
7035b81b6b3SRodney W. Grimes }
7045b81b6b3SRodney W. Grimes 
7055b81b6b3SRodney W. Grimes /*
7066182fdbdSPeter Wemm  * Aped dfr@freebsd.org's isa_add_device().
7075b81b6b3SRodney W. Grimes  */
7086182fdbdSPeter Wemm static void
7096182fdbdSPeter Wemm fdc_add_device(device_t dev, const char *name, int unit)
7105b81b6b3SRodney W. Grimes {
7116182fdbdSPeter Wemm 	int	disabled, *ivar;
7126182fdbdSPeter Wemm 	device_t child;
71392200632SGarrett Wollman 
7146182fdbdSPeter Wemm 	ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT);
7156182fdbdSPeter Wemm 	if (ivar == 0)
7166182fdbdSPeter Wemm 		return;
717a97c75b7SDoug Rabson 	if (resource_int_value(name, unit, "drive", ivar) != 0)
7186182fdbdSPeter Wemm 		*ivar = 0;
7196182fdbdSPeter Wemm 	child = device_add_child(dev, name, unit, ivar);
7206182fdbdSPeter Wemm 	if (child == 0)
7216182fdbdSPeter Wemm 		return;
722a97c75b7SDoug Rabson 	if (resource_int_value(name, unit, "disabled", &disabled) == 0
723a97c75b7SDoug Rabson 	    && disabled != 0)
7246182fdbdSPeter Wemm 		device_disable(child);
7256182fdbdSPeter Wemm }
7266182fdbdSPeter Wemm 
7276182fdbdSPeter Wemm static int
7286182fdbdSPeter Wemm fdc_attach(device_t dev)
7296182fdbdSPeter Wemm {
7306182fdbdSPeter Wemm 	struct	fdc_data *fdc = device_get_softc(dev);
7316182fdbdSPeter Wemm 	fdcu_t	fdcu = device_get_unit(dev);
7326182fdbdSPeter Wemm 
7335b81b6b3SRodney W. Grimes 	fdc->fdcu = fdcu;
7345b81b6b3SRodney W. Grimes 	fdc->flags |= FDC_ATTACHED;
7356182fdbdSPeter Wemm 
736100f78bbSSujal Patel 	/* Acquire the DMA channel forever, The driver will do the rest */
7376182fdbdSPeter Wemm 				/* XXX should integrate with rman */
738100f78bbSSujal Patel 	isa_dma_acquire(fdc->dmachan);
739dd87702aSBruce Evans 	isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */);
7405b81b6b3SRodney W. Grimes 	fdc->state = DEVIDLE;
7416182fdbdSPeter Wemm 
7423a2f7427SDavid Greenman 	/* reset controller, turn motor off, clear fdout mirror reg */
7433a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
74402a19910SJustin T. Gibbs 	bufq_init(&fdc->head);
7455b81b6b3SRodney W. Grimes 
7466182fdbdSPeter Wemm #ifdef FIFO_BEFORE_MOTORON
7476182fdbdSPeter Wemm 	/* Hmm, this doesn't work here - is set_motor() magic? -Peter */
7486182fdbdSPeter Wemm 	if (fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN
7496182fdbdSPeter Wemm 	    && enable_fifo(fdc) == 0) {
7506182fdbdSPeter Wemm 		device_print_prettyname(dev);
7516182fdbdSPeter Wemm 		printf("FIFO enabled, %d bytes threshold\n", fifo_threshold);
7526182fdbdSPeter Wemm 	}
7536182fdbdSPeter Wemm #endif
7546182fdbdSPeter Wemm 	/*
7556182fdbdSPeter Wemm 	 * Probe and attach any children as were configured above.
7566182fdbdSPeter Wemm 	 */
7576182fdbdSPeter Wemm 	return (bus_generic_attach(dev));
7586182fdbdSPeter Wemm }
7596182fdbdSPeter Wemm 
76015317dd8SMatthew N. Dodd static int
7616182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child)
7626182fdbdSPeter Wemm {
76315317dd8SMatthew N. Dodd 	int retval = 0;
76415317dd8SMatthew N. Dodd 
76515317dd8SMatthew N. Dodd 	retval += bus_print_child_header(me, child);
76615317dd8SMatthew N. Dodd 	retval += printf(" on %s drive %d\n", device_get_nameunit(me),
7676182fdbdSPeter Wemm 	       *(int *)device_get_ivars(child));
76815317dd8SMatthew N. Dodd 
76915317dd8SMatthew N. Dodd 	return (retval);
7706182fdbdSPeter Wemm }
7716182fdbdSPeter Wemm 
7726182fdbdSPeter Wemm static int
7736182fdbdSPeter Wemm fd_probe(device_t dev)
7746182fdbdSPeter Wemm {
7756182fdbdSPeter Wemm 	int	i;
7766182fdbdSPeter Wemm 	u_int	fdt, st0, st3;
7776182fdbdSPeter Wemm 	struct	fd_data *fd;
7786182fdbdSPeter Wemm 	struct	fdc_data *fdc;
7796182fdbdSPeter Wemm 	fdsu_t	fdsu;
7806182fdbdSPeter Wemm #ifndef FIFO_BEFORE_MOTORON
7816182fdbdSPeter Wemm 	static int fd_fifo = 0;
7826182fdbdSPeter Wemm #endif
7836182fdbdSPeter Wemm 
7846182fdbdSPeter Wemm 	fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */
7856182fdbdSPeter Wemm 	fd = device_get_softc(dev);
7866182fdbdSPeter Wemm 	fdc = device_get_softc(device_get_parent(dev));
7876182fdbdSPeter Wemm 
7886182fdbdSPeter Wemm 	bzero(fd, sizeof *fd);
7896182fdbdSPeter Wemm 	fd->dev = dev;
7906182fdbdSPeter Wemm 	fd->fdc = fdc;
7916182fdbdSPeter Wemm 	fd->fdsu = fdsu;
7926182fdbdSPeter Wemm 	fd->fdu = device_get_unit(dev);
7936182fdbdSPeter Wemm 
794a97c75b7SDoug Rabson #ifdef __i386__
795b99f0a4aSAndrew Moore 	/* look up what bios thinks we have */
7966182fdbdSPeter Wemm 	switch (fd->fdu) {
7976182fdbdSPeter Wemm 	case 0:
7986182fdbdSPeter Wemm 		if (isa_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0)
7990722d6abSJoerg Wunsch 			fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
8000722d6abSJoerg Wunsch 		else
8010722d6abSJoerg Wunsch 			fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
802b99f0a4aSAndrew Moore 		break;
8036182fdbdSPeter Wemm 	case 1:
8046182fdbdSPeter Wemm 		fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
805dc5df763SJoerg Wunsch 		break;
806dc5df763SJoerg Wunsch 	default:
8076182fdbdSPeter Wemm 		fdt = RTCFDT_NONE;
808dc5df763SJoerg Wunsch 		break;
8096b7bd95bSJoerg Wunsch 	}
810a97c75b7SDoug Rabson #else
811a97c75b7SDoug Rabson 	fdt = RTCFDT_144M;	/* XXX probably */
812a97c75b7SDoug Rabson #endif
8136182fdbdSPeter Wemm 
8146182fdbdSPeter Wemm 	/* is there a unit? */
8156182fdbdSPeter Wemm 	if (fdt == RTCFDT_NONE)
8166182fdbdSPeter Wemm 		return (ENXIO);
8176182fdbdSPeter Wemm 
8186182fdbdSPeter Wemm 	/* select it */
8196182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNON);
8206182fdbdSPeter Wemm 	DELAY(1000000);	/* 1 sec */
8216182fdbdSPeter Wemm 
8226182fdbdSPeter Wemm #ifndef FIFO_BEFORE_MOTORON
8236182fdbdSPeter Wemm 	if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN
8246182fdbdSPeter Wemm 	    && enable_fifo(fdc) == 0) {
8256182fdbdSPeter Wemm 		device_print_prettyname(device_get_parent(dev));
8266182fdbdSPeter Wemm 		printf("FIFO enabled, %d bytes threshold\n", fifo_threshold);
827d66c374fSTor Egge 	}
8286182fdbdSPeter Wemm 	fd_fifo = 1;
8296182fdbdSPeter Wemm #endif
8306182fdbdSPeter Wemm 
8316182fdbdSPeter Wemm 	if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0)
8326182fdbdSPeter Wemm 	    && (st3 & NE7_ST3_T0)) {
833dc5df763SJoerg Wunsch 		/* if at track 0, first seek inwards */
834dc5df763SJoerg Wunsch 		/* seek some steps: */
8356182fdbdSPeter Wemm 		fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0);
836dc5df763SJoerg Wunsch 		DELAY(300000); /* ...wait a moment... */
8376182fdbdSPeter Wemm 		fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
838dc5df763SJoerg Wunsch 	}
839dc5df763SJoerg Wunsch 
840dc5df763SJoerg Wunsch 	/* If we're at track 0 first seek inwards. */
8416182fdbdSPeter Wemm 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
842dc5df763SJoerg Wunsch 		/* Seek some steps... */
8436182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
844dc5df763SJoerg Wunsch 			/* ...wait a moment... */
845dc5df763SJoerg Wunsch 			DELAY(300000);
846dc5df763SJoerg Wunsch 			/* make ctrlr happy: */
8476182fdbdSPeter Wemm 			fd_sense_int(fdc, 0, 0);
848dc5df763SJoerg Wunsch 		}
849dc5df763SJoerg Wunsch 	}
850dc5df763SJoerg Wunsch 
8516b7bd95bSJoerg Wunsch 	for (i = 0; i < 2; i++) {
8526b7bd95bSJoerg Wunsch 		/*
8536b7bd95bSJoerg Wunsch 		 * we must recalibrate twice, just in case the
8546b7bd95bSJoerg Wunsch 		 * heads have been beyond cylinder 76, since most
8556b7bd95bSJoerg Wunsch 		 * FDCs still barf when attempting to recalibrate
8566b7bd95bSJoerg Wunsch 		 * more than 77 steps
8576b7bd95bSJoerg Wunsch 		 */
858dc5df763SJoerg Wunsch 		/* go back to 0: */
8596182fdbdSPeter Wemm 		if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
8606b7bd95bSJoerg Wunsch 			/* a second being enough for full stroke seek*/
8616b7bd95bSJoerg Wunsch 			DELAY(i == 0 ? 1000000 : 300000);
8625b81b6b3SRodney W. Grimes 
8636b7bd95bSJoerg Wunsch 			/* anything responding? */
864dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
865dc5df763SJoerg Wunsch 			    (st0 & NE7_ST0_EC) == 0)
8666b7bd95bSJoerg Wunsch 				break; /* already probed succesfully */
8676b7bd95bSJoerg Wunsch 		}
868dc5df763SJoerg Wunsch 	}
8696b7bd95bSJoerg Wunsch 
8706182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNOFF);
8713a2f7427SDavid Greenman 
8723a2f7427SDavid Greenman 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
8736182fdbdSPeter Wemm 		return (ENXIO);
8745b81b6b3SRodney W. Grimes 
875dc5df763SJoerg Wunsch 	fd->track = FD_NO_TRACK;
876b99f0a4aSAndrew Moore 	fd->fdc = fdc;
877b99f0a4aSAndrew Moore 	fd->fdsu = fdsu;
8783a2f7427SDavid Greenman 	fd->options = 0;
87902a19910SJustin T. Gibbs 	callout_handle_init(&fd->toffhandle);
88002a19910SJustin T. Gibbs 	callout_handle_init(&fd->tohandle);
8815b81b6b3SRodney W. Grimes 
882b99f0a4aSAndrew Moore 	switch (fdt) {
8837ca0641bSAndrey A. Chernov 	case RTCFDT_12M:
8846182fdbdSPeter Wemm 		device_set_desc(dev, "1200-KB 5.25\" drive");
885b99f0a4aSAndrew Moore 		fd->type = FD_1200;
8867ca0641bSAndrey A. Chernov 		break;
8870722d6abSJoerg Wunsch 	case RTCFDT_144M | RTCFDT_144M_PRETENDED:
8886182fdbdSPeter Wemm 		device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive");
8890722d6abSJoerg Wunsch 		fdt = RTCFDT_144M;
8906182fdbdSPeter Wemm 		fd->type = FD_1440;
8917ca0641bSAndrey A. Chernov 	case RTCFDT_144M:
8926182fdbdSPeter Wemm 		device_set_desc(dev, "1440-KB 3.5\" drive");
893b99f0a4aSAndrew Moore 		fd->type = FD_1440;
8947ca0641bSAndrey A. Chernov 		break;
895290dd077SJoerg Wunsch 	case RTCFDT_288M:
89686a727d9SJoerg Wunsch 	case RTCFDT_288M_1:
8976182fdbdSPeter Wemm 		device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)");
898290dd077SJoerg Wunsch 		fd->type = FD_1440;
899290dd077SJoerg Wunsch 		break;
9007ca0641bSAndrey A. Chernov 	case RTCFDT_360K:
9016182fdbdSPeter Wemm 		device_set_desc(dev, "360-KB 5.25\" drive");
902b99f0a4aSAndrew Moore 		fd->type = FD_360;
9037ca0641bSAndrey A. Chernov 		break;
904ed2fa05eSAndrey A. Chernov 	case RTCFDT_720K:
9056182fdbdSPeter Wemm 		printf("720-KB 3.5\" drive");
906b99f0a4aSAndrew Moore 		fd->type = FD_720;
907ed2fa05eSAndrey A. Chernov 		break;
9087ca0641bSAndrey A. Chernov 	default:
9096182fdbdSPeter Wemm 		return (ENXIO);
9105b81b6b3SRodney W. Grimes 	}
9116182fdbdSPeter Wemm 	return (0);
9126182fdbdSPeter Wemm }
9136182fdbdSPeter Wemm 
9146182fdbdSPeter Wemm static int
9156182fdbdSPeter Wemm fd_attach(device_t dev)
9166182fdbdSPeter Wemm {
9176182fdbdSPeter Wemm 	struct	fd_data *fd;
918edfdec19SPeter Wemm #ifdef DEVFS
919edfdec19SPeter Wemm 	int	i;
920edfdec19SPeter Wemm 	int     mynor;
921edfdec19SPeter Wemm 	int     typemynor;
922edfdec19SPeter Wemm 	int     typesize;
923edfdec19SPeter Wemm #endif
9246182fdbdSPeter Wemm 
9256182fdbdSPeter Wemm 	fd = device_get_softc(dev);
9266182fdbdSPeter Wemm 
9276182fdbdSPeter Wemm #ifdef DEVFS			/* XXX bitrot */
928edfdec19SPeter Wemm 	mynor = fd->fdu << 6;
929f7ea2f55SJulian Elischer 	fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK,
930f85120acSBruce Evans 					UID_ROOT, GID_OPERATOR, 0640,
931edfdec19SPeter Wemm 					"fd%d", fd->fdu);
93221519754SBruce Evans 	fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR,
933f85120acSBruce Evans 					UID_ROOT, GID_OPERATOR, 0640,
934edfdec19SPeter Wemm 					"rfd%d", fd->fdu);
93521519754SBruce Evans 	for (i = 1; i < 1 + NUMDENS; i++) {
93621519754SBruce Evans 		/*
93721519754SBruce Evans 		 * XXX this and the lookup in Fdopen() should be
93821519754SBruce Evans 		 * data driven.
93921519754SBruce Evans 		 */
94021519754SBruce Evans 		switch (fd->type) {
94121519754SBruce Evans 		case FD_360:
94221519754SBruce Evans 			if (i != FD_360)
94321519754SBruce Evans 				continue;
94421519754SBruce Evans 			break;
94521519754SBruce Evans 		case FD_720:
94621519754SBruce Evans 			if (i != FD_720 && i != FD_800 && i != FD_820)
94721519754SBruce Evans 				continue;
94821519754SBruce Evans 			break;
94921519754SBruce Evans 		case FD_1200:
95021519754SBruce Evans 			if (i != FD_360 && i != FD_720 && i != FD_800
95121519754SBruce Evans 			    && i != FD_820 && i != FD_1200
95221519754SBruce Evans 			    && i != FD_1440 && i != FD_1480)
95321519754SBruce Evans 				continue;
95421519754SBruce Evans 			break;
95521519754SBruce Evans 		case FD_1440:
95621519754SBruce Evans 			if (i != FD_720 && i != FD_800 && i != FD_820
95721519754SBruce Evans 			    && i != FD_1200 && i != FD_1440
95821519754SBruce Evans 			    && i != FD_1480 && i != FD_1720)
95921519754SBruce Evans 				continue;
96021519754SBruce Evans 			break;
96121519754SBruce Evans 		}
96221519754SBruce Evans 		typesize = fd_types[i - 1].size / 2;
96321519754SBruce Evans 		/*
96421519754SBruce Evans 		 * XXX all these conversions give bloated code and
96521519754SBruce Evans 		 * confusing names.
96621519754SBruce Evans 		 */
96721519754SBruce Evans 		if (typesize == 1476)
96821519754SBruce Evans 			typesize = 1480;
96921519754SBruce Evans 		if (typesize == 1722)
97021519754SBruce Evans 			typesize = 1720;
9713e425b96SJulian Elischer 		typemynor = mynor | i;
97221519754SBruce Evans 		fd->bdevs[i] =
973f7ea2f55SJulian Elischer 			devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK,
97421519754SBruce Evans 					 UID_ROOT, GID_OPERATOR, 0640,
975edfdec19SPeter Wemm 					 "fd%d.%d", fd->fdu, typesize);
97621519754SBruce Evans 		fd->cdevs[i] =
97721519754SBruce Evans 			devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR,
97821519754SBruce Evans 					 UID_ROOT, GID_OPERATOR, 0640,
979edfdec19SPeter Wemm 					 "rfd%d.%d", fd->fdu, typesize);
98021519754SBruce Evans 	}
9813e425b96SJulian Elischer 
98221519754SBruce Evans 	for (i = 0; i < MAXPARTITIONS; i++) {
9832ae353f9SEivind Eklund 		fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0],
984edfdec19SPeter Wemm 						"fd%d%c", fd->fdu, 'a' + i);
98521519754SBruce Evans 		fd->cdevs[1 + NUMDENS + i] =
9862ae353f9SEivind Eklund 			devfs_makelink(fd->cdevs[0],
987edfdec19SPeter Wemm 				   "rfd%d%c", fd->fdu, 'a' + i);
98821519754SBruce Evans 	}
989999422d7SJulian Elischer #endif /* DEVFS */
990671e2ceeSBruce Evans 	/*
991b2dfb1f9SJustin T. Gibbs 	 * Export the drive to the devstat interface.
992671e2ceeSBruce Evans 	 */
9936182fdbdSPeter Wemm 	devstat_add_entry(&fd->device_stats, device_get_name(dev),
9946182fdbdSPeter Wemm 			  device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS,
9952a888f93SKenneth D. Merry 			  DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER,
9962a888f93SKenneth D. Merry 			  DEVSTAT_PRIORITY_FD);
9976182fdbdSPeter Wemm 	return (0);
9985b81b6b3SRodney W. Grimes }
9995b81b6b3SRodney W. Grimes 
100069acd21dSWarner Losh #ifdef FDC_YE
100169acd21dSWarner Losh /*
100269acd21dSWarner Losh  * this is a subset of fdattach() optimized for the Y-E Data
100369acd21dSWarner Losh  * PCMCIA floppy drive.
100469acd21dSWarner Losh  */
100569acd21dSWarner Losh static int yeattach(struct isa_device *dev)
100669acd21dSWarner Losh {
100769acd21dSWarner Losh 	fdcu_t  fdcu = dev->id_unit;
100869acd21dSWarner Losh 	fdc_p   fdc = fdc_data + fdcu;
100969acd21dSWarner Losh 	fdsu_t  fdsu = 0;               /* assume 1 drive per YE controller */
101069acd21dSWarner Losh 	fdu_t   fdu;
101169acd21dSWarner Losh 	fd_p    fd;
101269acd21dSWarner Losh 	int     st0, st3, i;
101369acd21dSWarner Losh #ifdef DEVFS
101469acd21dSWarner Losh 	int     mynor;
101569acd21dSWarner Losh 	int     typemynor;
101669acd21dSWarner Losh 	int     typesize;
101769acd21dSWarner Losh #endif
101869acd21dSWarner Losh 	fdc->fdcu = fdcu;
101969acd21dSWarner Losh 	/*
102069acd21dSWarner Losh 	 * the FDC_PCMCIA flag is used to to indicate special PIO is used
102169acd21dSWarner Losh 	 * instead of DMA
102269acd21dSWarner Losh 	 */
102369acd21dSWarner Losh 	fdc->flags = FDC_ATTACHED|FDC_PCMCIA;
102469acd21dSWarner Losh 	fdc->state = DEVIDLE;
102569acd21dSWarner Losh 	/* reset controller, turn motor off, clear fdout mirror reg */
102669acd21dSWarner Losh 	outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
102769acd21dSWarner Losh 	bufq_init(&fdc->head);
102869acd21dSWarner Losh 	/*
102969acd21dSWarner Losh 	 * assume 2 drives/ "normal" controller
103069acd21dSWarner Losh 	 */
103169acd21dSWarner Losh 	fdu = fdcu * 2;
103269acd21dSWarner Losh 	if (fdu >= NFD) {
103369acd21dSWarner Losh 		printf("fdu %d >= NFD\n",fdu);
103469acd21dSWarner Losh 		return(0);
103569acd21dSWarner Losh 	};
103669acd21dSWarner Losh 	fd = &fd_data[fdu];
103769acd21dSWarner Losh 
103869acd21dSWarner Losh 	set_motor(fdcu, fdsu, TURNON);
103969acd21dSWarner Losh 	DELAY(1000000); /* 1 sec */
104069acd21dSWarner Losh 	fdc->fdct = FDC_NE765;
104169acd21dSWarner Losh 
104269acd21dSWarner Losh 	if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) &&
104369acd21dSWarner Losh 		(st3 & NE7_ST3_T0)) {
104469acd21dSWarner Losh 		/* if at track 0, first seek inwards */
104569acd21dSWarner Losh 		/* seek some steps: */
104669acd21dSWarner Losh 		(void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0);
104769acd21dSWarner Losh 		DELAY(300000); /* ...wait a moment... */
104869acd21dSWarner Losh 		(void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
104969acd21dSWarner Losh 	}
105069acd21dSWarner Losh 
105169acd21dSWarner Losh 	/* If we're at track 0 first seek inwards. */
105269acd21dSWarner Losh 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
105369acd21dSWarner Losh 		/* Seek some steps... */
105469acd21dSWarner Losh 		if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
105569acd21dSWarner Losh 			/* ...wait a moment... */
105669acd21dSWarner Losh 			DELAY(300000);
105769acd21dSWarner Losh 			/* make ctrlr happy: */
105869acd21dSWarner Losh 			(void)fd_sense_int(fdc, 0, 0);
105969acd21dSWarner Losh 		}
106069acd21dSWarner Losh 	}
106169acd21dSWarner Losh 
106269acd21dSWarner Losh 	for(i = 0; i < 2; i++) {
106369acd21dSWarner Losh 		/*
106469acd21dSWarner Losh 		 * we must recalibrate twice, just in case the
106569acd21dSWarner Losh 		 * heads have been beyond cylinder 76, since most
106669acd21dSWarner Losh 		 * FDCs still barf when attempting to recalibrate
106769acd21dSWarner Losh 		 * more than 77 steps
106869acd21dSWarner Losh 		 */
106969acd21dSWarner Losh 		/* go back to 0: */
107069acd21dSWarner Losh 		if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
107169acd21dSWarner Losh 			/* a second being enough for full stroke seek*/
107269acd21dSWarner Losh 			DELAY(i == 0? 1000000: 300000);
107369acd21dSWarner Losh 
107469acd21dSWarner Losh 			/* anything responding? */
107569acd21dSWarner Losh 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
107669acd21dSWarner Losh 				(st0 & NE7_ST0_EC) == 0)
107769acd21dSWarner Losh 				break; /* already probed succesfully */
107869acd21dSWarner Losh 		}
107969acd21dSWarner Losh 	}
108069acd21dSWarner Losh 
108169acd21dSWarner Losh 	set_motor(fdcu, fdsu, TURNOFF);
108269acd21dSWarner Losh 
108369acd21dSWarner Losh 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
108469acd21dSWarner Losh 		return(0);
108569acd21dSWarner Losh 
108669acd21dSWarner Losh 	fd->track = FD_NO_TRACK;
108769acd21dSWarner Losh 	fd->fdc = fdc;
108869acd21dSWarner Losh 	fd->fdsu = fdsu;
108969acd21dSWarner Losh 	fd->options = 0;
109069acd21dSWarner Losh 	printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu);
109169acd21dSWarner Losh 	fd->type = FD_1440;
109269acd21dSWarner Losh 
109369acd21dSWarner Losh #ifdef DEVFS
109469acd21dSWarner Losh 	mynor = fdcu << 6;
1095ff9607b0SBruce Evans 	fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK,
109669acd21dSWarner Losh 		UID_ROOT, GID_OPERATOR, 0640,
109769acd21dSWarner Losh 		"fd%d", fdu);
109869acd21dSWarner Losh 	fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR,
109969acd21dSWarner Losh 		UID_ROOT, GID_OPERATOR, 0640,
110069acd21dSWarner Losh 		"rfd%d", fdu);
110169acd21dSWarner Losh 	/*
110269acd21dSWarner Losh 	 * XXX this and the lookup in Fdopen() should be
110369acd21dSWarner Losh 	 * data driven.
110469acd21dSWarner Losh 	 */
110569acd21dSWarner Losh 	typemynor = mynor | FD_1440;
110669acd21dSWarner Losh 	typesize = fd_types[FD_1440 - 1].size / 2;
110769acd21dSWarner Losh 	/*
110869acd21dSWarner Losh 	 * XXX all these conversions give bloated code and
110969acd21dSWarner Losh 	 * confusing names.
111069acd21dSWarner Losh 	 */
111169acd21dSWarner Losh 	if (typesize == 1476)
111269acd21dSWarner Losh 		typesize = 1480;
111369acd21dSWarner Losh 	if (typesize == 1722)
111469acd21dSWarner Losh 		typesize = 1720;
1115ff9607b0SBruce Evans 	fd->bdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor,
111669acd21dSWarner Losh 		DV_BLK, UID_ROOT, GID_OPERATOR,
111769acd21dSWarner Losh 		0640, "fd%d.%d", fdu, typesize);
111869acd21dSWarner Losh 	fd->cdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor,
111969acd21dSWarner Losh 		DV_CHR, UID_ROOT, GID_OPERATOR,
112069acd21dSWarner Losh 		0640,"rfd%d.%d", fdu, typesize);
112169acd21dSWarner Losh 	for (i = 0; i < MAXPARTITIONS; i++) {
1122ff9607b0SBruce Evans 		fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0],
112369acd21dSWarner Losh 			"fd%d%c", fdu, 'a' + i);
1124ff9607b0SBruce Evans 		fd->cdevs[1 + NUMDENS + i] = devfs_makelink(fd->cdevs[0],
112569acd21dSWarner Losh 			"rfd%d%c", fdu, 'a' + i);
112669acd21dSWarner Losh 	}
112769acd21dSWarner Losh #endif /* DEVFS */
112869acd21dSWarner Losh 	return (1);
112969acd21dSWarner Losh }
113069acd21dSWarner Losh #endif
113169acd21dSWarner Losh 
11325b81b6b3SRodney W. Grimes /****************************************************************************/
11335b81b6b3SRodney W. Grimes /*                            motor control stuff                           */
11345e235068SJordan K. Hubbard /*		remember to not deselect the drive we're working on         */
11355b81b6b3SRodney W. Grimes /****************************************************************************/
11363a2f7427SDavid Greenman static void
11376182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon)
11385b81b6b3SRodney W. Grimes {
11396182fdbdSPeter Wemm 	int fdout = fdc->fdout;
11403a2f7427SDavid Greenman 	int needspecify = 0;
11413a2f7427SDavid Greenman 
11423a2f7427SDavid Greenman 	if(turnon) {
11433a2f7427SDavid Greenman 		fdout &= ~FDO_FDSEL;
11443a2f7427SDavid Greenman 		fdout |= (FDO_MOEN0 << fdsu) + fdsu;
11453a2f7427SDavid Greenman 	} else
11463a2f7427SDavid Greenman 		fdout &= ~(FDO_MOEN0 << fdsu);
11473a2f7427SDavid Greenman 
11485e235068SJordan K. Hubbard 	if(!turnon
11495e235068SJordan K. Hubbard 	   && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0)
11505e235068SJordan K. Hubbard 		/* gonna turn off the last drive, put FDC to bed */
11515e235068SJordan K. Hubbard 		fdout &= ~ (FDO_FRST|FDO_FDMAEN);
11525e235068SJordan K. Hubbard 	else {
11533a2f7427SDavid Greenman 		/* make sure controller is selected and specified */
11543a2f7427SDavid Greenman 		if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0)
11553a2f7427SDavid Greenman 			needspecify = 1;
11563a2f7427SDavid Greenman 		fdout |= (FDO_FRST|FDO_FDMAEN);
11575b81b6b3SRodney W. Grimes 	}
11585b81b6b3SRodney W. Grimes 
11596182fdbdSPeter Wemm 	outb(fdc->baseport+FDOUT, fdout);
11606182fdbdSPeter Wemm 	fdc->fdout = fdout;
11613a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdout);
11623a2f7427SDavid Greenman 
11633a2f7427SDavid Greenman 	if (needspecify) {
1164dc8603e3SJoerg Wunsch 		/*
1165dc5df763SJoerg Wunsch 		 * XXX
1166dc8603e3SJoerg Wunsch 		 * special case: since we have just woken up the FDC
1167dc8603e3SJoerg Wunsch 		 * from its sleep, we silently assume the command will
1168dc8603e3SJoerg Wunsch 		 * be accepted, and do not test for a timeout
1169dc8603e3SJoerg Wunsch 		 */
11706182fdbdSPeter Wemm 		(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1171dc5df763SJoerg Wunsch 			     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1172dc5df763SJoerg Wunsch 			     0);
11736182fdbdSPeter Wemm 		if (fdc->flags & FDC_HAS_FIFO)
11746182fdbdSPeter Wemm 			(void) enable_fifo(fdc);
11753a2f7427SDavid Greenman 	}
11763a2f7427SDavid Greenman }
11773a2f7427SDavid Greenman 
1178381fe1aaSGarrett Wollman static void
11796182fdbdSPeter Wemm fd_turnoff(void *xfd)
11805b81b6b3SRodney W. Grimes {
1181f5f7ba03SJordan K. Hubbard 	int	s;
11826182fdbdSPeter Wemm 	fd_p fd = xfd;
1183dc16046fSJoerg Wunsch 
11846182fdbdSPeter Wemm 	TRACE1("[fd%d: turnoff]", fd->fdu);
11858335c1b8SBruce Evans 
11868335c1b8SBruce Evans 	/*
11878335c1b8SBruce Evans 	 * Don't turn off the motor yet if the drive is active.
11888335c1b8SBruce Evans 	 * XXX shouldn't even schedule turnoff until drive is inactive
11898335c1b8SBruce Evans 	 * and nothing is queued on it.
11908335c1b8SBruce Evans 	 */
11916182fdbdSPeter Wemm 	if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) {
11926182fdbdSPeter Wemm 		fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
11938335c1b8SBruce Evans 		return;
11948335c1b8SBruce Evans 	}
11958335c1b8SBruce Evans 
1196f5f7ba03SJordan K. Hubbard 	s = splbio();
11975b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR;
11986182fdbdSPeter Wemm 	set_motor(fd->fdc, fd->fdsu, TURNOFF);
1199f5f7ba03SJordan K. Hubbard 	splx(s);
12005b81b6b3SRodney W. Grimes }
12015b81b6b3SRodney W. Grimes 
12023a2f7427SDavid Greenman static void
12036182fdbdSPeter Wemm fd_motor_on(void *xfd)
12045b81b6b3SRodney W. Grimes {
1205f5f7ba03SJordan K. Hubbard 	int	s;
12066182fdbdSPeter Wemm 	fd_p fd = xfd;
1207f5f7ba03SJordan K. Hubbard 
1208f5f7ba03SJordan K. Hubbard 	s = splbio();
12095b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR_WAIT;
12105b81b6b3SRodney W. Grimes 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
12115b81b6b3SRodney W. Grimes 	{
12126182fdbdSPeter Wemm 		fdc_intr(fd->fdc);
12135b81b6b3SRodney W. Grimes 	}
1214f5f7ba03SJordan K. Hubbard 	splx(s);
12155b81b6b3SRodney W. Grimes }
12165b81b6b3SRodney W. Grimes 
12173a2f7427SDavid Greenman static void
12186182fdbdSPeter Wemm fd_turnon(fd_p fd)
12195b81b6b3SRodney W. Grimes {
12205b81b6b3SRodney W. Grimes 	if(!(fd->flags & FD_MOTOR))
12215b81b6b3SRodney W. Grimes 	{
12223a2f7427SDavid Greenman 		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
12236182fdbdSPeter Wemm 		set_motor(fd->fdc, fd->fdsu, TURNON);
12246182fdbdSPeter Wemm 		timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */
12255b81b6b3SRodney W. Grimes 	}
12265b81b6b3SRodney W. Grimes }
12275b81b6b3SRodney W. Grimes 
1228381fe1aaSGarrett Wollman static void
1229dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc)
12305b81b6b3SRodney W. Grimes {
12313a2f7427SDavid Greenman 	/* Try a reset, keep motor on */
12323a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
12333a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
12343a2f7427SDavid Greenman 	DELAY(100);
12353a2f7427SDavid Greenman 	/* enable FDC, but defer interrupts a moment */
12363a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN);
12373a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
12383a2f7427SDavid Greenman 	DELAY(100);
12393a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout);
12403a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout);
12413a2f7427SDavid Greenman 
1242dc5df763SJoerg Wunsch 	/* XXX after a reset, silently believe the FDC will accept commands */
12436182fdbdSPeter Wemm 	(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1244dc5df763SJoerg Wunsch 		     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1245dc5df763SJoerg Wunsch 		     0);
1246d66c374fSTor Egge 	if (fdc->flags & FDC_HAS_FIFO)
1247d66c374fSTor Egge 		(void) enable_fifo(fdc);
12485b81b6b3SRodney W. Grimes }
12495b81b6b3SRodney W. Grimes 
12505b81b6b3SRodney W. Grimes /****************************************************************************/
12515b81b6b3SRodney W. Grimes /*                             fdc in/out                                   */
12525b81b6b3SRodney W. Grimes /****************************************************************************/
12535b81b6b3SRodney W. Grimes int
12546182fdbdSPeter Wemm in_fdc(struct fdc_data *fdc)
12555b81b6b3SRodney W. Grimes {
12566182fdbdSPeter Wemm 	int baseport = fdc->baseport;
12575b81b6b3SRodney W. Grimes 	int i, j = 100000;
12583a2f7427SDavid Greenman 	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
12595b81b6b3SRodney W. Grimes 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1260dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
12616182fdbdSPeter Wemm 			return fdc_err(fdc, "ready for output in input\n");
12625b81b6b3SRodney W. Grimes 	if (j <= 0)
12636182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "input ready timeout\n": 0);
1264d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
12653a2f7427SDavid Greenman 	i = inb(baseport+FDDATA);
12663a2f7427SDavid Greenman 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
12675b81b6b3SRodney W. Grimes 	return(i);
1268d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
12693a2f7427SDavid Greenman 	return inb(baseport+FDDATA);
1270d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
12715b81b6b3SRodney W. Grimes }
12725b81b6b3SRodney W. Grimes 
1273dc5df763SJoerg Wunsch /*
1274dc5df763SJoerg Wunsch  * fd_in: Like in_fdc, but allows you to see if it worked.
1275dc5df763SJoerg Wunsch  */
1276b5e8ce9fSBruce Evans static int
12776182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr)
1278dc5df763SJoerg Wunsch {
12796182fdbdSPeter Wemm 	int baseport = fdc->baseport;
1280dc5df763SJoerg Wunsch 	int i, j = 100000;
1281dc5df763SJoerg Wunsch 	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
1282dc5df763SJoerg Wunsch 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1283dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
12846182fdbdSPeter Wemm 			return fdc_err(fdc, "ready for output in input\n");
1285dc5df763SJoerg Wunsch 	if (j <= 0)
12866182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "input ready timeout\n": 0);
1287d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
1288dc5df763SJoerg Wunsch 	i = inb(baseport+FDDATA);
1289dc5df763SJoerg Wunsch 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
1290dc5df763SJoerg Wunsch 	*ptr = i;
1291dc5df763SJoerg Wunsch 	return 0;
1292d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
1293dc5df763SJoerg Wunsch 	i = inb(baseport+FDDATA);
1294dc5df763SJoerg Wunsch 	if (ptr)
1295dc5df763SJoerg Wunsch 		*ptr = i;
1296dc5df763SJoerg Wunsch 	return 0;
1297d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
1298dc5df763SJoerg Wunsch }
1299dc5df763SJoerg Wunsch 
1300dc5df763SJoerg Wunsch int
13016182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x)
13025b81b6b3SRodney W. Grimes {
13036182fdbdSPeter Wemm 	int baseport = fdc->baseport;
13043b3837dbSRodney W. Grimes 	int i;
13055b81b6b3SRodney W. Grimes 
13063b3837dbSRodney W. Grimes 	/* Check that the direction bit is set */
13073b3837dbSRodney W. Grimes 	i = 100000;
13083a2f7427SDavid Greenman 	while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0);
13096182fdbdSPeter Wemm 	if (i <= 0) return fdc_err(fdc, "direction bit not set\n");
13103b3837dbSRodney W. Grimes 
13113b3837dbSRodney W. Grimes 	/* Check that the floppy controller is ready for a command */
13123b3837dbSRodney W. Grimes 	i = 100000;
13133a2f7427SDavid Greenman 	while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0);
131416b04b6aSJoerg Wunsch 	if (i <= 0)
13156182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "output ready timeout\n": 0);
13163b3837dbSRodney W. Grimes 
13173b3837dbSRodney W. Grimes 	/* Send the command and return */
13183a2f7427SDavid Greenman 	outb(baseport+FDDATA, x);
13193a2f7427SDavid Greenman 	TRACE1("[0x%x->FDDATA]", x);
13205b81b6b3SRodney W. Grimes 	return (0);
13215b81b6b3SRodney W. Grimes }
13225b81b6b3SRodney W. Grimes 
13235b81b6b3SRodney W. Grimes /****************************************************************************/
13245b81b6b3SRodney W. Grimes /*                           fdopen/fdclose                                 */
13255b81b6b3SRodney W. Grimes /****************************************************************************/
1326381fe1aaSGarrett Wollman int
1327671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p)
13285b81b6b3SRodney W. Grimes {
13295b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
133020a29168SAndrey A. Chernov 	int type = FDTYPE(minor(dev));
13316182fdbdSPeter Wemm 	fd_p	fd;
1332b99f0a4aSAndrew Moore 	fdc_p	fdc;
13335b81b6b3SRodney W. Grimes 
13345b81b6b3SRodney W. Grimes 	/* check bounds */
13356182fdbdSPeter Wemm 	if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0)
1336b99f0a4aSAndrew Moore 		return (ENXIO);
13376182fdbdSPeter Wemm 	fdc = fd->fdc;
13386182fdbdSPeter Wemm 	if ((fdc == NULL) || (fd->type == NO_TYPE))
1339b99f0a4aSAndrew Moore 		return (ENXIO);
1340b99f0a4aSAndrew Moore 	if (type > NUMDENS)
1341b99f0a4aSAndrew Moore 		return (ENXIO);
13427ca0641bSAndrey A. Chernov 	if (type == 0)
13436182fdbdSPeter Wemm 		type = fd->type;
13447ca0641bSAndrey A. Chernov 	else {
13453e425b96SJulian Elischer 		/*
13463e425b96SJulian Elischer 		 * For each type of basic drive, make sure we are trying
13473e425b96SJulian Elischer 		 * to open a type it can do,
13483e425b96SJulian Elischer 		 */
13496182fdbdSPeter Wemm 		if (type != fd->type) {
13506182fdbdSPeter Wemm 			switch (fd->type) {
13517ca0641bSAndrey A. Chernov 			case FD_360:
13527ca0641bSAndrey A. Chernov 				return (ENXIO);
1353ed2fa05eSAndrey A. Chernov 			case FD_720:
1354b39c878eSAndrey A. Chernov 				if (   type != FD_820
1355b39c878eSAndrey A. Chernov 				    && type != FD_800
1356ed2fa05eSAndrey A. Chernov 				   )
1357ed2fa05eSAndrey A. Chernov 					return (ENXIO);
1358ed2fa05eSAndrey A. Chernov 				break;
13597ca0641bSAndrey A. Chernov 			case FD_1200:
1360b39c878eSAndrey A. Chernov 				switch (type) {
1361b39c878eSAndrey A. Chernov 				case FD_1480:
1362b39c878eSAndrey A. Chernov 					type = FD_1480in5_25;
1363fa4700b4SAndrey A. Chernov 					break;
13647ca0641bSAndrey A. Chernov 				case FD_1440:
1365b39c878eSAndrey A. Chernov 					type = FD_1440in5_25;
1366b39c878eSAndrey A. Chernov 					break;
1367b39c878eSAndrey A. Chernov 				case FD_820:
1368b39c878eSAndrey A. Chernov 					type = FD_820in5_25;
1369b39c878eSAndrey A. Chernov 					break;
1370b39c878eSAndrey A. Chernov 				case FD_800:
1371b39c878eSAndrey A. Chernov 					type = FD_800in5_25;
1372b39c878eSAndrey A. Chernov 					break;
1373b39c878eSAndrey A. Chernov 				case FD_720:
1374b39c878eSAndrey A. Chernov 					type = FD_720in5_25;
1375b39c878eSAndrey A. Chernov 					break;
1376b39c878eSAndrey A. Chernov 				case FD_360:
1377b39c878eSAndrey A. Chernov 					type = FD_360in5_25;
1378b39c878eSAndrey A. Chernov 					break;
1379b39c878eSAndrey A. Chernov 				default:
1380b39c878eSAndrey A. Chernov 					return(ENXIO);
1381b39c878eSAndrey A. Chernov 				}
1382b39c878eSAndrey A. Chernov 				break;
1383b39c878eSAndrey A. Chernov 			case FD_1440:
1384b39c878eSAndrey A. Chernov 				if (   type != FD_1720
1385b39c878eSAndrey A. Chernov 				    && type != FD_1480
1386ed2fa05eSAndrey A. Chernov 				    && type != FD_1200
1387b39c878eSAndrey A. Chernov 				    && type != FD_820
1388b39c878eSAndrey A. Chernov 				    && type != FD_800
1389b39c878eSAndrey A. Chernov 				    && type != FD_720
13907ca0641bSAndrey A. Chernov 				    )
1391dffff499SAndrey A. Chernov 					return(ENXIO);
1392fa4700b4SAndrey A. Chernov 				break;
13937ca0641bSAndrey A. Chernov 			}
13947ca0641bSAndrey A. Chernov 		}
1395fa4700b4SAndrey A. Chernov 	}
13966182fdbdSPeter Wemm 	fd->ft = fd_types + type - 1;
13976182fdbdSPeter Wemm 	fd->flags |= FD_OPEN;
13986182fdbdSPeter Wemm 	device_busy(fd->dev);
13996182fdbdSPeter Wemm 	device_busy(fd->fdc->fdc_dev);
14005b81b6b3SRodney W. Grimes 	return 0;
14015b81b6b3SRodney W. Grimes }
14025b81b6b3SRodney W. Grimes 
1403381fe1aaSGarrett Wollman int
1404671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p)
14055b81b6b3SRodney W. Grimes {
14065b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
14076182fdbdSPeter Wemm 	struct fd_data *fd;
1408b99f0a4aSAndrew Moore 
14096182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
14106182fdbdSPeter Wemm 	fd->flags &= ~FD_OPEN;
14116182fdbdSPeter Wemm 	fd->options &= ~FDOPT_NORETRY;
1412dc16046fSJoerg Wunsch 
14135b81b6b3SRodney W. Grimes 	return (0);
14145b81b6b3SRodney W. Grimes }
14155b81b6b3SRodney W. Grimes 
14163a2f7427SDavid Greenman /****************************************************************************/
14173a2f7427SDavid Greenman /*                               fdstrategy                                 */
14183a2f7427SDavid Greenman /****************************************************************************/
14193a2f7427SDavid Greenman void
14203a2f7427SDavid Greenman fdstrategy(struct buf *bp)
14213a2f7427SDavid Greenman {
1422bb6382faSJoerg Wunsch 	unsigned nblocks, blknum, cando;
14233a2f7427SDavid Greenman  	int	s;
14243a2f7427SDavid Greenman  	fdu_t	fdu;
14253a2f7427SDavid Greenman  	fdc_p	fdc;
14263a2f7427SDavid Greenman  	fd_p	fd;
14273a2f7427SDavid Greenman 	size_t	fdblk;
14283a2f7427SDavid Greenman 
14293a2f7427SDavid Greenman  	fdu = FDUNIT(minor(bp->b_dev));
14306182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
14316182fdbdSPeter Wemm 	if (fd == 0)
14326182fdbdSPeter Wemm 		panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)",
14336182fdbdSPeter Wemm 		      (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev));
14343a2f7427SDavid Greenman 	fdc = fd->fdc;
143569acd21dSWarner Losh #ifdef FDC_YE
143669acd21dSWarner Losh 	if (fd->type == NO_TYPE) {
143769acd21dSWarner Losh 		bp->b_error = ENXIO;
143869acd21dSWarner Losh 		bp->b_flags |= B_ERROR;
143969acd21dSWarner Losh 		/*
144069acd21dSWarner Losh 		 * I _refuse_ to use a goto
144169acd21dSWarner Losh 		 */
144269acd21dSWarner Losh 		biodone(bp);
144369acd21dSWarner Losh 		return;
144469acd21dSWarner Losh 	};
144569acd21dSWarner Losh #endif
14463a2f7427SDavid Greenman 
1447d3628763SRodney W. Grimes 	fdblk = 128 << (fd->ft->secsize);
14483a2f7427SDavid Greenman 	if (!(bp->b_flags & B_FORMAT)) {
14496182fdbdSPeter Wemm 		if (bp->b_blkno < 0) {
1450dc5df763SJoerg Wunsch 			printf(
14516a0e6f42SRodney W. Grimes 		"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
1452702c623aSPoul-Henning Kamp 			       fdu, (u_long)bp->b_blkno, bp->b_bcount);
14533a2f7427SDavid Greenman 			bp->b_error = EINVAL;
14543a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
14553a2f7427SDavid Greenman 			goto bad;
14563a2f7427SDavid Greenman 		}
14573a2f7427SDavid Greenman 		if ((bp->b_bcount % fdblk) != 0) {
14583a2f7427SDavid Greenman 			bp->b_error = EINVAL;
14593a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
14603a2f7427SDavid Greenman 			goto bad;
14613a2f7427SDavid Greenman 		}
14623a2f7427SDavid Greenman 	}
14633a2f7427SDavid Greenman 
14643a2f7427SDavid Greenman 	/*
14653a2f7427SDavid Greenman 	 * Set up block calculations.
14663a2f7427SDavid Greenman 	 */
1467bb6382faSJoerg Wunsch 	if (bp->b_blkno > 20000000) {
1468bb6382faSJoerg Wunsch 		/*
1469bb6382faSJoerg Wunsch 		 * Reject unreasonably high block number, prevent the
1470bb6382faSJoerg Wunsch 		 * multiplication below from overflowing.
1471bb6382faSJoerg Wunsch 		 */
1472bb6382faSJoerg Wunsch 		bp->b_error = EINVAL;
14733a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
14743a2f7427SDavid Greenman 		goto bad;
14753a2f7427SDavid Greenman 	}
1476bb6382faSJoerg Wunsch 	blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk;
1477bb6382faSJoerg Wunsch  	nblocks = fd->ft->size;
1478bb6382faSJoerg Wunsch 	bp->b_resid = 0;
1479bb6382faSJoerg Wunsch 	if (blknum + (bp->b_bcount / fdblk) > nblocks) {
1480bb6382faSJoerg Wunsch 		if (blknum <= nblocks) {
1481bb6382faSJoerg Wunsch 			cando = (nblocks - blknum) * fdblk;
1482bb6382faSJoerg Wunsch 			bp->b_resid = bp->b_bcount - cando;
1483bb6382faSJoerg Wunsch 			if (cando == 0)
1484bb6382faSJoerg Wunsch 				goto bad;	/* not actually bad but EOF */
1485bb6382faSJoerg Wunsch 		} else {
1486bb6382faSJoerg Wunsch 			bp->b_error = EINVAL;
1487bb6382faSJoerg Wunsch 			bp->b_flags |= B_ERROR;
1488bb6382faSJoerg Wunsch 			goto bad;
1489bb6382faSJoerg Wunsch 		}
1490bb6382faSJoerg Wunsch 	}
149127513ca7SBruce Evans  	bp->b_pblkno = bp->b_blkno;
14923a2f7427SDavid Greenman 	s = splbio();
149302a19910SJustin T. Gibbs 	bufqdisksort(&fdc->head, bp);
14946182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */
1495b2dfb1f9SJustin T. Gibbs 
1496b2dfb1f9SJustin T. Gibbs 	/* Tell devstat we are starting on the transaction */
1497b2dfb1f9SJustin T. Gibbs 	devstat_start_transaction(&fd->device_stats);
1498b2dfb1f9SJustin T. Gibbs 
14996182fdbdSPeter Wemm 	fdstart(fdc);
15003a2f7427SDavid Greenman 	splx(s);
15013a2f7427SDavid Greenman 	return;
15023a2f7427SDavid Greenman 
15033a2f7427SDavid Greenman bad:
15043a2f7427SDavid Greenman 	biodone(bp);
15053a2f7427SDavid Greenman }
15063a2f7427SDavid Greenman 
15075b81b6b3SRodney W. Grimes /***************************************************************\
15085b81b6b3SRodney W. Grimes *				fdstart				*
15095b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy	*
15105b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command	*
15115b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more*
15125b81b6b3SRodney W. Grimes * work to do and picks up what we just added.			*
15135b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it	*
15145b81b6b3SRodney W. Grimes * will pick up our work when the present work completes		*
15155b81b6b3SRodney W. Grimes \***************************************************************/
1516381fe1aaSGarrett Wollman static void
15176182fdbdSPeter Wemm fdstart(struct fdc_data *fdc)
15185b81b6b3SRodney W. Grimes {
15195b81b6b3SRodney W. Grimes 	int s;
15205b81b6b3SRodney W. Grimes 
15215b81b6b3SRodney W. Grimes 	s = splbio();
15226182fdbdSPeter Wemm 	if(fdc->state == DEVIDLE)
15235b81b6b3SRodney W. Grimes 	{
15246182fdbdSPeter Wemm 		fdc_intr(fdc);
15255b81b6b3SRodney W. Grimes 	}
15265b81b6b3SRodney W. Grimes 	splx(s);
15275b81b6b3SRodney W. Grimes }
15285b81b6b3SRodney W. Grimes 
1529381fe1aaSGarrett Wollman static void
15306182fdbdSPeter Wemm fd_iotimeout(void *xfdc)
15315b81b6b3SRodney W. Grimes {
15325c1a1eaeSBruce Evans  	fdc_p fdc;
1533f5f7ba03SJordan K. Hubbard 	int s;
15345b81b6b3SRodney W. Grimes 
15356182fdbdSPeter Wemm 	fdc = xfdc;
15365c1a1eaeSBruce Evans 	TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
15375b81b6b3SRodney W. Grimes 
15383a2f7427SDavid Greenman 	/*
15393a2f7427SDavid Greenman 	 * Due to IBM's brain-dead design, the FDC has a faked ready
15403a2f7427SDavid Greenman 	 * signal, hardwired to ready == true. Thus, any command
15413a2f7427SDavid Greenman 	 * issued if there's no diskette in the drive will _never_
15423a2f7427SDavid Greenman 	 * complete, and must be aborted by resetting the FDC.
15433a2f7427SDavid Greenman 	 * Many thanks, Big Blue!
15445c1a1eaeSBruce Evans 	 * The FDC must not be reset directly, since that would
15455c1a1eaeSBruce Evans 	 * interfere with the state machine.  Instead, pretend that
15465c1a1eaeSBruce Evans 	 * the command completed but was invalid.  The state machine
15475c1a1eaeSBruce Evans 	 * will reset the FDC and retry once.
15483a2f7427SDavid Greenman 	 */
15493a2f7427SDavid Greenman 	s = splbio();
15505c1a1eaeSBruce Evans 	fdc->status[0] = NE7_ST0_IC_IV;
15515c1a1eaeSBruce Evans 	fdc->flags &= ~FDC_STAT_VALID;
15525c1a1eaeSBruce Evans 	fdc->state = IOTIMEDOUT;
15536182fdbdSPeter Wemm 	fdc_intr(fdc);
1554f5f7ba03SJordan K. Hubbard 	splx(s);
15555b81b6b3SRodney W. Grimes }
15565b81b6b3SRodney W. Grimes 
15575b81b6b3SRodney W. Grimes /* just ensure it has the right spl */
1558381fe1aaSGarrett Wollman static void
15596182fdbdSPeter Wemm fd_pseudointr(void *xfdc)
15605b81b6b3SRodney W. Grimes {
15615b81b6b3SRodney W. Grimes 	int	s;
15623a2f7427SDavid Greenman 
15635b81b6b3SRodney W. Grimes 	s = splbio();
15646182fdbdSPeter Wemm 	fdc_intr(xfdc);
15655b81b6b3SRodney W. Grimes 	splx(s);
15665b81b6b3SRodney W. Grimes }
15675b81b6b3SRodney W. Grimes 
15685b81b6b3SRodney W. Grimes /***********************************************************************\
15695b81b6b3SRodney W. Grimes *                                 fdintr				*
15705b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0			*
15715b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO 						*
15725b81b6b3SRodney W. Grimes \***********************************************************************/
1573fe310de8SBruce Evans static void
15746182fdbdSPeter Wemm fdc_intr(void *xfdc)
15755b81b6b3SRodney W. Grimes {
15766182fdbdSPeter Wemm 	fdc_p fdc = xfdc;
15776182fdbdSPeter Wemm 	while(fdstate(fdc))
1578381fe1aaSGarrett Wollman 		;
15795b81b6b3SRodney W. Grimes }
15805b81b6b3SRodney W. Grimes 
158169acd21dSWarner Losh #ifdef FDC_YE
158269acd21dSWarner Losh /*
158369acd21dSWarner Losh  * magic pseudo-DMA initialization for YE FDC. Sets count and
158469acd21dSWarner Losh  * direction
158569acd21dSWarner Losh  */
158669acd21dSWarner Losh #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \
158769acd21dSWarner Losh 	outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f)))
158869acd21dSWarner Losh 
158969acd21dSWarner Losh /*
159069acd21dSWarner Losh  * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy
159169acd21dSWarner Losh  */
159269acd21dSWarner Losh static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count)
159369acd21dSWarner Losh {
159469acd21dSWarner Losh 	u_char *cptr = (u_char *)addr;
159569acd21dSWarner Losh 	fdc_p fdc = &fdc_data[fdcu];
159669acd21dSWarner Losh 	int io = fdc->baseport;
159769acd21dSWarner Losh 
159869acd21dSWarner Losh 	if (flags & B_READ) {
159969acd21dSWarner Losh 		if (fdc->state != PIOREAD) {
160069acd21dSWarner Losh 			fdc->state = PIOREAD;
160169acd21dSWarner Losh 			return(0);
160269acd21dSWarner Losh 		};
160369acd21dSWarner Losh 		SET_BCDR(0,count,io);
160469acd21dSWarner Losh 		insb(io+FDC_YE_DATAPORT,cptr,count);
160569acd21dSWarner Losh 	} else {
160669acd21dSWarner Losh 		outsb(io+FDC_YE_DATAPORT,cptr,count);
160769acd21dSWarner Losh 		SET_BCDR(0,count,io);
160869acd21dSWarner Losh 	};
160969acd21dSWarner Losh 	return(1);
161069acd21dSWarner Losh }
161169acd21dSWarner Losh #endif /* FDC_YE */
161269acd21dSWarner Losh 
16135b81b6b3SRodney W. Grimes /***********************************************************************\
16145b81b6b3SRodney W. Grimes * The controller state machine.						*
16155b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly	*
16165b81b6b3SRodney W. Grimes \***********************************************************************/
16173a2f7427SDavid Greenman static int
16186182fdbdSPeter Wemm fdstate(fdc_p fdc)
16195b81b6b3SRodney W. Grimes {
16205c1a1eaeSBruce Evans 	int read, format, head, i, sec = 0, sectrac, st0, cyl, st3;
1621bb6382faSJoerg Wunsch 	unsigned blknum = 0, b_cylinder = 0;
16225b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc->fdu;
16235b81b6b3SRodney W. Grimes 	fd_p fd;
162417542807SPoul-Henning Kamp 	register struct buf *bp;
1625b39c878eSAndrey A. Chernov 	struct fd_formb *finfo = NULL;
16263a2f7427SDavid Greenman 	size_t fdblk;
16275b81b6b3SRodney W. Grimes 
1628e93e63cbSBruce Evans 	bp = fdc->bp;
1629e93e63cbSBruce Evans 	if (bp == NULL) {
163002a19910SJustin T. Gibbs 		bp = bufq_first(&fdc->head);
1631e93e63cbSBruce Evans 		if (bp != NULL) {
1632e93e63cbSBruce Evans 			bufq_remove(&fdc->head, bp);
1633e93e63cbSBruce Evans 			fdc->bp = bp;
1634e93e63cbSBruce Evans 		}
1635e93e63cbSBruce Evans 	}
1636e93e63cbSBruce Evans 	if (bp == NULL) {
16375b81b6b3SRodney W. Grimes 		/***********************************************\
16385b81b6b3SRodney W. Grimes 		* nothing left for this controller to do	*
16395b81b6b3SRodney W. Grimes 		* Force into the IDLE state,			*
16405b81b6b3SRodney W. Grimes 		\***********************************************/
16415b81b6b3SRodney W. Grimes 		fdc->state = DEVIDLE;
16426182fdbdSPeter Wemm 		if (fdc->fd) {
16436182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
16446182fdbdSPeter Wemm 			printf("unexpected valid fd pointer\n");
16455b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
16465b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
16475b81b6b3SRodney W. Grimes 		}
16486182fdbdSPeter Wemm 		TRACE1("[fdc%d IDLE]", fdc->fdcu);
16495b81b6b3SRodney W. Grimes  		return (0);
16505b81b6b3SRodney W. Grimes 	}
16515b81b6b3SRodney W. Grimes 	fdu = FDUNIT(minor(bp->b_dev));
16526182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
16533a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
16546182fdbdSPeter Wemm 	if (fdc->fd && (fd != fdc->fd)) {
16556182fdbdSPeter Wemm 		device_print_prettyname(fd->dev);
16566182fdbdSPeter Wemm 		printf("confused fd pointers\n");
16575b81b6b3SRodney W. Grimes 	}
16585b81b6b3SRodney W. Grimes 	read = bp->b_flags & B_READ;
1659b39c878eSAndrey A. Chernov 	format = bp->b_flags & B_FORMAT;
1660bb6382faSJoerg Wunsch 	if (format) {
1661ab3f7469SPoul-Henning Kamp 		finfo = (struct fd_formb *)bp->b_data;
1662bb6382faSJoerg Wunsch 		fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1663bb6382faSJoerg Wunsch 			- (char *)finfo;
1664bb6382faSJoerg Wunsch 	}
1665bb6382faSJoerg Wunsch 	if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) {
16663e425b96SJulian Elischer 		blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk +
1667bb6382faSJoerg Wunsch 			fd->skip/fdblk;
1668bb6382faSJoerg Wunsch 		b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1669bb6382faSJoerg Wunsch 	}
16705b81b6b3SRodney W. Grimes 	TRACE1("fd%d", fdu);
16715b81b6b3SRodney W. Grimes 	TRACE1("[%s]", fdstates[fdc->state]);
16725b81b6b3SRodney W. Grimes 	TRACE1("(0x%x)", fd->flags);
16736182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle);
16746182fdbdSPeter Wemm 	fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
16755b81b6b3SRodney W. Grimes 	switch (fdc->state)
16765b81b6b3SRodney W. Grimes 	{
16775b81b6b3SRodney W. Grimes 	case DEVIDLE:
16785b81b6b3SRodney W. Grimes 	case FINDWORK:	/* we have found new work */
16795b81b6b3SRodney W. Grimes 		fdc->retry = 0;
16805b81b6b3SRodney W. Grimes 		fd->skip = 0;
16815b81b6b3SRodney W. Grimes 		fdc->fd = fd;
16825b81b6b3SRodney W. Grimes 		fdc->fdu = fdu;
16833a2f7427SDavid Greenman 		outb(fdc->baseport+FDCTL, fd->ft->trans);
16843a2f7427SDavid Greenman 		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
16855b81b6b3SRodney W. Grimes 		/*******************************************************\
16865b81b6b3SRodney W. Grimes 		* If the next drive has a motor startup pending, then	*
1687dc733423SDag-Erling Smørgrav 		* it will start up in its own good time		*
16885b81b6b3SRodney W. Grimes 		\*******************************************************/
16896182fdbdSPeter Wemm 		if(fd->flags & FD_MOTOR_WAIT) {
16905b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
16915b81b6b3SRodney W. Grimes 			return (0); /* come back later */
16925b81b6b3SRodney W. Grimes 		}
16935b81b6b3SRodney W. Grimes 		/*******************************************************\
16945b81b6b3SRodney W. Grimes 		* Maybe if it's not starting, it SHOULD be starting	*
16955b81b6b3SRodney W. Grimes 		\*******************************************************/
16965b81b6b3SRodney W. Grimes 		if (!(fd->flags & FD_MOTOR))
16975b81b6b3SRodney W. Grimes 		{
16985b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
16996182fdbdSPeter Wemm 			fd_turnon(fd);
17005b81b6b3SRodney W. Grimes 			return (0);
17015b81b6b3SRodney W. Grimes 		}
17025b81b6b3SRodney W. Grimes 		else	/* at least make sure we are selected */
17035b81b6b3SRodney W. Grimes 		{
17046182fdbdSPeter Wemm 			set_motor(fdc, fd->fdsu, TURNON);
17055b81b6b3SRodney W. Grimes 		}
17065c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
17075c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
17085c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
17095c1a1eaeSBruce Evans 		} else
17105e235068SJordan K. Hubbard 			fdc->state = DOSEEK;
17115b81b6b3SRodney W. Grimes 		break;
17125b81b6b3SRodney W. Grimes 	case DOSEEK:
1713bb6382faSJoerg Wunsch 		if (b_cylinder == (unsigned)fd->track)
17145b81b6b3SRodney W. Grimes 		{
17155b81b6b3SRodney W. Grimes 			fdc->state = SEEKCOMPLETE;
17165b81b6b3SRodney W. Grimes 			break;
17175b81b6b3SRodney W. Grimes 		}
17186182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK,
1719bb6382faSJoerg Wunsch 			   fd->fdsu, b_cylinder * fd->ft->steptrac,
1720dc5df763SJoerg Wunsch 			   0))
1721dc8603e3SJoerg Wunsch 		{
1722dc8603e3SJoerg Wunsch 			/*
1723dc8603e3SJoerg Wunsch 			 * seek command not accepted, looks like
1724dc8603e3SJoerg Wunsch 			 * the FDC went off to the Saints...
1725dc8603e3SJoerg Wunsch 			 */
1726dc8603e3SJoerg Wunsch 			fdc->retry = 6;	/* try a reset */
17276182fdbdSPeter Wemm 			return(retrier(fdc));
1728dc8603e3SJoerg Wunsch 		}
1729dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
17305b81b6b3SRodney W. Grimes 		fdc->state = SEEKWAIT;
17315b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
17325b81b6b3SRodney W. Grimes 	case SEEKWAIT:
17335b81b6b3SRodney W. Grimes 		/* allow heads to settle */
17346182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 16);
17355b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
17365b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
17375b81b6b3SRodney W. Grimes 	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
17385b81b6b3SRodney W. Grimes 		/* Make sure seek really happened*/
17396182fdbdSPeter Wemm 		if(fd->track == FD_NO_TRACK) {
1740bb6382faSJoerg Wunsch 			int descyl = b_cylinder * fd->ft->steptrac;
17413a2f7427SDavid Greenman 			do {
17423a2f7427SDavid Greenman 				/*
1743dc5df763SJoerg Wunsch 				 * This might be a "ready changed" interrupt,
1744dc5df763SJoerg Wunsch 				 * which cannot really happen since the
1745dc5df763SJoerg Wunsch 				 * RDY pin is hardwired to + 5 volts.  This
1746dc5df763SJoerg Wunsch 				 * generally indicates a "bouncing" intr
1747dc5df763SJoerg Wunsch 				 * line, so do one of the following:
1748dc5df763SJoerg Wunsch 				 *
1749dc5df763SJoerg Wunsch 				 * When running on an enhanced FDC that is
1750dc5df763SJoerg Wunsch 				 * known to not go stuck after responding
1751dc5df763SJoerg Wunsch 				 * with INVALID, fetch all interrupt states
1752dc5df763SJoerg Wunsch 				 * until seeing either an INVALID or a
1753dc5df763SJoerg Wunsch 				 * real interrupt condition.
1754dc5df763SJoerg Wunsch 				 *
1755dc5df763SJoerg Wunsch 				 * When running on a dumb old NE765, give
1756dc5df763SJoerg Wunsch 				 * up immediately.  The controller will
1757dc5df763SJoerg Wunsch 				 * provide up to four dummy RC interrupt
1758dc5df763SJoerg Wunsch 				 * conditions right after reset (for the
1759dc5df763SJoerg Wunsch 				 * corresponding four drives), so this is
1760dc5df763SJoerg Wunsch 				 * our only chance to get notice that it
1761dc5df763SJoerg Wunsch 				 * was not the FDC that caused the interrupt.
17623a2f7427SDavid Greenman 				 */
1763dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, &cyl)
1764dc5df763SJoerg Wunsch 				    == FD_NOT_VALID)
1765dc5df763SJoerg Wunsch 					return 0;
1766dc5df763SJoerg Wunsch 				if(fdc->fdct == FDC_NE765
1767dc5df763SJoerg Wunsch 				   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1768dc5df763SJoerg Wunsch 					return 0; /* hope for a real intr */
17693a2f7427SDavid Greenman 			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1770dc5df763SJoerg Wunsch 
17716182fdbdSPeter Wemm 			if (0 == descyl) {
1772dc5df763SJoerg Wunsch 				int failed = 0;
17733a2f7427SDavid Greenman 				/*
17743a2f7427SDavid Greenman 				 * seek to cyl 0 requested; make sure we are
17753a2f7427SDavid Greenman 				 * really there
17763a2f7427SDavid Greenman 				 */
1777dc5df763SJoerg Wunsch 				if (fd_sense_drive_status(fdc, &st3))
1778dc5df763SJoerg Wunsch 					failed = 1;
17793a2f7427SDavid Greenman 				if ((st3 & NE7_ST3_T0) == 0) {
17803a2f7427SDavid Greenman 					printf(
17813a2f7427SDavid Greenman 		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
17823a2f7427SDavid Greenman 					       fdu, st3, NE7_ST3BITS);
1783dc5df763SJoerg Wunsch 					failed = 1;
1784dc5df763SJoerg Wunsch 				}
1785dc5df763SJoerg Wunsch 
17866182fdbdSPeter Wemm 				if (failed) {
17873a2f7427SDavid Greenman 					if(fdc->retry < 3)
17883a2f7427SDavid Greenman 						fdc->retry = 3;
17896182fdbdSPeter Wemm 					return (retrier(fdc));
17903a2f7427SDavid Greenman 				}
17913a2f7427SDavid Greenman 			}
1792dc5df763SJoerg Wunsch 
17936182fdbdSPeter Wemm 			if (cyl != descyl) {
17943a2f7427SDavid Greenman 				printf(
17953a2f7427SDavid Greenman 		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
17962d9d0204SRodney W. Grimes 				       fdu, descyl, cyl, st0);
1797e5d7d243SBruce Evans 				if (fdc->retry < 3)
1798e5d7d243SBruce Evans 					fdc->retry = 3;
17996182fdbdSPeter Wemm 				return (retrier(fdc));
18005b81b6b3SRodney W. Grimes 			}
18015b81b6b3SRodney W. Grimes 		}
18025b81b6b3SRodney W. Grimes 
1803bb6382faSJoerg Wunsch 		fd->track = b_cylinder;
180469acd21dSWarner Losh #ifdef FDC_YE
180569acd21dSWarner Losh 		if (!(fdc->flags & FDC_PCMCIA))
180669acd21dSWarner Losh #endif
1807ab3f7469SPoul-Henning Kamp 			isa_dmastart(bp->b_flags, bp->b_data+fd->skip,
18083a2f7427SDavid Greenman 				format ? bp->b_bcount : fdblk, fdc->dmachan);
18095b81b6b3SRodney W. Grimes 		sectrac = fd->ft->sectrac;
18105b81b6b3SRodney W. Grimes 		sec = blknum %  (sectrac * fd->ft->heads);
18115b81b6b3SRodney W. Grimes 		head = sec / sectrac;
18125b81b6b3SRodney W. Grimes 		sec = sec % sectrac + 1;
18133a2f7427SDavid Greenman 		fd->hddrv = ((head&1)<<2)+fdu;
18143a2f7427SDavid Greenman 
18153a2f7427SDavid Greenman 		if(format || !read)
18163a2f7427SDavid Greenman 		{
18173a2f7427SDavid Greenman 			/* make sure the drive is writable */
1818dc5df763SJoerg Wunsch 			if(fd_sense_drive_status(fdc, &st3) != 0)
1819dc8603e3SJoerg Wunsch 			{
1820dc8603e3SJoerg Wunsch 				/* stuck controller? */
18215c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18225c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
18235c1a1eaeSBruce Evans 					    fdc->dmachan);
1824dc8603e3SJoerg Wunsch 				fdc->retry = 6;	/* reset the beast */
18256182fdbdSPeter Wemm 				return (retrier(fdc));
1826dc8603e3SJoerg Wunsch 			}
18273a2f7427SDavid Greenman 			if(st3 & NE7_ST3_WP)
18283a2f7427SDavid Greenman 			{
18293a2f7427SDavid Greenman 				/*
18303a2f7427SDavid Greenman 				 * XXX YES! this is ugly.
18313a2f7427SDavid Greenman 				 * in order to force the current operation
18323a2f7427SDavid Greenman 				 * to fail, we will have to fake an FDC
18333a2f7427SDavid Greenman 				 * error - all error handling is done
18343a2f7427SDavid Greenman 				 * by the retrier()
18353a2f7427SDavid Greenman 				 */
18363a2f7427SDavid Greenman 				fdc->status[0] = NE7_ST0_IC_AT;
18373a2f7427SDavid Greenman 				fdc->status[1] = NE7_ST1_NW;
18383a2f7427SDavid Greenman 				fdc->status[2] = 0;
18393a2f7427SDavid Greenman 				fdc->status[3] = fd->track;
18403a2f7427SDavid Greenman 				fdc->status[4] = head;
18413a2f7427SDavid Greenman 				fdc->status[5] = sec;
18423a2f7427SDavid Greenman 				fdc->retry = 8;	/* break out immediately */
18433a2f7427SDavid Greenman 				fdc->state = IOTIMEDOUT; /* not really... */
18443a2f7427SDavid Greenman 				return (1);
18453a2f7427SDavid Greenman 			}
18463a2f7427SDavid Greenman 		}
18475b81b6b3SRodney W. Grimes 
18486182fdbdSPeter Wemm 		if (format) {
184969acd21dSWarner Losh #ifdef FDC_YE
185069acd21dSWarner Losh 			if (fdc->flags & FDC_PCMCIA)
185169acd21dSWarner Losh 				(void)fdcpio(fdcu,bp->b_flags,
185269acd21dSWarner Losh 					bp->b_data+fd->skip,
185369acd21dSWarner Losh 					bp->b_bcount);
185469acd21dSWarner Losh #endif
1855b39c878eSAndrey A. Chernov 			/* formatting */
18566182fdbdSPeter Wemm 			if(fd_cmd(fdc, 6,  NE7CMD_FORMAT, head << 2 | fdu,
1857dc5df763SJoerg Wunsch 				  finfo->fd_formb_secshift,
1858dc5df763SJoerg Wunsch 				  finfo->fd_formb_nsecs,
1859dc5df763SJoerg Wunsch 				  finfo->fd_formb_gaplen,
18606182fdbdSPeter Wemm 				  finfo->fd_formb_fillbyte, 0)) {
1861dc8603e3SJoerg Wunsch 				/* controller fell over */
18625c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18635c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
18645c1a1eaeSBruce Evans 					    fdc->dmachan);
1865dc8603e3SJoerg Wunsch 				fdc->retry = 6;
18666182fdbdSPeter Wemm 				return (retrier(fdc));
1867dc8603e3SJoerg Wunsch 			}
18686182fdbdSPeter Wemm 		} else {
186969acd21dSWarner Losh #ifdef FDC_YE
187069acd21dSWarner Losh 			if (fdc->flags & FDC_PCMCIA) {
187169acd21dSWarner Losh 				/*
187269acd21dSWarner Losh 				 * this seems to be necessary even when
187369acd21dSWarner Losh 				 * reading data
187469acd21dSWarner Losh 				 */
187569acd21dSWarner Losh 				SET_BCDR(1,fdblk,fdc->baseport);
187669acd21dSWarner Losh 
187769acd21dSWarner Losh 				/*
187869acd21dSWarner Losh 				 * perform the write pseudo-DMA before
187969acd21dSWarner Losh 				 * the WRITE command is sent
188069acd21dSWarner Losh 				 */
188169acd21dSWarner Losh 				if (!read)
188269acd21dSWarner Losh 					(void)fdcpio(fdcu,bp->b_flags,
188369acd21dSWarner Losh 					    bp->b_data+fd->skip,
188469acd21dSWarner Losh 					    fdblk);
188569acd21dSWarner Losh 			}
188669acd21dSWarner Losh #endif
18876182fdbdSPeter Wemm 			if (fd_cmd(fdc, 9,
1888dc5df763SJoerg Wunsch 				   (read ? NE7CMD_READ : NE7CMD_WRITE),
1889dc5df763SJoerg Wunsch 				   head << 2 | fdu,  /* head & unit */
1890dc5df763SJoerg Wunsch 				   fd->track,        /* track */
1891dc5df763SJoerg Wunsch 				   head,
1892dc5df763SJoerg Wunsch 				   sec,              /* sector + 1 */
1893dc5df763SJoerg Wunsch 				   fd->ft->secsize,  /* sector size */
1894dc5df763SJoerg Wunsch 				   sectrac,          /* sectors/track */
1895dc5df763SJoerg Wunsch 				   fd->ft->gap,      /* gap size */
1896dc5df763SJoerg Wunsch 				   fd->ft->datalen,  /* data length */
18976182fdbdSPeter Wemm 				   0)) {
1898dc8603e3SJoerg Wunsch 				/* the beast is sleeping again */
18995c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
19005c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
19015c1a1eaeSBruce Evans 					    fdc->dmachan);
1902dc8603e3SJoerg Wunsch 				fdc->retry = 6;
19036182fdbdSPeter Wemm 				return (retrier(fdc));
19045b81b6b3SRodney W. Grimes 			}
1905b39c878eSAndrey A. Chernov 		}
190669acd21dSWarner Losh #ifdef FDC_YE
190769acd21dSWarner Losh 		if (fdc->flags & FDC_PCMCIA)
190869acd21dSWarner Losh 			/*
190969acd21dSWarner Losh 			 * if this is a read, then simply await interrupt
191069acd21dSWarner Losh 			 * before performing PIO
191169acd21dSWarner Losh 			 */
191269acd21dSWarner Losh 			if (read && !fdcpio(fdcu,bp->b_flags,
191369acd21dSWarner Losh 			    bp->b_data+fd->skip,fdblk)) {
191469acd21dSWarner Losh 				fd->tohandle = timeout(fd_iotimeout,
191569acd21dSWarner Losh 					(caddr_t)fdcu, hz);
191669acd21dSWarner Losh 				return(0);      /* will return later */
191769acd21dSWarner Losh 			};
191869acd21dSWarner Losh 
191969acd21dSWarner Losh 		/*
192069acd21dSWarner Losh 		 * write (or format) operation will fall through and
192169acd21dSWarner Losh 		 * await completion interrupt
192269acd21dSWarner Losh 		 */
192369acd21dSWarner Losh #endif
19245b81b6b3SRodney W. Grimes 		fdc->state = IOCOMPLETE;
19256182fdbdSPeter Wemm 		fd->tohandle = timeout(fd_iotimeout, fdc, hz);
19265b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
192769acd21dSWarner Losh #ifdef FDC_YE
192869acd21dSWarner Losh 	case PIOREAD:
192969acd21dSWarner Losh 		/*
193069acd21dSWarner Losh 		 * actually perform the PIO read.  The IOCOMPLETE case
193169acd21dSWarner Losh 		 * removes the timeout for us.
193269acd21dSWarner Losh 		 */
193369acd21dSWarner Losh 		(void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk);
193469acd21dSWarner Losh 		fdc->state = IOCOMPLETE;
193569acd21dSWarner Losh 		/* FALLTHROUGH */
193669acd21dSWarner Losh #endif
19375b81b6b3SRodney W. Grimes 	case IOCOMPLETE: /* IO DONE, post-analyze */
19386182fdbdSPeter Wemm 		untimeout(fd_iotimeout, fdc, fd->tohandle);
1939dc5df763SJoerg Wunsch 
19406182fdbdSPeter Wemm 		if (fd_read_status(fdc, fd->fdsu)) {
19415c1a1eaeSBruce Evans 			isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
19425c1a1eaeSBruce Evans 				    format ? bp->b_bcount : fdblk,
19435c1a1eaeSBruce Evans 				    fdc->dmachan);
1944dc5df763SJoerg Wunsch 			if (fdc->retry < 6)
1945dc5df763SJoerg Wunsch 				fdc->retry = 6;	/* force a reset */
19466182fdbdSPeter Wemm 			return (retrier(fdc));
19475b81b6b3SRodney W. Grimes   		}
1948dc5df763SJoerg Wunsch 
19493a2f7427SDavid Greenman 		fdc->state = IOTIMEDOUT;
1950dc5df763SJoerg Wunsch 
19513a2f7427SDavid Greenman 		/* FALLTHROUGH */
1952dc5df763SJoerg Wunsch 
19533a2f7427SDavid Greenman 	case IOTIMEDOUT:
195469acd21dSWarner Losh #ifdef FDC_YE
195569acd21dSWarner Losh 		if (!(fdc->flags & FDC_PCMCIA))
195669acd21dSWarner Losh #endif
1957ab3f7469SPoul-Henning Kamp 			isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
19583a2f7427SDavid Greenman 				format ? bp->b_bcount : fdblk, fdc->dmachan);
19596182fdbdSPeter Wemm 		if (fdc->status[0] & NE7_ST0_IC) {
19603a2f7427SDavid Greenman                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
19613a2f7427SDavid Greenman 			    && fdc->status[1] & NE7_ST1_OR) {
1962b39c878eSAndrey A. Chernov                                 /*
19633a2f7427SDavid Greenman 				 * DMA overrun. Someone hogged the bus
19643a2f7427SDavid Greenman 				 * and didn't release it in time for the
19653a2f7427SDavid Greenman 				 * next FDC transfer.
19663a2f7427SDavid Greenman 				 * Just restart it, don't increment retry
19673a2f7427SDavid Greenman 				 * count. (vak)
1968b39c878eSAndrey A. Chernov                                  */
1969b39c878eSAndrey A. Chernov                                 fdc->state = SEEKCOMPLETE;
1970b39c878eSAndrey A. Chernov                                 return (1);
1971b39c878eSAndrey A. Chernov                         }
19723a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
19733a2f7427SDavid Greenman 				&& fdc->retry < 6)
19743a2f7427SDavid Greenman 				fdc->retry = 6;	/* force a reset */
19753a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
19763a2f7427SDavid Greenman 				&& fdc->status[2] & NE7_ST2_WC
19773a2f7427SDavid Greenman 				&& fdc->retry < 3)
19783a2f7427SDavid Greenman 				fdc->retry = 3;	/* force recalibrate */
19796182fdbdSPeter Wemm 			return (retrier(fdc));
19805b81b6b3SRodney W. Grimes 		}
19815b81b6b3SRodney W. Grimes 		/* All OK */
19823a2f7427SDavid Greenman 		fd->skip += fdblk;
19836182fdbdSPeter Wemm 		if (!format && fd->skip < bp->b_bcount - bp->b_resid) {
19845b81b6b3SRodney W. Grimes 			/* set up next transfer */
19855b81b6b3SRodney W. Grimes 			fdc->state = DOSEEK;
19866182fdbdSPeter Wemm 		} else {
19875b81b6b3SRodney W. Grimes 			/* ALL DONE */
19885b81b6b3SRodney W. Grimes 			fd->skip = 0;
1989e93e63cbSBruce Evans 			fdc->bp = NULL;
1990b2dfb1f9SJustin T. Gibbs 			/* Tell devstat we have finished with the transaction */
1991b2dfb1f9SJustin T. Gibbs 			devstat_end_transaction(&fd->device_stats,
1992b2dfb1f9SJustin T. Gibbs 						bp->b_bcount - bp->b_resid,
1993b2dfb1f9SJustin T. Gibbs 						DEVSTAT_TAG_NONE,
1994b2dfb1f9SJustin T. Gibbs 						(bp->b_flags & B_READ) ?
1995b2dfb1f9SJustin T. Gibbs 						DEVSTAT_READ : DEVSTAT_WRITE);
19965b81b6b3SRodney W. Grimes 			biodone(bp);
19975b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
19985b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
19995b81b6b3SRodney W. Grimes 			fdc->state = FINDWORK;
20005b81b6b3SRodney W. Grimes 		}
20015b81b6b3SRodney W. Grimes 		return (1);
20025b81b6b3SRodney W. Grimes 	case RESETCTLR:
20033a2f7427SDavid Greenman 		fdc_reset(fdc);
20045b81b6b3SRodney W. Grimes 		fdc->retry++;
20055c1a1eaeSBruce Evans 		fdc->state = RESETCOMPLETE;
20065c1a1eaeSBruce Evans 		return (0);
20075c1a1eaeSBruce Evans 	case RESETCOMPLETE:
20085c1a1eaeSBruce Evans 		/*
20095c1a1eaeSBruce Evans 		 * Discard all the results from the reset so that they
20105c1a1eaeSBruce Evans 		 * can't cause an unexpected interrupt later.
20115c1a1eaeSBruce Evans 		 */
20120e317d05SJoerg Wunsch 		for (i = 0; i < 4; i++)
20130e317d05SJoerg Wunsch 			(void)fd_sense_int(fdc, &st0, &cyl);
20145c1a1eaeSBruce Evans 		fdc->state = STARTRECAL;
20155c1a1eaeSBruce Evans 		/* Fall through. */
20165c1a1eaeSBruce Evans 	case STARTRECAL:
20176182fdbdSPeter Wemm 		if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) {
2018dc8603e3SJoerg Wunsch 			/* arrgl */
2019dc8603e3SJoerg Wunsch 			fdc->retry = 6;
20206182fdbdSPeter Wemm 			return (retrier(fdc));
2021dc8603e3SJoerg Wunsch 		}
20225b81b6b3SRodney W. Grimes 		fdc->state = RECALWAIT;
20235b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
20245b81b6b3SRodney W. Grimes 	case RECALWAIT:
20255b81b6b3SRodney W. Grimes 		/* allow heads to settle */
20266182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 8);
20275b81b6b3SRodney W. Grimes 		fdc->state = RECALCOMPLETE;
20285b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
20295b81b6b3SRodney W. Grimes 	case RECALCOMPLETE:
20303a2f7427SDavid Greenman 		do {
20313a2f7427SDavid Greenman 			/*
2032dc5df763SJoerg Wunsch 			 * See SEEKCOMPLETE for a comment on this:
20333a2f7427SDavid Greenman 			 */
2034dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
2035dc5df763SJoerg Wunsch 				return 0;
2036dc5df763SJoerg Wunsch 			if(fdc->fdct == FDC_NE765
2037dc5df763SJoerg Wunsch 			   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
2038dc5df763SJoerg Wunsch 				return 0; /* hope for a real intr */
20393a2f7427SDavid Greenman 		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
20403a2f7427SDavid Greenman 		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
20415b81b6b3SRodney W. Grimes 		{
2042dc8603e3SJoerg Wunsch 			if(fdc->retry > 3)
2043dc8603e3SJoerg Wunsch 				/*
2044dc8603e3SJoerg Wunsch 				 * a recalibrate from beyond cylinder 77
2045dc8603e3SJoerg Wunsch 				 * will "fail" due to the FDC limitations;
2046dc8603e3SJoerg Wunsch 				 * since people used to complain much about
2047dc8603e3SJoerg Wunsch 				 * the failure message, try not logging
2048dc8603e3SJoerg Wunsch 				 * this one if it seems to be the first
2049dc8603e3SJoerg Wunsch 				 * time in a line
2050dc8603e3SJoerg Wunsch 				 */
2051dc8603e3SJoerg Wunsch 				printf("fd%d: recal failed ST0 %b cyl %d\n",
2052dc8603e3SJoerg Wunsch 				       fdu, st0, NE7_ST0BITS, cyl);
20533a2f7427SDavid Greenman 			if(fdc->retry < 3) fdc->retry = 3;
20546182fdbdSPeter Wemm 			return (retrier(fdc));
20555b81b6b3SRodney W. Grimes 		}
20565b81b6b3SRodney W. Grimes 		fd->track = 0;
20575b81b6b3SRodney W. Grimes 		/* Seek (probably) necessary */
20585b81b6b3SRodney W. Grimes 		fdc->state = DOSEEK;
20595b81b6b3SRodney W. Grimes 		return (1);	/* will return immediatly */
20605b81b6b3SRodney W. Grimes 	case MOTORWAIT:
20615b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
20625b81b6b3SRodney W. Grimes 		{
20635b81b6b3SRodney W. Grimes 			return (0); /* time's not up yet */
20645b81b6b3SRodney W. Grimes 		}
20655c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
20665c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
20675c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
20685c1a1eaeSBruce Evans 		} else {
20695e235068SJordan K. Hubbard 			/*
20705c1a1eaeSBruce Evans 			 * If all motors were off, then the controller was
20715c1a1eaeSBruce Evans 			 * reset, so it has lost track of the current
20725c1a1eaeSBruce Evans 			 * cylinder.  Recalibrate to handle this case.
2073f86e4077SBruce Evans 			 * But first, discard the results of the reset.
20745e235068SJordan K. Hubbard 			 */
2075f86e4077SBruce Evans 			fdc->state = RESETCOMPLETE;
20765c1a1eaeSBruce Evans 		}
20775b81b6b3SRodney W. Grimes 		return (1);	/* will return immediatly */
20785b81b6b3SRodney W. Grimes 	default:
20796182fdbdSPeter Wemm 		device_print_prettyname(fdc->fdc_dev);
20806182fdbdSPeter Wemm 		printf("unexpected FD int->");
2081dc5df763SJoerg Wunsch 		if (fd_read_status(fdc, fd->fdsu) == 0)
2082a838d83dSBruce Evans 			printf("FDC status :%x %x %x %x %x %x %x   ",
20835b81b6b3SRodney W. Grimes 			       fdc->status[0],
20845b81b6b3SRodney W. Grimes 			       fdc->status[1],
20855b81b6b3SRodney W. Grimes 			       fdc->status[2],
20865b81b6b3SRodney W. Grimes 			       fdc->status[3],
20875b81b6b3SRodney W. Grimes 			       fdc->status[4],
20885b81b6b3SRodney W. Grimes 			       fdc->status[5],
20895b81b6b3SRodney W. Grimes 			       fdc->status[6] );
20903a2f7427SDavid Greenman 		else
2091dac0f2dbSJoerg Wunsch 			printf("No status available   ");
2092dac0f2dbSJoerg Wunsch 		if (fd_sense_int(fdc, &st0, &cyl) != 0)
2093dac0f2dbSJoerg Wunsch 		{
2094dac0f2dbSJoerg Wunsch 			printf("[controller is dead now]\n");
20955b81b6b3SRodney W. Grimes 			return (0);
20965b81b6b3SRodney W. Grimes 		}
2097dac0f2dbSJoerg Wunsch 		printf("ST0 = %x, PCN = %x\n", st0, cyl);
2098dac0f2dbSJoerg Wunsch 		return (0);
2099dac0f2dbSJoerg Wunsch 	}
2100dac0f2dbSJoerg Wunsch 	/*XXX confusing: some branches return immediately, others end up here*/
21015b81b6b3SRodney W. Grimes 	return (1); /* Come back immediatly to new state */
21025b81b6b3SRodney W. Grimes }
21035b81b6b3SRodney W. Grimes 
2104aaf08d94SGarrett Wollman static int
21056182fdbdSPeter Wemm retrier(struct fdc_data *fdc)
21065b81b6b3SRodney W. Grimes {
210717542807SPoul-Henning Kamp 	register struct buf *bp;
21086182fdbdSPeter Wemm 	struct fd_data *fd;
21096182fdbdSPeter Wemm 	int fdu;
21105b81b6b3SRodney W. Grimes 
2111e93e63cbSBruce Evans 	bp = fdc->bp;
21125b81b6b3SRodney W. Grimes 
21136182fdbdSPeter Wemm 	/* XXX shouldn't this be cached somewhere?  */
21146182fdbdSPeter Wemm 	fdu = FDUNIT(minor(bp->b_dev));
21156182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
21166182fdbdSPeter Wemm 	if (fd->options & FDOPT_NORETRY)
21173a2f7427SDavid Greenman 		goto fail;
21186182fdbdSPeter Wemm 
21196182fdbdSPeter Wemm 	switch (fdc->retry) {
21205b81b6b3SRodney W. Grimes 	case 0: case 1: case 2:
21215b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
21225b81b6b3SRodney W. Grimes 		break;
21235b81b6b3SRodney W. Grimes 	case 3: case 4: case 5:
21245b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
21255b81b6b3SRodney W. Grimes 		break;
21265b81b6b3SRodney W. Grimes 	case 6:
21275b81b6b3SRodney W. Grimes 		fdc->state = RESETCTLR;
21285b81b6b3SRodney W. Grimes 		break;
21295b81b6b3SRodney W. Grimes 	case 7:
21305b81b6b3SRodney W. Grimes 		break;
21315b81b6b3SRodney W. Grimes 	default:
21323a2f7427SDavid Greenman 	fail:
21335b81b6b3SRodney W. Grimes 		{
21347ca0641bSAndrey A. Chernov 			dev_t sav_b_dev = bp->b_dev;
21357ca0641bSAndrey A. Chernov 			/* Trick diskerr */
21363a2f7427SDavid Greenman 			bp->b_dev = makedev(major(bp->b_dev),
21373a2f7427SDavid Greenman 				    (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART);
213892ed385aSRodney W. Grimes 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
21393a2f7427SDavid Greenman 				fdc->fd->skip / DEV_BSIZE,
21403a2f7427SDavid Greenman 				(struct disklabel *)NULL);
21417ca0641bSAndrey A. Chernov 			bp->b_dev = sav_b_dev;
2142dc5df763SJoerg Wunsch 			if (fdc->flags & FDC_STAT_VALID)
2143dc5df763SJoerg Wunsch 			{
2144dc5df763SJoerg Wunsch 				printf(
2145a838d83dSBruce Evans 			" (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n",
2146dc5df763SJoerg Wunsch 				       fdc->status[0], NE7_ST0BITS,
2147dc5df763SJoerg Wunsch 				       fdc->status[1], NE7_ST1BITS,
2148dc5df763SJoerg Wunsch 				       fdc->status[2], NE7_ST2BITS,
2149dc5df763SJoerg Wunsch 				       fdc->status[3], fdc->status[4],
2150dc5df763SJoerg Wunsch 				       fdc->status[5]);
2151dc5df763SJoerg Wunsch 			}
2152dc5df763SJoerg Wunsch 			else
2153dc5df763SJoerg Wunsch 				printf(" (No status)\n");
21545b81b6b3SRodney W. Grimes 		}
21555b81b6b3SRodney W. Grimes 		bp->b_flags |= B_ERROR;
21565b81b6b3SRodney W. Grimes 		bp->b_error = EIO;
2157bb6382faSJoerg Wunsch 		bp->b_resid += bp->b_bcount - fdc->fd->skip;
2158e93e63cbSBruce Evans 		fdc->bp = NULL;
2159b2dfb1f9SJustin T. Gibbs 
2160b2dfb1f9SJustin T. Gibbs 		/* Tell devstat we have finished with the transaction */
21619f02ad60SJustin T. Gibbs 		devstat_end_transaction(&fdc->fd->device_stats,
2162b2dfb1f9SJustin T. Gibbs 					bp->b_bcount - bp->b_resid,
2163b2dfb1f9SJustin T. Gibbs 					DEVSTAT_TAG_NONE,
2164b2dfb1f9SJustin T. Gibbs 					(bp->b_flags & B_READ) ? DEVSTAT_READ :
2165b2dfb1f9SJustin T. Gibbs 								 DEVSTAT_WRITE);
21665b81b6b3SRodney W. Grimes 		fdc->fd->skip = 0;
21675b81b6b3SRodney W. Grimes 		biodone(bp);
216892ed385aSRodney W. Grimes 		fdc->state = FINDWORK;
21695c1a1eaeSBruce Evans 		fdc->flags |= FDC_NEEDS_RESET;
21705b81b6b3SRodney W. Grimes 		fdc->fd = (fd_p) 0;
21715b81b6b3SRodney W. Grimes 		fdc->fdu = -1;
217292ed385aSRodney W. Grimes 		return (1);
21735b81b6b3SRodney W. Grimes 	}
21745b81b6b3SRodney W. Grimes 	fdc->retry++;
21755b81b6b3SRodney W. Grimes 	return (1);
21765b81b6b3SRodney W. Grimes }
21775b81b6b3SRodney W. Grimes 
2178b39c878eSAndrey A. Chernov static int
2179b39c878eSAndrey A. Chernov fdformat(dev, finfo, p)
2180b39c878eSAndrey A. Chernov 	dev_t dev;
2181b39c878eSAndrey A. Chernov 	struct fd_formb *finfo;
2182b39c878eSAndrey A. Chernov 	struct proc *p;
2183b39c878eSAndrey A. Chernov {
2184b39c878eSAndrey A. Chernov  	fdu_t	fdu;
2185b39c878eSAndrey A. Chernov  	fd_p	fd;
2186b39c878eSAndrey A. Chernov 
2187b39c878eSAndrey A. Chernov 	struct buf *bp;
2188b39c878eSAndrey A. Chernov 	int rv = 0, s;
21893a2f7427SDavid Greenman 	size_t fdblk;
2190b39c878eSAndrey A. Chernov 
2191b39c878eSAndrey A. Chernov  	fdu	= FDUNIT(minor(dev));
21926182fdbdSPeter Wemm 	fd	= devclass_get_softc(fd_devclass, fdu);
21933a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2194b39c878eSAndrey A. Chernov 
2195b39c878eSAndrey A. Chernov 	/* set up a buffer header for fdstrategy() */
2196b39c878eSAndrey A. Chernov 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
2197b39c878eSAndrey A. Chernov 	if(bp == 0)
2198b39c878eSAndrey A. Chernov 		return ENOBUFS;
219982f5379bSJoerg Wunsch 	/*
220082f5379bSJoerg Wunsch 	 * keep the process from being swapped
220182f5379bSJoerg Wunsch 	 */
22022ada239cSPeter Wemm 	PHOLD(p);
2203b39c878eSAndrey A. Chernov 	bzero((void *)bp, sizeof(struct buf));
220467812eacSKirk McKusick 	BUF_LOCKINIT(bp);
220567812eacSKirk McKusick 	BUF_LOCK(bp, LK_EXCLUSIVE);
220667812eacSKirk McKusick 	bp->b_flags = B_PHYS | B_FORMAT;
2207b39c878eSAndrey A. Chernov 
2208b39c878eSAndrey A. Chernov 	/*
2209b39c878eSAndrey A. Chernov 	 * calculate a fake blkno, so fdstrategy() would initiate a
2210b39c878eSAndrey A. Chernov 	 * seek to the requested cylinder
2211b39c878eSAndrey A. Chernov 	 */
2212b39c878eSAndrey A. Chernov 	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
22133a2f7427SDavid Greenman 		+ finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
2214b39c878eSAndrey A. Chernov 
2215b39c878eSAndrey A. Chernov 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
2216ab3f7469SPoul-Henning Kamp 	bp->b_data = (caddr_t)finfo;
2217b39c878eSAndrey A. Chernov 
2218b39c878eSAndrey A. Chernov 	/* now do the format */
22193e425b96SJulian Elischer 	bp->b_dev = dev;
222049ff4debSPoul-Henning Kamp 	BUF_STRATEGY(bp, 0);
2221b39c878eSAndrey A. Chernov 
2222b39c878eSAndrey A. Chernov 	/* ...and wait for it to complete */
2223b39c878eSAndrey A. Chernov 	s = splbio();
22246182fdbdSPeter Wemm 	while(!(bp->b_flags & B_DONE)) {
22255e235068SJordan K. Hubbard 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
2226b39c878eSAndrey A. Chernov 		if (rv == EWOULDBLOCK)
2227b39c878eSAndrey A. Chernov 			break;
2228b39c878eSAndrey A. Chernov 	}
2229b39c878eSAndrey A. Chernov 	splx(s);
2230b39c878eSAndrey A. Chernov 
223182f5379bSJoerg Wunsch 	if (rv == EWOULDBLOCK) {
2232b39c878eSAndrey A. Chernov 		/* timed out */
2233b39c878eSAndrey A. Chernov 		rv = EIO;
223482f5379bSJoerg Wunsch 		biodone(bp);
223582f5379bSJoerg Wunsch 	}
22363a2f7427SDavid Greenman 	if (bp->b_flags & B_ERROR)
22373a2f7427SDavid Greenman 		rv = bp->b_error;
223882f5379bSJoerg Wunsch 	/*
223982f5379bSJoerg Wunsch 	 * allow the process to be swapped
224082f5379bSJoerg Wunsch 	 */
22412ada239cSPeter Wemm 	PRELE(p);
224267812eacSKirk McKusick 	BUF_UNLOCK(bp);
224367812eacSKirk McKusick 	BUF_LOCKFREE(bp);
2244b39c878eSAndrey A. Chernov 	free(bp, M_TEMP);
2245b39c878eSAndrey A. Chernov 	return rv;
2246b39c878eSAndrey A. Chernov }
2247b39c878eSAndrey A. Chernov 
2248f5f7ba03SJordan K. Hubbard /*
2249671e2ceeSBruce Evans  * TODO: don't allocate buffer on stack.
2250f5f7ba03SJordan K. Hubbard  */
22515b81b6b3SRodney W. Grimes 
22523e425b96SJulian Elischer static int
2253b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p)
2254f5f7ba03SJordan K. Hubbard 	dev_t dev;
2255ecbb00a2SDoug Rabson 	u_long cmd;
2256f5f7ba03SJordan K. Hubbard 	caddr_t addr;
2257f5f7ba03SJordan K. Hubbard 	int flag;
2258b39c878eSAndrey A. Chernov 	struct proc *p;
2259f5f7ba03SJordan K. Hubbard {
22603a2f7427SDavid Greenman  	fdu_t	fdu = FDUNIT(minor(dev));
22616182fdbdSPeter Wemm  	fd_p	fd = devclass_get_softc(fd_devclass, fdu);
22623a2f7427SDavid Greenman 	size_t fdblk;
22633a2f7427SDavid Greenman 
2264f5f7ba03SJordan K. Hubbard 	struct fd_type *fdt;
2265f5f7ba03SJordan K. Hubbard 	struct disklabel *dl;
2266f5f7ba03SJordan K. Hubbard 	char buffer[DEV_BSIZE];
22673a2f7427SDavid Greenman 	int error = 0;
2268f5f7ba03SJordan K. Hubbard 
22693a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2270f5f7ba03SJordan K. Hubbard 
22716182fdbdSPeter Wemm 	switch (cmd) {
2272f5f7ba03SJordan K. Hubbard 	case DIOCGDINFO:
2273f5f7ba03SJordan K. Hubbard 		bzero(buffer, sizeof (buffer));
2274f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)buffer;
22753a2f7427SDavid Greenman 		dl->d_secsize = fdblk;
22766182fdbdSPeter Wemm 		fdt = fd->ft;
2277f5f7ba03SJordan K. Hubbard 		dl->d_secpercyl = fdt->size / fdt->tracks;
2278f5f7ba03SJordan K. Hubbard 		dl->d_type = DTYPE_FLOPPY;
2279f5f7ba03SJordan K. Hubbard 
228049ff4debSPoul-Henning Kamp 		if (readdisklabel(dkmodpart(dev, RAW_PART), dl)
2281191e1a59SBruce Evans 		    == NULL)
2282f5f7ba03SJordan K. Hubbard 			error = 0;
2283f5f7ba03SJordan K. Hubbard 		else
2284f5f7ba03SJordan K. Hubbard 			error = EINVAL;
2285f5f7ba03SJordan K. Hubbard 
2286f5f7ba03SJordan K. Hubbard 		*(struct disklabel *)addr = *dl;
2287f5f7ba03SJordan K. Hubbard 		break;
2288f5f7ba03SJordan K. Hubbard 
2289f5f7ba03SJordan K. Hubbard 	case DIOCSDINFO:
2290f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2291f5f7ba03SJordan K. Hubbard 			error = EBADF;
2292f5f7ba03SJordan K. Hubbard 		break;
2293f5f7ba03SJordan K. Hubbard 
2294f5f7ba03SJordan K. Hubbard 	case DIOCWLABEL:
2295f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2296f5f7ba03SJordan K. Hubbard 			error = EBADF;
2297f5f7ba03SJordan K. Hubbard 		break;
2298f5f7ba03SJordan K. Hubbard 
2299f5f7ba03SJordan K. Hubbard 	case DIOCWDINFO:
23006182fdbdSPeter Wemm 		if ((flag & FWRITE) == 0) {
2301f5f7ba03SJordan K. Hubbard 			error = EBADF;
2302f5f7ba03SJordan K. Hubbard 			break;
2303f5f7ba03SJordan K. Hubbard 		}
2304f5f7ba03SJordan K. Hubbard 
2305f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)addr;
2306f5f7ba03SJordan K. Hubbard 
2307191e1a59SBruce Evans 		if ((error = setdisklabel((struct disklabel *)buffer, dl,
2308191e1a59SBruce Evans 					  (u_long)0)) != 0)
2309f5f7ba03SJordan K. Hubbard 			break;
2310f5f7ba03SJordan K. Hubbard 
231149ff4debSPoul-Henning Kamp 		error = writedisklabel(dev, (struct disklabel *)buffer);
2312b39c878eSAndrey A. Chernov 		break;
2313b39c878eSAndrey A. Chernov 	case FD_FORM:
2314b39c878eSAndrey A. Chernov 		if ((flag & FWRITE) == 0)
2315b39c878eSAndrey A. Chernov 			error = EBADF;	/* must be opened for writing */
2316b39c878eSAndrey A. Chernov 		else if (((struct fd_formb *)addr)->format_version !=
2317b39c878eSAndrey A. Chernov 			FD_FORMAT_VERSION)
2318b39c878eSAndrey A. Chernov 			error = EINVAL;	/* wrong version of formatting prog */
2319b39c878eSAndrey A. Chernov 		else
2320b39c878eSAndrey A. Chernov 			error = fdformat(dev, (struct fd_formb *)addr, p);
2321b39c878eSAndrey A. Chernov 		break;
2322b39c878eSAndrey A. Chernov 
2323b39c878eSAndrey A. Chernov 	case FD_GTYPE:                  /* get drive type */
23243e425b96SJulian Elischer 		*(struct fd_type *)addr = *fd->ft;
2325f5f7ba03SJordan K. Hubbard 		break;
2326f5f7ba03SJordan K. Hubbard 
23273a2f7427SDavid Greenman 	case FD_STYPE:                  /* set drive type */
23283a2f7427SDavid Greenman 		/* this is considered harmful; only allow for superuser */
2329f711d546SPoul-Henning Kamp 		if (suser(p) != 0)
23303a2f7427SDavid Greenman 			return EPERM;
23313e425b96SJulian Elischer 		*fd->ft = *(struct fd_type *)addr;
23323a2f7427SDavid Greenman 		break;
23333a2f7427SDavid Greenman 
23343a2f7427SDavid Greenman 	case FD_GOPTS:			/* get drive options */
23353e425b96SJulian Elischer 		*(int *)addr = fd->options;
23363a2f7427SDavid Greenman 		break;
23373a2f7427SDavid Greenman 
23383a2f7427SDavid Greenman 	case FD_SOPTS:			/* set drive options */
23393e425b96SJulian Elischer 		fd->options = *(int *)addr;
23403a2f7427SDavid Greenman 		break;
23413a2f7427SDavid Greenman 
2342f5f7ba03SJordan K. Hubbard 	default:
23433a2f7427SDavid Greenman 		error = ENOTTY;
2344f5f7ba03SJordan K. Hubbard 		break;
2345f5f7ba03SJordan K. Hubbard 	}
2346f5f7ba03SJordan K. Hubbard 	return (error);
2347f5f7ba03SJordan K. Hubbard }
2348f5f7ba03SJordan K. Hubbard 
23496182fdbdSPeter Wemm static device_method_t fdc_methods[] = {
23506182fdbdSPeter Wemm 	/* Device interface */
23516182fdbdSPeter Wemm 	DEVMETHOD(device_probe,		fdc_probe),
23526182fdbdSPeter Wemm 	DEVMETHOD(device_attach,	fdc_attach),
23536182fdbdSPeter Wemm 	DEVMETHOD(device_detach,	bus_generic_detach),
23546182fdbdSPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
23556182fdbdSPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend),
23566182fdbdSPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume),
23577146c13eSJulian Elischer 
23586182fdbdSPeter Wemm 	/* Bus interface */
23596182fdbdSPeter Wemm 	DEVMETHOD(bus_print_child,	fdc_print_child),
23606182fdbdSPeter Wemm 	/* Our children never use any other bus interface methods. */
23617146c13eSJulian Elischer 
23626182fdbdSPeter Wemm 	{ 0, 0 }
23636182fdbdSPeter Wemm };
236487f6c662SJulian Elischer 
23656182fdbdSPeter Wemm static driver_t fdc_driver = {
23666182fdbdSPeter Wemm 	"fdc",
23676182fdbdSPeter Wemm 	fdc_methods,
23686182fdbdSPeter Wemm 	sizeof(struct fdc_data)
23696182fdbdSPeter Wemm };
237087f6c662SJulian Elischer 
23716182fdbdSPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0);
237287f6c662SJulian Elischer 
23736182fdbdSPeter Wemm static device_method_t fd_methods[] = {
23746182fdbdSPeter Wemm 	/* Device interface */
23756182fdbdSPeter Wemm 	DEVMETHOD(device_probe,		fd_probe),
23766182fdbdSPeter Wemm 	DEVMETHOD(device_attach,	fd_attach),
23776182fdbdSPeter Wemm 	DEVMETHOD(device_detach,	bus_generic_detach),
23786182fdbdSPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
23796182fdbdSPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend), /* XXX */
23806182fdbdSPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume), /* XXX */
23813e425b96SJulian Elischer 
23826182fdbdSPeter Wemm 	{ 0, 0 }
23836182fdbdSPeter Wemm };
23846182fdbdSPeter Wemm 
23856182fdbdSPeter Wemm static driver_t fd_driver = {
23866182fdbdSPeter Wemm 	"fd",
23876182fdbdSPeter Wemm 	fd_methods,
23886182fdbdSPeter Wemm 	sizeof(struct fd_data)
23896182fdbdSPeter Wemm };
23906182fdbdSPeter Wemm 
239103016f42SPoul-Henning Kamp DEV_DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, fd_cdevsw, 0, 0);
23926182fdbdSPeter Wemm 
23936182fdbdSPeter Wemm #endif /* NFDC > 0 */
23943e425b96SJulian Elischer 
23953a2f7427SDavid Greenman /*
23963a2f7427SDavid Greenman  * Hello emacs, these are the
23973a2f7427SDavid Greenman  * Local Variables:
23983a2f7427SDavid Greenman  *  c-indent-level:               8
23993a2f7427SDavid Greenman  *  c-continued-statement-offset: 8
24003a2f7427SDavid Greenman  *  c-continued-brace-offset:     0
24013a2f7427SDavid Greenman  *  c-brace-offset:              -8
24023a2f7427SDavid Greenman  *  c-brace-imaginary-offset:     0
24033a2f7427SDavid Greenman  *  c-argdecl-indent:             8
24043a2f7427SDavid Greenman  *  c-label-offset:              -8
24053a2f7427SDavid Greenman  *  c++-hanging-braces:           1
24063a2f7427SDavid Greenman  *  c++-access-specifier-offset: -8
24073a2f7427SDavid Greenman  *  c++-empty-arglist-indent:     8
24083a2f7427SDavid Greenman  *  c++-friend-offset:            0
24093a2f7427SDavid Greenman  * End:
24103a2f7427SDavid Greenman  */
2411