xref: /freebsd/sys/dev/fdc/fdc.c (revision 3b1782065f5fc9414f494f6c6ccab5287dcb4388)
187f6c662SJulian Elischer /*
25b81b6b3SRodney W. Grimes  * Copyright (c) 1990 The Regents of the University of California.
35b81b6b3SRodney W. Grimes  * All rights reserved.
45b81b6b3SRodney W. Grimes  *
55b81b6b3SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
65b81b6b3SRodney W. Grimes  * Don Ahn.
75b81b6b3SRodney W. Grimes  *
869acd21dSWarner Losh  * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu)
969acd21dSWarner Losh  * aided by the Linux floppy driver modifications from David Bateman
1069acd21dSWarner Losh  * (dbateman@eng.uts.edu.au).
1169acd21dSWarner Losh  *
12dc16046fSJoerg Wunsch  * Copyright (c) 1993, 1994 by
133a2f7427SDavid Greenman  *  jc@irbs.UUCP (John Capo)
143a2f7427SDavid Greenman  *  vak@zebub.msk.su (Serge Vakulenko)
153a2f7427SDavid Greenman  *  ache@astral.msk.su (Andrew A. Chernov)
16dc16046fSJoerg Wunsch  *
17dc16046fSJoerg Wunsch  * Copyright (c) 1993, 1994, 1995 by
183a2f7427SDavid Greenman  *  joerg_wunsch@uriah.sax.de (Joerg Wunsch)
19dc5df763SJoerg Wunsch  *  dufault@hda.com (Peter Dufault)
203a2f7427SDavid Greenman  *
215b81b6b3SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
225b81b6b3SRodney W. Grimes  * modification, are permitted provided that the following conditions
235b81b6b3SRodney W. Grimes  * are met:
245b81b6b3SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
255b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
265b81b6b3SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
275b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
285b81b6b3SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
295b81b6b3SRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
305b81b6b3SRodney W. Grimes  *    must display the following acknowledgement:
315b81b6b3SRodney W. Grimes  *	This product includes software developed by the University of
325b81b6b3SRodney W. Grimes  *	California, Berkeley and its contributors.
335b81b6b3SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
345b81b6b3SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
355b81b6b3SRodney W. Grimes  *    without specific prior written permission.
365b81b6b3SRodney W. Grimes  *
375b81b6b3SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
385b81b6b3SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
395b81b6b3SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
405b81b6b3SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
415b81b6b3SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
425b81b6b3SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
435b81b6b3SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
445b81b6b3SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
455b81b6b3SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
465b81b6b3SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
475b81b6b3SRodney W. Grimes  * SUCH DAMAGE.
485b81b6b3SRodney W. Grimes  *
49dc4ff321SRodney W. Grimes  *	from:	@(#)fd.c	7.4 (Berkeley) 5/25/91
50c3aac50fSPeter Wemm  * $FreeBSD$
515b81b6b3SRodney W. Grimes  *
525b81b6b3SRodney W. Grimes  */
535b81b6b3SRodney W. Grimes 
54d2fb4892SJoerg Wunsch #include "opt_fdc.h"
555b81b6b3SRodney W. Grimes 
56b99f0a4aSAndrew Moore #include <sys/param.h>
57b99f0a4aSAndrew Moore #include <sys/systm.h>
58b99f0a4aSAndrew Moore #include <sys/kernel.h>
59b99f0a4aSAndrew Moore #include <sys/buf.h>
606182fdbdSPeter Wemm #include <sys/bus.h>
616182fdbdSPeter Wemm #include <sys/conf.h>
626182fdbdSPeter Wemm #include <sys/disklabel.h>
63b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h>
646182fdbdSPeter Wemm #include <sys/fcntl.h>
65b99f0a4aSAndrew Moore #include <sys/malloc.h>
666182fdbdSPeter Wemm #include <sys/module.h>
673a2f7427SDavid Greenman #include <sys/proc.h>
68b99f0a4aSAndrew Moore #include <sys/syslog.h>
696182fdbdSPeter Wemm 
706182fdbdSPeter Wemm #include <sys/bus.h>
716182fdbdSPeter Wemm #include <machine/bus.h>
726182fdbdSPeter Wemm #include <sys/rman.h>
736182fdbdSPeter Wemm 
746182fdbdSPeter Wemm #include <machine/clock.h>
756182fdbdSPeter Wemm #include <machine/ioctl_fd.h>
766182fdbdSPeter Wemm #include <machine/resource.h>
77dc5df763SJoerg Wunsch #include <machine/stdarg.h>
786182fdbdSPeter Wemm 
796182fdbdSPeter Wemm #include <isa/isavar.h>
80a97c75b7SDoug Rabson #include <isa/isareg.h>
81a97c75b7SDoug Rabson #include <isa/fdreg.h>
82a97c75b7SDoug Rabson #include <isa/fdc.h>
83a97c75b7SDoug Rabson #include <isa/rtc.h>
846182fdbdSPeter Wemm 
85b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */
86b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX
875b81b6b3SRodney W. Grimes 
880722d6abSJoerg Wunsch /* configuration flags */
890722d6abSJoerg Wunsch #define FDC_PRETEND_D0	(1 << 0)	/* pretend drive 0 to be there */
90e34c71eaSJoerg Wunsch #define FDC_NO_FIFO	(1 << 2)	/* do not enable FIFO  */
910722d6abSJoerg Wunsch 
920722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */
930722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED	0x1000
940722d6abSJoerg Wunsch 
95dc5df763SJoerg Wunsch /* error returns for fd_cmd() */
96dc5df763SJoerg Wunsch #define FD_FAILED -1
97dc5df763SJoerg Wunsch #define FD_NOT_VALID -2
98dc5df763SJoerg Wunsch #define FDC_ERRMAX	100	/* do not log more */
99dc5df763SJoerg Wunsch 
1006fb89845SKATO Takenori #define NUMTYPES 17
1016fb89845SKATO Takenori #define NUMDENS  (NUMTYPES - 7)
1027ca0641bSAndrey A. Chernov 
1033a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */
104b99f0a4aSAndrew Moore #define F_TAPE_TYPE	0x020	/* bit for fd_types to indicate tape */
105b99f0a4aSAndrew Moore #define NO_TYPE		0	/* must match NO_TYPE in ft.c */
106b99f0a4aSAndrew Moore #define FD_1720         1
107b99f0a4aSAndrew Moore #define FD_1480         2
108b99f0a4aSAndrew Moore #define FD_1440         3
109b99f0a4aSAndrew Moore #define FD_1200         4
110b99f0a4aSAndrew Moore #define FD_820          5
111b99f0a4aSAndrew Moore #define FD_800          6
112b99f0a4aSAndrew Moore #define FD_720          7
113b99f0a4aSAndrew Moore #define FD_360          8
1146fb89845SKATO Takenori #define FD_640          9
1156fb89845SKATO Takenori #define FD_1232         10
116ed2fa05eSAndrey A. Chernov 
1176fb89845SKATO Takenori #define FD_1480in5_25   11
1186fb89845SKATO Takenori #define FD_1440in5_25   12
1196fb89845SKATO Takenori #define FD_820in5_25    13
1206fb89845SKATO Takenori #define FD_800in5_25    14
1216fb89845SKATO Takenori #define FD_720in5_25    15
1226fb89845SKATO Takenori #define FD_360in5_25    16
1236fb89845SKATO Takenori #define FD_640in5_25    17
124b99f0a4aSAndrew Moore 
1257ca0641bSAndrey A. Chernov 
1266f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] =
1275b81b6b3SRodney W. Grimes {
128126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
129126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
130126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
131126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
132126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
133126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
134126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
135b0568305SAndrey A. Chernov {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
1366fb89845SKATO Takenori {  8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /*  640K in DD 5.25in */
1376fb89845SKATO Takenori {  8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */
138ed2fa05eSAndrey A. Chernov 
139126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
140126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
141126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
142126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
143126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
144126518a1SAndrey A. Chernov {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
1456fb89845SKATO Takenori {  8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /*  640K in HD 5.25in */
1465b81b6b3SRodney W. Grimes };
1475b81b6b3SRodney W. Grimes 
148b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2		/* 2 floppies */
149dc16046fSJoerg Wunsch 
1505b81b6b3SRodney W. Grimes /***********************************************************************\
1515b81b6b3SRodney W. Grimes * Per controller structure.						*
1525b81b6b3SRodney W. Grimes \***********************************************************************/
1536182fdbdSPeter Wemm static devclass_t fdc_devclass;
1545b81b6b3SRodney W. Grimes 
1555b81b6b3SRodney W. Grimes /***********************************************************************\
1565b81b6b3SRodney W. Grimes * Per drive structure.							*
157b99f0a4aSAndrew Moore * N per controller  (DRVS_PER_CTLR)					*
1585b81b6b3SRodney W. Grimes \***********************************************************************/
1596182fdbdSPeter Wemm struct fd_data {
160b99f0a4aSAndrew Moore 	struct	fdc_data *fdc;	/* pointer to controller structure */
1615b81b6b3SRodney W. Grimes 	int	fdsu;		/* this units number on this controller */
1623a2f7427SDavid Greenman 	int	type;		/* Drive type (FD_1440...) */
1635b81b6b3SRodney W. Grimes 	struct	fd_type *ft;	/* pointer to the type descriptor */
1645b81b6b3SRodney W. Grimes 	int	flags;
1655b81b6b3SRodney W. Grimes #define	FD_OPEN		0x01	/* it's open		*/
1665b81b6b3SRodney W. Grimes #define	FD_ACTIVE	0x02	/* it's active		*/
1675b81b6b3SRodney W. Grimes #define	FD_MOTOR	0x04	/* motor should be on	*/
1685b81b6b3SRodney W. Grimes #define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
1695b81b6b3SRodney W. Grimes 	int	skip;
1705b81b6b3SRodney W. Grimes 	int	hddrv;
171dc5df763SJoerg Wunsch #define FD_NO_TRACK -2
1725b81b6b3SRodney W. Grimes 	int	track;		/* where we think the head is */
1733a2f7427SDavid Greenman 	int	options;	/* user configurable options, see ioctl_fd.h */
17402a19910SJustin T. Gibbs 	struct	callout_handle toffhandle;
17502a19910SJustin T. Gibbs 	struct	callout_handle tohandle;
176b2dfb1f9SJustin T. Gibbs 	struct	devstat device_stats;
1776182fdbdSPeter Wemm 	device_t dev;
1786182fdbdSPeter Wemm 	fdu_t	fdu;
1796182fdbdSPeter Wemm };
18037286586SPeter Wemm 
18137286586SPeter Wemm struct fdc_ivars {
18237286586SPeter Wemm 	int	fdunit;
18337286586SPeter Wemm };
1846182fdbdSPeter Wemm static devclass_t fd_devclass;
1855b81b6b3SRodney W. Grimes 
1865b81b6b3SRodney W. Grimes /***********************************************************************\
1875b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used:		*
1885b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question	*
1895b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller		*
1905b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number					*
1915b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number				*
1925b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit)	*
1935b81b6b3SRodney W. Grimes \***********************************************************************/
194b99f0a4aSAndrew Moore 
1953a2f7427SDavid Greenman /* needed for ft driver, thus exported */
1966182fdbdSPeter Wemm int in_fdc(struct fdc_data *);
1976182fdbdSPeter Wemm int out_fdc(struct fdc_data *, int);
1983a2f7427SDavid Greenman 
1993a2f7427SDavid Greenman /* internal functions */
2006182fdbdSPeter Wemm static	void fdc_intr(void *);
2016182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int);
2023a2f7427SDavid Greenman #  define TURNON 1
2033a2f7427SDavid Greenman #  define TURNOFF 0
2043a2f7427SDavid Greenman static timeout_t fd_turnoff;
2053a2f7427SDavid Greenman static timeout_t fd_motor_on;
2066182fdbdSPeter Wemm static void fd_turnon(struct fd_data *);
2073a2f7427SDavid Greenman static void fdc_reset(fdc_p);
2086182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *);
2096182fdbdSPeter Wemm static void fdstart(struct fdc_data *);
2105c1a1eaeSBruce Evans static timeout_t fd_iotimeout;
2113a2f7427SDavid Greenman static timeout_t fd_pseudointr;
2126182fdbdSPeter Wemm static int fdstate(struct fdc_data *);
2136182fdbdSPeter Wemm static int retrier(struct fdc_data *);
2143a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *);
2153a2f7427SDavid Greenman 
216d66c374fSTor Egge static int enable_fifo(fdc_p fdc);
217d66c374fSTor Egge 
218d66c374fSTor Egge static int fifo_threshold = 8;	/* XXX: should be accessible via sysctl */
219d66c374fSTor Egge 
220aaf08d94SGarrett Wollman 
2215b81b6b3SRodney W. Grimes #define DEVIDLE		0
2225b81b6b3SRodney W. Grimes #define FINDWORK	1
2235b81b6b3SRodney W. Grimes #define	DOSEEK		2
2245b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 	3
2255b81b6b3SRodney W. Grimes #define	IOCOMPLETE	4
2265b81b6b3SRodney W. Grimes #define RECALCOMPLETE	5
2275b81b6b3SRodney W. Grimes #define	STARTRECAL	6
2285b81b6b3SRodney W. Grimes #define	RESETCTLR	7
2295b81b6b3SRodney W. Grimes #define	SEEKWAIT	8
2305b81b6b3SRodney W. Grimes #define	RECALWAIT	9
2315b81b6b3SRodney W. Grimes #define	MOTORWAIT	10
2325b81b6b3SRodney W. Grimes #define	IOTIMEDOUT	11
2335c1a1eaeSBruce Evans #define	RESETCOMPLETE	12
23469acd21dSWarner Losh #define PIOREAD		13
2355b81b6b3SRodney W. Grimes 
236d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
237cba2a7c6SBruce Evans static char const * const fdstates[] =
2385b81b6b3SRodney W. Grimes {
2395b81b6b3SRodney W. Grimes "DEVIDLE",
2405b81b6b3SRodney W. Grimes "FINDWORK",
2415b81b6b3SRodney W. Grimes "DOSEEK",
2425b81b6b3SRodney W. Grimes "SEEKCOMPLETE",
2435b81b6b3SRodney W. Grimes "IOCOMPLETE",
2445b81b6b3SRodney W. Grimes "RECALCOMPLETE",
2455b81b6b3SRodney W. Grimes "STARTRECAL",
2465b81b6b3SRodney W. Grimes "RESETCTLR",
2475b81b6b3SRodney W. Grimes "SEEKWAIT",
2485b81b6b3SRodney W. Grimes "RECALWAIT",
2495b81b6b3SRodney W. Grimes "MOTORWAIT",
2505c1a1eaeSBruce Evans "IOTIMEDOUT",
2515c1a1eaeSBruce Evans "RESETCOMPLETE",
252ff9607b0SBruce Evans "PIOREAD",
2535b81b6b3SRodney W. Grimes };
2545b81b6b3SRodney W. Grimes 
2553a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */
256cba2a7c6SBruce Evans static int volatile fd_debug = 0;
2575b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg)
2585b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
259d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */
2605b81b6b3SRodney W. Grimes #define TRACE0(arg)
2615b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2)
262d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */
2635b81b6b3SRodney W. Grimes 
264427ccf00SDoug Rabson static void
265427ccf00SDoug Rabson fdout_wr(fdc_p fdc, u_int8_t v)
266427ccf00SDoug Rabson {
267427ccf00SDoug Rabson 	bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v);
268427ccf00SDoug Rabson }
269427ccf00SDoug Rabson 
270427ccf00SDoug Rabson static u_int8_t
271427ccf00SDoug Rabson fdsts_rd(fdc_p fdc)
272427ccf00SDoug Rabson {
273427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off);
274427ccf00SDoug Rabson }
275427ccf00SDoug Rabson 
276427ccf00SDoug Rabson static void
277427ccf00SDoug Rabson fddata_wr(fdc_p fdc, u_int8_t v)
278427ccf00SDoug Rabson {
279427ccf00SDoug Rabson 	bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v);
280427ccf00SDoug Rabson }
281427ccf00SDoug Rabson 
282427ccf00SDoug Rabson static u_int8_t
283427ccf00SDoug Rabson fddata_rd(fdc_p fdc)
284427ccf00SDoug Rabson {
285427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off);
286427ccf00SDoug Rabson }
287427ccf00SDoug Rabson 
288427ccf00SDoug Rabson static void
289427ccf00SDoug Rabson fdctl_wr(fdc_p fdc, u_int8_t v)
290427ccf00SDoug Rabson {
291427ccf00SDoug Rabson 	if (fdc->flags & FDC_ISPNP)
292427ccf00SDoug Rabson 		bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v);
293427ccf00SDoug Rabson 	else
294427ccf00SDoug Rabson 		bus_space_write_1(fdc->portt, fdc->porth, FDCTL, v);
295427ccf00SDoug Rabson }
296427ccf00SDoug Rabson 
297427ccf00SDoug Rabson #if 0
298427ccf00SDoug Rabson 
299427ccf00SDoug Rabson static u_int8_t
300427ccf00SDoug Rabson fdin_rd(fdc_p fdc)
301427ccf00SDoug Rabson {
302427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDIN);
303427ccf00SDoug Rabson }
304427ccf00SDoug Rabson 
305427ccf00SDoug Rabson #endif
306427ccf00SDoug Rabson 
30769acd21dSWarner Losh #ifdef FDC_YE
30869acd21dSWarner Losh #if NCARD > 0
30969acd21dSWarner Losh #include <sys/select.h>
310d17e4ee6SPeter Wemm #include <sys/module.h>
31169acd21dSWarner Losh #include <pccard/cardinfo.h>
31269acd21dSWarner Losh #include <pccard/driver.h>
31369acd21dSWarner Losh #include <pccard/slot.h>
31469acd21dSWarner Losh 
31569acd21dSWarner Losh /*
31669acd21dSWarner Losh  *	PC-Card (PCMCIA) specific code.
31769acd21dSWarner Losh  */
31869acd21dSWarner Losh static int yeinit(struct pccard_devinfo *);		/* init device */
31969acd21dSWarner Losh static void yeunload(struct pccard_devinfo *); 		/* Disable driver */
32069acd21dSWarner Losh static int yeintr(struct pccard_devinfo *); 		/* Interrupt handler */
32169acd21dSWarner Losh 
322d17e4ee6SPeter Wemm PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask);
32369acd21dSWarner Losh 
32469acd21dSWarner Losh /*
32569acd21dSWarner Losh  *	Initialize the device - called from Slot manager.
32669acd21dSWarner Losh  */
32769acd21dSWarner Losh static int yeinit(struct pccard_devinfo *devi)
32869acd21dSWarner Losh {
32969acd21dSWarner Losh 	fdc_p fdc = &fdc_data[devi->isahd.id_unit];
33069acd21dSWarner Losh 
33169acd21dSWarner Losh 	fdc->baseport = devi->isahd.id_iobase;
33269acd21dSWarner Losh 	/*
33369acd21dSWarner Losh 	 * reset controller
33469acd21dSWarner Losh 	 */
335427ccf00SDoug Rabson 	fdout_wr(fdc, 0);
33669acd21dSWarner Losh 	DELAY(100);
337427ccf00SDoug Rabson 	fdout_wr(fdc, FDO_FRST);
33869acd21dSWarner Losh 
33969acd21dSWarner Losh 	/*
34069acd21dSWarner Losh 	 * wire into system
34169acd21dSWarner Losh 	 */
34269acd21dSWarner Losh 	if (yeattach(&devi->isahd) == 0)
34369acd21dSWarner Losh 		return(ENXIO);
34469acd21dSWarner Losh 
34569acd21dSWarner Losh 	return(0);
34669acd21dSWarner Losh }
34769acd21dSWarner Losh 
34869acd21dSWarner Losh /*
34969acd21dSWarner Losh  *	yeunload - unload the driver and clear the table.
35069acd21dSWarner Losh  *	XXX TODO:
35169acd21dSWarner Losh  *	This is usually called when the card is ejected, but
35269acd21dSWarner Losh  *	can be caused by a modunload of a controller driver.
35369acd21dSWarner Losh  *	The idea is to reset the driver's view of the device
35469acd21dSWarner Losh  *	and ensure that any driver entry points such as
35569acd21dSWarner Losh  *	read and write do not hang.
35669acd21dSWarner Losh  */
35769acd21dSWarner Losh static void yeunload(struct pccard_devinfo *devi)
35869acd21dSWarner Losh {
35969acd21dSWarner Losh 	if (fd_data[devi->isahd.id_unit].type == NO_TYPE)
36069acd21dSWarner Losh 		return;
36169acd21dSWarner Losh 
36269acd21dSWarner Losh 	/*
36369acd21dSWarner Losh 	 * this prevents Fdopen() and fdstrategy() from attempting
36469acd21dSWarner Losh 	 * to access unloaded controller
36569acd21dSWarner Losh 	 */
36669acd21dSWarner Losh 	fd_data[devi->isahd.id_unit].type = NO_TYPE;
36769acd21dSWarner Losh 
36869acd21dSWarner Losh 	printf("fdc%d: unload\n", devi->isahd.id_unit);
36969acd21dSWarner Losh }
37069acd21dSWarner Losh 
37169acd21dSWarner Losh /*
37269acd21dSWarner Losh  *	yeintr - Shared interrupt called from
37369acd21dSWarner Losh  *	front end of PC-Card handler.
37469acd21dSWarner Losh  */
37569acd21dSWarner Losh static int yeintr(struct pccard_devinfo *devi)
37669acd21dSWarner Losh {
37769acd21dSWarner Losh 	fdintr((fdcu_t)devi->isahd.id_unit);
37869acd21dSWarner Losh 	return(1);
37969acd21dSWarner Losh }
38069acd21dSWarner Losh #endif /* NCARD > 0 */
38169acd21dSWarner Losh #endif /* FDC_YE */
38269acd21dSWarner Losh 
38387f6c662SJulian Elischer static	d_open_t	Fdopen;	/* NOTE, not fdopen */
38487f6c662SJulian Elischer static	d_close_t	fdclose;
38587f6c662SJulian Elischer static	d_ioctl_t	fdioctl;
38687f6c662SJulian Elischer static	d_strategy_t	fdstrategy;
38787f6c662SJulian Elischer 
38887f6c662SJulian Elischer #define CDEV_MAJOR 9
38987f6c662SJulian Elischer #define BDEV_MAJOR 2
390f7ea2f55SJulian Elischer 
3914e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = {
3924e2f199eSPoul-Henning Kamp 	/* open */	Fdopen,
3934e2f199eSPoul-Henning Kamp 	/* close */	fdclose,
3944e2f199eSPoul-Henning Kamp 	/* read */	physread,
3954e2f199eSPoul-Henning Kamp 	/* write */	physwrite,
3964e2f199eSPoul-Henning Kamp 	/* ioctl */	fdioctl,
3974e2f199eSPoul-Henning Kamp 	/* poll */	nopoll,
3984e2f199eSPoul-Henning Kamp 	/* mmap */	nommap,
3994e2f199eSPoul-Henning Kamp 	/* strategy */	fdstrategy,
4004e2f199eSPoul-Henning Kamp 	/* name */	"fd",
4014e2f199eSPoul-Henning Kamp 	/* maj */	CDEV_MAJOR,
4024e2f199eSPoul-Henning Kamp 	/* dump */	nodump,
4034e2f199eSPoul-Henning Kamp 	/* psize */	nopsize,
4044e2f199eSPoul-Henning Kamp 	/* flags */	D_DISK,
4054e2f199eSPoul-Henning Kamp 	/* bmaj */	BDEV_MAJOR
4064e2f199eSPoul-Henning Kamp };
4074e2f199eSPoul-Henning Kamp 
408dc5df763SJoerg Wunsch static int
4096182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s)
410dc5df763SJoerg Wunsch {
4116182fdbdSPeter Wemm 	fdc->fdc_errs++;
41216b04b6aSJoerg Wunsch 	if (s) {
4136182fdbdSPeter Wemm 		if (fdc->fdc_errs < FDC_ERRMAX) {
4146182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
4156182fdbdSPeter Wemm 			printf("%s", s);
4166182fdbdSPeter Wemm 		} else if (fdc->fdc_errs == FDC_ERRMAX) {
4176182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
4186182fdbdSPeter Wemm 			printf("too many errors, not logging any more\n");
4196182fdbdSPeter Wemm 		}
42016b04b6aSJoerg Wunsch 	}
421dc5df763SJoerg Wunsch 
422dc5df763SJoerg Wunsch 	return FD_FAILED;
423dc5df763SJoerg Wunsch }
424dc5df763SJoerg Wunsch 
425dc5df763SJoerg Wunsch /*
426dc5df763SJoerg Wunsch  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
427dc5df763SJoerg Wunsch  * Unit number,
428dc5df763SJoerg Wunsch  * # of output bytes, output bytes as ints ...,
429dc5df763SJoerg Wunsch  * # of input bytes, input bytes as ints ...
430dc5df763SJoerg Wunsch  */
4316f4e0bebSPoul-Henning Kamp static int
4326182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...)
433dc5df763SJoerg Wunsch {
434dc5df763SJoerg Wunsch 	u_char cmd;
435dc5df763SJoerg Wunsch 	int n_in;
436dc5df763SJoerg Wunsch 	int n;
437dc5df763SJoerg Wunsch 	va_list ap;
438dc5df763SJoerg Wunsch 
439dc5df763SJoerg Wunsch 	va_start(ap, n_out);
440dc5df763SJoerg Wunsch 	cmd = (u_char)(va_arg(ap, int));
441dc5df763SJoerg Wunsch 	va_end(ap);
442dc5df763SJoerg Wunsch 	va_start(ap, n_out);
443dc5df763SJoerg Wunsch 	for (n = 0; n < n_out; n++)
444dc5df763SJoerg Wunsch 	{
4456182fdbdSPeter Wemm 		if (out_fdc(fdc, va_arg(ap, int)) < 0)
446dc5df763SJoerg Wunsch 		{
447dc5df763SJoerg Wunsch 			char msg[50];
4482127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
449dc5df763SJoerg Wunsch 				"cmd %x failed at out byte %d of %d\n",
450dc5df763SJoerg Wunsch 				cmd, n + 1, n_out);
4516182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
452dc5df763SJoerg Wunsch 		}
453dc5df763SJoerg Wunsch 	}
454dc5df763SJoerg Wunsch 	n_in = va_arg(ap, int);
455dc5df763SJoerg Wunsch 	for (n = 0; n < n_in; n++)
456dc5df763SJoerg Wunsch 	{
457dc5df763SJoerg Wunsch 		int *ptr = va_arg(ap, int *);
4586182fdbdSPeter Wemm 		if (fd_in(fdc, ptr) < 0)
459dc5df763SJoerg Wunsch 		{
460dc5df763SJoerg Wunsch 			char msg[50];
4612127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
462dc5df763SJoerg Wunsch 				"cmd %02x failed at in byte %d of %d\n",
463dc5df763SJoerg Wunsch 				cmd, n + 1, n_in);
4646182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
465dc5df763SJoerg Wunsch 		}
466dc5df763SJoerg Wunsch 	}
467dc5df763SJoerg Wunsch 
468dc5df763SJoerg Wunsch 	return 0;
469dc5df763SJoerg Wunsch }
470dc5df763SJoerg Wunsch 
4716f4e0bebSPoul-Henning Kamp static int
472d66c374fSTor Egge enable_fifo(fdc_p fdc)
473d66c374fSTor Egge {
474d66c374fSTor Egge 	int i, j;
475d66c374fSTor Egge 
476d66c374fSTor Egge 	if ((fdc->flags & FDC_HAS_FIFO) == 0) {
477d66c374fSTor Egge 
478d66c374fSTor Egge 		/*
479d66c374fSTor Egge 		 * XXX:
480d66c374fSTor Egge 		 * Cannot use fd_cmd the normal way here, since
481d66c374fSTor Egge 		 * this might be an invalid command. Thus we send the
482d66c374fSTor Egge 		 * first byte, and check for an early turn of data directon.
483d66c374fSTor Egge 		 */
484d66c374fSTor Egge 
4856182fdbdSPeter Wemm 		if (out_fdc(fdc, I8207X_CONFIGURE) < 0)
4866182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
487d66c374fSTor Egge 
488d66c374fSTor Egge 		/* If command is invalid, return */
489d66c374fSTor Egge 		j = 100000;
490427ccf00SDoug Rabson 		while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM))
491d66c374fSTor Egge 		       != NE7_RQM && j-- > 0)
492d66c374fSTor Egge 			if (i == (NE7_DIO | NE7_RQM)) {
493d66c374fSTor Egge 				fdc_reset(fdc);
494d66c374fSTor Egge 				return FD_FAILED;
495d66c374fSTor Egge 			}
496d66c374fSTor Egge 		if (j<0 ||
4976182fdbdSPeter Wemm 		    fd_cmd(fdc, 3,
498d66c374fSTor Egge 			   0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) {
499d66c374fSTor Egge 			fdc_reset(fdc);
5006182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
501d66c374fSTor Egge 		}
502d66c374fSTor Egge 		fdc->flags |= FDC_HAS_FIFO;
503d66c374fSTor Egge 		return 0;
504d66c374fSTor Egge 	}
5056182fdbdSPeter Wemm 	if (fd_cmd(fdc, 4,
506d66c374fSTor Egge 		   I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0)
5076182fdbdSPeter Wemm 		return fdc_err(fdc, "Re-enable FIFO failed\n");
508d66c374fSTor Egge 	return 0;
509d66c374fSTor Egge }
510d66c374fSTor Egge 
511d66c374fSTor Egge static int
512dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p)
513dc5df763SJoerg Wunsch {
514dc5df763SJoerg Wunsch 	int st3;
515dc5df763SJoerg Wunsch 
5166182fdbdSPeter Wemm 	if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
517dc5df763SJoerg Wunsch 	{
5186182fdbdSPeter Wemm 		return fdc_err(fdc, "Sense Drive Status failed\n");
519dc5df763SJoerg Wunsch 	}
520dc5df763SJoerg Wunsch 	if (st3p)
521dc5df763SJoerg Wunsch 		*st3p = st3;
522dc5df763SJoerg Wunsch 
523dc5df763SJoerg Wunsch 	return 0;
524dc5df763SJoerg Wunsch }
525dc5df763SJoerg Wunsch 
5266f4e0bebSPoul-Henning Kamp static int
527dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
528dc5df763SJoerg Wunsch {
5296182fdbdSPeter Wemm 	int cyl, st0, ret;
530dc5df763SJoerg Wunsch 
5316182fdbdSPeter Wemm 	ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0);
5326182fdbdSPeter Wemm 	if (ret) {
5336182fdbdSPeter Wemm 		(void)fdc_err(fdc,
534dc5df763SJoerg Wunsch 			      "sense intr err reading stat reg 0\n");
535dc5df763SJoerg Wunsch 		return ret;
536dc5df763SJoerg Wunsch 	}
537dc5df763SJoerg Wunsch 
538dc5df763SJoerg Wunsch 	if (st0p)
539dc5df763SJoerg Wunsch 		*st0p = st0;
540dc5df763SJoerg Wunsch 
5416182fdbdSPeter Wemm 	if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) {
542dc5df763SJoerg Wunsch 		/*
543dc5df763SJoerg Wunsch 		 * There doesn't seem to have been an interrupt.
544dc5df763SJoerg Wunsch 		 */
545dc5df763SJoerg Wunsch 		return FD_NOT_VALID;
546dc5df763SJoerg Wunsch 	}
547dc5df763SJoerg Wunsch 
5486182fdbdSPeter Wemm 	if (fd_in(fdc, &cyl) < 0) {
5496182fdbdSPeter Wemm 		return fdc_err(fdc, "can't get cyl num\n");
550dc5df763SJoerg Wunsch 	}
551dc5df763SJoerg Wunsch 
552dc5df763SJoerg Wunsch 	if (cylp)
553dc5df763SJoerg Wunsch 		*cylp = cyl;
554dc5df763SJoerg Wunsch 
555dc5df763SJoerg Wunsch 	return 0;
556dc5df763SJoerg Wunsch }
557dc5df763SJoerg Wunsch 
558dc5df763SJoerg Wunsch 
5596f4e0bebSPoul-Henning Kamp static int
560dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu)
561dc5df763SJoerg Wunsch {
562dc5df763SJoerg Wunsch 	int i, ret;
563b5e8ce9fSBruce Evans 
5646182fdbdSPeter Wemm 	for (i = 0; i < 7; i++) {
565b5e8ce9fSBruce Evans 		/*
566b5e8ce9fSBruce Evans 		 * XXX types are poorly chosen.  Only bytes can by read
567a838d83dSBruce Evans 		 * from the hardware, but fdc->status[] wants u_ints and
568b5e8ce9fSBruce Evans 		 * fd_in() gives ints.
569b5e8ce9fSBruce Evans 		 */
570b5e8ce9fSBruce Evans 		int status;
571b5e8ce9fSBruce Evans 
5726182fdbdSPeter Wemm 		ret = fd_in(fdc, &status);
573b5e8ce9fSBruce Evans 		fdc->status[i] = status;
574b5e8ce9fSBruce Evans 		if (ret != 0)
575dc5df763SJoerg Wunsch 			break;
576dc5df763SJoerg Wunsch 	}
577dc5df763SJoerg Wunsch 
578dc5df763SJoerg Wunsch 	if (ret == 0)
579dc5df763SJoerg Wunsch 		fdc->flags |= FDC_STAT_VALID;
580dc5df763SJoerg Wunsch 	else
581dc5df763SJoerg Wunsch 		fdc->flags &= ~FDC_STAT_VALID;
582dc5df763SJoerg Wunsch 
583dc5df763SJoerg Wunsch 	return ret;
584dc5df763SJoerg Wunsch }
585dc5df763SJoerg Wunsch 
5865b81b6b3SRodney W. Grimes /****************************************************************************/
5875b81b6b3SRodney W. Grimes /*                      autoconfiguration stuff                             */
5885b81b6b3SRodney W. Grimes /****************************************************************************/
589dc5df763SJoerg Wunsch 
5903a2f7427SDavid Greenman static int
59137286586SPeter Wemm fdc_alloc_resources(struct fdc_data *fdc)
5925b81b6b3SRodney W. Grimes {
59337286586SPeter Wemm 	device_t dev;
59437286586SPeter Wemm 	int ispnp;
5955b81b6b3SRodney W. Grimes 
59637286586SPeter Wemm 	dev = fdc->fdc_dev;
59737286586SPeter Wemm 	ispnp = fdc->fdc_ispnp;
5986182fdbdSPeter Wemm 	fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0;
5996182fdbdSPeter Wemm 	fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0;
6006182fdbdSPeter Wemm 
6016182fdbdSPeter Wemm 	fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
6026182fdbdSPeter Wemm 					     &fdc->rid_ioport, 0ul, ~0ul,
603427ccf00SDoug Rabson 					     ispnp ? 1 : IO_FDCSIZE,
604427ccf00SDoug Rabson 					     RF_ACTIVE);
6056182fdbdSPeter Wemm 	if (fdc->res_ioport == 0) {
606427ccf00SDoug Rabson 		device_printf(dev, "cannot reserve I/O port range\n");
60737286586SPeter Wemm 		return ENXIO;
6086182fdbdSPeter Wemm 	}
609427ccf00SDoug Rabson 	fdc->portt = rman_get_bustag(fdc->res_ioport);
610427ccf00SDoug Rabson 	fdc->porth = rman_get_bushandle(fdc->res_ioport);
611427ccf00SDoug Rabson 
612427ccf00SDoug Rabson 	/*
613a2639a18SPeter Wemm 	 * Some bios' report the device at 0x3f2-0x3f5,0x3f7 and some at
614a2639a18SPeter Wemm 	 * 0x3f0-0x3f5,0x3f7. We detect the former by checking the size
615a2639a18SPeter Wemm 	 * and adjust the port address accordingly.
616a2639a18SPeter Wemm 	 *
617a2639a18SPeter Wemm 	 * And some (!!) report 0x3f2-0x3f5 and completely leave out the
618a2639a18SPeter Wemm 	 * control register!  It seems that some non-antique controller chips
619a2639a18SPeter Wemm 	 * have a different method of programming the transfer speed which
620a2639a18SPeter Wemm 	 * doesn't require the control register, but it's mighty bogus as the
621a2639a18SPeter Wemm 	 * chip still responds to the address for the control register.
622427ccf00SDoug Rabson 	 */
623a2639a18SPeter Wemm 	if (ispnp) {
624b9da888fSPeter Wemm 		int cntport0;
625b9da888fSPeter Wemm 		int cntport1;
626b9da888fSPeter Wemm 		u_long ctlstart;
627b9da888fSPeter Wemm 		u_long ctlend;
628b9da888fSPeter Wemm 
629b9da888fSPeter Wemm 		cntport0 = bus_get_resource_count(dev, SYS_RES_IOPORT, 0);
630b9da888fSPeter Wemm 		cntport1 = bus_get_resource_count(dev, SYS_RES_IOPORT, 1);
631b9da888fSPeter Wemm 		ctlstart = 0ul;
632b9da888fSPeter Wemm 		ctlend = ~0ul;
633b9da888fSPeter Wemm 		if (cntport0 == 4)
634427ccf00SDoug Rabson 			fdc->port_off = -2;
635b9da888fSPeter Wemm 		if (cntport1 == 0) {
636b9da888fSPeter Wemm 			/* GRRR, request a specific port */
637b9da888fSPeter Wemm 			ctlstart = rman_get_start(fdc->res_ioport) +
638b9da888fSPeter Wemm 			    fdc->port_off + 7;	/* usually 0x3f7 */
639b9da888fSPeter Wemm 			ctlend = ctlstart;
640b9da888fSPeter Wemm 			if (bootverbose)
641b9da888fSPeter Wemm 				device_printf(dev, "added missing ctrl port\n");
642b9da888fSPeter Wemm 		}
643427ccf00SDoug Rabson 		fdc->flags |= FDC_ISPNP;
644427ccf00SDoug Rabson 		fdc->rid_ctl = 1;
645427ccf00SDoug Rabson 		fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT,
646b9da888fSPeter Wemm 						  &fdc->rid_ctl, ctlstart,
647b9da888fSPeter Wemm 						  ctlend, 1, RF_ACTIVE);
648427ccf00SDoug Rabson 		if (fdc->res_ctl == 0) {
64937286586SPeter Wemm 			device_printf(dev, "cannot reserve I/O port range 2\n");
65037286586SPeter Wemm 			return ENXIO;
651427ccf00SDoug Rabson 		}
652427ccf00SDoug Rabson 		fdc->ctlt = rman_get_bustag(fdc->res_ctl);
653427ccf00SDoug Rabson 		fdc->ctlh = rman_get_bushandle(fdc->res_ctl);
654427ccf00SDoug Rabson 	}
6556182fdbdSPeter Wemm 
6566182fdbdSPeter Wemm 	fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ,
6576182fdbdSPeter Wemm 					  &fdc->rid_irq, 0ul, ~0ul, 1,
6586182fdbdSPeter Wemm 					  RF_ACTIVE);
6596182fdbdSPeter Wemm 	if (fdc->res_irq == 0) {
660427ccf00SDoug Rabson 		device_printf(dev, "cannot reserve interrupt line\n");
66137286586SPeter Wemm 		return ENXIO;
6626182fdbdSPeter Wemm 	}
6636182fdbdSPeter Wemm 	fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ,
6646182fdbdSPeter Wemm 					  &fdc->rid_drq, 0ul, ~0ul, 1,
6656182fdbdSPeter Wemm 					  RF_ACTIVE);
6666182fdbdSPeter Wemm 	if (fdc->res_drq == 0) {
667427ccf00SDoug Rabson 		device_printf(dev, "cannot reserve DMA request line\n");
66837286586SPeter Wemm 		return ENXIO;
6696182fdbdSPeter Wemm 	}
6706182fdbdSPeter Wemm 	fdc->dmachan = fdc->res_drq->r_start;
67137286586SPeter Wemm 
67237286586SPeter Wemm 	return 0;
67337286586SPeter Wemm }
67437286586SPeter Wemm 
67537286586SPeter Wemm static void
67637286586SPeter Wemm fdc_release_resources(struct fdc_data *fdc)
67737286586SPeter Wemm {
67837286586SPeter Wemm 	device_t dev;
67937286586SPeter Wemm 
68037286586SPeter Wemm 	dev = fdc->fdc_dev;
68137286586SPeter Wemm 	if (fdc->res_irq != 0) {
68237286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
68337286586SPeter Wemm 					fdc->res_irq);
68437286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
68537286586SPeter Wemm 				     fdc->res_irq);
68637286586SPeter Wemm 	}
68737286586SPeter Wemm 	if (fdc->res_ctl != 0) {
68837286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
68937286586SPeter Wemm 					fdc->res_ctl);
69037286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
69137286586SPeter Wemm 				     fdc->res_ctl);
69237286586SPeter Wemm 	}
69337286586SPeter Wemm 	if (fdc->res_ioport != 0) {
69437286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
69537286586SPeter Wemm 					fdc->res_ioport);
69637286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
69737286586SPeter Wemm 				     fdc->res_ioport);
69837286586SPeter Wemm 	}
69937286586SPeter Wemm 	if (fdc->res_drq != 0) {
70037286586SPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
70137286586SPeter Wemm 					fdc->res_drq);
70237286586SPeter Wemm 		bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
70337286586SPeter Wemm 				     fdc->res_drq);
70437286586SPeter Wemm 	}
70537286586SPeter Wemm }
70637286586SPeter Wemm 
70737286586SPeter Wemm /****************************************************************************/
70837286586SPeter Wemm /*                      autoconfiguration stuff                             */
70937286586SPeter Wemm /****************************************************************************/
71037286586SPeter Wemm 
71137286586SPeter Wemm static struct isa_pnp_id fdc_ids[] = {
71237286586SPeter Wemm 	{0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */
71337286586SPeter Wemm 	{0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */
71437286586SPeter Wemm 	{0}
71537286586SPeter Wemm };
71637286586SPeter Wemm 
71737286586SPeter Wemm static int
71837286586SPeter Wemm fdc_read_ivar(device_t dev, device_t child, int which, u_long *result)
71937286586SPeter Wemm {
72037286586SPeter Wemm 	struct fdc_ivars *ivars = device_get_ivars(child);
72137286586SPeter Wemm 
72237286586SPeter Wemm 	switch (which) {
72337286586SPeter Wemm 	case FDC_IVAR_FDUNIT:
72437286586SPeter Wemm 		*result = ivars->fdunit;
72537286586SPeter Wemm 		break;
72637286586SPeter Wemm 	default:
72737286586SPeter Wemm 		return ENOENT;
72837286586SPeter Wemm 	}
72937286586SPeter Wemm 	return 0;
73037286586SPeter Wemm }
73137286586SPeter Wemm 
73237286586SPeter Wemm /*
73337286586SPeter Wemm  * fdc controller section.
73437286586SPeter Wemm  */
73537286586SPeter Wemm static int
73637286586SPeter Wemm fdc_probe(device_t dev)
73737286586SPeter Wemm {
73837286586SPeter Wemm 	int	error, ic_type;
73937286586SPeter Wemm 	struct	fdc_data *fdc;
74037286586SPeter Wemm 
74137286586SPeter Wemm 	fdc = device_get_softc(dev);
74237286586SPeter Wemm 	bzero(fdc, sizeof *fdc);
74337286586SPeter Wemm 	fdc->fdc_dev = dev;
74437286586SPeter Wemm 
74537286586SPeter Wemm 	/* Check pnp ids */
74637286586SPeter Wemm 	error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids);
74737286586SPeter Wemm 	if (error == ENXIO)
74837286586SPeter Wemm 		return ENXIO;
74937286586SPeter Wemm 	fdc->fdc_ispnp = (error == 0);
75037286586SPeter Wemm 
75137286586SPeter Wemm 	/* Attempt to allocate our resources for the duration of the probe */
75237286586SPeter Wemm 	error = fdc_alloc_resources(fdc);
75337286586SPeter Wemm 	if (error)
75437286586SPeter Wemm 		goto out;
7555b81b6b3SRodney W. Grimes 
75616111cedSAndrew Moore 	/* First - lets reset the floppy controller */
757427ccf00SDoug Rabson 	fdout_wr(fdc, 0);
75816111cedSAndrew Moore 	DELAY(100);
759427ccf00SDoug Rabson 	fdout_wr(fdc, FDO_FRST);
76016111cedSAndrew Moore 
7615b81b6b3SRodney W. Grimes 	/* see if it can handle a command */
7626182fdbdSPeter Wemm 	if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240),
7636182fdbdSPeter Wemm 		   NE7_SPEC_2(2, 0), 0)) {
7646182fdbdSPeter Wemm 		error = ENXIO;
7656182fdbdSPeter Wemm 		goto out;
7665b81b6b3SRodney W. Grimes 	}
7676182fdbdSPeter Wemm 
7686182fdbdSPeter Wemm 	if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) {
7696182fdbdSPeter Wemm 		ic_type = (u_char)ic_type;
7706182fdbdSPeter Wemm 		switch (ic_type) {
7716182fdbdSPeter Wemm 		case 0x80:
7726182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 765 or clone");
7736182fdbdSPeter Wemm 			fdc->fdct = FDC_NE765;
7746182fdbdSPeter Wemm 			break;
7756182fdbdSPeter Wemm 		case 0x81:
7766182fdbdSPeter Wemm 			device_set_desc(dev, "Intel 82077 or clone");
7776182fdbdSPeter Wemm 			fdc->fdct = FDC_I82077;
7786182fdbdSPeter Wemm 			break;
7796182fdbdSPeter Wemm 		case 0x90:
7806182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 72065B or clone");
7816182fdbdSPeter Wemm 			fdc->fdct = FDC_NE72065;
7826182fdbdSPeter Wemm 			break;
7836182fdbdSPeter Wemm 		default:
7846182fdbdSPeter Wemm 			device_set_desc(dev, "generic floppy controller");
7856182fdbdSPeter Wemm 			fdc->fdct = FDC_UNKNOWN;
7866182fdbdSPeter Wemm 			break;
7876182fdbdSPeter Wemm 		}
7886182fdbdSPeter Wemm 	}
7896182fdbdSPeter Wemm 
7906182fdbdSPeter Wemm out:
79137286586SPeter Wemm 	fdc_release_resources(fdc);
7926182fdbdSPeter Wemm 	return (error);
7935b81b6b3SRodney W. Grimes }
7945b81b6b3SRodney W. Grimes 
7955b81b6b3SRodney W. Grimes /*
79637286586SPeter Wemm  * Add a child device to the fdc controller.  It will then be probed etc.
7975b81b6b3SRodney W. Grimes  */
7986182fdbdSPeter Wemm static void
79937286586SPeter Wemm fdc_add_child(device_t dev, const char *name, int unit)
8005b81b6b3SRodney W. Grimes {
80137286586SPeter Wemm 	int	disabled;
80237286586SPeter Wemm 	struct fdc_ivars *ivar;
8036182fdbdSPeter Wemm 	device_t child;
80492200632SGarrett Wollman 
8056182fdbdSPeter Wemm 	ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT);
80637286586SPeter Wemm 	if (ivar == NULL)
8076182fdbdSPeter Wemm 		return;
80837286586SPeter Wemm 	bzero(ivar, sizeof *ivar);
80937286586SPeter Wemm 	if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0)
81037286586SPeter Wemm 		ivar->fdunit = 0;
811fe0d4089SMatthew N. Dodd 	child = device_add_child(dev, name, unit);
81237286586SPeter Wemm 	if (child == NULL)
8136182fdbdSPeter Wemm 		return;
81437286586SPeter Wemm 	device_set_ivars(child, ivar);
815a97c75b7SDoug Rabson 	if (resource_int_value(name, unit, "disabled", &disabled) == 0
816a97c75b7SDoug Rabson 	    && disabled != 0)
8176182fdbdSPeter Wemm 		device_disable(child);
8186182fdbdSPeter Wemm }
8196182fdbdSPeter Wemm 
8206182fdbdSPeter Wemm static int
8216182fdbdSPeter Wemm fdc_attach(device_t dev)
8226182fdbdSPeter Wemm {
82337286586SPeter Wemm 	struct	fdc_data *fdc;
82437286586SPeter Wemm 	int	i, error;
825427ccf00SDoug Rabson 
82637286586SPeter Wemm 	fdc = device_get_softc(dev);
82737286586SPeter Wemm 	error = fdc_alloc_resources(fdc);
82837286586SPeter Wemm 	if (error) {
82937286586SPeter Wemm 		device_printf(dev, "cannot re-aquire resources\n");
83037286586SPeter Wemm 		return error;
83137286586SPeter Wemm 	}
83237286586SPeter Wemm 	error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq,
83337286586SPeter Wemm 			       INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr);
83437286586SPeter Wemm 	if (error) {
83537286586SPeter Wemm 		device_printf(dev, "cannot setup interrupt\n");
83637286586SPeter Wemm 		return error;
83737286586SPeter Wemm 	}
83837286586SPeter Wemm 	fdc->fdcu = device_get_unit(dev);
8395b81b6b3SRodney W. Grimes 	fdc->flags |= FDC_ATTACHED;
8406182fdbdSPeter Wemm 
841100f78bbSSujal Patel 	/* Acquire the DMA channel forever, The driver will do the rest */
8426182fdbdSPeter Wemm 				/* XXX should integrate with rman */
843100f78bbSSujal Patel 	isa_dma_acquire(fdc->dmachan);
844dd87702aSBruce Evans 	isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */);
8455b81b6b3SRodney W. Grimes 	fdc->state = DEVIDLE;
8466182fdbdSPeter Wemm 
8473a2f7427SDavid Greenman 	/* reset controller, turn motor off, clear fdout mirror reg */
848427ccf00SDoug Rabson 	fdout_wr(fdc, ((fdc->fdout = 0)));
84902a19910SJustin T. Gibbs 	bufq_init(&fdc->head);
8505b81b6b3SRodney W. Grimes 
8516182fdbdSPeter Wemm 	/*
85237286586SPeter Wemm 	 * Probe and attach any children.  We should probably detect
85337286586SPeter Wemm 	 * devices from the BIOS unless overridden.
8546182fdbdSPeter Wemm 	 */
85537286586SPeter Wemm 	for (i = resource_query_string(-1, "at", device_get_nameunit(dev));
85637286586SPeter Wemm 	     i != -1;
85737286586SPeter Wemm 	     i = resource_query_string(i, "at", device_get_nameunit(dev)))
85837286586SPeter Wemm 		fdc_add_child(dev, resource_query_name(i),
85937286586SPeter Wemm 			       resource_query_unit(i));
86037286586SPeter Wemm 
8616182fdbdSPeter Wemm 	return (bus_generic_attach(dev));
8626182fdbdSPeter Wemm }
8636182fdbdSPeter Wemm 
86415317dd8SMatthew N. Dodd static int
8656182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child)
8666182fdbdSPeter Wemm {
86715317dd8SMatthew N. Dodd 	int retval = 0;
86815317dd8SMatthew N. Dodd 
86915317dd8SMatthew N. Dodd 	retval += bus_print_child_header(me, child);
87015317dd8SMatthew N. Dodd 	retval += printf(" on %s drive %d\n", device_get_nameunit(me),
87137286586SPeter Wemm 	       fdc_get_fdunit(child));
87215317dd8SMatthew N. Dodd 
87315317dd8SMatthew N. Dodd 	return (retval);
8746182fdbdSPeter Wemm }
8756182fdbdSPeter Wemm 
87616e68fc6SPeter Wemm static device_method_t fdc_methods[] = {
87716e68fc6SPeter Wemm 	/* Device interface */
87816e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fdc_probe),
87916e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fdc_attach),
88016e68fc6SPeter Wemm 	DEVMETHOD(device_detach,	bus_generic_detach),
88116e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
88216e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend),
88316e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume),
88416e68fc6SPeter Wemm 
88516e68fc6SPeter Wemm 	/* Bus interface */
88616e68fc6SPeter Wemm 	DEVMETHOD(bus_print_child,	fdc_print_child),
88737286586SPeter Wemm 	DEVMETHOD(bus_read_ivar,	fdc_read_ivar),
88816e68fc6SPeter Wemm 	/* Our children never use any other bus interface methods. */
88916e68fc6SPeter Wemm 
89016e68fc6SPeter Wemm 	{ 0, 0 }
89116e68fc6SPeter Wemm };
89216e68fc6SPeter Wemm 
89316e68fc6SPeter Wemm static driver_t fdc_driver = {
89416e68fc6SPeter Wemm 	"fdc",
89516e68fc6SPeter Wemm 	fdc_methods,
89616e68fc6SPeter Wemm 	sizeof(struct fdc_data)
89716e68fc6SPeter Wemm };
89816e68fc6SPeter Wemm 
89916e68fc6SPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0);
90016e68fc6SPeter Wemm 
90116e68fc6SPeter Wemm /******************************************************************/
90216e68fc6SPeter Wemm /*
90316e68fc6SPeter Wemm  * devices attached to the controller section.
90416e68fc6SPeter Wemm  */
9056182fdbdSPeter Wemm static int
9066182fdbdSPeter Wemm fd_probe(device_t dev)
9076182fdbdSPeter Wemm {
9086182fdbdSPeter Wemm 	int	i;
9096182fdbdSPeter Wemm 	u_int	fdt, st0, st3;
9106182fdbdSPeter Wemm 	struct	fd_data *fd;
9116182fdbdSPeter Wemm 	struct	fdc_data *fdc;
9126182fdbdSPeter Wemm 	fdsu_t	fdsu;
9136182fdbdSPeter Wemm 	static int fd_fifo = 0;
9146182fdbdSPeter Wemm 
9156182fdbdSPeter Wemm 	fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */
9166182fdbdSPeter Wemm 	fd = device_get_softc(dev);
9176182fdbdSPeter Wemm 	fdc = device_get_softc(device_get_parent(dev));
9186182fdbdSPeter Wemm 
9196182fdbdSPeter Wemm 	bzero(fd, sizeof *fd);
9206182fdbdSPeter Wemm 	fd->dev = dev;
9216182fdbdSPeter Wemm 	fd->fdc = fdc;
9226182fdbdSPeter Wemm 	fd->fdsu = fdsu;
9236182fdbdSPeter Wemm 	fd->fdu = device_get_unit(dev);
9246182fdbdSPeter Wemm 
925a97c75b7SDoug Rabson #ifdef __i386__
926b99f0a4aSAndrew Moore 	/* look up what bios thinks we have */
9276182fdbdSPeter Wemm 	switch (fd->fdu) {
9286182fdbdSPeter Wemm 	case 0:
929062acdb7SDoug Rabson 		if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0)
9300722d6abSJoerg Wunsch 			fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
9310722d6abSJoerg Wunsch 		else
9320722d6abSJoerg Wunsch 			fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
933b99f0a4aSAndrew Moore 		break;
9346182fdbdSPeter Wemm 	case 1:
9356182fdbdSPeter Wemm 		fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
936dc5df763SJoerg Wunsch 		break;
937dc5df763SJoerg Wunsch 	default:
9386182fdbdSPeter Wemm 		fdt = RTCFDT_NONE;
939dc5df763SJoerg Wunsch 		break;
9406b7bd95bSJoerg Wunsch 	}
941a97c75b7SDoug Rabson #else
942a97c75b7SDoug Rabson 	fdt = RTCFDT_144M;	/* XXX probably */
943a97c75b7SDoug Rabson #endif
9446182fdbdSPeter Wemm 
9456182fdbdSPeter Wemm 	/* is there a unit? */
9466182fdbdSPeter Wemm 	if (fdt == RTCFDT_NONE)
9476182fdbdSPeter Wemm 		return (ENXIO);
9486182fdbdSPeter Wemm 
9496182fdbdSPeter Wemm 	/* select it */
9506182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNON);
9516182fdbdSPeter Wemm 	DELAY(1000000);	/* 1 sec */
9526182fdbdSPeter Wemm 
9538de0675cSPeter Wemm 	/* XXX This doesn't work before the first set_motor() */
9546182fdbdSPeter Wemm 	if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN
955e34c71eaSJoerg Wunsch 	    && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0
9566182fdbdSPeter Wemm 	    && enable_fifo(fdc) == 0) {
9576182fdbdSPeter Wemm 		device_print_prettyname(device_get_parent(dev));
9586182fdbdSPeter Wemm 		printf("FIFO enabled, %d bytes threshold\n", fifo_threshold);
959d66c374fSTor Egge 	}
9606182fdbdSPeter Wemm 	fd_fifo = 1;
9616182fdbdSPeter Wemm 
9626182fdbdSPeter Wemm 	if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0)
9636182fdbdSPeter Wemm 	    && (st3 & NE7_ST3_T0)) {
964dc5df763SJoerg Wunsch 		/* if at track 0, first seek inwards */
965dc5df763SJoerg Wunsch 		/* seek some steps: */
9666182fdbdSPeter Wemm 		fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0);
967dc5df763SJoerg Wunsch 		DELAY(300000); /* ...wait a moment... */
9686182fdbdSPeter Wemm 		fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
969dc5df763SJoerg Wunsch 	}
970dc5df763SJoerg Wunsch 
971dc5df763SJoerg Wunsch 	/* If we're at track 0 first seek inwards. */
9726182fdbdSPeter Wemm 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
973dc5df763SJoerg Wunsch 		/* Seek some steps... */
9746182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
975dc5df763SJoerg Wunsch 			/* ...wait a moment... */
976dc5df763SJoerg Wunsch 			DELAY(300000);
977dc5df763SJoerg Wunsch 			/* make ctrlr happy: */
9786182fdbdSPeter Wemm 			fd_sense_int(fdc, 0, 0);
979dc5df763SJoerg Wunsch 		}
980dc5df763SJoerg Wunsch 	}
981dc5df763SJoerg Wunsch 
9826b7bd95bSJoerg Wunsch 	for (i = 0; i < 2; i++) {
9836b7bd95bSJoerg Wunsch 		/*
9846b7bd95bSJoerg Wunsch 		 * we must recalibrate twice, just in case the
9856b7bd95bSJoerg Wunsch 		 * heads have been beyond cylinder 76, since most
9866b7bd95bSJoerg Wunsch 		 * FDCs still barf when attempting to recalibrate
9876b7bd95bSJoerg Wunsch 		 * more than 77 steps
9886b7bd95bSJoerg Wunsch 		 */
989dc5df763SJoerg Wunsch 		/* go back to 0: */
9906182fdbdSPeter Wemm 		if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
9916b7bd95bSJoerg Wunsch 			/* a second being enough for full stroke seek*/
9926b7bd95bSJoerg Wunsch 			DELAY(i == 0 ? 1000000 : 300000);
9935b81b6b3SRodney W. Grimes 
9946b7bd95bSJoerg Wunsch 			/* anything responding? */
995dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
996dc5df763SJoerg Wunsch 			    (st0 & NE7_ST0_EC) == 0)
9976b7bd95bSJoerg Wunsch 				break; /* already probed succesfully */
9986b7bd95bSJoerg Wunsch 		}
999dc5df763SJoerg Wunsch 	}
10006b7bd95bSJoerg Wunsch 
10016182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNOFF);
10023a2f7427SDavid Greenman 
10033a2f7427SDavid Greenman 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
10046182fdbdSPeter Wemm 		return (ENXIO);
10055b81b6b3SRodney W. Grimes 
1006dc5df763SJoerg Wunsch 	fd->track = FD_NO_TRACK;
1007b99f0a4aSAndrew Moore 	fd->fdc = fdc;
1008b99f0a4aSAndrew Moore 	fd->fdsu = fdsu;
10093a2f7427SDavid Greenman 	fd->options = 0;
101002a19910SJustin T. Gibbs 	callout_handle_init(&fd->toffhandle);
101102a19910SJustin T. Gibbs 	callout_handle_init(&fd->tohandle);
10125b81b6b3SRodney W. Grimes 
1013b99f0a4aSAndrew Moore 	switch (fdt) {
10147ca0641bSAndrey A. Chernov 	case RTCFDT_12M:
10156182fdbdSPeter Wemm 		device_set_desc(dev, "1200-KB 5.25\" drive");
1016b99f0a4aSAndrew Moore 		fd->type = FD_1200;
10177ca0641bSAndrey A. Chernov 		break;
10180722d6abSJoerg Wunsch 	case RTCFDT_144M | RTCFDT_144M_PRETENDED:
10196182fdbdSPeter Wemm 		device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive");
10200722d6abSJoerg Wunsch 		fdt = RTCFDT_144M;
10216182fdbdSPeter Wemm 		fd->type = FD_1440;
10227ca0641bSAndrey A. Chernov 	case RTCFDT_144M:
10236182fdbdSPeter Wemm 		device_set_desc(dev, "1440-KB 3.5\" drive");
1024b99f0a4aSAndrew Moore 		fd->type = FD_1440;
10257ca0641bSAndrey A. Chernov 		break;
1026290dd077SJoerg Wunsch 	case RTCFDT_288M:
102786a727d9SJoerg Wunsch 	case RTCFDT_288M_1:
10286182fdbdSPeter Wemm 		device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)");
1029290dd077SJoerg Wunsch 		fd->type = FD_1440;
1030290dd077SJoerg Wunsch 		break;
10317ca0641bSAndrey A. Chernov 	case RTCFDT_360K:
10326182fdbdSPeter Wemm 		device_set_desc(dev, "360-KB 5.25\" drive");
1033b99f0a4aSAndrew Moore 		fd->type = FD_360;
10347ca0641bSAndrey A. Chernov 		break;
1035ed2fa05eSAndrey A. Chernov 	case RTCFDT_720K:
10366182fdbdSPeter Wemm 		printf("720-KB 3.5\" drive");
1037b99f0a4aSAndrew Moore 		fd->type = FD_720;
1038ed2fa05eSAndrey A. Chernov 		break;
10397ca0641bSAndrey A. Chernov 	default:
10406182fdbdSPeter Wemm 		return (ENXIO);
10415b81b6b3SRodney W. Grimes 	}
10426182fdbdSPeter Wemm 	return (0);
10436182fdbdSPeter Wemm }
10446182fdbdSPeter Wemm 
10456182fdbdSPeter Wemm static int
10466182fdbdSPeter Wemm fd_attach(device_t dev)
10476182fdbdSPeter Wemm {
10486182fdbdSPeter Wemm 	struct	fd_data *fd;
1049ada9bd8cSJulian Elischer #if 0
1050ada9bd8cSJulian Elischer 	int	i;
1051ada9bd8cSJulian Elischer 	int	mynor;
1052ada9bd8cSJulian Elischer 	int	typemynor;
1053ada9bd8cSJulian Elischer 	int	typesize;
1054ada9bd8cSJulian Elischer #endif
10556182fdbdSPeter Wemm 
10566182fdbdSPeter Wemm 	fd = device_get_softc(dev);
10576182fdbdSPeter Wemm 
1058475ad603SPeter Wemm 	cdevsw_add(&fd_cdevsw);	/* XXX */
1059ada9bd8cSJulian Elischer 	make_dev(&fd_cdevsw, (fd->fdu << 6),
1060ada9bd8cSJulian Elischer 		UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fd->fdu);
1061ada9bd8cSJulian Elischer 
1062ada9bd8cSJulian Elischer #if 0
1063ada9bd8cSJulian Elischer 	/* Other make_dev() go here. */
1064ada9bd8cSJulian Elischer #endif
1065ada9bd8cSJulian Elischer 
1066671e2ceeSBruce Evans 	/*
1067b2dfb1f9SJustin T. Gibbs 	 * Export the drive to the devstat interface.
1068671e2ceeSBruce Evans 	 */
10696182fdbdSPeter Wemm 	devstat_add_entry(&fd->device_stats, device_get_name(dev),
10706182fdbdSPeter Wemm 			  device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS,
10712a888f93SKenneth D. Merry 			  DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER,
10722a888f93SKenneth D. Merry 			  DEVSTAT_PRIORITY_FD);
10736182fdbdSPeter Wemm 	return (0);
10745b81b6b3SRodney W. Grimes }
10755b81b6b3SRodney W. Grimes 
107616e68fc6SPeter Wemm static device_method_t fd_methods[] = {
107716e68fc6SPeter Wemm 	/* Device interface */
107816e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fd_probe),
107916e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fd_attach),
108016e68fc6SPeter Wemm 	DEVMETHOD(device_detach,	bus_generic_detach),
108116e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
108216e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend), /* XXX */
108316e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume), /* XXX */
108416e68fc6SPeter Wemm 
108516e68fc6SPeter Wemm 	{ 0, 0 }
108616e68fc6SPeter Wemm };
108716e68fc6SPeter Wemm 
108816e68fc6SPeter Wemm static driver_t fd_driver = {
108916e68fc6SPeter Wemm 	"fd",
109016e68fc6SPeter Wemm 	fd_methods,
109116e68fc6SPeter Wemm 	sizeof(struct fd_data)
109216e68fc6SPeter Wemm };
109316e68fc6SPeter Wemm 
1094475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0);
109516e68fc6SPeter Wemm 
109616e68fc6SPeter Wemm /******************************************************************/
109716e68fc6SPeter Wemm 
109869acd21dSWarner Losh #ifdef FDC_YE
109969acd21dSWarner Losh /*
110069acd21dSWarner Losh  * this is a subset of fdattach() optimized for the Y-E Data
110169acd21dSWarner Losh  * PCMCIA floppy drive.
110269acd21dSWarner Losh  */
110369acd21dSWarner Losh static int yeattach(struct isa_device *dev)
110469acd21dSWarner Losh {
110569acd21dSWarner Losh 	fdcu_t  fdcu = dev->id_unit;
110669acd21dSWarner Losh 	fdc_p   fdc = fdc_data + fdcu;
110769acd21dSWarner Losh 	fdsu_t  fdsu = 0;               /* assume 1 drive per YE controller */
110869acd21dSWarner Losh 	fdu_t   fdu;
110969acd21dSWarner Losh 	fd_p    fd;
111069acd21dSWarner Losh 	int     st0, st3, i;
111169acd21dSWarner Losh 	fdc->fdcu = fdcu;
111269acd21dSWarner Losh 	/*
11133b178206SWarner Losh 	 * the FDC_NODMA flag is used to to indicate special PIO is used
111469acd21dSWarner Losh 	 * instead of DMA
111569acd21dSWarner Losh 	 */
11163b178206SWarner Losh 	fdc->flags = FDC_ATTACHED|FDC_NODMA;
111769acd21dSWarner Losh 	fdc->state = DEVIDLE;
111869acd21dSWarner Losh 	/* reset controller, turn motor off, clear fdout mirror reg */
1119427ccf00SDoug Rabson 	fdout_wr(fdc, ((fdc->fdout = 0)));
112069acd21dSWarner Losh 	bufq_init(&fdc->head);
112169acd21dSWarner Losh 	/*
112269acd21dSWarner Losh 	 * assume 2 drives/ "normal" controller
112369acd21dSWarner Losh 	 */
112469acd21dSWarner Losh 	fdu = fdcu * 2;
112569acd21dSWarner Losh 	if (fdu >= NFD) {
112669acd21dSWarner Losh 		printf("fdu %d >= NFD\n",fdu);
112769acd21dSWarner Losh 		return(0);
112869acd21dSWarner Losh 	};
112969acd21dSWarner Losh 	fd = &fd_data[fdu];
113069acd21dSWarner Losh 
113169acd21dSWarner Losh 	set_motor(fdcu, fdsu, TURNON);
113269acd21dSWarner Losh 	DELAY(1000000); /* 1 sec */
113369acd21dSWarner Losh 	fdc->fdct = FDC_NE765;
113469acd21dSWarner Losh 
113569acd21dSWarner Losh 	if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) &&
113669acd21dSWarner Losh 		(st3 & NE7_ST3_T0)) {
113769acd21dSWarner Losh 		/* if at track 0, first seek inwards */
113869acd21dSWarner Losh 		/* seek some steps: */
113969acd21dSWarner Losh 		(void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0);
114069acd21dSWarner Losh 		DELAY(300000); /* ...wait a moment... */
114169acd21dSWarner Losh 		(void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
114269acd21dSWarner Losh 	}
114369acd21dSWarner Losh 
114469acd21dSWarner Losh 	/* If we're at track 0 first seek inwards. */
114569acd21dSWarner Losh 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
114669acd21dSWarner Losh 		/* Seek some steps... */
114769acd21dSWarner Losh 		if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
114869acd21dSWarner Losh 			/* ...wait a moment... */
114969acd21dSWarner Losh 			DELAY(300000);
115069acd21dSWarner Losh 			/* make ctrlr happy: */
115169acd21dSWarner Losh 			(void)fd_sense_int(fdc, 0, 0);
115269acd21dSWarner Losh 		}
115369acd21dSWarner Losh 	}
115469acd21dSWarner Losh 
115569acd21dSWarner Losh 	for(i = 0; i < 2; i++) {
115669acd21dSWarner Losh 		/*
115769acd21dSWarner Losh 		 * we must recalibrate twice, just in case the
115869acd21dSWarner Losh 		 * heads have been beyond cylinder 76, since most
115969acd21dSWarner Losh 		 * FDCs still barf when attempting to recalibrate
116069acd21dSWarner Losh 		 * more than 77 steps
116169acd21dSWarner Losh 		 */
116269acd21dSWarner Losh 		/* go back to 0: */
116369acd21dSWarner Losh 		if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
116469acd21dSWarner Losh 			/* a second being enough for full stroke seek*/
116569acd21dSWarner Losh 			DELAY(i == 0? 1000000: 300000);
116669acd21dSWarner Losh 
116769acd21dSWarner Losh 			/* anything responding? */
116869acd21dSWarner Losh 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
116969acd21dSWarner Losh 				(st0 & NE7_ST0_EC) == 0)
117069acd21dSWarner Losh 				break; /* already probed succesfully */
117169acd21dSWarner Losh 		}
117269acd21dSWarner Losh 	}
117369acd21dSWarner Losh 
117469acd21dSWarner Losh 	set_motor(fdcu, fdsu, TURNOFF);
117569acd21dSWarner Losh 
117669acd21dSWarner Losh 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
117769acd21dSWarner Losh 		return(0);
117869acd21dSWarner Losh 
117969acd21dSWarner Losh 	fd->track = FD_NO_TRACK;
118069acd21dSWarner Losh 	fd->fdc = fdc;
118169acd21dSWarner Losh 	fd->fdsu = fdsu;
118269acd21dSWarner Losh 	fd->options = 0;
118369acd21dSWarner Losh 	printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu);
118469acd21dSWarner Losh 	fd->type = FD_1440;
118569acd21dSWarner Losh 
118669acd21dSWarner Losh 	return (1);
118769acd21dSWarner Losh }
118869acd21dSWarner Losh #endif
118969acd21dSWarner Losh 
11905b81b6b3SRodney W. Grimes /****************************************************************************/
11915b81b6b3SRodney W. Grimes /*                            motor control stuff                           */
11925e235068SJordan K. Hubbard /*		remember to not deselect the drive we're working on         */
11935b81b6b3SRodney W. Grimes /****************************************************************************/
11943a2f7427SDavid Greenman static void
11956182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon)
11965b81b6b3SRodney W. Grimes {
11976182fdbdSPeter Wemm 	int fdout = fdc->fdout;
11983a2f7427SDavid Greenman 	int needspecify = 0;
11993a2f7427SDavid Greenman 
12003a2f7427SDavid Greenman 	if(turnon) {
12013a2f7427SDavid Greenman 		fdout &= ~FDO_FDSEL;
12023a2f7427SDavid Greenman 		fdout |= (FDO_MOEN0 << fdsu) + fdsu;
12033a2f7427SDavid Greenman 	} else
12043a2f7427SDavid Greenman 		fdout &= ~(FDO_MOEN0 << fdsu);
12053a2f7427SDavid Greenman 
12065e235068SJordan K. Hubbard 	if(!turnon
12075e235068SJordan K. Hubbard 	   && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0)
12085e235068SJordan K. Hubbard 		/* gonna turn off the last drive, put FDC to bed */
12095e235068SJordan K. Hubbard 		fdout &= ~ (FDO_FRST|FDO_FDMAEN);
12105e235068SJordan K. Hubbard 	else {
12113a2f7427SDavid Greenman 		/* make sure controller is selected and specified */
12123a2f7427SDavid Greenman 		if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0)
12133a2f7427SDavid Greenman 			needspecify = 1;
12143a2f7427SDavid Greenman 		fdout |= (FDO_FRST|FDO_FDMAEN);
12155b81b6b3SRodney W. Grimes 	}
12165b81b6b3SRodney W. Grimes 
1217427ccf00SDoug Rabson 	fdout_wr(fdc, fdout);
12186182fdbdSPeter Wemm 	fdc->fdout = fdout;
12193a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdout);
12203a2f7427SDavid Greenman 
12213a2f7427SDavid Greenman 	if (needspecify) {
1222dc8603e3SJoerg Wunsch 		/*
1223dc5df763SJoerg Wunsch 		 * XXX
1224dc8603e3SJoerg Wunsch 		 * special case: since we have just woken up the FDC
1225dc8603e3SJoerg Wunsch 		 * from its sleep, we silently assume the command will
1226dc8603e3SJoerg Wunsch 		 * be accepted, and do not test for a timeout
1227dc8603e3SJoerg Wunsch 		 */
12286182fdbdSPeter Wemm 		(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1229dc5df763SJoerg Wunsch 			     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1230dc5df763SJoerg Wunsch 			     0);
12316182fdbdSPeter Wemm 		if (fdc->flags & FDC_HAS_FIFO)
12326182fdbdSPeter Wemm 			(void) enable_fifo(fdc);
12333a2f7427SDavid Greenman 	}
12343a2f7427SDavid Greenman }
12353a2f7427SDavid Greenman 
1236381fe1aaSGarrett Wollman static void
12376182fdbdSPeter Wemm fd_turnoff(void *xfd)
12385b81b6b3SRodney W. Grimes {
1239f5f7ba03SJordan K. Hubbard 	int	s;
12406182fdbdSPeter Wemm 	fd_p fd = xfd;
1241dc16046fSJoerg Wunsch 
12426182fdbdSPeter Wemm 	TRACE1("[fd%d: turnoff]", fd->fdu);
12438335c1b8SBruce Evans 
12448335c1b8SBruce Evans 	/*
12458335c1b8SBruce Evans 	 * Don't turn off the motor yet if the drive is active.
12468335c1b8SBruce Evans 	 * XXX shouldn't even schedule turnoff until drive is inactive
12478335c1b8SBruce Evans 	 * and nothing is queued on it.
12488335c1b8SBruce Evans 	 */
12496182fdbdSPeter Wemm 	if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) {
12506182fdbdSPeter Wemm 		fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
12518335c1b8SBruce Evans 		return;
12528335c1b8SBruce Evans 	}
12538335c1b8SBruce Evans 
1254f5f7ba03SJordan K. Hubbard 	s = splbio();
12555b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR;
12566182fdbdSPeter Wemm 	set_motor(fd->fdc, fd->fdsu, TURNOFF);
1257f5f7ba03SJordan K. Hubbard 	splx(s);
12585b81b6b3SRodney W. Grimes }
12595b81b6b3SRodney W. Grimes 
12603a2f7427SDavid Greenman static void
12616182fdbdSPeter Wemm fd_motor_on(void *xfd)
12625b81b6b3SRodney W. Grimes {
1263f5f7ba03SJordan K. Hubbard 	int	s;
12646182fdbdSPeter Wemm 	fd_p fd = xfd;
1265f5f7ba03SJordan K. Hubbard 
1266f5f7ba03SJordan K. Hubbard 	s = splbio();
12675b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR_WAIT;
12685b81b6b3SRodney W. Grimes 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
12695b81b6b3SRodney W. Grimes 	{
12706182fdbdSPeter Wemm 		fdc_intr(fd->fdc);
12715b81b6b3SRodney W. Grimes 	}
1272f5f7ba03SJordan K. Hubbard 	splx(s);
12735b81b6b3SRodney W. Grimes }
12745b81b6b3SRodney W. Grimes 
12753a2f7427SDavid Greenman static void
12766182fdbdSPeter Wemm fd_turnon(fd_p fd)
12775b81b6b3SRodney W. Grimes {
12785b81b6b3SRodney W. Grimes 	if(!(fd->flags & FD_MOTOR))
12795b81b6b3SRodney W. Grimes 	{
12803a2f7427SDavid Greenman 		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
12816182fdbdSPeter Wemm 		set_motor(fd->fdc, fd->fdsu, TURNON);
12826182fdbdSPeter Wemm 		timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */
12835b81b6b3SRodney W. Grimes 	}
12845b81b6b3SRodney W. Grimes }
12855b81b6b3SRodney W. Grimes 
1286381fe1aaSGarrett Wollman static void
1287dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc)
12885b81b6b3SRodney W. Grimes {
12893a2f7427SDavid Greenman 	/* Try a reset, keep motor on */
1290427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
12913a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
12923a2f7427SDavid Greenman 	DELAY(100);
12933a2f7427SDavid Greenman 	/* enable FDC, but defer interrupts a moment */
1294427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN);
12953a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
12963a2f7427SDavid Greenman 	DELAY(100);
1297427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout);
12983a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout);
12993a2f7427SDavid Greenman 
1300dc5df763SJoerg Wunsch 	/* XXX after a reset, silently believe the FDC will accept commands */
13016182fdbdSPeter Wemm 	(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1302dc5df763SJoerg Wunsch 		     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1303dc5df763SJoerg Wunsch 		     0);
1304d66c374fSTor Egge 	if (fdc->flags & FDC_HAS_FIFO)
1305d66c374fSTor Egge 		(void) enable_fifo(fdc);
13065b81b6b3SRodney W. Grimes }
13075b81b6b3SRodney W. Grimes 
13085b81b6b3SRodney W. Grimes /****************************************************************************/
13095b81b6b3SRodney W. Grimes /*                             fdc in/out                                   */
13105b81b6b3SRodney W. Grimes /****************************************************************************/
13115b81b6b3SRodney W. Grimes int
13126182fdbdSPeter Wemm in_fdc(struct fdc_data *fdc)
13135b81b6b3SRodney W. Grimes {
13145b81b6b3SRodney W. Grimes 	int i, j = 100000;
1315427ccf00SDoug Rabson 	while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM))
13165b81b6b3SRodney W. Grimes 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1317dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
13186182fdbdSPeter Wemm 			return fdc_err(fdc, "ready for output in input\n");
13195b81b6b3SRodney W. Grimes 	if (j <= 0)
13206182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "input ready timeout\n": 0);
1321d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
1322427ccf00SDoug Rabson 	i = fddata_rd(fdc);
13233a2f7427SDavid Greenman 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
13245b81b6b3SRodney W. Grimes 	return(i);
1325d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
1326427ccf00SDoug Rabson 	return fddata_rd(fdc);
1327d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
13285b81b6b3SRodney W. Grimes }
13295b81b6b3SRodney W. Grimes 
1330dc5df763SJoerg Wunsch /*
1331dc5df763SJoerg Wunsch  * fd_in: Like in_fdc, but allows you to see if it worked.
1332dc5df763SJoerg Wunsch  */
1333b5e8ce9fSBruce Evans static int
13346182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr)
1335dc5df763SJoerg Wunsch {
1336dc5df763SJoerg Wunsch 	int i, j = 100000;
1337427ccf00SDoug Rabson 	while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM))
1338dc5df763SJoerg Wunsch 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1339dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
13406182fdbdSPeter Wemm 			return fdc_err(fdc, "ready for output in input\n");
1341dc5df763SJoerg Wunsch 	if (j <= 0)
13426182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "input ready timeout\n": 0);
1343d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
1344427ccf00SDoug Rabson 	i = fddata_rd(fdc);
1345dc5df763SJoerg Wunsch 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
1346dc5df763SJoerg Wunsch 	*ptr = i;
1347dc5df763SJoerg Wunsch 	return 0;
1348d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
1349427ccf00SDoug Rabson 	i = fddata_rd(fdc);
1350dc5df763SJoerg Wunsch 	if (ptr)
1351dc5df763SJoerg Wunsch 		*ptr = i;
1352dc5df763SJoerg Wunsch 	return 0;
1353d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
1354dc5df763SJoerg Wunsch }
1355dc5df763SJoerg Wunsch 
1356dc5df763SJoerg Wunsch int
13576182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x)
13585b81b6b3SRodney W. Grimes {
13593b3837dbSRodney W. Grimes 	int i;
13605b81b6b3SRodney W. Grimes 
13613b3837dbSRodney W. Grimes 	/* Check that the direction bit is set */
13623b3837dbSRodney W. Grimes 	i = 100000;
1363427ccf00SDoug Rabson 	while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0);
13646182fdbdSPeter Wemm 	if (i <= 0) return fdc_err(fdc, "direction bit not set\n");
13653b3837dbSRodney W. Grimes 
13663b3837dbSRodney W. Grimes 	/* Check that the floppy controller is ready for a command */
13673b3837dbSRodney W. Grimes 	i = 100000;
1368427ccf00SDoug Rabson 	while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0);
136916b04b6aSJoerg Wunsch 	if (i <= 0)
13706182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "output ready timeout\n": 0);
13713b3837dbSRodney W. Grimes 
13723b3837dbSRodney W. Grimes 	/* Send the command and return */
1373427ccf00SDoug Rabson 	fddata_wr(fdc, x);
13743a2f7427SDavid Greenman 	TRACE1("[0x%x->FDDATA]", x);
13755b81b6b3SRodney W. Grimes 	return (0);
13765b81b6b3SRodney W. Grimes }
13775b81b6b3SRodney W. Grimes 
13785b81b6b3SRodney W. Grimes /****************************************************************************/
13795b81b6b3SRodney W. Grimes /*                           fdopen/fdclose                                 */
13805b81b6b3SRodney W. Grimes /****************************************************************************/
1381381fe1aaSGarrett Wollman int
1382671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p)
13835b81b6b3SRodney W. Grimes {
13845b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
138520a29168SAndrey A. Chernov 	int type = FDTYPE(minor(dev));
13866182fdbdSPeter Wemm 	fd_p	fd;
1387b99f0a4aSAndrew Moore 	fdc_p	fdc;
13885b81b6b3SRodney W. Grimes 
13895b81b6b3SRodney W. Grimes 	/* check bounds */
13906182fdbdSPeter Wemm 	if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0)
1391b99f0a4aSAndrew Moore 		return (ENXIO);
13926182fdbdSPeter Wemm 	fdc = fd->fdc;
13936182fdbdSPeter Wemm 	if ((fdc == NULL) || (fd->type == NO_TYPE))
1394b99f0a4aSAndrew Moore 		return (ENXIO);
1395b99f0a4aSAndrew Moore 	if (type > NUMDENS)
1396b99f0a4aSAndrew Moore 		return (ENXIO);
13977ca0641bSAndrey A. Chernov 	if (type == 0)
13986182fdbdSPeter Wemm 		type = fd->type;
13997ca0641bSAndrey A. Chernov 	else {
14003e425b96SJulian Elischer 		/*
14013e425b96SJulian Elischer 		 * For each type of basic drive, make sure we are trying
14023e425b96SJulian Elischer 		 * to open a type it can do,
14033e425b96SJulian Elischer 		 */
14046182fdbdSPeter Wemm 		if (type != fd->type) {
14056182fdbdSPeter Wemm 			switch (fd->type) {
14067ca0641bSAndrey A. Chernov 			case FD_360:
14077ca0641bSAndrey A. Chernov 				return (ENXIO);
1408ed2fa05eSAndrey A. Chernov 			case FD_720:
1409b39c878eSAndrey A. Chernov 				if (   type != FD_820
1410b39c878eSAndrey A. Chernov 				    && type != FD_800
14116fb89845SKATO Takenori 				    && type != FD_640
1412ed2fa05eSAndrey A. Chernov 				   )
1413ed2fa05eSAndrey A. Chernov 					return (ENXIO);
1414ed2fa05eSAndrey A. Chernov 				break;
14157ca0641bSAndrey A. Chernov 			case FD_1200:
1416b39c878eSAndrey A. Chernov 				switch (type) {
1417b39c878eSAndrey A. Chernov 				case FD_1480:
1418b39c878eSAndrey A. Chernov 					type = FD_1480in5_25;
1419fa4700b4SAndrey A. Chernov 					break;
14207ca0641bSAndrey A. Chernov 				case FD_1440:
1421b39c878eSAndrey A. Chernov 					type = FD_1440in5_25;
1422b39c878eSAndrey A. Chernov 					break;
14236fb89845SKATO Takenori 				case FD_1232:
14246fb89845SKATO Takenori 					break;
1425b39c878eSAndrey A. Chernov 				case FD_820:
1426b39c878eSAndrey A. Chernov 					type = FD_820in5_25;
1427b39c878eSAndrey A. Chernov 					break;
1428b39c878eSAndrey A. Chernov 				case FD_800:
1429b39c878eSAndrey A. Chernov 					type = FD_800in5_25;
1430b39c878eSAndrey A. Chernov 					break;
1431b39c878eSAndrey A. Chernov 				case FD_720:
1432b39c878eSAndrey A. Chernov 					type = FD_720in5_25;
1433b39c878eSAndrey A. Chernov 					break;
14346fb89845SKATO Takenori 				case FD_640:
14356fb89845SKATO Takenori 					type = FD_640in5_25;
14366fb89845SKATO Takenori 					break;
1437b39c878eSAndrey A. Chernov 				case FD_360:
1438b39c878eSAndrey A. Chernov 					type = FD_360in5_25;
1439b39c878eSAndrey A. Chernov 					break;
1440b39c878eSAndrey A. Chernov 				default:
1441b39c878eSAndrey A. Chernov 					return(ENXIO);
1442b39c878eSAndrey A. Chernov 				}
1443b39c878eSAndrey A. Chernov 				break;
1444b39c878eSAndrey A. Chernov 			case FD_1440:
1445b39c878eSAndrey A. Chernov 				if (   type != FD_1720
1446b39c878eSAndrey A. Chernov 				    && type != FD_1480
1447ed2fa05eSAndrey A. Chernov 				    && type != FD_1200
1448b39c878eSAndrey A. Chernov 				    && type != FD_820
1449b39c878eSAndrey A. Chernov 				    && type != FD_800
1450b39c878eSAndrey A. Chernov 				    && type != FD_720
14516fb89845SKATO Takenori 				    && type != FD_640
14527ca0641bSAndrey A. Chernov 				    )
1453dffff499SAndrey A. Chernov 					return(ENXIO);
1454fa4700b4SAndrey A. Chernov 				break;
14557ca0641bSAndrey A. Chernov 			}
14567ca0641bSAndrey A. Chernov 		}
1457fa4700b4SAndrey A. Chernov 	}
14586182fdbdSPeter Wemm 	fd->ft = fd_types + type - 1;
14596182fdbdSPeter Wemm 	fd->flags |= FD_OPEN;
14606182fdbdSPeter Wemm 	device_busy(fd->dev);
14616182fdbdSPeter Wemm 	device_busy(fd->fdc->fdc_dev);
14625b81b6b3SRodney W. Grimes 	return 0;
14635b81b6b3SRodney W. Grimes }
14645b81b6b3SRodney W. Grimes 
1465381fe1aaSGarrett Wollman int
1466671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p)
14675b81b6b3SRodney W. Grimes {
14685b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
14696182fdbdSPeter Wemm 	struct fd_data *fd;
1470b99f0a4aSAndrew Moore 
14716182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
14726182fdbdSPeter Wemm 	fd->flags &= ~FD_OPEN;
14736182fdbdSPeter Wemm 	fd->options &= ~FDOPT_NORETRY;
1474dc16046fSJoerg Wunsch 
14755b81b6b3SRodney W. Grimes 	return (0);
14765b81b6b3SRodney W. Grimes }
14775b81b6b3SRodney W. Grimes 
14783a2f7427SDavid Greenman /****************************************************************************/
14793a2f7427SDavid Greenman /*                               fdstrategy                                 */
14803a2f7427SDavid Greenman /****************************************************************************/
14813a2f7427SDavid Greenman void
14823a2f7427SDavid Greenman fdstrategy(struct buf *bp)
14833a2f7427SDavid Greenman {
1484bb6382faSJoerg Wunsch 	unsigned nblocks, blknum, cando;
14853a2f7427SDavid Greenman  	int	s;
14863a2f7427SDavid Greenman  	fdu_t	fdu;
14873a2f7427SDavid Greenman  	fdc_p	fdc;
14883a2f7427SDavid Greenman  	fd_p	fd;
14893a2f7427SDavid Greenman 	size_t	fdblk;
14903a2f7427SDavid Greenman 
14913a2f7427SDavid Greenman  	fdu = FDUNIT(minor(bp->b_dev));
14926182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
14936182fdbdSPeter Wemm 	if (fd == 0)
14946182fdbdSPeter Wemm 		panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)",
14956182fdbdSPeter Wemm 		      (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev));
14963a2f7427SDavid Greenman 	fdc = fd->fdc;
149769acd21dSWarner Losh 	if (fd->type == NO_TYPE) {
149869acd21dSWarner Losh 		bp->b_error = ENXIO;
149969acd21dSWarner Losh 		bp->b_flags |= B_ERROR;
15003b178206SWarner Losh 		goto bad;
150169acd21dSWarner Losh 	};
15023a2f7427SDavid Greenman 
1503d3628763SRodney W. Grimes 	fdblk = 128 << (fd->ft->secsize);
15043a2f7427SDavid Greenman 	if (!(bp->b_flags & B_FORMAT)) {
15056182fdbdSPeter Wemm 		if (bp->b_blkno < 0) {
1506dc5df763SJoerg Wunsch 			printf(
15076a0e6f42SRodney W. Grimes 		"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
1508702c623aSPoul-Henning Kamp 			       fdu, (u_long)bp->b_blkno, bp->b_bcount);
15093a2f7427SDavid Greenman 			bp->b_error = EINVAL;
15103a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
15113a2f7427SDavid Greenman 			goto bad;
15123a2f7427SDavid Greenman 		}
15133a2f7427SDavid Greenman 		if ((bp->b_bcount % fdblk) != 0) {
15143a2f7427SDavid Greenman 			bp->b_error = EINVAL;
15153a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
15163a2f7427SDavid Greenman 			goto bad;
15173a2f7427SDavid Greenman 		}
15183a2f7427SDavid Greenman 	}
15193a2f7427SDavid Greenman 
15203a2f7427SDavid Greenman 	/*
15213a2f7427SDavid Greenman 	 * Set up block calculations.
15223a2f7427SDavid Greenman 	 */
1523bb6382faSJoerg Wunsch 	if (bp->b_blkno > 20000000) {
1524bb6382faSJoerg Wunsch 		/*
1525bb6382faSJoerg Wunsch 		 * Reject unreasonably high block number, prevent the
1526bb6382faSJoerg Wunsch 		 * multiplication below from overflowing.
1527bb6382faSJoerg Wunsch 		 */
1528bb6382faSJoerg Wunsch 		bp->b_error = EINVAL;
15293a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
15303a2f7427SDavid Greenman 		goto bad;
15313a2f7427SDavid Greenman 	}
1532bb6382faSJoerg Wunsch 	blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk;
1533bb6382faSJoerg Wunsch  	nblocks = fd->ft->size;
1534bb6382faSJoerg Wunsch 	bp->b_resid = 0;
1535bb6382faSJoerg Wunsch 	if (blknum + (bp->b_bcount / fdblk) > nblocks) {
1536bb6382faSJoerg Wunsch 		if (blknum <= nblocks) {
1537bb6382faSJoerg Wunsch 			cando = (nblocks - blknum) * fdblk;
1538bb6382faSJoerg Wunsch 			bp->b_resid = bp->b_bcount - cando;
1539bb6382faSJoerg Wunsch 			if (cando == 0)
1540bb6382faSJoerg Wunsch 				goto bad;	/* not actually bad but EOF */
1541bb6382faSJoerg Wunsch 		} else {
1542bb6382faSJoerg Wunsch 			bp->b_error = EINVAL;
1543bb6382faSJoerg Wunsch 			bp->b_flags |= B_ERROR;
1544bb6382faSJoerg Wunsch 			goto bad;
1545bb6382faSJoerg Wunsch 		}
1546bb6382faSJoerg Wunsch 	}
154727513ca7SBruce Evans  	bp->b_pblkno = bp->b_blkno;
15483a2f7427SDavid Greenman 	s = splbio();
154902a19910SJustin T. Gibbs 	bufqdisksort(&fdc->head, bp);
15506182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */
1551b2dfb1f9SJustin T. Gibbs 
1552b2dfb1f9SJustin T. Gibbs 	/* Tell devstat we are starting on the transaction */
1553b2dfb1f9SJustin T. Gibbs 	devstat_start_transaction(&fd->device_stats);
1554b2dfb1f9SJustin T. Gibbs 
15556182fdbdSPeter Wemm 	fdstart(fdc);
15563a2f7427SDavid Greenman 	splx(s);
15573a2f7427SDavid Greenman 	return;
15583a2f7427SDavid Greenman 
15593a2f7427SDavid Greenman bad:
15603a2f7427SDavid Greenman 	biodone(bp);
15613a2f7427SDavid Greenman }
15623a2f7427SDavid Greenman 
15635b81b6b3SRodney W. Grimes /***************************************************************\
15645b81b6b3SRodney W. Grimes *				fdstart				*
15655b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy	*
15665b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command	*
15675b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more*
15685b81b6b3SRodney W. Grimes * work to do and picks up what we just added.			*
15695b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it	*
15705b81b6b3SRodney W. Grimes * will pick up our work when the present work completes		*
15715b81b6b3SRodney W. Grimes \***************************************************************/
1572381fe1aaSGarrett Wollman static void
15736182fdbdSPeter Wemm fdstart(struct fdc_data *fdc)
15745b81b6b3SRodney W. Grimes {
15755b81b6b3SRodney W. Grimes 	int s;
15765b81b6b3SRodney W. Grimes 
15775b81b6b3SRodney W. Grimes 	s = splbio();
15786182fdbdSPeter Wemm 	if(fdc->state == DEVIDLE)
15795b81b6b3SRodney W. Grimes 	{
15806182fdbdSPeter Wemm 		fdc_intr(fdc);
15815b81b6b3SRodney W. Grimes 	}
15825b81b6b3SRodney W. Grimes 	splx(s);
15835b81b6b3SRodney W. Grimes }
15845b81b6b3SRodney W. Grimes 
1585381fe1aaSGarrett Wollman static void
15866182fdbdSPeter Wemm fd_iotimeout(void *xfdc)
15875b81b6b3SRodney W. Grimes {
15885c1a1eaeSBruce Evans  	fdc_p fdc;
1589f5f7ba03SJordan K. Hubbard 	int s;
15905b81b6b3SRodney W. Grimes 
15916182fdbdSPeter Wemm 	fdc = xfdc;
15925c1a1eaeSBruce Evans 	TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
15935b81b6b3SRodney W. Grimes 
15943a2f7427SDavid Greenman 	/*
15953a2f7427SDavid Greenman 	 * Due to IBM's brain-dead design, the FDC has a faked ready
15963a2f7427SDavid Greenman 	 * signal, hardwired to ready == true. Thus, any command
15973a2f7427SDavid Greenman 	 * issued if there's no diskette in the drive will _never_
15983a2f7427SDavid Greenman 	 * complete, and must be aborted by resetting the FDC.
15993a2f7427SDavid Greenman 	 * Many thanks, Big Blue!
16005c1a1eaeSBruce Evans 	 * The FDC must not be reset directly, since that would
16015c1a1eaeSBruce Evans 	 * interfere with the state machine.  Instead, pretend that
16025c1a1eaeSBruce Evans 	 * the command completed but was invalid.  The state machine
16035c1a1eaeSBruce Evans 	 * will reset the FDC and retry once.
16043a2f7427SDavid Greenman 	 */
16053a2f7427SDavid Greenman 	s = splbio();
16065c1a1eaeSBruce Evans 	fdc->status[0] = NE7_ST0_IC_IV;
16075c1a1eaeSBruce Evans 	fdc->flags &= ~FDC_STAT_VALID;
16085c1a1eaeSBruce Evans 	fdc->state = IOTIMEDOUT;
16096182fdbdSPeter Wemm 	fdc_intr(fdc);
1610f5f7ba03SJordan K. Hubbard 	splx(s);
16115b81b6b3SRodney W. Grimes }
16125b81b6b3SRodney W. Grimes 
16135b81b6b3SRodney W. Grimes /* just ensure it has the right spl */
1614381fe1aaSGarrett Wollman static void
16156182fdbdSPeter Wemm fd_pseudointr(void *xfdc)
16165b81b6b3SRodney W. Grimes {
16175b81b6b3SRodney W. Grimes 	int	s;
16183a2f7427SDavid Greenman 
16195b81b6b3SRodney W. Grimes 	s = splbio();
16206182fdbdSPeter Wemm 	fdc_intr(xfdc);
16215b81b6b3SRodney W. Grimes 	splx(s);
16225b81b6b3SRodney W. Grimes }
16235b81b6b3SRodney W. Grimes 
16245b81b6b3SRodney W. Grimes /***********************************************************************\
16255b81b6b3SRodney W. Grimes *                                 fdintr				*
16265b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0			*
16275b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO 						*
16285b81b6b3SRodney W. Grimes \***********************************************************************/
1629fe310de8SBruce Evans static void
16306182fdbdSPeter Wemm fdc_intr(void *xfdc)
16315b81b6b3SRodney W. Grimes {
16326182fdbdSPeter Wemm 	fdc_p fdc = xfdc;
16336182fdbdSPeter Wemm 	while(fdstate(fdc))
1634381fe1aaSGarrett Wollman 		;
16355b81b6b3SRodney W. Grimes }
16365b81b6b3SRodney W. Grimes 
163769acd21dSWarner Losh /*
163869acd21dSWarner Losh  * magic pseudo-DMA initialization for YE FDC. Sets count and
163969acd21dSWarner Losh  * direction
164069acd21dSWarner Losh  */
16413b178206SWarner Losh #define SET_BCDR(fdc,wr,cnt,port) \
16423b178206SWarner Losh 	bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port,	 \
16433b178206SWarner Losh 	    ((cnt)-1) & 0xff);						 \
16443b178206SWarner Losh 	bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \
16453b178206SWarner Losh 	    ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f)));
164669acd21dSWarner Losh 
164769acd21dSWarner Losh /*
164869acd21dSWarner Losh  * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy
164969acd21dSWarner Losh  */
16503b178206SWarner Losh static int fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count)
165169acd21dSWarner Losh {
165269acd21dSWarner Losh 	u_char *cptr = (u_char *)addr;
165369acd21dSWarner Losh 
165469acd21dSWarner Losh 	if (flags & B_READ) {
165569acd21dSWarner Losh 		if (fdc->state != PIOREAD) {
165669acd21dSWarner Losh 			fdc->state = PIOREAD;
165769acd21dSWarner Losh 			return(0);
165869acd21dSWarner Losh 		};
16593b178206SWarner Losh 		SET_BCDR(fdc, 0, count, 0);
16603b178206SWarner Losh 		bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off +
16613b178206SWarner Losh 		    FDC_YE_DATAPORT, cptr, count);
166269acd21dSWarner Losh 	} else {
16633b178206SWarner Losh 		bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off +
16643b178206SWarner Losh 		    FDC_YE_DATAPORT, cptr, count);
16653b178206SWarner Losh 		SET_BCDR(fdc, 0, count, 0);
166669acd21dSWarner Losh 	};
166769acd21dSWarner Losh 	return(1);
166869acd21dSWarner Losh }
166969acd21dSWarner Losh 
16705b81b6b3SRodney W. Grimes /***********************************************************************\
16715b81b6b3SRodney W. Grimes * The controller state machine.						*
16725b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly	*
16735b81b6b3SRodney W. Grimes \***********************************************************************/
16743a2f7427SDavid Greenman static int
16756182fdbdSPeter Wemm fdstate(fdc_p fdc)
16765b81b6b3SRodney W. Grimes {
16775c1a1eaeSBruce Evans 	int read, format, head, i, sec = 0, sectrac, st0, cyl, st3;
1678bb6382faSJoerg Wunsch 	unsigned blknum = 0, b_cylinder = 0;
16795b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc->fdu;
16805b81b6b3SRodney W. Grimes 	fd_p fd;
168117542807SPoul-Henning Kamp 	register struct buf *bp;
1682b39c878eSAndrey A. Chernov 	struct fd_formb *finfo = NULL;
16833a2f7427SDavid Greenman 	size_t fdblk;
16845b81b6b3SRodney W. Grimes 
1685e93e63cbSBruce Evans 	bp = fdc->bp;
1686e93e63cbSBruce Evans 	if (bp == NULL) {
168702a19910SJustin T. Gibbs 		bp = bufq_first(&fdc->head);
1688e93e63cbSBruce Evans 		if (bp != NULL) {
1689e93e63cbSBruce Evans 			bufq_remove(&fdc->head, bp);
1690e93e63cbSBruce Evans 			fdc->bp = bp;
1691e93e63cbSBruce Evans 		}
1692e93e63cbSBruce Evans 	}
1693e93e63cbSBruce Evans 	if (bp == NULL) {
16945b81b6b3SRodney W. Grimes 		/***********************************************\
16955b81b6b3SRodney W. Grimes 		* nothing left for this controller to do	*
16965b81b6b3SRodney W. Grimes 		* Force into the IDLE state,			*
16975b81b6b3SRodney W. Grimes 		\***********************************************/
16985b81b6b3SRodney W. Grimes 		fdc->state = DEVIDLE;
16996182fdbdSPeter Wemm 		if (fdc->fd) {
17006182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
17016182fdbdSPeter Wemm 			printf("unexpected valid fd pointer\n");
17025b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
17035b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
17045b81b6b3SRodney W. Grimes 		}
17056182fdbdSPeter Wemm 		TRACE1("[fdc%d IDLE]", fdc->fdcu);
17065b81b6b3SRodney W. Grimes  		return (0);
17075b81b6b3SRodney W. Grimes 	}
17085b81b6b3SRodney W. Grimes 	fdu = FDUNIT(minor(bp->b_dev));
17096182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
17103a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
17116182fdbdSPeter Wemm 	if (fdc->fd && (fd != fdc->fd)) {
17126182fdbdSPeter Wemm 		device_print_prettyname(fd->dev);
17136182fdbdSPeter Wemm 		printf("confused fd pointers\n");
17145b81b6b3SRodney W. Grimes 	}
17155b81b6b3SRodney W. Grimes 	read = bp->b_flags & B_READ;
1716b39c878eSAndrey A. Chernov 	format = bp->b_flags & B_FORMAT;
1717bb6382faSJoerg Wunsch 	if (format) {
1718ab3f7469SPoul-Henning Kamp 		finfo = (struct fd_formb *)bp->b_data;
1719bb6382faSJoerg Wunsch 		fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1720bb6382faSJoerg Wunsch 			- (char *)finfo;
1721bb6382faSJoerg Wunsch 	}
1722bb6382faSJoerg Wunsch 	if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) {
17233e425b96SJulian Elischer 		blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk +
1724bb6382faSJoerg Wunsch 			fd->skip/fdblk;
1725bb6382faSJoerg Wunsch 		b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1726bb6382faSJoerg Wunsch 	}
17275b81b6b3SRodney W. Grimes 	TRACE1("fd%d", fdu);
17285b81b6b3SRodney W. Grimes 	TRACE1("[%s]", fdstates[fdc->state]);
17295b81b6b3SRodney W. Grimes 	TRACE1("(0x%x)", fd->flags);
17306182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle);
17316182fdbdSPeter Wemm 	fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
17325b81b6b3SRodney W. Grimes 	switch (fdc->state)
17335b81b6b3SRodney W. Grimes 	{
17345b81b6b3SRodney W. Grimes 	case DEVIDLE:
17355b81b6b3SRodney W. Grimes 	case FINDWORK:	/* we have found new work */
17365b81b6b3SRodney W. Grimes 		fdc->retry = 0;
17375b81b6b3SRodney W. Grimes 		fd->skip = 0;
17385b81b6b3SRodney W. Grimes 		fdc->fd = fd;
17395b81b6b3SRodney W. Grimes 		fdc->fdu = fdu;
1740427ccf00SDoug Rabson 		fdctl_wr(fdc, fd->ft->trans);
17413a2f7427SDavid Greenman 		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
17425b81b6b3SRodney W. Grimes 		/*******************************************************\
17435b81b6b3SRodney W. Grimes 		* If the next drive has a motor startup pending, then	*
1744dc733423SDag-Erling Smørgrav 		* it will start up in its own good time		*
17455b81b6b3SRodney W. Grimes 		\*******************************************************/
17466182fdbdSPeter Wemm 		if(fd->flags & FD_MOTOR_WAIT) {
17475b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
17485b81b6b3SRodney W. Grimes 			return (0); /* come back later */
17495b81b6b3SRodney W. Grimes 		}
17505b81b6b3SRodney W. Grimes 		/*******************************************************\
17515b81b6b3SRodney W. Grimes 		* Maybe if it's not starting, it SHOULD be starting	*
17525b81b6b3SRodney W. Grimes 		\*******************************************************/
17535b81b6b3SRodney W. Grimes 		if (!(fd->flags & FD_MOTOR))
17545b81b6b3SRodney W. Grimes 		{
17555b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
17566182fdbdSPeter Wemm 			fd_turnon(fd);
17575b81b6b3SRodney W. Grimes 			return (0);
17585b81b6b3SRodney W. Grimes 		}
17595b81b6b3SRodney W. Grimes 		else	/* at least make sure we are selected */
17605b81b6b3SRodney W. Grimes 		{
17616182fdbdSPeter Wemm 			set_motor(fdc, fd->fdsu, TURNON);
17625b81b6b3SRodney W. Grimes 		}
17635c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
17645c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
17655c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
17665c1a1eaeSBruce Evans 		} else
17675e235068SJordan K. Hubbard 			fdc->state = DOSEEK;
17685b81b6b3SRodney W. Grimes 		break;
17695b81b6b3SRodney W. Grimes 	case DOSEEK:
1770bb6382faSJoerg Wunsch 		if (b_cylinder == (unsigned)fd->track)
17715b81b6b3SRodney W. Grimes 		{
17725b81b6b3SRodney W. Grimes 			fdc->state = SEEKCOMPLETE;
17735b81b6b3SRodney W. Grimes 			break;
17745b81b6b3SRodney W. Grimes 		}
17756182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK,
1776bb6382faSJoerg Wunsch 			   fd->fdsu, b_cylinder * fd->ft->steptrac,
1777dc5df763SJoerg Wunsch 			   0))
1778dc8603e3SJoerg Wunsch 		{
1779dc8603e3SJoerg Wunsch 			/*
1780dc8603e3SJoerg Wunsch 			 * seek command not accepted, looks like
1781dc8603e3SJoerg Wunsch 			 * the FDC went off to the Saints...
1782dc8603e3SJoerg Wunsch 			 */
1783dc8603e3SJoerg Wunsch 			fdc->retry = 6;	/* try a reset */
17846182fdbdSPeter Wemm 			return(retrier(fdc));
1785dc8603e3SJoerg Wunsch 		}
1786dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
17875b81b6b3SRodney W. Grimes 		fdc->state = SEEKWAIT;
17885b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
17895b81b6b3SRodney W. Grimes 	case SEEKWAIT:
17905b81b6b3SRodney W. Grimes 		/* allow heads to settle */
17916182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 16);
17925b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
17935b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
17945b81b6b3SRodney W. Grimes 	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
17955b81b6b3SRodney W. Grimes 		/* Make sure seek really happened*/
17966182fdbdSPeter Wemm 		if(fd->track == FD_NO_TRACK) {
1797bb6382faSJoerg Wunsch 			int descyl = b_cylinder * fd->ft->steptrac;
17983a2f7427SDavid Greenman 			do {
17993a2f7427SDavid Greenman 				/*
1800dc5df763SJoerg Wunsch 				 * This might be a "ready changed" interrupt,
1801dc5df763SJoerg Wunsch 				 * which cannot really happen since the
1802dc5df763SJoerg Wunsch 				 * RDY pin is hardwired to + 5 volts.  This
1803dc5df763SJoerg Wunsch 				 * generally indicates a "bouncing" intr
1804dc5df763SJoerg Wunsch 				 * line, so do one of the following:
1805dc5df763SJoerg Wunsch 				 *
1806dc5df763SJoerg Wunsch 				 * When running on an enhanced FDC that is
1807dc5df763SJoerg Wunsch 				 * known to not go stuck after responding
1808dc5df763SJoerg Wunsch 				 * with INVALID, fetch all interrupt states
1809dc5df763SJoerg Wunsch 				 * until seeing either an INVALID or a
1810dc5df763SJoerg Wunsch 				 * real interrupt condition.
1811dc5df763SJoerg Wunsch 				 *
1812dc5df763SJoerg Wunsch 				 * When running on a dumb old NE765, give
1813dc5df763SJoerg Wunsch 				 * up immediately.  The controller will
1814dc5df763SJoerg Wunsch 				 * provide up to four dummy RC interrupt
1815dc5df763SJoerg Wunsch 				 * conditions right after reset (for the
1816dc5df763SJoerg Wunsch 				 * corresponding four drives), so this is
1817dc5df763SJoerg Wunsch 				 * our only chance to get notice that it
1818dc5df763SJoerg Wunsch 				 * was not the FDC that caused the interrupt.
18193a2f7427SDavid Greenman 				 */
1820dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, &cyl)
1821dc5df763SJoerg Wunsch 				    == FD_NOT_VALID)
1822dc5df763SJoerg Wunsch 					return 0;
1823dc5df763SJoerg Wunsch 				if(fdc->fdct == FDC_NE765
1824dc5df763SJoerg Wunsch 				   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1825dc5df763SJoerg Wunsch 					return 0; /* hope for a real intr */
18263a2f7427SDavid Greenman 			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1827dc5df763SJoerg Wunsch 
18286182fdbdSPeter Wemm 			if (0 == descyl) {
1829dc5df763SJoerg Wunsch 				int failed = 0;
18303a2f7427SDavid Greenman 				/*
18313a2f7427SDavid Greenman 				 * seek to cyl 0 requested; make sure we are
18323a2f7427SDavid Greenman 				 * really there
18333a2f7427SDavid Greenman 				 */
1834dc5df763SJoerg Wunsch 				if (fd_sense_drive_status(fdc, &st3))
1835dc5df763SJoerg Wunsch 					failed = 1;
18363a2f7427SDavid Greenman 				if ((st3 & NE7_ST3_T0) == 0) {
18373a2f7427SDavid Greenman 					printf(
18383a2f7427SDavid Greenman 		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
18393a2f7427SDavid Greenman 					       fdu, st3, NE7_ST3BITS);
1840dc5df763SJoerg Wunsch 					failed = 1;
1841dc5df763SJoerg Wunsch 				}
1842dc5df763SJoerg Wunsch 
18436182fdbdSPeter Wemm 				if (failed) {
18443a2f7427SDavid Greenman 					if(fdc->retry < 3)
18453a2f7427SDavid Greenman 						fdc->retry = 3;
18466182fdbdSPeter Wemm 					return (retrier(fdc));
18473a2f7427SDavid Greenman 				}
18483a2f7427SDavid Greenman 			}
1849dc5df763SJoerg Wunsch 
18506182fdbdSPeter Wemm 			if (cyl != descyl) {
18513a2f7427SDavid Greenman 				printf(
18523a2f7427SDavid Greenman 		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
18532d9d0204SRodney W. Grimes 				       fdu, descyl, cyl, st0);
1854e5d7d243SBruce Evans 				if (fdc->retry < 3)
1855e5d7d243SBruce Evans 					fdc->retry = 3;
18566182fdbdSPeter Wemm 				return (retrier(fdc));
18575b81b6b3SRodney W. Grimes 			}
18585b81b6b3SRodney W. Grimes 		}
18595b81b6b3SRodney W. Grimes 
1860bb6382faSJoerg Wunsch 		fd->track = b_cylinder;
18613b178206SWarner Losh 		if (!(fdc->flags & FDC_NODMA))
1862ab3f7469SPoul-Henning Kamp 			isa_dmastart(bp->b_flags, bp->b_data+fd->skip,
18633a2f7427SDavid Greenman 				format ? bp->b_bcount : fdblk, fdc->dmachan);
18645b81b6b3SRodney W. Grimes 		sectrac = fd->ft->sectrac;
18655b81b6b3SRodney W. Grimes 		sec = blknum %  (sectrac * fd->ft->heads);
18665b81b6b3SRodney W. Grimes 		head = sec / sectrac;
18675b81b6b3SRodney W. Grimes 		sec = sec % sectrac + 1;
18683a2f7427SDavid Greenman 		fd->hddrv = ((head&1)<<2)+fdu;
18693a2f7427SDavid Greenman 
18703a2f7427SDavid Greenman 		if(format || !read)
18713a2f7427SDavid Greenman 		{
18723a2f7427SDavid Greenman 			/* make sure the drive is writable */
1873dc5df763SJoerg Wunsch 			if(fd_sense_drive_status(fdc, &st3) != 0)
1874dc8603e3SJoerg Wunsch 			{
1875dc8603e3SJoerg Wunsch 				/* stuck controller? */
18765c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18775c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
18785c1a1eaeSBruce Evans 					    fdc->dmachan);
1879dc8603e3SJoerg Wunsch 				fdc->retry = 6;	/* reset the beast */
18806182fdbdSPeter Wemm 				return (retrier(fdc));
1881dc8603e3SJoerg Wunsch 			}
18823a2f7427SDavid Greenman 			if(st3 & NE7_ST3_WP)
18833a2f7427SDavid Greenman 			{
18843a2f7427SDavid Greenman 				/*
18853a2f7427SDavid Greenman 				 * XXX YES! this is ugly.
18863a2f7427SDavid Greenman 				 * in order to force the current operation
18873a2f7427SDavid Greenman 				 * to fail, we will have to fake an FDC
18883a2f7427SDavid Greenman 				 * error - all error handling is done
18893a2f7427SDavid Greenman 				 * by the retrier()
18903a2f7427SDavid Greenman 				 */
18913a2f7427SDavid Greenman 				fdc->status[0] = NE7_ST0_IC_AT;
18923a2f7427SDavid Greenman 				fdc->status[1] = NE7_ST1_NW;
18933a2f7427SDavid Greenman 				fdc->status[2] = 0;
18943a2f7427SDavid Greenman 				fdc->status[3] = fd->track;
18953a2f7427SDavid Greenman 				fdc->status[4] = head;
18963a2f7427SDavid Greenman 				fdc->status[5] = sec;
18973a2f7427SDavid Greenman 				fdc->retry = 8;	/* break out immediately */
18983a2f7427SDavid Greenman 				fdc->state = IOTIMEDOUT; /* not really... */
18993a2f7427SDavid Greenman 				return (1);
19003a2f7427SDavid Greenman 			}
19013a2f7427SDavid Greenman 		}
19025b81b6b3SRodney W. Grimes 
19036182fdbdSPeter Wemm 		if (format) {
19043b178206SWarner Losh 			if (fdc->flags & FDC_NODMA)
19053b178206SWarner Losh 				(void)fdcpio(fdc,bp->b_flags,
190669acd21dSWarner Losh 					bp->b_data+fd->skip,
190769acd21dSWarner Losh 					bp->b_bcount);
1908b39c878eSAndrey A. Chernov 			/* formatting */
19096182fdbdSPeter Wemm 			if(fd_cmd(fdc, 6,  NE7CMD_FORMAT, head << 2 | fdu,
1910dc5df763SJoerg Wunsch 				  finfo->fd_formb_secshift,
1911dc5df763SJoerg Wunsch 				  finfo->fd_formb_nsecs,
1912dc5df763SJoerg Wunsch 				  finfo->fd_formb_gaplen,
19136182fdbdSPeter Wemm 				  finfo->fd_formb_fillbyte, 0)) {
1914dc8603e3SJoerg Wunsch 				/* controller fell over */
19155c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
19165c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
19175c1a1eaeSBruce Evans 					    fdc->dmachan);
1918dc8603e3SJoerg Wunsch 				fdc->retry = 6;
19196182fdbdSPeter Wemm 				return (retrier(fdc));
1920dc8603e3SJoerg Wunsch 			}
19216182fdbdSPeter Wemm 		} else {
19223b178206SWarner Losh 			if (fdc->flags & FDC_NODMA) {
192369acd21dSWarner Losh 				/*
192469acd21dSWarner Losh 				 * this seems to be necessary even when
192569acd21dSWarner Losh 				 * reading data
192669acd21dSWarner Losh 				 */
19273b178206SWarner Losh 				SET_BCDR(fdc, 1, fdblk, 0);
192869acd21dSWarner Losh 
192969acd21dSWarner Losh 				/*
193069acd21dSWarner Losh 				 * perform the write pseudo-DMA before
193169acd21dSWarner Losh 				 * the WRITE command is sent
193269acd21dSWarner Losh 				 */
193369acd21dSWarner Losh 				if (!read)
19343b178206SWarner Losh 					(void)fdcpio(fdc,bp->b_flags,
193569acd21dSWarner Losh 					    bp->b_data+fd->skip,
193669acd21dSWarner Losh 					    fdblk);
193769acd21dSWarner Losh 			}
19386182fdbdSPeter Wemm 			if (fd_cmd(fdc, 9,
1939dc5df763SJoerg Wunsch 				   (read ? NE7CMD_READ : NE7CMD_WRITE),
1940dc5df763SJoerg Wunsch 				   head << 2 | fdu,  /* head & unit */
1941dc5df763SJoerg Wunsch 				   fd->track,        /* track */
1942dc5df763SJoerg Wunsch 				   head,
1943dc5df763SJoerg Wunsch 				   sec,              /* sector + 1 */
1944dc5df763SJoerg Wunsch 				   fd->ft->secsize,  /* sector size */
1945dc5df763SJoerg Wunsch 				   sectrac,          /* sectors/track */
1946dc5df763SJoerg Wunsch 				   fd->ft->gap,      /* gap size */
1947dc5df763SJoerg Wunsch 				   fd->ft->datalen,  /* data length */
19486182fdbdSPeter Wemm 				   0)) {
1949dc8603e3SJoerg Wunsch 				/* the beast is sleeping again */
19505c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
19515c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
19525c1a1eaeSBruce Evans 					    fdc->dmachan);
1953dc8603e3SJoerg Wunsch 				fdc->retry = 6;
19546182fdbdSPeter Wemm 				return (retrier(fdc));
19555b81b6b3SRodney W. Grimes 			}
1956b39c878eSAndrey A. Chernov 		}
19573b178206SWarner Losh 		if (fdc->flags & FDC_NODMA)
195869acd21dSWarner Losh 			/*
195969acd21dSWarner Losh 			 * if this is a read, then simply await interrupt
196069acd21dSWarner Losh 			 * before performing PIO
196169acd21dSWarner Losh 			 */
19623b178206SWarner Losh 			if (read && !fdcpio(fdc,bp->b_flags,
196369acd21dSWarner Losh 			    bp->b_data+fd->skip,fdblk)) {
19643b178206SWarner Losh 				fd->tohandle = timeout(fd_iotimeout, fdc, hz);
196569acd21dSWarner Losh 				return(0);      /* will return later */
196669acd21dSWarner Losh 			};
196769acd21dSWarner Losh 
196869acd21dSWarner Losh 		/*
196969acd21dSWarner Losh 		 * write (or format) operation will fall through and
197069acd21dSWarner Losh 		 * await completion interrupt
197169acd21dSWarner Losh 		 */
19725b81b6b3SRodney W. Grimes 		fdc->state = IOCOMPLETE;
19736182fdbdSPeter Wemm 		fd->tohandle = timeout(fd_iotimeout, fdc, hz);
19745b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
197569acd21dSWarner Losh 	case PIOREAD:
197669acd21dSWarner Losh 		/*
197769acd21dSWarner Losh 		 * actually perform the PIO read.  The IOCOMPLETE case
197869acd21dSWarner Losh 		 * removes the timeout for us.
197969acd21dSWarner Losh 		 */
19803b178206SWarner Losh 		(void)fdcpio(fdc,bp->b_flags,bp->b_data+fd->skip,fdblk);
198169acd21dSWarner Losh 		fdc->state = IOCOMPLETE;
198269acd21dSWarner Losh 		/* FALLTHROUGH */
19835b81b6b3SRodney W. Grimes 	case IOCOMPLETE: /* IO DONE, post-analyze */
19846182fdbdSPeter Wemm 		untimeout(fd_iotimeout, fdc, fd->tohandle);
1985dc5df763SJoerg Wunsch 
19866182fdbdSPeter Wemm 		if (fd_read_status(fdc, fd->fdsu)) {
19875c1a1eaeSBruce Evans 			isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
19885c1a1eaeSBruce Evans 				    format ? bp->b_bcount : fdblk,
19895c1a1eaeSBruce Evans 				    fdc->dmachan);
1990dc5df763SJoerg Wunsch 			if (fdc->retry < 6)
1991dc5df763SJoerg Wunsch 				fdc->retry = 6;	/* force a reset */
19926182fdbdSPeter Wemm 			return (retrier(fdc));
19935b81b6b3SRodney W. Grimes   		}
1994dc5df763SJoerg Wunsch 
19953a2f7427SDavid Greenman 		fdc->state = IOTIMEDOUT;
1996dc5df763SJoerg Wunsch 
19973a2f7427SDavid Greenman 		/* FALLTHROUGH */
1998dc5df763SJoerg Wunsch 
19993a2f7427SDavid Greenman 	case IOTIMEDOUT:
20003b178206SWarner Losh 		if (!(fdc->flags & FDC_NODMA))
2001ab3f7469SPoul-Henning Kamp 			isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
20023a2f7427SDavid Greenman 				format ? bp->b_bcount : fdblk, fdc->dmachan);
20036182fdbdSPeter Wemm 		if (fdc->status[0] & NE7_ST0_IC) {
20043a2f7427SDavid Greenman                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
20053a2f7427SDavid Greenman 			    && fdc->status[1] & NE7_ST1_OR) {
2006b39c878eSAndrey A. Chernov                                 /*
20073a2f7427SDavid Greenman 				 * DMA overrun. Someone hogged the bus
20083a2f7427SDavid Greenman 				 * and didn't release it in time for the
20093a2f7427SDavid Greenman 				 * next FDC transfer.
20103a2f7427SDavid Greenman 				 * Just restart it, don't increment retry
20113a2f7427SDavid Greenman 				 * count. (vak)
2012b39c878eSAndrey A. Chernov                                  */
2013b39c878eSAndrey A. Chernov                                 fdc->state = SEEKCOMPLETE;
2014b39c878eSAndrey A. Chernov                                 return (1);
2015b39c878eSAndrey A. Chernov                         }
20163a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
20173a2f7427SDavid Greenman 				&& fdc->retry < 6)
20183a2f7427SDavid Greenman 				fdc->retry = 6;	/* force a reset */
20193a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
20203a2f7427SDavid Greenman 				&& fdc->status[2] & NE7_ST2_WC
20213a2f7427SDavid Greenman 				&& fdc->retry < 3)
20223a2f7427SDavid Greenman 				fdc->retry = 3;	/* force recalibrate */
20236182fdbdSPeter Wemm 			return (retrier(fdc));
20245b81b6b3SRodney W. Grimes 		}
20255b81b6b3SRodney W. Grimes 		/* All OK */
20263a2f7427SDavid Greenman 		fd->skip += fdblk;
20276182fdbdSPeter Wemm 		if (!format && fd->skip < bp->b_bcount - bp->b_resid) {
20285b81b6b3SRodney W. Grimes 			/* set up next transfer */
20295b81b6b3SRodney W. Grimes 			fdc->state = DOSEEK;
20306182fdbdSPeter Wemm 		} else {
20315b81b6b3SRodney W. Grimes 			/* ALL DONE */
20325b81b6b3SRodney W. Grimes 			fd->skip = 0;
2033e93e63cbSBruce Evans 			fdc->bp = NULL;
20342186cd9eSPoul-Henning Kamp 			devstat_end_transaction_buf(&fd->device_stats, bp);
20355b81b6b3SRodney W. Grimes 			biodone(bp);
20365b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
20375b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
20385b81b6b3SRodney W. Grimes 			fdc->state = FINDWORK;
20395b81b6b3SRodney W. Grimes 		}
20405b81b6b3SRodney W. Grimes 		return (1);
20415b81b6b3SRodney W. Grimes 	case RESETCTLR:
20423a2f7427SDavid Greenman 		fdc_reset(fdc);
20435b81b6b3SRodney W. Grimes 		fdc->retry++;
20445c1a1eaeSBruce Evans 		fdc->state = RESETCOMPLETE;
20455c1a1eaeSBruce Evans 		return (0);
20465c1a1eaeSBruce Evans 	case RESETCOMPLETE:
20475c1a1eaeSBruce Evans 		/*
20485c1a1eaeSBruce Evans 		 * Discard all the results from the reset so that they
20495c1a1eaeSBruce Evans 		 * can't cause an unexpected interrupt later.
20505c1a1eaeSBruce Evans 		 */
20510e317d05SJoerg Wunsch 		for (i = 0; i < 4; i++)
20520e317d05SJoerg Wunsch 			(void)fd_sense_int(fdc, &st0, &cyl);
20535c1a1eaeSBruce Evans 		fdc->state = STARTRECAL;
20545c1a1eaeSBruce Evans 		/* Fall through. */
20555c1a1eaeSBruce Evans 	case STARTRECAL:
20566182fdbdSPeter Wemm 		if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) {
2057dc8603e3SJoerg Wunsch 			/* arrgl */
2058dc8603e3SJoerg Wunsch 			fdc->retry = 6;
20596182fdbdSPeter Wemm 			return (retrier(fdc));
2060dc8603e3SJoerg Wunsch 		}
20615b81b6b3SRodney W. Grimes 		fdc->state = RECALWAIT;
20625b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
20635b81b6b3SRodney W. Grimes 	case RECALWAIT:
20645b81b6b3SRodney W. Grimes 		/* allow heads to settle */
20656182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 8);
20665b81b6b3SRodney W. Grimes 		fdc->state = RECALCOMPLETE;
20675b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
20685b81b6b3SRodney W. Grimes 	case RECALCOMPLETE:
20693a2f7427SDavid Greenman 		do {
20703a2f7427SDavid Greenman 			/*
2071dc5df763SJoerg Wunsch 			 * See SEEKCOMPLETE for a comment on this:
20723a2f7427SDavid Greenman 			 */
2073dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
2074dc5df763SJoerg Wunsch 				return 0;
2075dc5df763SJoerg Wunsch 			if(fdc->fdct == FDC_NE765
2076dc5df763SJoerg Wunsch 			   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
2077dc5df763SJoerg Wunsch 				return 0; /* hope for a real intr */
20783a2f7427SDavid Greenman 		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
20793a2f7427SDavid Greenman 		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
20805b81b6b3SRodney W. Grimes 		{
2081dc8603e3SJoerg Wunsch 			if(fdc->retry > 3)
2082dc8603e3SJoerg Wunsch 				/*
2083dc8603e3SJoerg Wunsch 				 * a recalibrate from beyond cylinder 77
2084dc8603e3SJoerg Wunsch 				 * will "fail" due to the FDC limitations;
2085dc8603e3SJoerg Wunsch 				 * since people used to complain much about
2086dc8603e3SJoerg Wunsch 				 * the failure message, try not logging
2087dc8603e3SJoerg Wunsch 				 * this one if it seems to be the first
2088dc8603e3SJoerg Wunsch 				 * time in a line
2089dc8603e3SJoerg Wunsch 				 */
2090dc8603e3SJoerg Wunsch 				printf("fd%d: recal failed ST0 %b cyl %d\n",
2091dc8603e3SJoerg Wunsch 				       fdu, st0, NE7_ST0BITS, cyl);
20923a2f7427SDavid Greenman 			if(fdc->retry < 3) fdc->retry = 3;
20936182fdbdSPeter Wemm 			return (retrier(fdc));
20945b81b6b3SRodney W. Grimes 		}
20955b81b6b3SRodney W. Grimes 		fd->track = 0;
20965b81b6b3SRodney W. Grimes 		/* Seek (probably) necessary */
20975b81b6b3SRodney W. Grimes 		fdc->state = DOSEEK;
20985b81b6b3SRodney W. Grimes 		return (1);	/* will return immediatly */
20995b81b6b3SRodney W. Grimes 	case MOTORWAIT:
21005b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
21015b81b6b3SRodney W. Grimes 		{
21025b81b6b3SRodney W. Grimes 			return (0); /* time's not up yet */
21035b81b6b3SRodney W. Grimes 		}
21045c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
21055c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
21065c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
21075c1a1eaeSBruce Evans 		} else {
21085e235068SJordan K. Hubbard 			/*
21095c1a1eaeSBruce Evans 			 * If all motors were off, then the controller was
21105c1a1eaeSBruce Evans 			 * reset, so it has lost track of the current
21115c1a1eaeSBruce Evans 			 * cylinder.  Recalibrate to handle this case.
2112f86e4077SBruce Evans 			 * But first, discard the results of the reset.
21135e235068SJordan K. Hubbard 			 */
2114f86e4077SBruce Evans 			fdc->state = RESETCOMPLETE;
21155c1a1eaeSBruce Evans 		}
21165b81b6b3SRodney W. Grimes 		return (1);	/* will return immediatly */
21175b81b6b3SRodney W. Grimes 	default:
21186182fdbdSPeter Wemm 		device_print_prettyname(fdc->fdc_dev);
21196182fdbdSPeter Wemm 		printf("unexpected FD int->");
2120dc5df763SJoerg Wunsch 		if (fd_read_status(fdc, fd->fdsu) == 0)
2121a838d83dSBruce Evans 			printf("FDC status :%x %x %x %x %x %x %x   ",
21225b81b6b3SRodney W. Grimes 			       fdc->status[0],
21235b81b6b3SRodney W. Grimes 			       fdc->status[1],
21245b81b6b3SRodney W. Grimes 			       fdc->status[2],
21255b81b6b3SRodney W. Grimes 			       fdc->status[3],
21265b81b6b3SRodney W. Grimes 			       fdc->status[4],
21275b81b6b3SRodney W. Grimes 			       fdc->status[5],
21285b81b6b3SRodney W. Grimes 			       fdc->status[6] );
21293a2f7427SDavid Greenman 		else
2130dac0f2dbSJoerg Wunsch 			printf("No status available   ");
2131dac0f2dbSJoerg Wunsch 		if (fd_sense_int(fdc, &st0, &cyl) != 0)
2132dac0f2dbSJoerg Wunsch 		{
2133dac0f2dbSJoerg Wunsch 			printf("[controller is dead now]\n");
21345b81b6b3SRodney W. Grimes 			return (0);
21355b81b6b3SRodney W. Grimes 		}
2136dac0f2dbSJoerg Wunsch 		printf("ST0 = %x, PCN = %x\n", st0, cyl);
2137dac0f2dbSJoerg Wunsch 		return (0);
2138dac0f2dbSJoerg Wunsch 	}
2139dac0f2dbSJoerg Wunsch 	/*XXX confusing: some branches return immediately, others end up here*/
21405b81b6b3SRodney W. Grimes 	return (1); /* Come back immediatly to new state */
21415b81b6b3SRodney W. Grimes }
21425b81b6b3SRodney W. Grimes 
2143aaf08d94SGarrett Wollman static int
21446182fdbdSPeter Wemm retrier(struct fdc_data *fdc)
21455b81b6b3SRodney W. Grimes {
214617542807SPoul-Henning Kamp 	register struct buf *bp;
21476182fdbdSPeter Wemm 	struct fd_data *fd;
21486182fdbdSPeter Wemm 	int fdu;
21495b81b6b3SRodney W. Grimes 
2150e93e63cbSBruce Evans 	bp = fdc->bp;
21515b81b6b3SRodney W. Grimes 
21526182fdbdSPeter Wemm 	/* XXX shouldn't this be cached somewhere?  */
21536182fdbdSPeter Wemm 	fdu = FDUNIT(minor(bp->b_dev));
21546182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
21556182fdbdSPeter Wemm 	if (fd->options & FDOPT_NORETRY)
21563a2f7427SDavid Greenman 		goto fail;
21576182fdbdSPeter Wemm 
21586182fdbdSPeter Wemm 	switch (fdc->retry) {
21595b81b6b3SRodney W. Grimes 	case 0: case 1: case 2:
21605b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
21615b81b6b3SRodney W. Grimes 		break;
21625b81b6b3SRodney W. Grimes 	case 3: case 4: case 5:
21635b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
21645b81b6b3SRodney W. Grimes 		break;
21655b81b6b3SRodney W. Grimes 	case 6:
21665b81b6b3SRodney W. Grimes 		fdc->state = RESETCTLR;
21675b81b6b3SRodney W. Grimes 		break;
21685b81b6b3SRodney W. Grimes 	case 7:
21695b81b6b3SRodney W. Grimes 		break;
21705b81b6b3SRodney W. Grimes 	default:
21713a2f7427SDavid Greenman 	fail:
21725b81b6b3SRodney W. Grimes 		{
21737ca0641bSAndrey A. Chernov 			dev_t sav_b_dev = bp->b_dev;
21747ca0641bSAndrey A. Chernov 			/* Trick diskerr */
21753a2f7427SDavid Greenman 			bp->b_dev = makedev(major(bp->b_dev),
21763a2f7427SDavid Greenman 				    (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART);
2177887ba12fSBruce Evans 			diskerr(bp, "hard error", LOG_PRINTF,
21783a2f7427SDavid Greenman 				fdc->fd->skip / DEV_BSIZE,
21793a2f7427SDavid Greenman 				(struct disklabel *)NULL);
21807ca0641bSAndrey A. Chernov 			bp->b_dev = sav_b_dev;
2181dc5df763SJoerg Wunsch 			if (fdc->flags & FDC_STAT_VALID)
2182dc5df763SJoerg Wunsch 			{
2183dc5df763SJoerg Wunsch 				printf(
2184a838d83dSBruce Evans 			" (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n",
2185dc5df763SJoerg Wunsch 				       fdc->status[0], NE7_ST0BITS,
2186dc5df763SJoerg Wunsch 				       fdc->status[1], NE7_ST1BITS,
2187dc5df763SJoerg Wunsch 				       fdc->status[2], NE7_ST2BITS,
2188dc5df763SJoerg Wunsch 				       fdc->status[3], fdc->status[4],
2189dc5df763SJoerg Wunsch 				       fdc->status[5]);
2190dc5df763SJoerg Wunsch 			}
2191dc5df763SJoerg Wunsch 			else
2192dc5df763SJoerg Wunsch 				printf(" (No status)\n");
21935b81b6b3SRodney W. Grimes 		}
21945b81b6b3SRodney W. Grimes 		bp->b_flags |= B_ERROR;
21955b81b6b3SRodney W. Grimes 		bp->b_error = EIO;
2196bb6382faSJoerg Wunsch 		bp->b_resid += bp->b_bcount - fdc->fd->skip;
2197e93e63cbSBruce Evans 		fdc->bp = NULL;
21985b81b6b3SRodney W. Grimes 		fdc->fd->skip = 0;
219911a0be87SPoul-Henning Kamp 		devstat_end_transaction_buf(&fdc->fd->device_stats, bp);
22005b81b6b3SRodney W. Grimes 		biodone(bp);
220192ed385aSRodney W. Grimes 		fdc->state = FINDWORK;
22025c1a1eaeSBruce Evans 		fdc->flags |= FDC_NEEDS_RESET;
22035b81b6b3SRodney W. Grimes 		fdc->fd = (fd_p) 0;
22045b81b6b3SRodney W. Grimes 		fdc->fdu = -1;
220592ed385aSRodney W. Grimes 		return (1);
22065b81b6b3SRodney W. Grimes 	}
22075b81b6b3SRodney W. Grimes 	fdc->retry++;
22085b81b6b3SRodney W. Grimes 	return (1);
22095b81b6b3SRodney W. Grimes }
22105b81b6b3SRodney W. Grimes 
2211b39c878eSAndrey A. Chernov static int
2212b39c878eSAndrey A. Chernov fdformat(dev, finfo, p)
2213b39c878eSAndrey A. Chernov 	dev_t dev;
2214b39c878eSAndrey A. Chernov 	struct fd_formb *finfo;
2215b39c878eSAndrey A. Chernov 	struct proc *p;
2216b39c878eSAndrey A. Chernov {
2217b39c878eSAndrey A. Chernov  	fdu_t	fdu;
2218b39c878eSAndrey A. Chernov  	fd_p	fd;
2219b39c878eSAndrey A. Chernov 
2220b39c878eSAndrey A. Chernov 	struct buf *bp;
2221b39c878eSAndrey A. Chernov 	int rv = 0, s;
22223a2f7427SDavid Greenman 	size_t fdblk;
2223b39c878eSAndrey A. Chernov 
2224b39c878eSAndrey A. Chernov  	fdu	= FDUNIT(minor(dev));
22256182fdbdSPeter Wemm 	fd	= devclass_get_softc(fd_devclass, fdu);
22263a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2227b39c878eSAndrey A. Chernov 
2228b39c878eSAndrey A. Chernov 	/* set up a buffer header for fdstrategy() */
2229b39c878eSAndrey A. Chernov 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
2230b39c878eSAndrey A. Chernov 	if(bp == 0)
2231b39c878eSAndrey A. Chernov 		return ENOBUFS;
223282f5379bSJoerg Wunsch 	/*
223382f5379bSJoerg Wunsch 	 * keep the process from being swapped
223482f5379bSJoerg Wunsch 	 */
22352ada239cSPeter Wemm 	PHOLD(p);
2236b39c878eSAndrey A. Chernov 	bzero((void *)bp, sizeof(struct buf));
223767812eacSKirk McKusick 	BUF_LOCKINIT(bp);
223867812eacSKirk McKusick 	BUF_LOCK(bp, LK_EXCLUSIVE);
223967812eacSKirk McKusick 	bp->b_flags = B_PHYS | B_FORMAT;
2240b39c878eSAndrey A. Chernov 
2241b39c878eSAndrey A. Chernov 	/*
2242b39c878eSAndrey A. Chernov 	 * calculate a fake blkno, so fdstrategy() would initiate a
2243b39c878eSAndrey A. Chernov 	 * seek to the requested cylinder
2244b39c878eSAndrey A. Chernov 	 */
2245b39c878eSAndrey A. Chernov 	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
22463a2f7427SDavid Greenman 		+ finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
2247b39c878eSAndrey A. Chernov 
2248b39c878eSAndrey A. Chernov 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
2249ab3f7469SPoul-Henning Kamp 	bp->b_data = (caddr_t)finfo;
2250b39c878eSAndrey A. Chernov 
2251b39c878eSAndrey A. Chernov 	/* now do the format */
22523e425b96SJulian Elischer 	bp->b_dev = dev;
225349ff4debSPoul-Henning Kamp 	BUF_STRATEGY(bp, 0);
2254b39c878eSAndrey A. Chernov 
2255b39c878eSAndrey A. Chernov 	/* ...and wait for it to complete */
2256b39c878eSAndrey A. Chernov 	s = splbio();
22576182fdbdSPeter Wemm 	while(!(bp->b_flags & B_DONE)) {
22585e235068SJordan K. Hubbard 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
2259b39c878eSAndrey A. Chernov 		if (rv == EWOULDBLOCK)
2260b39c878eSAndrey A. Chernov 			break;
2261b39c878eSAndrey A. Chernov 	}
2262b39c878eSAndrey A. Chernov 	splx(s);
2263b39c878eSAndrey A. Chernov 
226482f5379bSJoerg Wunsch 	if (rv == EWOULDBLOCK) {
2265b39c878eSAndrey A. Chernov 		/* timed out */
2266b39c878eSAndrey A. Chernov 		rv = EIO;
226782f5379bSJoerg Wunsch 		biodone(bp);
226882f5379bSJoerg Wunsch 	}
22693a2f7427SDavid Greenman 	if (bp->b_flags & B_ERROR)
22703a2f7427SDavid Greenman 		rv = bp->b_error;
227182f5379bSJoerg Wunsch 	/*
227282f5379bSJoerg Wunsch 	 * allow the process to be swapped
227382f5379bSJoerg Wunsch 	 */
22742ada239cSPeter Wemm 	PRELE(p);
227567812eacSKirk McKusick 	BUF_UNLOCK(bp);
227667812eacSKirk McKusick 	BUF_LOCKFREE(bp);
2277b39c878eSAndrey A. Chernov 	free(bp, M_TEMP);
2278b39c878eSAndrey A. Chernov 	return rv;
2279b39c878eSAndrey A. Chernov }
2280b39c878eSAndrey A. Chernov 
2281f5f7ba03SJordan K. Hubbard /*
2282671e2ceeSBruce Evans  * TODO: don't allocate buffer on stack.
2283f5f7ba03SJordan K. Hubbard  */
22845b81b6b3SRodney W. Grimes 
22853e425b96SJulian Elischer static int
2286b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p)
2287f5f7ba03SJordan K. Hubbard 	dev_t dev;
2288ecbb00a2SDoug Rabson 	u_long cmd;
2289f5f7ba03SJordan K. Hubbard 	caddr_t addr;
2290f5f7ba03SJordan K. Hubbard 	int flag;
2291b39c878eSAndrey A. Chernov 	struct proc *p;
2292f5f7ba03SJordan K. Hubbard {
22933a2f7427SDavid Greenman  	fdu_t	fdu = FDUNIT(minor(dev));
22946182fdbdSPeter Wemm  	fd_p	fd = devclass_get_softc(fd_devclass, fdu);
22953a2f7427SDavid Greenman 	size_t fdblk;
22963a2f7427SDavid Greenman 
2297f5f7ba03SJordan K. Hubbard 	struct fd_type *fdt;
2298f5f7ba03SJordan K. Hubbard 	struct disklabel *dl;
2299f5f7ba03SJordan K. Hubbard 	char buffer[DEV_BSIZE];
23003a2f7427SDavid Greenman 	int error = 0;
2301f5f7ba03SJordan K. Hubbard 
23023a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2303f5f7ba03SJordan K. Hubbard 
23046182fdbdSPeter Wemm 	switch (cmd) {
2305f5f7ba03SJordan K. Hubbard 	case DIOCGDINFO:
2306f5f7ba03SJordan K. Hubbard 		bzero(buffer, sizeof (buffer));
2307f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)buffer;
23083a2f7427SDavid Greenman 		dl->d_secsize = fdblk;
23096182fdbdSPeter Wemm 		fdt = fd->ft;
2310f5f7ba03SJordan K. Hubbard 		dl->d_secpercyl = fdt->size / fdt->tracks;
2311f5f7ba03SJordan K. Hubbard 		dl->d_type = DTYPE_FLOPPY;
2312f5f7ba03SJordan K. Hubbard 
231349ff4debSPoul-Henning Kamp 		if (readdisklabel(dkmodpart(dev, RAW_PART), dl)
2314191e1a59SBruce Evans 		    == NULL)
2315f5f7ba03SJordan K. Hubbard 			error = 0;
2316f5f7ba03SJordan K. Hubbard 		else
2317f5f7ba03SJordan K. Hubbard 			error = EINVAL;
2318f5f7ba03SJordan K. Hubbard 
2319f5f7ba03SJordan K. Hubbard 		*(struct disklabel *)addr = *dl;
2320f5f7ba03SJordan K. Hubbard 		break;
2321f5f7ba03SJordan K. Hubbard 
2322f5f7ba03SJordan K. Hubbard 	case DIOCSDINFO:
2323f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2324f5f7ba03SJordan K. Hubbard 			error = EBADF;
2325f5f7ba03SJordan K. Hubbard 		break;
2326f5f7ba03SJordan K. Hubbard 
2327f5f7ba03SJordan K. Hubbard 	case DIOCWLABEL:
2328f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2329f5f7ba03SJordan K. Hubbard 			error = EBADF;
2330f5f7ba03SJordan K. Hubbard 		break;
2331f5f7ba03SJordan K. Hubbard 
2332f5f7ba03SJordan K. Hubbard 	case DIOCWDINFO:
23336182fdbdSPeter Wemm 		if ((flag & FWRITE) == 0) {
2334f5f7ba03SJordan K. Hubbard 			error = EBADF;
2335f5f7ba03SJordan K. Hubbard 			break;
2336f5f7ba03SJordan K. Hubbard 		}
2337f5f7ba03SJordan K. Hubbard 
2338f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)addr;
2339f5f7ba03SJordan K. Hubbard 
2340191e1a59SBruce Evans 		if ((error = setdisklabel((struct disklabel *)buffer, dl,
2341191e1a59SBruce Evans 					  (u_long)0)) != 0)
2342f5f7ba03SJordan K. Hubbard 			break;
2343f5f7ba03SJordan K. Hubbard 
234449ff4debSPoul-Henning Kamp 		error = writedisklabel(dev, (struct disklabel *)buffer);
2345b39c878eSAndrey A. Chernov 		break;
2346b39c878eSAndrey A. Chernov 	case FD_FORM:
2347b39c878eSAndrey A. Chernov 		if ((flag & FWRITE) == 0)
2348b39c878eSAndrey A. Chernov 			error = EBADF;	/* must be opened for writing */
2349b39c878eSAndrey A. Chernov 		else if (((struct fd_formb *)addr)->format_version !=
2350b39c878eSAndrey A. Chernov 			FD_FORMAT_VERSION)
2351b39c878eSAndrey A. Chernov 			error = EINVAL;	/* wrong version of formatting prog */
2352b39c878eSAndrey A. Chernov 		else
2353b39c878eSAndrey A. Chernov 			error = fdformat(dev, (struct fd_formb *)addr, p);
2354b39c878eSAndrey A. Chernov 		break;
2355b39c878eSAndrey A. Chernov 
2356b39c878eSAndrey A. Chernov 	case FD_GTYPE:                  /* get drive type */
23573e425b96SJulian Elischer 		*(struct fd_type *)addr = *fd->ft;
2358f5f7ba03SJordan K. Hubbard 		break;
2359f5f7ba03SJordan K. Hubbard 
23603a2f7427SDavid Greenman 	case FD_STYPE:                  /* set drive type */
23613a2f7427SDavid Greenman 		/* this is considered harmful; only allow for superuser */
2362f711d546SPoul-Henning Kamp 		if (suser(p) != 0)
23633a2f7427SDavid Greenman 			return EPERM;
23643e425b96SJulian Elischer 		*fd->ft = *(struct fd_type *)addr;
23653a2f7427SDavid Greenman 		break;
23663a2f7427SDavid Greenman 
23673a2f7427SDavid Greenman 	case FD_GOPTS:			/* get drive options */
23683e425b96SJulian Elischer 		*(int *)addr = fd->options;
23693a2f7427SDavid Greenman 		break;
23703a2f7427SDavid Greenman 
23713a2f7427SDavid Greenman 	case FD_SOPTS:			/* set drive options */
23723e425b96SJulian Elischer 		fd->options = *(int *)addr;
23733a2f7427SDavid Greenman 		break;
23743a2f7427SDavid Greenman 
2375f5f7ba03SJordan K. Hubbard 	default:
23763a2f7427SDavid Greenman 		error = ENOTTY;
2377f5f7ba03SJordan K. Hubbard 		break;
2378f5f7ba03SJordan K. Hubbard 	}
2379f5f7ba03SJordan K. Hubbard 	return (error);
2380f5f7ba03SJordan K. Hubbard }
2381f5f7ba03SJordan K. Hubbard 
23823a2f7427SDavid Greenman /*
23833a2f7427SDavid Greenman  * Hello emacs, these are the
23843a2f7427SDavid Greenman  * Local Variables:
23853a2f7427SDavid Greenman  *  c-indent-level:               8
23863a2f7427SDavid Greenman  *  c-continued-statement-offset: 8
23873a2f7427SDavid Greenman  *  c-continued-brace-offset:     0
23883a2f7427SDavid Greenman  *  c-brace-offset:              -8
23893a2f7427SDavid Greenman  *  c-brace-imaginary-offset:     0
23903a2f7427SDavid Greenman  *  c-argdecl-indent:             8
23913a2f7427SDavid Greenman  *  c-label-offset:              -8
23923a2f7427SDavid Greenman  *  c++-hanging-braces:           1
23933a2f7427SDavid Greenman  *  c++-access-specifier-offset: -8
23943a2f7427SDavid Greenman  *  c++-empty-arglist-indent:     8
23953a2f7427SDavid Greenman  *  c++-friend-offset:            0
23963a2f7427SDavid Greenman  * End:
23973a2f7427SDavid Greenman  */
2398