xref: /netbsd-src/sys/arch/sparc64/dev/fdc.c (revision 19bd2f7501efb5091ec87def228b32a4ada4bfc4)
1*19bd2f75Sjdc /*	$NetBSD: fdc.c,v 1.52 2023/09/02 05:51:57 jdc Exp $	*/
26238d5faSjnemeth 
36238d5faSjnemeth /*-
46238d5faSjnemeth  * Copyright (c) 2000 The NetBSD Foundation, Inc.
56238d5faSjnemeth  * All rights reserved.
66238d5faSjnemeth  *
76238d5faSjnemeth  * This code is derived from software contributed to The NetBSD Foundation
86238d5faSjnemeth  * by Paul Kranenburg.
96238d5faSjnemeth  *
106238d5faSjnemeth  * Redistribution and use in source and binary forms, with or without
116238d5faSjnemeth  * modification, are permitted provided that the following conditions
126238d5faSjnemeth  * are met:
136238d5faSjnemeth  * 1. Redistributions of source code must retain the above copyright
146238d5faSjnemeth  *    notice, this list of conditions and the following disclaimer.
156238d5faSjnemeth  * 2. Redistributions in binary form must reproduce the above copyright
166238d5faSjnemeth  *    notice, this list of conditions and the following disclaimer in the
176238d5faSjnemeth  *    documentation and/or other materials provided with the distribution.
186238d5faSjnemeth  *
196238d5faSjnemeth  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
206238d5faSjnemeth  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
216238d5faSjnemeth  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
226238d5faSjnemeth  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
236238d5faSjnemeth  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
246238d5faSjnemeth  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
256238d5faSjnemeth  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
266238d5faSjnemeth  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
276238d5faSjnemeth  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
286238d5faSjnemeth  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
296238d5faSjnemeth  * POSSIBILITY OF SUCH DAMAGE.
306238d5faSjnemeth  */
316238d5faSjnemeth 
326238d5faSjnemeth /*-
336238d5faSjnemeth  * Copyright (c) 1990 The Regents of the University of California.
346238d5faSjnemeth  * All rights reserved.
356238d5faSjnemeth  *
366238d5faSjnemeth  * This code is derived from software contributed to Berkeley by
376238d5faSjnemeth  * Don Ahn.
386238d5faSjnemeth  *
396238d5faSjnemeth  * Redistribution and use in source and binary forms, with or without
406238d5faSjnemeth  * modification, are permitted provided that the following conditions
416238d5faSjnemeth  * are met:
426238d5faSjnemeth  * 1. Redistributions of source code must retain the above copyright
436238d5faSjnemeth  *    notice, this list of conditions and the following disclaimer.
446238d5faSjnemeth  * 2. Redistributions in binary form must reproduce the above copyright
456238d5faSjnemeth  *    notice, this list of conditions and the following disclaimer in the
466238d5faSjnemeth  *    documentation and/or other materials provided with the distribution.
476238d5faSjnemeth  * 3. Neither the name of the University nor the names of its contributors
486238d5faSjnemeth  *    may be used to endorse or promote products derived from this software
496238d5faSjnemeth  *    without specific prior written permission.
506238d5faSjnemeth  *
516238d5faSjnemeth  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
526238d5faSjnemeth  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
536238d5faSjnemeth  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
546238d5faSjnemeth  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
556238d5faSjnemeth  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
566238d5faSjnemeth  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
576238d5faSjnemeth  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
586238d5faSjnemeth  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
596238d5faSjnemeth  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
606238d5faSjnemeth  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
616238d5faSjnemeth  * SUCH DAMAGE.
626238d5faSjnemeth  *
636238d5faSjnemeth  *	@(#)fd.c	7.4 (Berkeley) 5/25/91
646238d5faSjnemeth  */
656238d5faSjnemeth 
666238d5faSjnemeth /*-
676238d5faSjnemeth  * Copyright (c) 1993, 1994, 1995 Charles M. Hannum.
686238d5faSjnemeth  *
696238d5faSjnemeth  * This code is derived from software contributed to Berkeley by
706238d5faSjnemeth  * Don Ahn.
716238d5faSjnemeth  *
726238d5faSjnemeth  * Redistribution and use in source and binary forms, with or without
736238d5faSjnemeth  * modification, are permitted provided that the following conditions
746238d5faSjnemeth  * are met:
756238d5faSjnemeth  * 1. Redistributions of source code must retain the above copyright
766238d5faSjnemeth  *    notice, this list of conditions and the following disclaimer.
776238d5faSjnemeth  * 2. Redistributions in binary form must reproduce the above copyright
786238d5faSjnemeth  *    notice, this list of conditions and the following disclaimer in the
796238d5faSjnemeth  *    documentation and/or other materials provided with the distribution.
806238d5faSjnemeth  * 3. All advertising materials mentioning features or use of this software
816238d5faSjnemeth  *    must display the following acknowledgement:
826238d5faSjnemeth  *	This product includes software developed by the University of
836238d5faSjnemeth  *	California, Berkeley and its contributors.
846238d5faSjnemeth  * 4. Neither the name of the University nor the names of its contributors
856238d5faSjnemeth  *    may be used to endorse or promote products derived from this software
866238d5faSjnemeth  *    without specific prior written permission.
876238d5faSjnemeth  *
886238d5faSjnemeth  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
896238d5faSjnemeth  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
906238d5faSjnemeth  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
916238d5faSjnemeth  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
926238d5faSjnemeth  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
936238d5faSjnemeth  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
946238d5faSjnemeth  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
956238d5faSjnemeth  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
966238d5faSjnemeth  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
976238d5faSjnemeth  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
986238d5faSjnemeth  * SUCH DAMAGE.
996238d5faSjnemeth  *
1006238d5faSjnemeth  *	@(#)fd.c	7.4 (Berkeley) 5/25/91
1016238d5faSjnemeth  */
1026238d5faSjnemeth 
1036238d5faSjnemeth #include <sys/cdefs.h>
104*19bd2f75Sjdc __KERNEL_RCSID(0, "$NetBSD: fdc.c,v 1.52 2023/09/02 05:51:57 jdc Exp $");
1056238d5faSjnemeth 
1066238d5faSjnemeth #include "opt_ddb.h"
1076238d5faSjnemeth #include "opt_md.h"
1086238d5faSjnemeth 
1096238d5faSjnemeth #include <sys/param.h>
1100b59ea5aSjnemeth #include <sys/types.h>
1116238d5faSjnemeth #include <sys/systm.h>
1126238d5faSjnemeth #include <sys/callout.h>
1136238d5faSjnemeth #include <sys/kernel.h>
1146238d5faSjnemeth #include <sys/file.h>
1156238d5faSjnemeth #include <sys/ioctl.h>
1166238d5faSjnemeth #include <sys/device.h>
1176238d5faSjnemeth #include <sys/disklabel.h>
1186238d5faSjnemeth #include <sys/disk.h>
1196238d5faSjnemeth #include <sys/fdio.h>
1206238d5faSjnemeth #include <sys/buf.h>
1216238d5faSjnemeth #include <sys/bufq.h>
122fc256c4aSthorpej #include <sys/kmem.h>
1236238d5faSjnemeth #include <sys/proc.h>
1246238d5faSjnemeth #include <sys/uio.h>
1256238d5faSjnemeth #include <sys/stat.h>
1266238d5faSjnemeth #include <sys/syslog.h>
1276238d5faSjnemeth #include <sys/queue.h>
1286238d5faSjnemeth #include <sys/conf.h>
1291a823187Sad #include <sys/intr.h>
1306238d5faSjnemeth 
1316238d5faSjnemeth #include <dev/cons.h>
1326238d5faSjnemeth 
1336238d5faSjnemeth #include <uvm/uvm_extern.h>
1346238d5faSjnemeth 
1356238d5faSjnemeth #include <machine/autoconf.h>
1366238d5faSjnemeth 
137d34b8a70Sjnemeth #ifdef SUN4
138d34b8a70Sjnemeth #include <sparc/sparc/auxreg.h>
139d34b8a70Sjnemeth #include <sparc/dev/fdreg.h>
140d34b8a70Sjnemeth #include <sparc/dev/fdvar.h>
141d34b8a70Sjnemeth #elif SUN4U
1426238d5faSjnemeth #include <dev/ebus/ebusreg.h>
1436238d5faSjnemeth #include <dev/ebus/ebusvar.h>
1444887604bSnakayama #include <dev/sbus/sbusvar.h>
1456238d5faSjnemeth #include <sparc64/dev/auxioreg.h>
1466238d5faSjnemeth #include <sparc64/dev/auxiovar.h>
1476238d5faSjnemeth #include <sparc64/dev/fdcreg.h>
1486238d5faSjnemeth #include <sparc64/dev/fdcvar.h>
149d34b8a70Sjnemeth #endif
1506238d5faSjnemeth 
1513d612181Sjnemeth #include <prop/proplib.h>
1523d612181Sjnemeth 
1536238d5faSjnemeth #define FDUNIT(dev)	(minor(dev) / 8)
1546238d5faSjnemeth #define FDTYPE(dev)	(minor(dev) % 8)
1556238d5faSjnemeth 
15643061c75Sreinoud /* (mis)use device use flag to identify format operation */
15743061c75Sreinoud #define B_FORMAT B_DEVPRIVATE
1586238d5faSjnemeth 
1596238d5faSjnemeth #define FD_DEBUG
1606238d5faSjnemeth #ifdef FD_DEBUG
1616238d5faSjnemeth int	fdc_debug = 0;
1626238d5faSjnemeth #endif
1636238d5faSjnemeth 
1646238d5faSjnemeth enum fdc_state {
1656238d5faSjnemeth 	DEVIDLE = 0,
1666238d5faSjnemeth 	MOTORWAIT,	/*  1 */
1676238d5faSjnemeth 	DOSEEK,		/*  2 */
1686238d5faSjnemeth 	SEEKWAIT,	/*  3 */
1696238d5faSjnemeth 	SEEKTIMEDOUT,	/*  4 */
1706238d5faSjnemeth 	SEEKCOMPLETE,	/*  5 */
1716238d5faSjnemeth 	DOIO,		/*  6 */
1726238d5faSjnemeth 	IOCOMPLETE,	/*  7 */
1736238d5faSjnemeth 	IOTIMEDOUT,	/*  8 */
1746238d5faSjnemeth 	IOCLEANUPWAIT,	/*  9 */
1756238d5faSjnemeth 	IOCLEANUPTIMEDOUT,/*10 */
1766238d5faSjnemeth 	DORESET,	/* 11 */
1776238d5faSjnemeth 	RESETCOMPLETE,	/* 12 */
1786238d5faSjnemeth 	RESETTIMEDOUT,	/* 13 */
1796238d5faSjnemeth 	DORECAL,	/* 14 */
1806238d5faSjnemeth 	RECALWAIT,	/* 15 */
1816238d5faSjnemeth 	RECALTIMEDOUT,	/* 16 */
1826238d5faSjnemeth 	RECALCOMPLETE,	/* 17 */
1836238d5faSjnemeth 	DODSKCHG,	/* 18 */
1846238d5faSjnemeth 	DSKCHGWAIT,	/* 19 */
1856238d5faSjnemeth 	DSKCHGTIMEDOUT,	/* 20 */
1866238d5faSjnemeth };
1876238d5faSjnemeth 
1886238d5faSjnemeth /* software state, per controller */
1896238d5faSjnemeth struct fdc_softc {
190ae8fd9fdSchristos 	device_t	sc_dev;		/* boilerplate */
1916238d5faSjnemeth 	bus_space_tag_t	sc_bustag;
1926238d5faSjnemeth 
1936238d5faSjnemeth 	struct callout sc_timo_ch;	/* timeout callout */
1946238d5faSjnemeth 	struct callout sc_intr_ch;	/* pseudo-intr callout */
1956238d5faSjnemeth 
1966238d5faSjnemeth 	struct fd_softc *sc_fd[4];	/* pointers to children */
1976238d5faSjnemeth 	TAILQ_HEAD(drivehead, fd_softc) sc_drives;
1986238d5faSjnemeth 	enum fdc_state	sc_state;
1996238d5faSjnemeth 	int		sc_flags;
2006238d5faSjnemeth #define FDC_82077		0x01
2016238d5faSjnemeth #define FDC_NEEDHEADSETTLE	0x02
2026238d5faSjnemeth #define FDC_EIS			0x04
2036238d5faSjnemeth #define FDC_NEEDMOTORWAIT	0x08
2046238d5faSjnemeth #define FDC_NOEJECT		0x10
2056238d5faSjnemeth #define FDC_EBUS		0x20
2066238d5faSjnemeth 	int		sc_errors;		/* number of retries so far */
2076238d5faSjnemeth 	int		sc_overruns;		/* number of DMA overruns */
2086238d5faSjnemeth 	int		sc_cfg;			/* current configuration */
2096238d5faSjnemeth 	struct fdcio	sc_io;
2106238d5faSjnemeth #define sc_handle	sc_io.fdcio_handle
2116238d5faSjnemeth #define sc_reg_msr	sc_io.fdcio_reg_msr
2126238d5faSjnemeth #define sc_reg_fifo	sc_io.fdcio_reg_fifo
2136238d5faSjnemeth #define sc_reg_dor	sc_io.fdcio_reg_dor
2146238d5faSjnemeth #define sc_reg_dir	sc_io.fdcio_reg_dir
2156238d5faSjnemeth #define sc_reg_drs	sc_io.fdcio_reg_msr
2166238d5faSjnemeth #define sc_itask	sc_io.fdcio_itask
2176238d5faSjnemeth #define sc_istatus	sc_io.fdcio_istatus
2186238d5faSjnemeth #define sc_data		sc_io.fdcio_data
2196238d5faSjnemeth #define sc_tc		sc_io.fdcio_tc
2206238d5faSjnemeth #define sc_nstat	sc_io.fdcio_nstat
2216238d5faSjnemeth #define sc_status	sc_io.fdcio_status
2226238d5faSjnemeth #define sc_intrcnt	sc_io.fdcio_intrcnt
2236238d5faSjnemeth 
224fd5cef85Sjnemeth 	void		*sc_sicookie;	/* softint(9) cookie */
2256238d5faSjnemeth };
2266238d5faSjnemeth 
227d34b8a70Sjnemeth #ifdef SUN4
2286238d5faSjnemeth extern	struct fdcio	*fdciop;	/* I/O descriptor used in fdintr.s */
2296238d5faSjnemeth #endif
2306238d5faSjnemeth 
2316238d5faSjnemeth /* controller driver configuration */
232d34b8a70Sjnemeth #ifdef SUN4
233ae8fd9fdSchristos int	fdcmatch_mainbus(device_t, cfdata_t, void*);
234ae8fd9fdSchristos int	fdcmatch_obio(device_t, cfdata_t, void *);
235ae8fd9fdSchristos void	fdcattach_mainbus(device_t, device_t, void *);
236ae8fd9fdSchristos void	fdcattach_obio(device_t, device_t, void *);
237d34b8a70Sjnemeth #elif SUN4U
238ae8fd9fdSchristos int	fdcmatch_sbus(device_t, cfdata_t, void *);
239ae8fd9fdSchristos int	fdcmatch_ebus(device_t, cfdata_t, void *);
240ae8fd9fdSchristos void	fdcattach_sbus(device_t, device_t, void *);
241ae8fd9fdSchristos void	fdcattach_ebus(device_t, device_t, void *);
242d34b8a70Sjnemeth #endif
2436238d5faSjnemeth 
2446238d5faSjnemeth int	fdcattach(struct fdc_softc *, int);
2456238d5faSjnemeth 
246d34b8a70Sjnemeth #ifdef SUN4
247ae8fd9fdSchristos CFATTACH_DECL_NEW(fdc_mainbus, sizeof(struct fdc_softc),
248d34b8a70Sjnemeth     fdcmatch_mainbus, fdcattach_mainbus, NULL, NULL);
249d34b8a70Sjnemeth 
250ae8fd9fdSchristos CFATTACH_DECL_NEW(fdc_obio, sizeof(struct fdc_softc),
251d34b8a70Sjnemeth     fdcmatch_obio, fdcattach_obio, NULL, NULL);
252d34b8a70Sjnemeth #elif SUN4U
253ae8fd9fdSchristos CFATTACH_DECL_NEW(fdc_sbus, sizeof(struct fdc_softc),
2546238d5faSjnemeth     fdcmatch_sbus, fdcattach_sbus, NULL, NULL);
2556238d5faSjnemeth 
256ae8fd9fdSchristos CFATTACH_DECL_NEW(fdc_ebus, sizeof(struct fdc_softc),
2576238d5faSjnemeth     fdcmatch_ebus, fdcattach_ebus, NULL, NULL);
258d34b8a70Sjnemeth #endif
2596238d5faSjnemeth 
2608255ce9eSmrg static struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t);
2616238d5faSjnemeth 
2626238d5faSjnemeth /*
2636238d5faSjnemeth  * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
2646238d5faSjnemeth  * we tell them apart.
2656238d5faSjnemeth  */
2666238d5faSjnemeth struct fd_type {
2676238d5faSjnemeth 	int	sectrac;	/* sectors per track */
2686238d5faSjnemeth 	int	heads;		/* number of heads */
2696238d5faSjnemeth 	int	seccyl;		/* sectors per cylinder */
2706238d5faSjnemeth 	int	secsize;	/* size code for sectors */
2716238d5faSjnemeth 	int	datalen;	/* data len when secsize = 0 */
2726238d5faSjnemeth 	int	steprate;	/* step rate and head unload time */
2736238d5faSjnemeth 	int	gap1;		/* gap len between sectors */
2746238d5faSjnemeth 	int	gap2;		/* formatting gap */
2756238d5faSjnemeth 	int	cylinders;	/* total num of cylinders */
2766238d5faSjnemeth 	int	size;		/* size of disk in sectors */
2776238d5faSjnemeth 	int	step;		/* steps per cylinder */
2786238d5faSjnemeth 	int	rate;		/* transfer speed code */
2796238d5faSjnemeth 	int	fillbyte;	/* format fill byte */
2806238d5faSjnemeth 	int	interleave;	/* interleave factor (formatting) */
2816238d5faSjnemeth 	const char *name;
2826238d5faSjnemeth };
2836238d5faSjnemeth 
2846238d5faSjnemeth /* The order of entries in the following table is important -- BEWARE! */
2856238d5faSjnemeth struct fd_type fd_types[] = {
2865b888d73Sjnemeth 	{ 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,0xf6,1, "1.44MB"    }, /* 1.44MB diskette */
2876238d5faSjnemeth 	{  9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS,0xf6,1, "720KB"    }, /* 3.5" 720kB diskette */
2886238d5faSjnemeth 	{  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS,0xf6,1, "360KB/x"  }, /* 360kB in 720kB drive */
2896238d5faSjnemeth 	{  8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS,0xf6,1, "1.2MB/NEC" } /* 1.2 MB japanese format */
2906238d5faSjnemeth };
2916238d5faSjnemeth 
2926238d5faSjnemeth /* software state, per disk (with up to 4 disks per ctlr) */
2936238d5faSjnemeth struct fd_softc {
294ae8fd9fdSchristos 	device_t	sc_dev;		/* generic device info */
2956238d5faSjnemeth 	struct disk	sc_dk;		/* generic disk info */
2966238d5faSjnemeth 
2976238d5faSjnemeth 	struct fd_type *sc_deftype;	/* default type descriptor */
2986238d5faSjnemeth 	struct fd_type *sc_type;	/* current type descriptor */
2996238d5faSjnemeth 
3006238d5faSjnemeth 	struct callout sc_motoron_ch;
3016238d5faSjnemeth 	struct callout sc_motoroff_ch;
3026238d5faSjnemeth 
3036238d5faSjnemeth 	daddr_t	sc_blkno;	/* starting block number */
3046238d5faSjnemeth 	int sc_bcount;		/* byte count left */
3056238d5faSjnemeth 	int sc_skip;		/* bytes already transferred */
3066238d5faSjnemeth 	int sc_nblks;		/* number of blocks currently transferring */
3076238d5faSjnemeth 	int sc_nbytes;		/* number of bytes currently transferring */
3086238d5faSjnemeth 
3096238d5faSjnemeth 	int sc_drive;		/* physical unit number */
3106238d5faSjnemeth 	int sc_flags;
3116238d5faSjnemeth #define	FD_OPEN		0x01		/* it's open */
3126238d5faSjnemeth #define	FD_MOTOR	0x02		/* motor should be on */
3136238d5faSjnemeth #define	FD_MOTOR_WAIT	0x04		/* motor coming up */
3146238d5faSjnemeth 	int sc_cylin;		/* where we think the head is */
3156238d5faSjnemeth 	int sc_opts;		/* user-set options */
3166238d5faSjnemeth 
3176238d5faSjnemeth 	TAILQ_ENTRY(fd_softc) sc_drivechain;
3186238d5faSjnemeth 	int sc_ops;		/* I/O ops since last switch */
3196238d5faSjnemeth 	struct bufq_state *sc_q;/* pending I/O requests */
3206238d5faSjnemeth 	int sc_active;		/* number of active I/O requests */
3216238d5faSjnemeth };
3226238d5faSjnemeth 
3236238d5faSjnemeth /* floppy driver configuration */
324ae8fd9fdSchristos int	fdmatch(device_t, cfdata_t, void *);
325ae8fd9fdSchristos void	fdattach(device_t, device_t, void *);
326f2975a45Sjnemeth bool	fdshutdown(device_t, int);
327c1b390d4Sdyoung bool	fdsuspend(device_t, const pmf_qual_t *);
3286238d5faSjnemeth 
329ae8fd9fdSchristos CFATTACH_DECL_NEW(fd, sizeof(struct fd_softc),
3306238d5faSjnemeth     fdmatch, fdattach, NULL, NULL);
3316238d5faSjnemeth 
3326238d5faSjnemeth extern struct cfdriver fd_cd;
3336238d5faSjnemeth 
3346238d5faSjnemeth dev_type_open(fdopen);
3356238d5faSjnemeth dev_type_close(fdclose);
3366238d5faSjnemeth dev_type_read(fdread);
3376238d5faSjnemeth dev_type_write(fdwrite);
3386238d5faSjnemeth dev_type_ioctl(fdioctl);
3396238d5faSjnemeth dev_type_strategy(fdstrategy);
3406238d5faSjnemeth 
3416238d5faSjnemeth const struct bdevsw fd_bdevsw = {
342a68f9396Sdholland 	.d_open = fdopen,
343a68f9396Sdholland 	.d_close = fdclose,
344a68f9396Sdholland 	.d_strategy = fdstrategy,
345a68f9396Sdholland 	.d_ioctl = fdioctl,
346a68f9396Sdholland 	.d_dump = nodump,
347a68f9396Sdholland 	.d_psize = nosize,
3488c70ef39Sdholland 	.d_discard = nodiscard,
349a68f9396Sdholland 	.d_flag = D_DISK
3506238d5faSjnemeth };
3516238d5faSjnemeth 
3526238d5faSjnemeth const struct cdevsw fd_cdevsw = {
353a68f9396Sdholland 	.d_open = fdopen,
354a68f9396Sdholland 	.d_close = fdclose,
355a68f9396Sdholland 	.d_read = fdread,
356a68f9396Sdholland 	.d_write = fdwrite,
357a68f9396Sdholland 	.d_ioctl = fdioctl,
358a68f9396Sdholland 	.d_stop = nostop,
359a68f9396Sdholland 	.d_tty = notty,
360a68f9396Sdholland 	.d_poll = nopoll,
361a68f9396Sdholland 	.d_mmap = nommap,
362a68f9396Sdholland 	.d_kqfilter = nokqfilter,
363f9228f42Sdholland 	.d_discard = nodiscard,
364a68f9396Sdholland 	.d_flag = D_DISK
3656238d5faSjnemeth };
3666238d5faSjnemeth 
3676238d5faSjnemeth void fdgetdisklabel(dev_t);
3686238d5faSjnemeth int fd_get_parms(struct fd_softc *);
3696238d5faSjnemeth void fdstrategy(struct buf *);
3706238d5faSjnemeth void fdstart(struct fd_softc *);
3716238d5faSjnemeth int fdprint(void *, const char *);
3726238d5faSjnemeth 
3736f00c789Smlelstv struct dkdriver fddkdriver = {
3746f00c789Smlelstv 	.d_strategy = fdstrategy
3756f00c789Smlelstv };
3766238d5faSjnemeth 
3776238d5faSjnemeth struct	fd_type *fd_nvtotype(char *, int, int);
3786238d5faSjnemeth void	fd_set_motor(struct fdc_softc *);
3796238d5faSjnemeth void	fd_motor_off(void *);
3806238d5faSjnemeth void	fd_motor_on(void *);
3816238d5faSjnemeth int	fdcresult(struct fdc_softc *);
3826238d5faSjnemeth int	fdc_wrfifo(struct fdc_softc *, uint8_t);
3836238d5faSjnemeth void	fdcstart(struct fdc_softc *);
3846238d5faSjnemeth void	fdcstatus(struct fdc_softc *, const char *);
3856238d5faSjnemeth void	fdc_reset(struct fdc_softc *);
3866238d5faSjnemeth int	fdc_diskchange(struct fdc_softc *);
3876238d5faSjnemeth void	fdctimeout(void *);
3886238d5faSjnemeth void	fdcpseudointr(void *);
3896238d5faSjnemeth int	fdc_c_hwintr(void *);
3906238d5faSjnemeth void	fdchwintr(void);
3916238d5faSjnemeth void	fdcswintr(void *);
3926238d5faSjnemeth int	fdcstate(struct fdc_softc *);
3936238d5faSjnemeth void	fdcretry(struct fdc_softc *);
3946238d5faSjnemeth void	fdfinish(struct fd_softc *, struct buf *);
3956238d5faSjnemeth int	fdformat(dev_t, struct ne7_fd_formb *, struct proc *);
3966238d5faSjnemeth void	fd_do_eject(struct fd_softc *);
397ae8fd9fdSchristos void	fd_mountroot_hook(device_t );
3986238d5faSjnemeth static int fdconf(struct fdc_softc *);
3996238d5faSjnemeth static void establish_chip_type(
4006238d5faSjnemeth 		struct fdc_softc *,
4016238d5faSjnemeth 		bus_space_tag_t,
4026238d5faSjnemeth 		bus_addr_t,
4036238d5faSjnemeth 		bus_size_t,
4046238d5faSjnemeth 		bus_space_handle_t);
4057b845fa9Schristos static void	fd_set_geometry(struct fd_softc *);
4066238d5faSjnemeth 
4076238d5faSjnemeth #ifdef MEMORY_DISK_HOOKS
40853524e44Schristos int	fd_read_md_image(size_t *, void **);
4096238d5faSjnemeth #endif
4106238d5faSjnemeth 
411d34b8a70Sjnemeth #ifdef SUN4
412d34b8a70Sjnemeth #define OBP_FDNAME	(CPU_ISSUN4M ? "SUNW,fdtwo" : "fd")
413d34b8a70Sjnemeth 
414d34b8a70Sjnemeth int
fdcmatch_mainbus(device_t parent,cfdata_t match,void * aux)415ae8fd9fdSchristos fdcmatch_mainbus(device_t parent, cfdata_t match, void *aux)
416d34b8a70Sjnemeth {
417d34b8a70Sjnemeth 	struct mainbus_attach_args *ma = aux;
418d34b8a70Sjnemeth 
419d34b8a70Sjnemeth 	/*
420d34b8a70Sjnemeth 	 * Floppy controller is on mainbus on sun4c.
421d34b8a70Sjnemeth 	 */
422d34b8a70Sjnemeth 	if (!CPU_ISSUN4C)
423d34b8a70Sjnemeth 		return 0;
424d34b8a70Sjnemeth 
425d34b8a70Sjnemeth 	/* sun4c PROMs call the controller "fd" */
426d34b8a70Sjnemeth 	if (strcmp("fd", ma->ma_name) != 0)
427d34b8a70Sjnemeth 		return 0;
428d34b8a70Sjnemeth 
429d34b8a70Sjnemeth 	return bus_space_probe(ma->ma_bustag,
430d34b8a70Sjnemeth 			       ma->ma_paddr,
431d34b8a70Sjnemeth 			       1,	/* probe size */
432d34b8a70Sjnemeth 			       0,	/* offset */
433d34b8a70Sjnemeth 			       0,	/* flags */
434d34b8a70Sjnemeth 			       NULL, NULL);
435d34b8a70Sjnemeth }
436d34b8a70Sjnemeth 
437d34b8a70Sjnemeth int
fdcmatch_obio(device_t parent,cfdata_t match,void * aux)438ae8fd9fdSchristos fdcmatch_obio(device_t parent, cfdata_t match, void *aux)
439d34b8a70Sjnemeth {
440d34b8a70Sjnemeth 	union obio_attach_args *uoba = aux;
441d34b8a70Sjnemeth 	struct sbus_attach_args *sa;
442d34b8a70Sjnemeth 
443d34b8a70Sjnemeth 	/*
444d34b8a70Sjnemeth 	 * Floppy controller is on obio on sun4m.
445d34b8a70Sjnemeth 	 */
446d34b8a70Sjnemeth 	if (uoba->uoba_isobio4 != 0)
447d34b8a70Sjnemeth 		return 0;
448d34b8a70Sjnemeth 
449d34b8a70Sjnemeth 	sa = &uoba->uoba_sbus;
450d34b8a70Sjnemeth 
451d34b8a70Sjnemeth 	/* sun4m PROMs call the controller "SUNW,fdtwo" */
452d34b8a70Sjnemeth 	if (strcmp("SUNW,fdtwo", sa->sa_name) != 0)
453d34b8a70Sjnemeth 		return 0;
454d34b8a70Sjnemeth 
455d34b8a70Sjnemeth 	return bus_space_probe(sa->sa_bustag,
456d34b8a70Sjnemeth 			sbus_bus_addr(sa->sa_bustag,
457d34b8a70Sjnemeth 					sa->sa_slot, sa->sa_offset),
458d34b8a70Sjnemeth 			1,	/* probe size */
459d34b8a70Sjnemeth 			0,	/* offset */
460d34b8a70Sjnemeth 			0,	/* flags */
461d34b8a70Sjnemeth 			NULL, NULL);
462d34b8a70Sjnemeth }
463d34b8a70Sjnemeth 
464d34b8a70Sjnemeth #elif SUN4U
465d34b8a70Sjnemeth 
4666238d5faSjnemeth int
fdcmatch_sbus(device_t parent,cfdata_t match,void * aux)467ae8fd9fdSchristos fdcmatch_sbus(device_t parent, cfdata_t match, void *aux)
4686238d5faSjnemeth {
4696238d5faSjnemeth 	struct sbus_attach_args *sa = aux;
4706238d5faSjnemeth 
4716238d5faSjnemeth 	return strcmp("SUNW,fdtwo", sa->sa_name) == 0;
4726238d5faSjnemeth }
4736238d5faSjnemeth 
4746238d5faSjnemeth int
fdcmatch_ebus(device_t parent,cfdata_t match,void * aux)475ae8fd9fdSchristos fdcmatch_ebus(device_t parent, cfdata_t match, void *aux)
4766238d5faSjnemeth {
4776238d5faSjnemeth 	struct ebus_attach_args *ea = aux;
4786238d5faSjnemeth 
4796238d5faSjnemeth 	return strcmp("fdthree", ea->ea_name) == 0;
4806238d5faSjnemeth }
481d34b8a70Sjnemeth #endif
4826238d5faSjnemeth 
4836238d5faSjnemeth static void
establish_chip_type(struct fdc_softc * fdc,bus_space_tag_t tag,bus_addr_t addr,bus_size_t size,bus_space_handle_t handle)4846238d5faSjnemeth establish_chip_type(struct fdc_softc *fdc,
4856238d5faSjnemeth 		    bus_space_tag_t tag, bus_addr_t addr, bus_size_t size,
4866238d5faSjnemeth 		    bus_space_handle_t handle)
4876238d5faSjnemeth {
4886238d5faSjnemeth 	uint8_t v;
4896238d5faSjnemeth 
4906238d5faSjnemeth 	/*
4916238d5faSjnemeth 	 * This hack from Chris Torek: apparently DOR really
4926238d5faSjnemeth 	 * addresses MSR/DRS on a 82072.
4936238d5faSjnemeth 	 * We used to rely on the VERSION command to tell the
4946238d5faSjnemeth 	 * difference (which did not work).
4956238d5faSjnemeth 	 */
4966238d5faSjnemeth 
4976238d5faSjnemeth 	/* First, check the size of the register bank */
4986238d5faSjnemeth 	if (size < 8)
4996238d5faSjnemeth 		/* It isn't a 82077 */
5006238d5faSjnemeth 		return;
5016238d5faSjnemeth 
502d34b8a70Sjnemeth #ifdef SUN4
5036238d5faSjnemeth 	/* Then probe the DOR register offset */
5046238d5faSjnemeth 	if (bus_space_probe(tag, addr,
5056238d5faSjnemeth 			    1,			/* probe size */
5066238d5faSjnemeth 			    FDREG77_DOR,	/* offset */
5076238d5faSjnemeth 			    0,			/* flags */
5086238d5faSjnemeth 			    NULL, NULL) == 0) {
5096238d5faSjnemeth 
5106238d5faSjnemeth 		/* It isn't a 82077 */
5116238d5faSjnemeth 		return;
5126238d5faSjnemeth 	}
5136238d5faSjnemeth #endif
5146238d5faSjnemeth 
5156238d5faSjnemeth 	v = bus_space_read_1(tag, handle, FDREG77_DOR);
5166238d5faSjnemeth 	if (v == NE7_RQM) {
5176238d5faSjnemeth 		/*
5186238d5faSjnemeth 		 * Value in DOR looks like it's really MSR
5196238d5faSjnemeth 		 */
5206238d5faSjnemeth 		bus_space_write_1(tag, handle, FDREG77_DOR, FDC_250KBPS);
5216238d5faSjnemeth 		v = bus_space_read_1(tag, handle, FDREG77_DOR);
5226238d5faSjnemeth 		if (v == NE7_RQM) {
5236238d5faSjnemeth 			/*
5246238d5faSjnemeth 			 * The value in the DOR didn't stick;
5256238d5faSjnemeth 			 * it isn't a 82077
5266238d5faSjnemeth 			 */
5276238d5faSjnemeth 			return;
5286238d5faSjnemeth 		}
5296238d5faSjnemeth 	}
5306238d5faSjnemeth 
5316238d5faSjnemeth 	fdc->sc_flags |= FDC_82077;
5326238d5faSjnemeth }
5336238d5faSjnemeth 
5346238d5faSjnemeth /*
5356238d5faSjnemeth  * Arguments passed between fdcattach and fdprobe.
5366238d5faSjnemeth  */
5376238d5faSjnemeth struct fdc_attach_args {
5386238d5faSjnemeth 	int fa_drive;
5396238d5faSjnemeth 	struct fd_type *fa_deftype;
5406238d5faSjnemeth };
5416238d5faSjnemeth 
5426238d5faSjnemeth /*
5436238d5faSjnemeth  * Print the location of a disk drive (called just before attaching the
5446238d5faSjnemeth  * the drive).  If `fdc' is not NULL, the drive was found but was not
5456238d5faSjnemeth  * in the system config file; print the drive name as well.
5466238d5faSjnemeth  * Return QUIET (config_find ignores this if the device was configured) to
5476238d5faSjnemeth  * avoid printing `fdN not configured' messages.
5486238d5faSjnemeth  */
5496238d5faSjnemeth int
fdprint(void * aux,const char * fdc)5506238d5faSjnemeth fdprint(void *aux, const char *fdc)
5516238d5faSjnemeth {
5526238d5faSjnemeth 	register struct fdc_attach_args *fa = aux;
5536238d5faSjnemeth 
5546238d5faSjnemeth 	if (!fdc)
5556238d5faSjnemeth 		aprint_normal(" drive %d", fa->fa_drive);
5566238d5faSjnemeth 	return QUIET;
5576238d5faSjnemeth }
5586238d5faSjnemeth 
5596238d5faSjnemeth /*
5606238d5faSjnemeth  * Configure several parameters and features on the FDC.
5616238d5faSjnemeth  * Return 0 on success.
5626238d5faSjnemeth  */
5636238d5faSjnemeth static int
fdconf(struct fdc_softc * fdc)5646238d5faSjnemeth fdconf(struct fdc_softc *fdc)
5656238d5faSjnemeth {
5666238d5faSjnemeth 	int	vroom;
5676238d5faSjnemeth 
5686238d5faSjnemeth 	if (fdc_wrfifo(fdc, NE7CMD_DUMPREG) || fdcresult(fdc) != 10)
5696238d5faSjnemeth 		return -1;
5706238d5faSjnemeth 
5716238d5faSjnemeth 	/*
5726238d5faSjnemeth 	 * dumpreg[7] seems to be a motor-off timeout; set it to whatever
5736238d5faSjnemeth 	 * the PROM thinks is appropriate.
5746238d5faSjnemeth 	 */
5756238d5faSjnemeth 	if ((vroom = fdc->sc_status[7]) == 0)
5766238d5faSjnemeth 		vroom = 0x64;
5776238d5faSjnemeth 
5786238d5faSjnemeth 	/* Configure controller to use FIFO and Implied Seek */
5796238d5faSjnemeth 	if (fdc_wrfifo(fdc, NE7CMD_CFG) != 0)
5806238d5faSjnemeth 		return -1;
5816238d5faSjnemeth 	if (fdc_wrfifo(fdc, vroom) != 0)
5826238d5faSjnemeth 		return -1;
5836238d5faSjnemeth 	if (fdc_wrfifo(fdc, fdc->sc_cfg) != 0)
5846238d5faSjnemeth 		return -1;
5856238d5faSjnemeth 	if (fdc_wrfifo(fdc, 0) != 0)	/* PRETRK */
5866238d5faSjnemeth 		return -1;
5876238d5faSjnemeth 	/* No result phase for the NE7CMD_CFG command */
5886238d5faSjnemeth 
5896238d5faSjnemeth 	if ((fdc->sc_flags & FDC_82077) != 0) {
5906238d5faSjnemeth 		/* Lock configuration across soft resets. */
5916238d5faSjnemeth 		if (fdc_wrfifo(fdc, NE7CMD_LOCK | CFG_LOCK) != 0 ||
5926238d5faSjnemeth 		    fdcresult(fdc) != 1) {
5936238d5faSjnemeth #ifdef DEBUG
5946238d5faSjnemeth 			printf("fdconf: CFGLOCK failed");
5956238d5faSjnemeth #endif
5966238d5faSjnemeth 			return -1;
5976238d5faSjnemeth 		}
5986238d5faSjnemeth 	}
5996238d5faSjnemeth 
6006238d5faSjnemeth 	return 0;
6016238d5faSjnemeth #if 0
6026238d5faSjnemeth 	if (fdc_wrfifo(fdc, NE7CMD_VERSION) == 0 &&
6036238d5faSjnemeth 	    fdcresult(fdc) == 1 && fdc->sc_status[0] == 0x90) {
6046238d5faSjnemeth 		if (fdc_debug)
6056238d5faSjnemeth 			printf("[version cmd]");
6066238d5faSjnemeth 	}
6076238d5faSjnemeth #endif
6086238d5faSjnemeth }
6096238d5faSjnemeth 
610d34b8a70Sjnemeth #ifdef SUN4
611d34b8a70Sjnemeth void
fdcattach_mainbus(device_t parent,device_t self,void * aux)612ae8fd9fdSchristos fdcattach_mainbus(device_t parent, device_t self, void *aux)
613d34b8a70Sjnemeth {
614ae8fd9fdSchristos 	struct fdc_softc *fdc = device_private(self);
615d34b8a70Sjnemeth 	struct mainbus_attach_args *ma = aux;
616d34b8a70Sjnemeth 
617ae8fd9fdSchristos 	fdc->sc_dev = self;
618d34b8a70Sjnemeth 	fdc->sc_bustag = ma->ma_bustag;
619d34b8a70Sjnemeth 
620d34b8a70Sjnemeth 	if (bus_space_map(
621d34b8a70Sjnemeth 			ma->ma_bustag,
622d34b8a70Sjnemeth 			ma->ma_paddr,
623d34b8a70Sjnemeth 			ma->ma_size,
624d34b8a70Sjnemeth 			BUS_SPACE_MAP_LINEAR,
625d34b8a70Sjnemeth 			&fdc->sc_handle) != 0) {
62609275ba7Scegger 		aprint_error_dev(self, "cannot map registers\n");
627d34b8a70Sjnemeth 		return;
628d34b8a70Sjnemeth 	}
629d34b8a70Sjnemeth 
630d34b8a70Sjnemeth 	establish_chip_type(fdc,
631d34b8a70Sjnemeth 			    ma->ma_bustag,
632d34b8a70Sjnemeth 			    ma->ma_paddr,
633d34b8a70Sjnemeth 			    ma->ma_size,
634d34b8a70Sjnemeth 			    fdc->sc_handle);
635d34b8a70Sjnemeth 
636d34b8a70Sjnemeth 	if (fdcattach(fdc, ma->ma_pri) != 0)
637d34b8a70Sjnemeth 		bus_space_unmap(ma->ma_bustag, fdc->sc_handle, ma->ma_size);
638d34b8a70Sjnemeth }
639d34b8a70Sjnemeth 
640d34b8a70Sjnemeth void
fdcattach_obio(device_t parent,device_t self,void * aux)641ae8fd9fdSchristos fdcattach_obio(device_t parent, device_t self, void *aux)
642d34b8a70Sjnemeth {
643ae8fd9fdSchristos 	struct fdc_softc *fdc = device_private(self);
644d34b8a70Sjnemeth 	union obio_attach_args *uoba = aux;
645d34b8a70Sjnemeth 	struct sbus_attach_args *sa = &uoba->uoba_sbus;
646d34b8a70Sjnemeth 
647d34b8a70Sjnemeth 	if (sa->sa_nintr == 0) {
648d34b8a70Sjnemeth 		printf(": no interrupt line configured\n");
649d34b8a70Sjnemeth 		return;
650d34b8a70Sjnemeth 	}
651d34b8a70Sjnemeth 
652ae8fd9fdSchristos 	fdc->sc_dev = self;
653d34b8a70Sjnemeth 	fdc->sc_bustag = sa->sa_bustag;
654d34b8a70Sjnemeth 
655d34b8a70Sjnemeth 	if (sbus_bus_map(sa->sa_bustag,
656d34b8a70Sjnemeth 			 sa->sa_slot, sa->sa_offset, sa->sa_size,
657d34b8a70Sjnemeth 			 BUS_SPACE_MAP_LINEAR, &fdc->sc_handle) != 0) {
65809275ba7Scegger 		aprint_error_dev(self, "cannot map control registers\n");
659d34b8a70Sjnemeth 		return;
660d34b8a70Sjnemeth 	}
661d34b8a70Sjnemeth 
662d34b8a70Sjnemeth 	establish_chip_type(fdc,
663d34b8a70Sjnemeth 		sa->sa_bustag,
664d34b8a70Sjnemeth 		sbus_bus_addr(sa->sa_bustag, sa->sa_slot, sa->sa_offset),
665d34b8a70Sjnemeth 		sa->sa_size,
666d34b8a70Sjnemeth 		fdc->sc_handle);
667d34b8a70Sjnemeth 
668d34b8a70Sjnemeth 	if (strcmp(prom_getpropstring(sa->sa_node, "status"), "disabled") == 0) {
66945ed5307Sjnemeth 		printf(": no drives attached\n");
670d34b8a70Sjnemeth 		return;
671d34b8a70Sjnemeth 	}
672d34b8a70Sjnemeth 
673d34b8a70Sjnemeth 	if (fdcattach(fdc, sa->sa_pri) != 0)
674d34b8a70Sjnemeth 		bus_space_unmap(sa->sa_bustag, fdc->sc_handle, sa->sa_size);
675d34b8a70Sjnemeth }
676d34b8a70Sjnemeth 
677d34b8a70Sjnemeth #elif SUN4U
678d34b8a70Sjnemeth 
6796238d5faSjnemeth void
fdcattach_sbus(device_t parent,device_t self,void * aux)680ae8fd9fdSchristos fdcattach_sbus(device_t parent, device_t self, void *aux)
6816238d5faSjnemeth {
682ae8fd9fdSchristos 	struct fdc_softc *fdc = device_private(self);
6836238d5faSjnemeth 	struct sbus_attach_args *sa = aux;
6846238d5faSjnemeth 
6856238d5faSjnemeth 	if (sa->sa_nintr == 0) {
6866238d5faSjnemeth 		printf(": no interrupt line configured\n");
6876238d5faSjnemeth 		return;
6886238d5faSjnemeth 	}
6896238d5faSjnemeth 
6906238d5faSjnemeth 	if (auxio_fd_control(0) != 0) {
6916238d5faSjnemeth 		printf(": can't attach before auxio\n");
6926238d5faSjnemeth 		return;
6936238d5faSjnemeth 	}
6946238d5faSjnemeth 
695ae8fd9fdSchristos 	fdc->sc_dev = self;
6966238d5faSjnemeth 	fdc->sc_bustag = sa->sa_bustag;
6976238d5faSjnemeth 
6986238d5faSjnemeth 	if (bus_space_map(sa->sa_bustag, BUS_ADDR(sa->sa_slot, sa->sa_offset),
6996238d5faSjnemeth 			  sa->sa_size, 0, &fdc->sc_handle) != 0) {
7006238d5faSjnemeth 		printf(": cannot map control registers\n");
7016238d5faSjnemeth 		return;
7026238d5faSjnemeth 	}
7036238d5faSjnemeth 
7046238d5faSjnemeth 	establish_chip_type(fdc,
7056238d5faSjnemeth 			    sa->sa_bustag,
7066238d5faSjnemeth 			    BUS_ADDR(sa->sa_slot, sa->sa_offset),
7076238d5faSjnemeth 			    sa->sa_size,
7086238d5faSjnemeth 			    fdc->sc_handle);
7096238d5faSjnemeth 
7106238d5faSjnemeth 	if (strcmp(prom_getpropstring(sa->sa_node, "status"), "disabled") == 0) {
7116238d5faSjnemeth 		printf(": no drives attached\n");
7126238d5faSjnemeth 		return;
7136238d5faSjnemeth 	}
7146238d5faSjnemeth 
7156238d5faSjnemeth 	if (prom_getproplen(sa->sa_node, "manual") >= 0)
7166238d5faSjnemeth 		fdc->sc_flags |= FDC_NOEJECT;
7176238d5faSjnemeth 
7186238d5faSjnemeth 
7196238d5faSjnemeth 	if (fdcattach(fdc, sa->sa_pri) != 0)
7206238d5faSjnemeth 		bus_space_unmap(sa->sa_bustag, fdc->sc_handle, sa->sa_size);
7216238d5faSjnemeth }
7226238d5faSjnemeth 
7236238d5faSjnemeth void
fdcattach_ebus(device_t parent,device_t self,void * aux)724ae8fd9fdSchristos fdcattach_ebus(device_t parent, device_t self, void *aux)
7256238d5faSjnemeth {
726ae8fd9fdSchristos 	struct fdc_softc *fdc = device_private(self);
7276238d5faSjnemeth 	struct ebus_attach_args *ea = aux;
7286238d5faSjnemeth 	int map_vaddr;
7296238d5faSjnemeth 
7306238d5faSjnemeth 	if (ea->ea_nintr == 0) {
7316238d5faSjnemeth 		printf(": no interrupt line configured\n");
7326238d5faSjnemeth 		return;
7336238d5faSjnemeth 	}
7346238d5faSjnemeth 
7356238d5faSjnemeth 	if (ea->ea_nreg < 3) {
7366238d5faSjnemeth 		printf(": expected 3 registers, only got %d\n",
7376238d5faSjnemeth 		    ea->ea_nreg);
7386238d5faSjnemeth 		return;
7396238d5faSjnemeth 	}
7406238d5faSjnemeth 
741ae8fd9fdSchristos 	fdc->sc_dev = self;
7426238d5faSjnemeth 	fdc->sc_bustag = ea->ea_bustag;
7436238d5faSjnemeth 
7446238d5faSjnemeth 	if (ea->ea_nvaddr > 0) {
7456238d5faSjnemeth 		sparc_promaddr_to_handle(ea->ea_bustag,
7466238d5faSjnemeth 		    ea->ea_vaddr[0], &fdc->sc_handle);
7476238d5faSjnemeth 		map_vaddr = 1;
7486238d5faSjnemeth 	} else if (bus_space_map(fdc->sc_bustag,
7496238d5faSjnemeth 	    EBUS_ADDR_FROM_REG(&ea->ea_reg[0]),
7506238d5faSjnemeth 	    ea->ea_reg[0].size, 0, &fdc->sc_handle) == 0) {
7516238d5faSjnemeth 		map_vaddr = 0;
7526238d5faSjnemeth 	} else {
7536238d5faSjnemeth 		printf(": can't map control registers\n");
7546238d5faSjnemeth 		return;
7556238d5faSjnemeth 	}
7566238d5faSjnemeth 
7576238d5faSjnemeth 	establish_chip_type(fdc,
7586238d5faSjnemeth 		fdc->sc_bustag,
7596238d5faSjnemeth 		map_vaddr ? ea->ea_vaddr[0] :
7606238d5faSjnemeth 		    EBUS_ADDR_FROM_REG(&ea->ea_reg[0]),
7616238d5faSjnemeth 		ea->ea_reg[0].size,
7626238d5faSjnemeth 		fdc->sc_handle);
7636238d5faSjnemeth 
7646238d5faSjnemeth 	fdc->sc_flags |= FDC_EBUS;
7656238d5faSjnemeth 
7666238d5faSjnemeth 	if (prom_getproplen(ea->ea_node, "manual") >= 0)
7676238d5faSjnemeth 		fdc->sc_flags |= FDC_NOEJECT;
7686238d5faSjnemeth 
769d62719e5Sjnemeth 	if (fdcattach(fdc, ea->ea_intr[0]) != 0)
770d62719e5Sjnemeth 		if (map_vaddr == 0)
771d62719e5Sjnemeth 			bus_space_unmap(ea->ea_bustag, fdc->sc_handle,
772d62719e5Sjnemeth 			    ea->ea_reg[0].size);
7736238d5faSjnemeth }
774d34b8a70Sjnemeth #endif
7756238d5faSjnemeth 
7766238d5faSjnemeth int
fdcattach(struct fdc_softc * fdc,int pri)7776238d5faSjnemeth fdcattach(struct fdc_softc *fdc, int pri)
7786238d5faSjnemeth {
7796238d5faSjnemeth 	struct fdc_attach_args fa;
7806238d5faSjnemeth 	int drive_attached;
7816238d5faSjnemeth 	char code;
7826238d5faSjnemeth 
78388ab7da9Sad 	callout_init(&fdc->sc_timo_ch, 0);
78488ab7da9Sad 	callout_init(&fdc->sc_intr_ch, 0);
7856238d5faSjnemeth 
7866238d5faSjnemeth 	fdc->sc_state = DEVIDLE;
7876238d5faSjnemeth 	fdc->sc_itask = FDC_ITASK_NONE;
7886238d5faSjnemeth 	fdc->sc_istatus = FDC_ISTATUS_NONE;
7896238d5faSjnemeth 	fdc->sc_flags |= FDC_EIS;
7906238d5faSjnemeth 	TAILQ_INIT(&fdc->sc_drives);
7916238d5faSjnemeth 
7926238d5faSjnemeth 	if ((fdc->sc_flags & FDC_82077) != 0) {
7936238d5faSjnemeth 		fdc->sc_reg_msr = FDREG77_MSR;
7946238d5faSjnemeth 		fdc->sc_reg_fifo = FDREG77_FIFO;
7956238d5faSjnemeth 		fdc->sc_reg_dor = FDREG77_DOR;
7966238d5faSjnemeth 		fdc->sc_reg_dir = FDREG77_DIR;
7976238d5faSjnemeth 		code = '7';
7986238d5faSjnemeth 		fdc->sc_flags |= FDC_NEEDMOTORWAIT;
7996238d5faSjnemeth 	} else {
8006238d5faSjnemeth 		fdc->sc_reg_msr = FDREG72_MSR;
8016238d5faSjnemeth 		fdc->sc_reg_fifo = FDREG72_FIFO;
8026238d5faSjnemeth 		fdc->sc_reg_dor = 0;
8036238d5faSjnemeth 		code = '2';
8046238d5faSjnemeth 	}
8056238d5faSjnemeth 
8066238d5faSjnemeth 	/*
8076238d5faSjnemeth 	 * Configure controller; enable FIFO, Implied seek, no POLL mode?.
8086238d5faSjnemeth 	 * Note: CFG_EFIFO is active-low, initial threshold value: 8
8096238d5faSjnemeth 	 */
8106238d5faSjnemeth 	fdc->sc_cfg = CFG_EIS|/*CFG_EFIFO|*/CFG_POLL|(8 & CFG_THRHLD_MASK);
8116238d5faSjnemeth 	if (fdconf(fdc) != 0) {
8126238d5faSjnemeth 		printf(": no drives attached\n");
8136238d5faSjnemeth 		return -1;
8146238d5faSjnemeth 	}
8156238d5faSjnemeth 
816fd5cef85Sjnemeth 	fdc->sc_sicookie = softint_establish(SOFTINT_BIO, fdcswintr, fdc);
8176238d5faSjnemeth 	if (fdc->sc_sicookie == NULL) {
81809275ba7Scegger 		aprint_normal("\n");
819ae8fd9fdSchristos 		aprint_error_dev(fdc->sc_dev,
820ae8fd9fdSchristos 		    "cannot register soft interrupt handler\n");
821d62719e5Sjnemeth 		callout_stop(&fdc->sc_timo_ch);
822d62719e5Sjnemeth 		callout_stop(&fdc->sc_intr_ch);
8236238d5faSjnemeth 		return -1;
8246238d5faSjnemeth 	}
825d34b8a70Sjnemeth #ifdef SUN4
826d34b8a70Sjnemeth 	printf(" softpri %d: chip 8207%c\n", IPL_SOFTFDC, code);
827d34b8a70Sjnemeth #elif SUN4U
828*19bd2f75Sjdc 	printf(" softpri %d: chip 8207%c", IPL_BIO, code);
8296238d5faSjnemeth 	if (fdc->sc_flags & FDC_NOEJECT)
8306238d5faSjnemeth 		printf(": manual eject");
8316238d5faSjnemeth 	printf("\n");
832d34b8a70Sjnemeth #endif
8336238d5faSjnemeth 
834d62719e5Sjnemeth #ifdef SUN4
835d62719e5Sjnemeth 	fdciop = &fdc->sc_io;
83645ed5307Sjnemeth 	if (bus_intr_establish2(fdc->sc_bustag, pri, 0,
837d62719e5Sjnemeth 				fdc_c_hwintr, fdc, fdchwintr) == NULL) {
838d62719e5Sjnemeth #elif SUN4U
839d62719e5Sjnemeth 	if (bus_intr_establish(fdc->sc_bustag, pri, IPL_BIO,
840d62719e5Sjnemeth 				fdc_c_hwintr, fdc) == NULL) {
841d62719e5Sjnemeth #endif
84209275ba7Scegger 		aprint_normal("\n");
843ae8fd9fdSchristos 		aprint_error_dev(fdc->sc_dev,
844ae8fd9fdSchristos 		    "cannot register interrupt handler\n");
845d62719e5Sjnemeth 		callout_stop(&fdc->sc_timo_ch);
846d62719e5Sjnemeth 		callout_stop(&fdc->sc_intr_ch);
847fd5cef85Sjnemeth 		softint_disestablish(fdc->sc_sicookie);
848d62719e5Sjnemeth 		return -1;
849d62719e5Sjnemeth 	}
850d62719e5Sjnemeth 
8516238d5faSjnemeth 	evcnt_attach_dynamic(&fdc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
852ae8fd9fdSchristos 	    device_xname(fdc->sc_dev), "intr");
8536238d5faSjnemeth 
8546238d5faSjnemeth 	/* physical limit: four drives per controller. */
8556238d5faSjnemeth 	drive_attached = 0;
8566238d5faSjnemeth 	for (fa.fa_drive = 0; fa.fa_drive < 4; fa.fa_drive++) {
8576238d5faSjnemeth 		fa.fa_deftype = NULL;		/* unknown */
8586238d5faSjnemeth 		fa.fa_deftype = &fd_types[0];	/* XXX */
8592685996bSthorpej 		if (config_found(fdc->sc_dev, (void *)&fa, fdprint,
860c7fb772bSthorpej 				 CFARGS_NONE) != NULL)
8616238d5faSjnemeth 			drive_attached = 1;
8626238d5faSjnemeth 	}
8636238d5faSjnemeth 
8646238d5faSjnemeth 	if (drive_attached == 0) {
8656238d5faSjnemeth 		/* XXX - dis-establish interrupts here */
8666238d5faSjnemeth 		/* return -1; */
8676238d5faSjnemeth 	}
8686238d5faSjnemeth 
8696238d5faSjnemeth 	return 0;
8706238d5faSjnemeth }
8716238d5faSjnemeth 
8726238d5faSjnemeth int
873ae8fd9fdSchristos fdmatch(device_t parent, cfdata_t match, void *aux)
8746238d5faSjnemeth {
875ae8fd9fdSchristos 	struct fdc_softc *fdc = device_private(parent);
8766238d5faSjnemeth 	bus_space_tag_t t = fdc->sc_bustag;
8776238d5faSjnemeth 	bus_space_handle_t h = fdc->sc_handle;
8786238d5faSjnemeth 	struct fdc_attach_args *fa = aux;
8796238d5faSjnemeth 	int drive = fa->fa_drive;
8806238d5faSjnemeth 	int n, ok;
8816238d5faSjnemeth 
8826238d5faSjnemeth 	if (drive > 0)
8836238d5faSjnemeth 		/* XXX - for now, punt on more than one drive */
8846238d5faSjnemeth 		return 0;
8856238d5faSjnemeth 
8866238d5faSjnemeth 	if ((fdc->sc_flags & FDC_82077) != 0) {
8876238d5faSjnemeth 		/* select drive and turn on motor */
8886238d5faSjnemeth 		bus_space_write_1(t, h, fdc->sc_reg_dor,
8896238d5faSjnemeth 				  drive | FDO_FRST | FDO_MOEN(drive));
8906238d5faSjnemeth 		/* wait for motor to spin up */
8916238d5faSjnemeth 		delay(250000);
892d34b8a70Sjnemeth #ifdef SUN4
8936238d5faSjnemeth 	} else {
8946238d5faSjnemeth 		auxregbisc(AUXIO4C_FDS, 0);
8956238d5faSjnemeth #endif
8966238d5faSjnemeth 	}
8976238d5faSjnemeth 	fdc->sc_nstat = 0;
8986238d5faSjnemeth 	fdc_wrfifo(fdc, NE7CMD_RECAL);
8996238d5faSjnemeth 	fdc_wrfifo(fdc, drive);
9006238d5faSjnemeth 
9016238d5faSjnemeth 	/* Wait for recalibration to complete */
9026238d5faSjnemeth 	for (n = 0; n < 10000; n++) {
9036238d5faSjnemeth 		uint8_t v;
9046238d5faSjnemeth 
9056238d5faSjnemeth 		delay(1000);
9066238d5faSjnemeth 		v = bus_space_read_1(t, h, fdc->sc_reg_msr);
9076238d5faSjnemeth 		if ((v & (NE7_RQM|NE7_DIO|NE7_CB)) == NE7_RQM) {
9086238d5faSjnemeth 			/* wait a bit longer till device *really* is ready */
9096238d5faSjnemeth 			delay(100000);
9106238d5faSjnemeth 			if (fdc_wrfifo(fdc, NE7CMD_SENSEI))
9116238d5faSjnemeth 				break;
9126238d5faSjnemeth 			if (fdcresult(fdc) == 1 && fdc->sc_status[0] == 0x80)
9136238d5faSjnemeth 				/*
9146238d5faSjnemeth 				 * Got `invalid command'; we interpret it
9156238d5faSjnemeth 				 * to mean that the re-calibrate hasn't in
9166238d5faSjnemeth 				 * fact finished yet
9176238d5faSjnemeth 				 */
9186238d5faSjnemeth 				continue;
9196238d5faSjnemeth 			break;
9206238d5faSjnemeth 		}
9216238d5faSjnemeth 	}
9226238d5faSjnemeth 	n = fdc->sc_nstat;
9236238d5faSjnemeth #ifdef FD_DEBUG
9246238d5faSjnemeth 	if (fdc_debug) {
9256238d5faSjnemeth 		int i;
9266238d5faSjnemeth 		printf("fdprobe: %d stati:", n);
9276238d5faSjnemeth 		for (i = 0; i < n; i++)
9286238d5faSjnemeth 			printf(" 0x%x", fdc->sc_status[i]);
9296238d5faSjnemeth 		printf("\n");
9306238d5faSjnemeth 	}
9316238d5faSjnemeth #endif
9326238d5faSjnemeth 	ok = (n == 2 && (fdc->sc_status[0] & 0xf8) == 0x20) ? 1 : 0;
9336238d5faSjnemeth 
9346238d5faSjnemeth 	/* turn off motor */
9356238d5faSjnemeth 	if ((fdc->sc_flags & FDC_82077) != 0) {
9366238d5faSjnemeth 		/* deselect drive and turn motor off */
9376238d5faSjnemeth 		bus_space_write_1(t, h, fdc->sc_reg_dor, FDO_FRST | FDO_DS);
938d34b8a70Sjnemeth #ifdef SUN4
9396238d5faSjnemeth 	} else {
9406238d5faSjnemeth 		auxregbisc(0, AUXIO4C_FDS);
9416238d5faSjnemeth #endif
9426238d5faSjnemeth 	}
9436238d5faSjnemeth 
9446238d5faSjnemeth 	return ok;
9456238d5faSjnemeth }
9466238d5faSjnemeth 
9476238d5faSjnemeth /*
9486238d5faSjnemeth  * Controller is working, and drive responded.  Attach it.
9496238d5faSjnemeth  */
9506238d5faSjnemeth void
951ae8fd9fdSchristos fdattach(device_t parent, device_t self, void *aux)
9526238d5faSjnemeth {
953ae8fd9fdSchristos 	struct fdc_softc *fdc = device_private(parent);
954ae8fd9fdSchristos 	struct fd_softc *fd = device_private(self);
9556238d5faSjnemeth 	struct fdc_attach_args *fa = aux;
9566238d5faSjnemeth 	struct fd_type *type = fa->fa_deftype;
9576238d5faSjnemeth 	int drive = fa->fa_drive;
9586238d5faSjnemeth 
959ae8fd9fdSchristos 	fd->sc_dev = self;
96088ab7da9Sad 	callout_init(&fd->sc_motoron_ch, 0);
96188ab7da9Sad 	callout_init(&fd->sc_motoroff_ch, 0);
9626238d5faSjnemeth 
9636238d5faSjnemeth 	/* XXX Allow `flags' to override device type? */
9646238d5faSjnemeth 
9656238d5faSjnemeth 	if (type)
9666238d5faSjnemeth 		printf(": %s %d cyl, %d head, %d sec\n", type->name,
9676238d5faSjnemeth 		    type->cylinders, type->heads, type->sectrac);
9686238d5faSjnemeth 	else
9696238d5faSjnemeth 		printf(": density unknown\n");
9706238d5faSjnemeth 
9716238d5faSjnemeth 	bufq_alloc(&fd->sc_q, "disksort", BUFQ_SORT_CYLINDER);
9726238d5faSjnemeth 	fd->sc_cylin = -1;
9736238d5faSjnemeth 	fd->sc_drive = drive;
9746238d5faSjnemeth 	fd->sc_deftype = type;
9756238d5faSjnemeth 	fdc->sc_fd[drive] = fd;
9766238d5faSjnemeth 
9776238d5faSjnemeth 	fdc_wrfifo(fdc, NE7CMD_SPECIFY);
9786238d5faSjnemeth 	fdc_wrfifo(fdc, type->steprate);
9796238d5faSjnemeth 	/* XXX head load time == 6ms */
9806238d5faSjnemeth 	fdc_wrfifo(fdc, 6 | NE7_SPECIFY_NODMA);
9816238d5faSjnemeth 
9826238d5faSjnemeth 	/*
9836238d5faSjnemeth 	 * Initialize and attach the disk structure.
9846238d5faSjnemeth 	 */
985ae8fd9fdSchristos 	disk_init(&fd->sc_dk, device_xname(fd->sc_dev), &fddkdriver);
9866238d5faSjnemeth 	disk_attach(&fd->sc_dk);
9876238d5faSjnemeth 
9886238d5faSjnemeth 	/*
9896238d5faSjnemeth 	 * Establish a mountroot_hook anyway in case we booted
9906238d5faSjnemeth 	 * with RB_ASKNAME and get selected as the boot device.
9916238d5faSjnemeth 	 */
992ae8fd9fdSchristos 	mountroothook_establish(fd_mountroot_hook, fd->sc_dev);
9936238d5faSjnemeth 
9947b845fa9Schristos 	fd_set_geometry(fd);
9953d612181Sjnemeth 
9966238d5faSjnemeth 	/* Make sure the drive motor gets turned off at shutdown time. */
997f2975a45Sjnemeth 	if (!pmf_device_register1(self, fdsuspend, NULL, fdshutdown))
998f2975a45Sjnemeth 		aprint_error_dev(self, "couldn't establish power handler\n");
9996238d5faSjnemeth }
10006238d5faSjnemeth 
1001f2975a45Sjnemeth bool fdshutdown(device_t self, int how)
1002f2975a45Sjnemeth {
1003f2975a45Sjnemeth 	struct fd_softc *fd = device_private(self);
1004f2975a45Sjnemeth 
1005f2975a45Sjnemeth 	fd_motor_off(fd);
1006f2975a45Sjnemeth 	return true;
1007f2975a45Sjnemeth }
1008f2975a45Sjnemeth 
1009c1b390d4Sdyoung bool fdsuspend(device_t self, const pmf_qual_t *qual)
1010f2975a45Sjnemeth {
1011f2975a45Sjnemeth 
1012f2975a45Sjnemeth 	return fdshutdown(self, boothowto);
1013f2975a45Sjnemeth }
1014f2975a45Sjnemeth 
1015f2975a45Sjnemeth 
10168255ce9eSmrg static struct fd_type *
10176238d5faSjnemeth fd_dev_to_type(struct fd_softc *fd, dev_t dev)
10186238d5faSjnemeth {
10196238d5faSjnemeth 	int type = FDTYPE(dev);
10206238d5faSjnemeth 
10216238d5faSjnemeth 	if (type > (sizeof(fd_types) / sizeof(fd_types[0])))
10226238d5faSjnemeth 		return NULL;
10236238d5faSjnemeth 	return type ? &fd_types[type - 1] : fd->sc_deftype;
10246238d5faSjnemeth }
10256238d5faSjnemeth 
10266238d5faSjnemeth void
10276238d5faSjnemeth fdstrategy(struct buf *bp)
10286238d5faSjnemeth {
10296238d5faSjnemeth 	struct fd_softc *fd;
10306238d5faSjnemeth 	int sz;
10316238d5faSjnemeth  	int s;
10326238d5faSjnemeth 
10336238d5faSjnemeth 	/* Valid unit, controller, and request? */
10342dea63feScegger 	fd = device_lookup_private(&fd_cd, FDUNIT(bp->b_dev));
10352dea63feScegger 	if (fd == NULL) {
10362dea63feScegger 		bp->b_error = EINVAL;
10372dea63feScegger 		goto done;
10382dea63feScegger 	}
10392dea63feScegger 
10402dea63feScegger 	if (bp->b_blkno < 0 ||
10416238d5faSjnemeth 	    (((bp->b_bcount % FD_BSIZE(fd)) != 0 ||
10426238d5faSjnemeth 	      (bp->b_blkno * DEV_BSIZE) % FD_BSIZE(fd) != 0) &&
10436238d5faSjnemeth 	     (bp->b_flags & B_FORMAT) == 0)) {
10446238d5faSjnemeth 		bp->b_error = EINVAL;
104566fefd11Sad 		goto done;
10466238d5faSjnemeth 	}
10476238d5faSjnemeth 
10486238d5faSjnemeth 	/* If it's a null transfer, return immediately. */
10496238d5faSjnemeth 	if (bp->b_bcount == 0)
10506238d5faSjnemeth 		goto done;
10516238d5faSjnemeth 
10526238d5faSjnemeth 	sz = howmany(bp->b_bcount, DEV_BSIZE);
10536238d5faSjnemeth 
10546238d5faSjnemeth 	if (bp->b_blkno + sz > (fd->sc_type->size * DEV_BSIZE) / FD_BSIZE(fd)) {
10556238d5faSjnemeth 		sz = (fd->sc_type->size * DEV_BSIZE) / FD_BSIZE(fd)
10566238d5faSjnemeth 		     - bp->b_blkno;
10576238d5faSjnemeth 		if (sz == 0) {
10586238d5faSjnemeth 			/* If exactly at end of disk, return EOF. */
10596238d5faSjnemeth 			bp->b_resid = bp->b_bcount;
10606238d5faSjnemeth 			goto done;
10616238d5faSjnemeth 		}
10626238d5faSjnemeth 		if (sz < 0) {
10636238d5faSjnemeth 			/* If past end of disk, return EINVAL. */
10646238d5faSjnemeth 			bp->b_error = EINVAL;
106566fefd11Sad 			goto done;
10666238d5faSjnemeth 		}
10676238d5faSjnemeth 		/* Otherwise, truncate request. */
10686238d5faSjnemeth 		bp->b_bcount = sz << DEV_BSHIFT;
10696238d5faSjnemeth 	}
10706238d5faSjnemeth 
10716238d5faSjnemeth 	bp->b_rawblkno = bp->b_blkno;
10726238d5faSjnemeth  	bp->b_cylinder = (bp->b_blkno * DEV_BSIZE) /
10736238d5faSjnemeth 		      (FD_BSIZE(fd) * fd->sc_type->seccyl);
10746238d5faSjnemeth 
10756238d5faSjnemeth #ifdef FD_DEBUG
10766238d5faSjnemeth 	if (fdc_debug > 1)
10775b888d73Sjnemeth 	    printf("fdstrategy: b_blkno %lld b_bcount %d blkno %lld cylin %d sz %d\n",
10786238d5faSjnemeth 		    (long long)bp->b_blkno, bp->b_bcount,
10795b888d73Sjnemeth 		    (long long)fd->sc_blkno, bp->b_cylinder, sz);
10806238d5faSjnemeth #endif
10816238d5faSjnemeth 
10826238d5faSjnemeth 	/* Queue transfer on drive, activate drive and controller if idle. */
10836238d5faSjnemeth 	s = splbio();
108470de9736Syamt 	bufq_put(fd->sc_q, bp);
10856238d5faSjnemeth 	callout_stop(&fd->sc_motoroff_ch);		/* a good idea */
10866238d5faSjnemeth 	if (fd->sc_active == 0)
10876238d5faSjnemeth 		fdstart(fd);
10886238d5faSjnemeth #ifdef DIAGNOSTIC
10896238d5faSjnemeth 	else {
1090ae8fd9fdSchristos 		struct fdc_softc *fdc = device_private(
1091ae8fd9fdSchristos 		    device_parent(fd->sc_dev));
10926238d5faSjnemeth 		if (fdc->sc_state == DEVIDLE) {
10936238d5faSjnemeth 			printf("fdstrategy: controller inactive\n");
10946238d5faSjnemeth 			fdcstart(fdc);
10956238d5faSjnemeth 		}
10966238d5faSjnemeth 	}
10976238d5faSjnemeth #endif
10986238d5faSjnemeth 	splx(s);
10996238d5faSjnemeth 	return;
11006238d5faSjnemeth 
11016238d5faSjnemeth done:
11026238d5faSjnemeth 	/* Toss transfer; we're done early. */
11036238d5faSjnemeth 	biodone(bp);
11046238d5faSjnemeth }
11056238d5faSjnemeth 
11066238d5faSjnemeth void
11076238d5faSjnemeth fdstart(struct fd_softc *fd)
11086238d5faSjnemeth {
1109ae8fd9fdSchristos 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
11106238d5faSjnemeth 	int active = fdc->sc_drives.tqh_first != 0;
11116238d5faSjnemeth 
11126238d5faSjnemeth 	/* Link into controller queue. */
11136238d5faSjnemeth 	fd->sc_active = 1;
11146238d5faSjnemeth 	TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
11156238d5faSjnemeth 
11166238d5faSjnemeth 	/* If controller not already active, start it. */
11176238d5faSjnemeth 	if (!active)
11186238d5faSjnemeth 		fdcstart(fdc);
11196238d5faSjnemeth }
11206238d5faSjnemeth 
11216238d5faSjnemeth void
11226238d5faSjnemeth fdfinish(struct fd_softc *fd, struct buf *bp)
11236238d5faSjnemeth {
1124ae8fd9fdSchristos 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
11256238d5faSjnemeth 
11266238d5faSjnemeth 	/*
11276238d5faSjnemeth 	 * Move this drive to the end of the queue to give others a `fair'
11286238d5faSjnemeth 	 * chance.  We only force a switch if N operations are completed while
11296238d5faSjnemeth 	 * another drive is waiting to be serviced, since there is a long motor
11306238d5faSjnemeth 	 * startup delay whenever we switch.
11316238d5faSjnemeth 	 */
113270de9736Syamt 	(void)bufq_get(fd->sc_q);
11336238d5faSjnemeth 	if (fd->sc_drivechain.tqe_next && ++fd->sc_ops >= 8) {
11346238d5faSjnemeth 		fd->sc_ops = 0;
11356238d5faSjnemeth 		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
113670de9736Syamt 		if (bufq_peek(fd->sc_q) != NULL) {
11376238d5faSjnemeth 			TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
11386238d5faSjnemeth 		} else
11396238d5faSjnemeth 			fd->sc_active = 0;
11406238d5faSjnemeth 	}
11416238d5faSjnemeth 	bp->b_resid = fd->sc_bcount;
11426238d5faSjnemeth 	fd->sc_skip = 0;
11436238d5faSjnemeth 
11446238d5faSjnemeth 	biodone(bp);
11456238d5faSjnemeth 	/* turn off motor 5s from now */
11466238d5faSjnemeth 	callout_reset(&fd->sc_motoroff_ch, 5 * hz, fd_motor_off, fd);
11476238d5faSjnemeth 	fdc->sc_state = DEVIDLE;
11486238d5faSjnemeth }
11496238d5faSjnemeth 
11506238d5faSjnemeth void
11516238d5faSjnemeth fdc_reset(struct fdc_softc *fdc)
11526238d5faSjnemeth {
11536238d5faSjnemeth 	bus_space_tag_t t = fdc->sc_bustag;
11546238d5faSjnemeth 	bus_space_handle_t h = fdc->sc_handle;
11556238d5faSjnemeth 
11566238d5faSjnemeth 	if ((fdc->sc_flags & FDC_82077) != 0) {
11576238d5faSjnemeth 		bus_space_write_1(t, h, fdc->sc_reg_dor,
11586238d5faSjnemeth 				  FDO_FDMAEN | FDO_MOEN(0));
11596238d5faSjnemeth 	}
11606238d5faSjnemeth 
11616238d5faSjnemeth 	bus_space_write_1(t, h, fdc->sc_reg_drs, DRS_RESET);
11626238d5faSjnemeth 	delay(10);
11636238d5faSjnemeth 	bus_space_write_1(t, h, fdc->sc_reg_drs, 0);
11646238d5faSjnemeth 
11656238d5faSjnemeth 	if ((fdc->sc_flags & FDC_82077) != 0) {
11666238d5faSjnemeth 		bus_space_write_1(t, h, fdc->sc_reg_dor,
11676238d5faSjnemeth 				  FDO_FRST | FDO_FDMAEN | FDO_DS);
11686238d5faSjnemeth 	}
11696238d5faSjnemeth #ifdef FD_DEBUG
11706238d5faSjnemeth 	if (fdc_debug)
11716238d5faSjnemeth 		printf("fdc reset\n");
11726238d5faSjnemeth #endif
11736238d5faSjnemeth }
11746238d5faSjnemeth 
11756238d5faSjnemeth void
11766238d5faSjnemeth fd_set_motor(struct fdc_softc *fdc)
11776238d5faSjnemeth {
11786238d5faSjnemeth 	struct fd_softc *fd;
11796238d5faSjnemeth 	u_char status;
11806238d5faSjnemeth 	int n;
11816238d5faSjnemeth 
11826238d5faSjnemeth 	if ((fdc->sc_flags & FDC_82077) != 0) {
11836238d5faSjnemeth 		status = FDO_FRST | FDO_FDMAEN;
11846238d5faSjnemeth 		if ((fd = fdc->sc_drives.tqh_first) != NULL)
11856238d5faSjnemeth 			status |= fd->sc_drive;
11866238d5faSjnemeth 
11876238d5faSjnemeth 		for (n = 0; n < 4; n++)
11886238d5faSjnemeth 			if ((fd = fdc->sc_fd[n]) && (fd->sc_flags & FD_MOTOR))
11896238d5faSjnemeth 				status |= FDO_MOEN(n);
11906238d5faSjnemeth 		bus_space_write_1(fdc->sc_bustag, fdc->sc_handle,
11916238d5faSjnemeth 				  fdc->sc_reg_dor, status);
1192d34b8a70Sjnemeth #ifdef SUN4
11936238d5faSjnemeth 	} else {
11946238d5faSjnemeth 
11956238d5faSjnemeth 		for (n = 0; n < 4; n++) {
11966238d5faSjnemeth 			if ((fd = fdc->sc_fd[n]) != NULL  &&
11976238d5faSjnemeth 			    (fd->sc_flags & FD_MOTOR) != 0) {
11986238d5faSjnemeth 				auxregbisc(AUXIO4C_FDS, 0);
11996238d5faSjnemeth 				return;
12006238d5faSjnemeth 			}
12016238d5faSjnemeth 		}
12026238d5faSjnemeth 		auxregbisc(0, AUXIO4C_FDS);
12036238d5faSjnemeth #endif
12046238d5faSjnemeth 	}
12056238d5faSjnemeth }
12066238d5faSjnemeth 
12076238d5faSjnemeth void
12086238d5faSjnemeth fd_motor_off(void *arg)
12096238d5faSjnemeth {
12106238d5faSjnemeth 	struct fd_softc *fd = arg;
12116238d5faSjnemeth 	int s;
12126238d5faSjnemeth 
12136238d5faSjnemeth 	s = splbio();
12146238d5faSjnemeth 	fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
1215ae8fd9fdSchristos 	fd_set_motor(device_private(device_parent(fd->sc_dev)));
12166238d5faSjnemeth 	splx(s);
12176238d5faSjnemeth }
12186238d5faSjnemeth 
12196238d5faSjnemeth void
12206238d5faSjnemeth fd_motor_on(void *arg)
12216238d5faSjnemeth {
12226238d5faSjnemeth 	struct fd_softc *fd = arg;
1223ae8fd9fdSchristos 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
12246238d5faSjnemeth 	int s;
12256238d5faSjnemeth 
12266238d5faSjnemeth 	s = splbio();
12276238d5faSjnemeth 	fd->sc_flags &= ~FD_MOTOR_WAIT;
12286238d5faSjnemeth 	if ((fdc->sc_drives.tqh_first == fd) && (fdc->sc_state == MOTORWAIT))
12296238d5faSjnemeth 		(void)fdcstate(fdc);
12306238d5faSjnemeth 	splx(s);
12316238d5faSjnemeth }
12326238d5faSjnemeth 
12336238d5faSjnemeth /*
12346238d5faSjnemeth  * Get status bytes off the FDC after a command has finished
12356238d5faSjnemeth  * Returns the number of status bytes read; -1 on error.
12366238d5faSjnemeth  * The return value is also stored in `sc_nstat'.
12376238d5faSjnemeth  */
12386238d5faSjnemeth int
12396238d5faSjnemeth fdcresult(struct fdc_softc *fdc)
12406238d5faSjnemeth {
12416238d5faSjnemeth 	bus_space_tag_t t = fdc->sc_bustag;
12426238d5faSjnemeth 	bus_space_handle_t h = fdc->sc_handle;
12436238d5faSjnemeth 	int j, n = 0;
12446238d5faSjnemeth 
12456238d5faSjnemeth 	for (j = 10000; j; j--) {
12466238d5faSjnemeth 		uint8_t v = bus_space_read_1(t, h, fdc->sc_reg_msr);
12476238d5faSjnemeth 		v &= (NE7_DIO | NE7_RQM | NE7_CB);
12486238d5faSjnemeth 		if (v == NE7_RQM)
12496238d5faSjnemeth 			return fdc->sc_nstat = n;
12506238d5faSjnemeth 		if (v == (NE7_DIO | NE7_RQM | NE7_CB)) {
12516238d5faSjnemeth 			if (n >= sizeof(fdc->sc_status)) {
12526238d5faSjnemeth 				log(LOG_ERR, "fdcresult: overrun\n");
12536238d5faSjnemeth 				return -1;
12546238d5faSjnemeth 			}
12556238d5faSjnemeth 			fdc->sc_status[n++] =
12566238d5faSjnemeth 				bus_space_read_1(t, h, fdc->sc_reg_fifo);
12576238d5faSjnemeth 		} else
12586238d5faSjnemeth 			delay(1);
12596238d5faSjnemeth 	}
12606238d5faSjnemeth 
12616238d5faSjnemeth 	log(LOG_ERR, "fdcresult: timeout\n");
12626238d5faSjnemeth 	return fdc->sc_nstat = -1;
12636238d5faSjnemeth }
12646238d5faSjnemeth 
12656238d5faSjnemeth /*
12666238d5faSjnemeth  * Write a command byte to the FDC.
12676238d5faSjnemeth  * Returns 0 on success; -1 on failure (i.e. timeout)
12686238d5faSjnemeth  */
12696238d5faSjnemeth int
12706238d5faSjnemeth fdc_wrfifo(struct fdc_softc *fdc, uint8_t x)
12716238d5faSjnemeth {
12726238d5faSjnemeth 	bus_space_tag_t t = fdc->sc_bustag;
12736238d5faSjnemeth 	bus_space_handle_t h = fdc->sc_handle;
12746238d5faSjnemeth 	int i;
12756238d5faSjnemeth 
12766238d5faSjnemeth 	for (i = 100000; i-- > 0;) {
12776238d5faSjnemeth 		uint8_t v = bus_space_read_1(t, h, fdc->sc_reg_msr);
12786238d5faSjnemeth 		if ((v & (NE7_DIO|NE7_RQM)) == NE7_RQM) {
12796238d5faSjnemeth 			/* The chip is ready */
12806238d5faSjnemeth 			bus_space_write_1(t, h, fdc->sc_reg_fifo, x);
12816238d5faSjnemeth 			return 0;
12826238d5faSjnemeth 		}
12836238d5faSjnemeth 		delay(1);
12846238d5faSjnemeth 	}
12856238d5faSjnemeth 	return -1;
12866238d5faSjnemeth }
12876238d5faSjnemeth 
12886238d5faSjnemeth int
12896238d5faSjnemeth fdc_diskchange(struct fdc_softc *fdc)
12906238d5faSjnemeth {
12916238d5faSjnemeth 
1292d34b8a70Sjnemeth #ifdef SUN4
12936238d5faSjnemeth 	if (CPU_ISSUN4M && (fdc->sc_flags & FDC_82077) != 0) {
12946238d5faSjnemeth #endif
12956238d5faSjnemeth 		bus_space_tag_t t = fdc->sc_bustag;
12966238d5faSjnemeth 		bus_space_handle_t h = fdc->sc_handle;
12976238d5faSjnemeth 		uint8_t v = bus_space_read_1(t, h, fdc->sc_reg_dir);
12986238d5faSjnemeth 		return (v & FDI_DCHG) != 0;
1299d34b8a70Sjnemeth #ifdef SUN4
13006238d5faSjnemeth 	} else if (CPU_ISSUN4C) {
13016238d5faSjnemeth 		return (*AUXIO4C_REG & AUXIO4C_FDC) != 0;
13026238d5faSjnemeth 	}
13036238d5faSjnemeth 	return 0;
13046238d5faSjnemeth #endif
13056238d5faSjnemeth }
13066238d5faSjnemeth 
13076238d5faSjnemeth int
13086238d5faSjnemeth fdopen(dev_t dev, int flags, int fmt, struct lwp *l)
13096238d5faSjnemeth {
13102dea63feScegger  	int pmask;
13116238d5faSjnemeth 	struct fd_softc *fd;
13126238d5faSjnemeth 	struct fd_type *type;
13136238d5faSjnemeth 
13142dea63feScegger 	fd = device_lookup_private(&fd_cd, FDUNIT(dev));
13156238d5faSjnemeth 	if (fd == NULL)
13166238d5faSjnemeth 		return ENXIO;
13176238d5faSjnemeth 	type = fd_dev_to_type(fd, dev);
13186238d5faSjnemeth 	if (type == NULL)
13196238d5faSjnemeth 		return ENXIO;
13206238d5faSjnemeth 
13216238d5faSjnemeth 	if ((fd->sc_flags & FD_OPEN) != 0 &&
13226238d5faSjnemeth 	    fd->sc_type != type)
13236238d5faSjnemeth 		return EBUSY;
13246238d5faSjnemeth 
13256238d5faSjnemeth 	fd->sc_type = type;
13266238d5faSjnemeth 	fd->sc_cylin = -1;
13276238d5faSjnemeth 	fd->sc_flags |= FD_OPEN;
13286238d5faSjnemeth 
13296238d5faSjnemeth 	/*
13306238d5faSjnemeth 	 * Only update the disklabel if we're not open anywhere else.
13316238d5faSjnemeth 	 */
13326238d5faSjnemeth 	if (fd->sc_dk.dk_openmask == 0)
13336238d5faSjnemeth 		fdgetdisklabel(dev);
13346238d5faSjnemeth 
13356238d5faSjnemeth 	pmask = (1 << DISKPART(dev));
13366238d5faSjnemeth 
13376238d5faSjnemeth 	switch (fmt) {
13386238d5faSjnemeth 	case S_IFCHR:
13396238d5faSjnemeth 		fd->sc_dk.dk_copenmask |= pmask;
13406238d5faSjnemeth 		break;
13416238d5faSjnemeth 
13426238d5faSjnemeth 	case S_IFBLK:
13436238d5faSjnemeth 		fd->sc_dk.dk_bopenmask |= pmask;
13446238d5faSjnemeth 		break;
13456238d5faSjnemeth 	}
13466238d5faSjnemeth 	fd->sc_dk.dk_openmask =
13476238d5faSjnemeth 	    fd->sc_dk.dk_copenmask | fd->sc_dk.dk_bopenmask;
13486238d5faSjnemeth 
13496238d5faSjnemeth 	return 0;
13506238d5faSjnemeth }
13516238d5faSjnemeth 
13526238d5faSjnemeth int
13536238d5faSjnemeth fdclose(dev_t dev, int flags, int fmt, struct lwp *l)
13546238d5faSjnemeth {
13552dea63feScegger 	struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(dev));
13566238d5faSjnemeth 	int pmask = (1 << DISKPART(dev));
13576238d5faSjnemeth 
13586238d5faSjnemeth 	fd->sc_flags &= ~FD_OPEN;
13596238d5faSjnemeth 	fd->sc_opts &= ~(FDOPT_NORETRY|FDOPT_SILENT);
13606238d5faSjnemeth 
13616238d5faSjnemeth 	switch (fmt) {
13626238d5faSjnemeth 	case S_IFCHR:
13636238d5faSjnemeth 		fd->sc_dk.dk_copenmask &= ~pmask;
13646238d5faSjnemeth 		break;
13656238d5faSjnemeth 
13666238d5faSjnemeth 	case S_IFBLK:
13676238d5faSjnemeth 		fd->sc_dk.dk_bopenmask &= ~pmask;
13686238d5faSjnemeth 		break;
13696238d5faSjnemeth 	}
13706238d5faSjnemeth 	fd->sc_dk.dk_openmask =
13716238d5faSjnemeth 	    fd->sc_dk.dk_copenmask | fd->sc_dk.dk_bopenmask;
13726238d5faSjnemeth 
13736238d5faSjnemeth 	return 0;
13746238d5faSjnemeth }
13756238d5faSjnemeth 
13766238d5faSjnemeth int
13776238d5faSjnemeth fdread(dev_t dev, struct uio *uio, int flag)
13786238d5faSjnemeth {
13796238d5faSjnemeth 
13806238d5faSjnemeth         return physio(fdstrategy, NULL, dev, B_READ, minphys, uio);
13816238d5faSjnemeth }
13826238d5faSjnemeth 
13836238d5faSjnemeth int
13846238d5faSjnemeth fdwrite(dev_t dev, struct uio *uio, int flag)
13856238d5faSjnemeth {
13866238d5faSjnemeth 
13876238d5faSjnemeth         return physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio);
13886238d5faSjnemeth }
13896238d5faSjnemeth 
13906238d5faSjnemeth void
13916238d5faSjnemeth fdcstart(struct fdc_softc *fdc)
13926238d5faSjnemeth {
13936238d5faSjnemeth 
13946238d5faSjnemeth #ifdef DIAGNOSTIC
13956238d5faSjnemeth 	/* only got here if controller's drive queue was inactive; should
13966238d5faSjnemeth 	   be in idle state */
13976238d5faSjnemeth 	if (fdc->sc_state != DEVIDLE) {
13986238d5faSjnemeth 		printf("fdcstart: not idle\n");
13996238d5faSjnemeth 		return;
14006238d5faSjnemeth 	}
14016238d5faSjnemeth #endif
14026238d5faSjnemeth 	(void)fdcstate(fdc);
14036238d5faSjnemeth }
14046238d5faSjnemeth 
14059a5d3f28Schristos static void
14069a5d3f28Schristos fdcpstatus(struct fdc_softc *fdc)
14079a5d3f28Schristos {
14089a5d3f28Schristos 	char bits[64];
14099a5d3f28Schristos 
14109a5d3f28Schristos 	snprintb(bits, sizeof(bits), NE7_ST0BITS, fdc->sc_status[0]);
14119a5d3f28Schristos 	printf(" (st0 %s", bits);
14129a5d3f28Schristos 	snprintb(bits, sizeof(bits), NE7_ST1BITS, fdc->sc_status[1]);
14139a5d3f28Schristos 	printf(" st1 %s", bits);
14149a5d3f28Schristos 	snprintb(bits, sizeof(bits), NE7_ST2BITS, fdc->sc_status[2]);
14159a5d3f28Schristos 	printf(" st2 %s", bits);
14169a5d3f28Schristos 	printf(" cyl %d head %d sec %d)\n",
14179a5d3f28Schristos 	    fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
14189a5d3f28Schristos }
14199a5d3f28Schristos 
14206238d5faSjnemeth void
14216238d5faSjnemeth fdcstatus(struct fdc_softc *fdc, const char *s)
14226238d5faSjnemeth {
14236238d5faSjnemeth 	struct fd_softc *fd = fdc->sc_drives.tqh_first;
14246238d5faSjnemeth 	int n;
14256238d5faSjnemeth 	char bits[64];
14266238d5faSjnemeth 
14276238d5faSjnemeth 	/* Just print last status */
14286238d5faSjnemeth 	n = fdc->sc_nstat;
14296238d5faSjnemeth 
14306238d5faSjnemeth #if 0
14316238d5faSjnemeth 	/*
14326238d5faSjnemeth 	 * A 82072 seems to return <invalid command> on
14336238d5faSjnemeth 	 * gratuitous Sense Interrupt commands.
14346238d5faSjnemeth 	 */
14356238d5faSjnemeth 	if (n == 0 && (fdc->sc_flags & FDC_82077) != 0) {
14366238d5faSjnemeth 		fdc_wrfifo(fdc, NE7CMD_SENSEI);
14376238d5faSjnemeth 		(void)fdcresult(fdc);
14386238d5faSjnemeth 		n = 2;
14396238d5faSjnemeth 	}
14406238d5faSjnemeth #endif
14416238d5faSjnemeth 
14426238d5faSjnemeth 	printf("%s: %s: state %d",
1443ae8fd9fdSchristos 		fd ? device_xname(fd->sc_dev) : "fdc", s, fdc->sc_state);
14446238d5faSjnemeth 
14456238d5faSjnemeth 	switch (n) {
14466238d5faSjnemeth 	case 0:
14476238d5faSjnemeth 		printf("\n");
14486238d5faSjnemeth 		break;
14496238d5faSjnemeth 	case 2:
14509a5d3f28Schristos 		snprintb(bits, sizeof(bits), NE7_ST0BITS, fdc->sc_status[0]);
14519a5d3f28Schristos 		printf(" (st0 %s cyl %d)\n", bits, fdc->sc_status[1]);
14526238d5faSjnemeth 		break;
14536238d5faSjnemeth 	case 7:
14546b7c41cfScegger 		fdcpstatus(fdc);
14556238d5faSjnemeth 		break;
14566238d5faSjnemeth #ifdef DIAGNOSTIC
14576238d5faSjnemeth 	default:
14586238d5faSjnemeth 		printf(" fdcstatus: weird size: %d\n", n);
14596238d5faSjnemeth 		break;
14606238d5faSjnemeth #endif
14616238d5faSjnemeth 	}
14626238d5faSjnemeth }
14636238d5faSjnemeth 
14646238d5faSjnemeth void
14656238d5faSjnemeth fdctimeout(void *arg)
14666238d5faSjnemeth {
14676238d5faSjnemeth 	struct fdc_softc *fdc = arg;
14686238d5faSjnemeth 	struct fd_softc *fd;
14696238d5faSjnemeth 	int s;
14706238d5faSjnemeth 
14716238d5faSjnemeth 	s = splbio();
14726238d5faSjnemeth 	fd = fdc->sc_drives.tqh_first;
14736238d5faSjnemeth 	if (fd == NULL) {
1474ae8fd9fdSchristos 		aprint_error_dev(fdc->sc_dev, "timeout but no I/O pending: state %d, istatus=%d\n",
14756238d5faSjnemeth 			fdc->sc_state, fdc->sc_istatus);
14766238d5faSjnemeth 		fdc->sc_state = DEVIDLE;
14776238d5faSjnemeth 		goto out;
14786238d5faSjnemeth 	}
14796238d5faSjnemeth 
148070de9736Syamt 	if (bufq_peek(fd->sc_q) != NULL)
14816238d5faSjnemeth 		fdc->sc_state++;
14826238d5faSjnemeth 	else
14836238d5faSjnemeth 		fdc->sc_state = DEVIDLE;
14846238d5faSjnemeth 
14856238d5faSjnemeth 	(void)fdcstate(fdc);
14866238d5faSjnemeth out:
14876238d5faSjnemeth 	splx(s);
14886238d5faSjnemeth 
14896238d5faSjnemeth }
14906238d5faSjnemeth 
14916238d5faSjnemeth void
14926238d5faSjnemeth fdcpseudointr(void *arg)
14936238d5faSjnemeth {
14946238d5faSjnemeth 	struct fdc_softc *fdc = arg;
14956238d5faSjnemeth 	int s;
14966238d5faSjnemeth 
14976238d5faSjnemeth 	/* Just ensure it has the right spl. */
14986238d5faSjnemeth 	s = splbio();
14996238d5faSjnemeth 	(void)fdcstate(fdc);
15006238d5faSjnemeth 	splx(s);
15016238d5faSjnemeth }
15026238d5faSjnemeth 
15036238d5faSjnemeth 
15046238d5faSjnemeth /*
15056238d5faSjnemeth  * hardware interrupt entry point: used only if no `fast trap' * (in-window)
15066238d5faSjnemeth  * handler is available. Unfortunately, we have no reliable way to
15076238d5faSjnemeth  * determine that the interrupt really came from the floppy controller;
15086238d5faSjnemeth  * just hope that the other devices that share this interrupt level
15096238d5faSjnemeth  * can do better..
15106238d5faSjnemeth  */
15116238d5faSjnemeth int
15126238d5faSjnemeth fdc_c_hwintr(void *arg)
15136238d5faSjnemeth {
15146238d5faSjnemeth 	struct fdc_softc *fdc = arg;
15156238d5faSjnemeth 	bus_space_tag_t t = fdc->sc_bustag;
15166238d5faSjnemeth 	bus_space_handle_t h = fdc->sc_handle;
15176238d5faSjnemeth 
15186238d5faSjnemeth 	switch (fdc->sc_itask) {
15196238d5faSjnemeth 	case FDC_ITASK_NONE:
15206238d5faSjnemeth 		return 0;
15216238d5faSjnemeth 	case FDC_ITASK_SENSEI:
15226238d5faSjnemeth 		if (fdc_wrfifo(fdc, NE7CMD_SENSEI) != 0 || fdcresult(fdc) == -1)
15236238d5faSjnemeth 			fdc->sc_istatus = FDC_ISTATUS_ERROR;
15246238d5faSjnemeth 		else
15256238d5faSjnemeth 			fdc->sc_istatus = FDC_ISTATUS_DONE;
1526fd5cef85Sjnemeth 		softint_schedule(fdc->sc_sicookie);
15276238d5faSjnemeth 		return 1;
15286238d5faSjnemeth 	case FDC_ITASK_RESULT:
15296238d5faSjnemeth 		if (fdcresult(fdc) == -1)
15306238d5faSjnemeth 			fdc->sc_istatus = FDC_ISTATUS_ERROR;
15316238d5faSjnemeth 		else
15326238d5faSjnemeth 			fdc->sc_istatus = FDC_ISTATUS_DONE;
1533fd5cef85Sjnemeth 		softint_schedule(fdc->sc_sicookie);
15346238d5faSjnemeth 		return 1;
15356238d5faSjnemeth 	case FDC_ITASK_DMA:
15366238d5faSjnemeth 		/* Proceed with pseudo-DMA below */
15376238d5faSjnemeth 		break;
15386238d5faSjnemeth 	default:
15396238d5faSjnemeth 		printf("fdc: stray hard interrupt: itask=%d\n", fdc->sc_itask);
15406238d5faSjnemeth 		fdc->sc_istatus = FDC_ISTATUS_SPURIOUS;
1541fd5cef85Sjnemeth 		softint_schedule(fdc->sc_sicookie);
15426238d5faSjnemeth 		return 1;
15436238d5faSjnemeth 	}
15446238d5faSjnemeth 
15456238d5faSjnemeth 	/*
15466238d5faSjnemeth 	 * Pseudo DMA in progress
15476238d5faSjnemeth 	 */
15486238d5faSjnemeth 	for (;;) {
15496238d5faSjnemeth 		uint8_t msr;
15506238d5faSjnemeth 
15516238d5faSjnemeth 		msr = bus_space_read_1(t, h, fdc->sc_reg_msr);
15526238d5faSjnemeth 
15536238d5faSjnemeth 		if ((msr & NE7_RQM) == 0)
15540b59ea5aSjnemeth 			/* That's all this round. */
15556238d5faSjnemeth 			break;
15566238d5faSjnemeth 
15576238d5faSjnemeth 		if ((msr & NE7_NDM) == 0) {
15580b59ea5aSjnemeth 			/* Execution phase finished, get result. */
15596238d5faSjnemeth 			fdcresult(fdc);
15606238d5faSjnemeth 			fdc->sc_istatus = FDC_ISTATUS_DONE;
1561fd5cef85Sjnemeth 			softint_schedule(fdc->sc_sicookie);
15626238d5faSjnemeth 			break;
15636238d5faSjnemeth 		}
15646238d5faSjnemeth 
15650b59ea5aSjnemeth 		if (fdc->sc_tc == 0)
15660b59ea5aSjnemeth 			/* For some reason the controller wants to transfer
15670b59ea5aSjnemeth 			   more data then what we want to transfer. */
15680b59ea5aSjnemeth 			panic("fdc: overrun");
15690b59ea5aSjnemeth 
15706238d5faSjnemeth 		/* Another byte can be transferred */
15716238d5faSjnemeth 		if ((msr & NE7_DIO) != 0)
15726238d5faSjnemeth 			*fdc->sc_data =
15736238d5faSjnemeth 				bus_space_read_1(t, h, fdc->sc_reg_fifo);
15746238d5faSjnemeth 		else
15756238d5faSjnemeth 			bus_space_write_1(t, h, fdc->sc_reg_fifo,
15766238d5faSjnemeth 					  *fdc->sc_data);
15776238d5faSjnemeth 
15786238d5faSjnemeth 		fdc->sc_data++;
15796238d5faSjnemeth 		if (--fdc->sc_tc == 0) {
15806238d5faSjnemeth 			FTC_FLIP;
15816238d5faSjnemeth 			break;
15826238d5faSjnemeth 		}
15836238d5faSjnemeth 	}
15846238d5faSjnemeth 	return 1;
15856238d5faSjnemeth }
15866238d5faSjnemeth 
15876238d5faSjnemeth void
15886238d5faSjnemeth fdcswintr(void *arg)
15896238d5faSjnemeth {
15906238d5faSjnemeth 	struct fdc_softc *fdc = arg;
15916238d5faSjnemeth 
15926238d5faSjnemeth 	if (fdc->sc_istatus == FDC_ISTATUS_NONE)
15936238d5faSjnemeth 		/* This (software) interrupt is not for us */
15946238d5faSjnemeth 		return;
15956238d5faSjnemeth 
15966238d5faSjnemeth 	switch (fdc->sc_istatus) {
15976238d5faSjnemeth 	case FDC_ISTATUS_ERROR:
15986238d5faSjnemeth 		printf("fdc: ierror status: state %d\n", fdc->sc_state);
15996238d5faSjnemeth 		break;
16006238d5faSjnemeth 	case FDC_ISTATUS_SPURIOUS:
16016238d5faSjnemeth 		printf("fdc: spurious interrupt: state %d\n", fdc->sc_state);
16026238d5faSjnemeth 		break;
16036238d5faSjnemeth 	}
16046238d5faSjnemeth 
16056238d5faSjnemeth 	fdcstate(fdc);
16066238d5faSjnemeth 	return;
16076238d5faSjnemeth }
16086238d5faSjnemeth 
16096238d5faSjnemeth int
16106238d5faSjnemeth fdcstate(struct fdc_softc *fdc)
16116238d5faSjnemeth {
16126238d5faSjnemeth 
16136238d5faSjnemeth #define	st0	fdc->sc_status[0]
16146238d5faSjnemeth #define	st1	fdc->sc_status[1]
16156238d5faSjnemeth #define	cyl	fdc->sc_status[1]
16166238d5faSjnemeth #define FDC_WRFIFO(fdc, c) do {			\
16176238d5faSjnemeth 	if (fdc_wrfifo(fdc, (c))) {		\
16186238d5faSjnemeth 		goto xxx;			\
16196238d5faSjnemeth 	}					\
16206238d5faSjnemeth } while(0)
16216238d5faSjnemeth 
16226238d5faSjnemeth 	struct fd_softc *fd;
16236238d5faSjnemeth 	struct buf *bp;
16246238d5faSjnemeth 	int read, head, sec, nblks;
16256238d5faSjnemeth 	struct fd_type *type;
16266238d5faSjnemeth 	struct ne7_fd_formb *finfo = NULL;
16276238d5faSjnemeth 
16286238d5faSjnemeth 	if (fdc->sc_istatus == FDC_ISTATUS_ERROR) {
16296238d5faSjnemeth 		/* Prevent loop if the reset sequence produces errors */
16306238d5faSjnemeth 		if (fdc->sc_state != RESETCOMPLETE &&
16316238d5faSjnemeth 		    fdc->sc_state != RECALWAIT &&
16326238d5faSjnemeth 		    fdc->sc_state != RECALCOMPLETE)
16336238d5faSjnemeth 			fdc->sc_state = DORESET;
16346238d5faSjnemeth 	}
16356238d5faSjnemeth 
16366238d5faSjnemeth 	/* Clear I task/status field */
16376238d5faSjnemeth 	fdc->sc_istatus = FDC_ISTATUS_NONE;
16386238d5faSjnemeth 	fdc->sc_itask = FDC_ITASK_NONE;
16396238d5faSjnemeth 
16406238d5faSjnemeth loop:
16416238d5faSjnemeth 	/* Is there a drive for the controller to do a transfer with? */
16426238d5faSjnemeth 	fd = fdc->sc_drives.tqh_first;
16436238d5faSjnemeth 	if (fd == NULL) {
16446238d5faSjnemeth 		fdc->sc_state = DEVIDLE;
16456238d5faSjnemeth  		return 0;
16466238d5faSjnemeth 	}
16476238d5faSjnemeth 
16486238d5faSjnemeth 	/* Is there a transfer to this drive?  If not, deactivate drive. */
164970de9736Syamt 	bp = bufq_peek(fd->sc_q);
16506238d5faSjnemeth 	if (bp == NULL) {
16516238d5faSjnemeth 		fd->sc_ops = 0;
16526238d5faSjnemeth 		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
16536238d5faSjnemeth 		fd->sc_active = 0;
16546238d5faSjnemeth 		goto loop;
16556238d5faSjnemeth 	}
16566238d5faSjnemeth 
16576238d5faSjnemeth 	if (bp->b_flags & B_FORMAT)
16586238d5faSjnemeth 		finfo = (struct ne7_fd_formb *)bp->b_data;
16596238d5faSjnemeth 
16606238d5faSjnemeth 	switch (fdc->sc_state) {
16616238d5faSjnemeth 	case DEVIDLE:
16626238d5faSjnemeth 		fdc->sc_errors = 0;
16636238d5faSjnemeth 		fd->sc_skip = 0;
16646238d5faSjnemeth 		fd->sc_bcount = bp->b_bcount;
16656238d5faSjnemeth 		fd->sc_blkno = (bp->b_blkno * DEV_BSIZE) / FD_BSIZE(fd);
16666238d5faSjnemeth 		callout_stop(&fd->sc_motoroff_ch);
16676238d5faSjnemeth 		if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
16686238d5faSjnemeth 			fdc->sc_state = MOTORWAIT;
16696238d5faSjnemeth 			return 1;
16706238d5faSjnemeth 		}
16716238d5faSjnemeth 		if ((fd->sc_flags & FD_MOTOR) == 0) {
16726238d5faSjnemeth 			/* Turn on the motor, being careful about pairing. */
16736238d5faSjnemeth 			struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1];
16746238d5faSjnemeth 			if (ofd && ofd->sc_flags & FD_MOTOR) {
16756238d5faSjnemeth 				callout_stop(&ofd->sc_motoroff_ch);
16766238d5faSjnemeth 				ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
16776238d5faSjnemeth 			}
16786238d5faSjnemeth 			fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
16796238d5faSjnemeth 			fd_set_motor(fdc);
16806238d5faSjnemeth 			fdc->sc_state = MOTORWAIT;
16816238d5faSjnemeth 			if ((fdc->sc_flags & FDC_NEEDMOTORWAIT) != 0) { /*XXX*/
16826238d5faSjnemeth 				/* Allow .25s for motor to stabilize. */
16836238d5faSjnemeth 				callout_reset(&fd->sc_motoron_ch, hz / 4,
16846238d5faSjnemeth 				    fd_motor_on, fd);
16856238d5faSjnemeth 			} else {
16866238d5faSjnemeth 				fd->sc_flags &= ~FD_MOTOR_WAIT;
16876238d5faSjnemeth 				goto loop;
16886238d5faSjnemeth 			}
16896238d5faSjnemeth 			return 1;
16906238d5faSjnemeth 		}
16916238d5faSjnemeth 		/* Make sure the right drive is selected. */
16926238d5faSjnemeth 		fd_set_motor(fdc);
16936238d5faSjnemeth 
16946238d5faSjnemeth 		if (fdc_diskchange(fdc))
16956238d5faSjnemeth 			goto dodskchg;
16966238d5faSjnemeth 
16976238d5faSjnemeth 		/*FALLTHROUGH*/
16986238d5faSjnemeth 	case DOSEEK:
16996238d5faSjnemeth 	doseek:
17006238d5faSjnemeth 		if ((fdc->sc_flags & FDC_EIS) &&
17016238d5faSjnemeth 		    (bp->b_flags & B_FORMAT) == 0) {
17026238d5faSjnemeth 			fd->sc_cylin = bp->b_cylinder;
17036238d5faSjnemeth 			/* We use implied seek */
17046238d5faSjnemeth 			goto doio;
17056238d5faSjnemeth 		}
17066238d5faSjnemeth 
17076238d5faSjnemeth 		if (fd->sc_cylin == bp->b_cylinder)
17086238d5faSjnemeth 			goto doio;
17096238d5faSjnemeth 
17106238d5faSjnemeth 		fd->sc_cylin = -1;
17116238d5faSjnemeth 		fdc->sc_state = SEEKWAIT;
17126238d5faSjnemeth 		fdc->sc_nstat = 0;
17136238d5faSjnemeth 
17146238d5faSjnemeth 		iostat_seek(fd->sc_dk.dk_stats);
17156238d5faSjnemeth 
17166238d5faSjnemeth 		disk_busy(&fd->sc_dk);
17176238d5faSjnemeth 		callout_reset(&fdc->sc_timo_ch, 4 * hz, fdctimeout, fdc);
17186238d5faSjnemeth 
17196238d5faSjnemeth 		/* specify command */
17206238d5faSjnemeth 		FDC_WRFIFO(fdc, NE7CMD_SPECIFY);
17216238d5faSjnemeth 		FDC_WRFIFO(fdc, fd->sc_type->steprate);
17226238d5faSjnemeth 		/* XXX head load time == 6ms */
17236238d5faSjnemeth 		FDC_WRFIFO(fdc, 6 | NE7_SPECIFY_NODMA);
17246238d5faSjnemeth 
17256238d5faSjnemeth 		fdc->sc_itask = FDC_ITASK_SENSEI;
17266238d5faSjnemeth 		/* seek function */
17276238d5faSjnemeth 		FDC_WRFIFO(fdc, NE7CMD_SEEK);
17286238d5faSjnemeth 		FDC_WRFIFO(fdc, fd->sc_drive); /* drive number */
17296238d5faSjnemeth 		FDC_WRFIFO(fdc, bp->b_cylinder * fd->sc_type->step);
17306238d5faSjnemeth 		return 1;
17316238d5faSjnemeth 
17326238d5faSjnemeth 	case DODSKCHG:
17336238d5faSjnemeth 	dodskchg:
17346238d5faSjnemeth 		/*
17356238d5faSjnemeth 		 * Disk change: force a seek operation by going to cyl 1
17366238d5faSjnemeth 		 * followed by a recalibrate.
17376238d5faSjnemeth 		 */
17386238d5faSjnemeth 		disk_busy(&fd->sc_dk);
17396238d5faSjnemeth 		callout_reset(&fdc->sc_timo_ch, 4 * hz, fdctimeout, fdc);
17406238d5faSjnemeth 		fd->sc_cylin = -1;
17416238d5faSjnemeth 		fdc->sc_nstat = 0;
17426238d5faSjnemeth 		fdc->sc_state = DSKCHGWAIT;
17436238d5faSjnemeth 
17446238d5faSjnemeth 		fdc->sc_itask = FDC_ITASK_SENSEI;
17456238d5faSjnemeth 		/* seek function */
17466238d5faSjnemeth 		FDC_WRFIFO(fdc, NE7CMD_SEEK);
17476238d5faSjnemeth 		FDC_WRFIFO(fdc, fd->sc_drive); /* drive number */
17486238d5faSjnemeth 		FDC_WRFIFO(fdc, 1 * fd->sc_type->step);
17496238d5faSjnemeth 		return 1;
17506238d5faSjnemeth 
17516238d5faSjnemeth 	case DSKCHGWAIT:
17526238d5faSjnemeth 		callout_stop(&fdc->sc_timo_ch);
17536238d5faSjnemeth 		disk_unbusy(&fd->sc_dk, 0, 0);
17546238d5faSjnemeth 		if (fdc->sc_nstat != 2 || (st0 & 0xf8) != 0x20 ||
17556238d5faSjnemeth 		    cyl != 1 * fd->sc_type->step) {
17566238d5faSjnemeth 			fdcstatus(fdc, "dskchg seek failed");
17576238d5faSjnemeth 			fdc->sc_state = DORESET;
17586238d5faSjnemeth 		} else
17596238d5faSjnemeth 			fdc->sc_state = DORECAL;
17606238d5faSjnemeth 
17616238d5faSjnemeth 		if (fdc_diskchange(fdc)) {
1762ae8fd9fdSchristos 			aprint_error_dev(fdc->sc_dev,
1763ae8fd9fdSchristos 			    "cannot clear disk change status\n");
17646238d5faSjnemeth 			fdc->sc_state = DORESET;
17656238d5faSjnemeth 		}
17666238d5faSjnemeth 		goto loop;
17676238d5faSjnemeth 
17686238d5faSjnemeth 	case DOIO:
17696238d5faSjnemeth 	doio:
17706238d5faSjnemeth 		if (finfo != NULL)
17716238d5faSjnemeth 			fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
17726238d5faSjnemeth 				      (char *)finfo;
17736238d5faSjnemeth 		type = fd->sc_type;
17746238d5faSjnemeth 		sec = fd->sc_blkno % type->seccyl;
17756238d5faSjnemeth 		nblks = type->seccyl - sec;
1776d1579b2dSriastradh 		nblks = uimin(nblks, fd->sc_bcount / FD_BSIZE(fd));
1777d1579b2dSriastradh 		nblks = uimin(nblks, FDC_MAXIOSIZE / FD_BSIZE(fd));
17786238d5faSjnemeth 		fd->sc_nblks = nblks;
17796238d5faSjnemeth 		fd->sc_nbytes = finfo ? bp->b_bcount : nblks * FD_BSIZE(fd);
17806238d5faSjnemeth 		head = sec / type->sectrac;
17816238d5faSjnemeth 		sec -= head * type->sectrac;
17826238d5faSjnemeth #ifdef DIAGNOSTIC
17836238d5faSjnemeth 		{int block;
17846238d5faSjnemeth 		 block = (fd->sc_cylin * type->heads + head) * type->sectrac +
17856238d5faSjnemeth 			 sec;
17866238d5faSjnemeth 		 if (block != fd->sc_blkno) {
17876238d5faSjnemeth 			 printf("fdcintr: block %d != blkno %d\n", block,
17886238d5faSjnemeth 			        (int)fd->sc_blkno);
17896238d5faSjnemeth #ifdef DDB
17906238d5faSjnemeth 			 Debugger();
17916238d5faSjnemeth #endif
17926238d5faSjnemeth 		 }}
17936238d5faSjnemeth #endif
17946238d5faSjnemeth 		read = bp->b_flags & B_READ;
17956238d5faSjnemeth 
17966238d5faSjnemeth 		/* Setup for pseudo DMA */
1797fffc9c66Schristos 		fdc->sc_data = (char *)bp->b_data + fd->sc_skip;
17986238d5faSjnemeth 		fdc->sc_tc = fd->sc_nbytes;
17996238d5faSjnemeth 
18006238d5faSjnemeth 		bus_space_write_1(fdc->sc_bustag, fdc->sc_handle,
18016238d5faSjnemeth 				  fdc->sc_reg_drs, type->rate);
18026238d5faSjnemeth #ifdef FD_DEBUG
18036238d5faSjnemeth 		if (fdc_debug > 1)
18046238d5faSjnemeth 			printf("fdcstate: doio: %s drive %d "
18056238d5faSjnemeth 				"track %d head %d sec %d nblks %d\n",
18066238d5faSjnemeth 				finfo ? "format" :
18076238d5faSjnemeth 					(read ? "read" : "write"),
18086238d5faSjnemeth 				fd->sc_drive, fd->sc_cylin, head, sec, nblks);
18096238d5faSjnemeth #endif
18106238d5faSjnemeth 		fdc->sc_state = IOCOMPLETE;
18116238d5faSjnemeth 		fdc->sc_itask = FDC_ITASK_DMA;
18126238d5faSjnemeth 		fdc->sc_nstat = 0;
18136238d5faSjnemeth 
18146238d5faSjnemeth 		disk_busy(&fd->sc_dk);
18156238d5faSjnemeth 
18166238d5faSjnemeth 		/* allow 3 seconds for operation */
18176238d5faSjnemeth 		callout_reset(&fdc->sc_timo_ch, 3 * hz, fdctimeout, fdc);
18186238d5faSjnemeth 
18196238d5faSjnemeth 		if (finfo != NULL) {
18206238d5faSjnemeth 			/* formatting */
18216238d5faSjnemeth 			FDC_WRFIFO(fdc, NE7CMD_FORMAT);
18226238d5faSjnemeth 			FDC_WRFIFO(fdc, (head << 2) | fd->sc_drive);
18236238d5faSjnemeth 			FDC_WRFIFO(fdc, finfo->fd_formb_secshift);
18246238d5faSjnemeth 			FDC_WRFIFO(fdc, finfo->fd_formb_nsecs);
18256238d5faSjnemeth 			FDC_WRFIFO(fdc, finfo->fd_formb_gaplen);
18266238d5faSjnemeth 			FDC_WRFIFO(fdc, finfo->fd_formb_fillbyte);
18276238d5faSjnemeth 		} else {
18286238d5faSjnemeth 			if (read)
18296238d5faSjnemeth 				FDC_WRFIFO(fdc, NE7CMD_READ);
18306238d5faSjnemeth 			else
18316238d5faSjnemeth 				FDC_WRFIFO(fdc, NE7CMD_WRITE);
18326238d5faSjnemeth 			FDC_WRFIFO(fdc, (head << 2) | fd->sc_drive);
18336238d5faSjnemeth 			FDC_WRFIFO(fdc, fd->sc_cylin);	/*track*/
18346238d5faSjnemeth 			FDC_WRFIFO(fdc, head);
18356238d5faSjnemeth 			FDC_WRFIFO(fdc, sec + 1);	/*sector+1*/
18366238d5faSjnemeth 			FDC_WRFIFO(fdc, type->secsize);	/*sector size*/
18376238d5faSjnemeth 			FDC_WRFIFO(fdc, type->sectrac);	/*secs/track*/
18386238d5faSjnemeth 			FDC_WRFIFO(fdc, type->gap1);	/*gap1 size*/
18396238d5faSjnemeth 			FDC_WRFIFO(fdc, type->datalen);	/*data length*/
18406238d5faSjnemeth 		}
18416238d5faSjnemeth 
18426238d5faSjnemeth 		return 1;				/* will return later */
18436238d5faSjnemeth 
18446238d5faSjnemeth 	case SEEKWAIT:
18456238d5faSjnemeth 		callout_stop(&fdc->sc_timo_ch);
18466238d5faSjnemeth 		fdc->sc_state = SEEKCOMPLETE;
18476238d5faSjnemeth 		if (fdc->sc_flags & FDC_NEEDHEADSETTLE) {
18486238d5faSjnemeth 			/* allow 1/50 second for heads to settle */
18496238d5faSjnemeth 			callout_reset(&fdc->sc_intr_ch, hz / 50,
18506238d5faSjnemeth 			    fdcpseudointr, fdc);
18516238d5faSjnemeth 			return 1;		/* will return later */
18526238d5faSjnemeth 		}
18536238d5faSjnemeth 		/*FALLTHROUGH*/
18546238d5faSjnemeth 	case SEEKCOMPLETE:
18556238d5faSjnemeth 		/* no data on seek */
18566238d5faSjnemeth 		disk_unbusy(&fd->sc_dk, 0, 0);
18576238d5faSjnemeth 
18586238d5faSjnemeth 		/* Make sure seek really happened. */
18596238d5faSjnemeth 		if (fdc->sc_nstat != 2 || (st0 & 0xf8) != 0x20 ||
18606238d5faSjnemeth 		    cyl != bp->b_cylinder * fd->sc_type->step) {
18616238d5faSjnemeth #ifdef FD_DEBUG
18626238d5faSjnemeth 			if (fdc_debug)
18636238d5faSjnemeth 				fdcstatus(fdc, "seek failed");
18646238d5faSjnemeth #endif
18656238d5faSjnemeth 			fdcretry(fdc);
18666238d5faSjnemeth 			goto loop;
18676238d5faSjnemeth 		}
18686238d5faSjnemeth 		fd->sc_cylin = bp->b_cylinder;
18696238d5faSjnemeth 		goto doio;
18706238d5faSjnemeth 
18716238d5faSjnemeth 	case IOTIMEDOUT:
18726238d5faSjnemeth 		/*
18736238d5faSjnemeth 		 * Try to abort the I/O operation without resetting
18746238d5faSjnemeth 		 * the chip first.  Poke TC and arrange to pick up
18756238d5faSjnemeth 		 * the timed out I/O command's status.
18766238d5faSjnemeth 		 */
18776238d5faSjnemeth 		fdc->sc_itask = FDC_ITASK_RESULT;
18786238d5faSjnemeth 		fdc->sc_state = IOCLEANUPWAIT;
18796238d5faSjnemeth 		fdc->sc_nstat = 0;
18806238d5faSjnemeth 		/* 1/10 second should be enough */
18816238d5faSjnemeth 		callout_reset(&fdc->sc_timo_ch, hz / 10, fdctimeout, fdc);
18826238d5faSjnemeth 		FTC_FLIP;
18836238d5faSjnemeth 		return 1;
18846238d5faSjnemeth 
18856238d5faSjnemeth 	case IOCLEANUPTIMEDOUT:
18866238d5faSjnemeth 	case SEEKTIMEDOUT:
18876238d5faSjnemeth 	case RECALTIMEDOUT:
18886238d5faSjnemeth 	case RESETTIMEDOUT:
18896238d5faSjnemeth 	case DSKCHGTIMEDOUT:
18906238d5faSjnemeth 		fdcstatus(fdc, "timeout");
18916238d5faSjnemeth 
18926238d5faSjnemeth 		/* All other timeouts always roll through to a chip reset */
18936238d5faSjnemeth 		fdcretry(fdc);
18946238d5faSjnemeth 
18956238d5faSjnemeth 		/* Force reset, no matter what fdcretry() says */
18966238d5faSjnemeth 		fdc->sc_state = DORESET;
18976238d5faSjnemeth 		goto loop;
18986238d5faSjnemeth 
18996238d5faSjnemeth 	case IOCLEANUPWAIT: /* IO FAILED, cleanup succeeded */
19006238d5faSjnemeth 		callout_stop(&fdc->sc_timo_ch);
19016238d5faSjnemeth 		disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid),
19026238d5faSjnemeth 		    (bp->b_flags & B_READ));
19036238d5faSjnemeth 		fdcretry(fdc);
19046238d5faSjnemeth 		goto loop;
19056238d5faSjnemeth 
19066238d5faSjnemeth 	case IOCOMPLETE: /* IO DONE, post-analyze */
19076238d5faSjnemeth 		callout_stop(&fdc->sc_timo_ch);
19086238d5faSjnemeth 
19096238d5faSjnemeth 		disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid),
19106238d5faSjnemeth 		    (bp->b_flags & B_READ));
19116238d5faSjnemeth 
19126238d5faSjnemeth 		if (fdc->sc_nstat != 7 || st1 != 0 ||
19136238d5faSjnemeth 		    ((st0 & 0xf8) != 0 &&
19146238d5faSjnemeth 		     ((st0 & 0xf8) != 0x20 || (fdc->sc_cfg & CFG_EIS) == 0))) {
19156238d5faSjnemeth #ifdef FD_DEBUG
19166238d5faSjnemeth 			if (fdc_debug) {
19176238d5faSjnemeth 				fdcstatus(fdc, bp->b_flags & B_READ ?
19186238d5faSjnemeth 				     "read failed" : "write failed");
19196238d5faSjnemeth 				printf("blkno %lld nblks %d nstat %d tc %d\n",
19206238d5faSjnemeth 				       (long long)fd->sc_blkno, fd->sc_nblks,
19216238d5faSjnemeth 				       fdc->sc_nstat, fdc->sc_tc);
19226238d5faSjnemeth 			}
19236238d5faSjnemeth #endif
19246238d5faSjnemeth 			if (fdc->sc_nstat == 7 &&
19256238d5faSjnemeth 			    (st1 & ST1_OVERRUN) == ST1_OVERRUN) {
19266238d5faSjnemeth 
19276238d5faSjnemeth 				/*
19286238d5faSjnemeth 				 * Silently retry overruns if no other
19296238d5faSjnemeth 				 * error bit is set. Adjust threshold.
19306238d5faSjnemeth 				 */
19316238d5faSjnemeth 				int thr = fdc->sc_cfg & CFG_THRHLD_MASK;
19326238d5faSjnemeth 				if (thr < 15) {
19336238d5faSjnemeth 					thr++;
19346238d5faSjnemeth 					fdc->sc_cfg &= ~CFG_THRHLD_MASK;
19356238d5faSjnemeth 					fdc->sc_cfg |= (thr & CFG_THRHLD_MASK);
19366238d5faSjnemeth #ifdef FD_DEBUG
19376238d5faSjnemeth 					if (fdc_debug)
19386238d5faSjnemeth 						printf("fdc: %d -> threshold\n",
19396238d5faSjnemeth 						       thr);
19406238d5faSjnemeth #endif
19416238d5faSjnemeth 					fdconf(fdc);
19426238d5faSjnemeth 					fdc->sc_overruns = 0;
19436238d5faSjnemeth 				}
19446238d5faSjnemeth 				if (++fdc->sc_overruns < 3) {
19456238d5faSjnemeth 					fdc->sc_state = DOIO;
19466238d5faSjnemeth 					goto loop;
19476238d5faSjnemeth 				}
19486238d5faSjnemeth 			}
19496238d5faSjnemeth 			fdcretry(fdc);
19506238d5faSjnemeth 			goto loop;
19516238d5faSjnemeth 		}
19526238d5faSjnemeth 		if (fdc->sc_errors) {
19536238d5faSjnemeth 			diskerr(bp, "fd", "soft error", LOG_PRINTF,
19546238d5faSjnemeth 			    fd->sc_skip / FD_BSIZE(fd),
19556238d5faSjnemeth 			    (struct disklabel *)NULL);
19566238d5faSjnemeth 			printf("\n");
19576238d5faSjnemeth 			fdc->sc_errors = 0;
19586238d5faSjnemeth 		} else {
19596238d5faSjnemeth 			if (--fdc->sc_overruns < -20) {
19606238d5faSjnemeth 				int thr = fdc->sc_cfg & CFG_THRHLD_MASK;
19616238d5faSjnemeth 				if (thr > 0) {
19626238d5faSjnemeth 					thr--;
19636238d5faSjnemeth 					fdc->sc_cfg &= ~CFG_THRHLD_MASK;
19646238d5faSjnemeth 					fdc->sc_cfg |= (thr & CFG_THRHLD_MASK);
19656238d5faSjnemeth #ifdef FD_DEBUG
19666238d5faSjnemeth 					if (fdc_debug)
19676238d5faSjnemeth 						printf("fdc: %d -> threshold\n",
19686238d5faSjnemeth 						       thr);
19696238d5faSjnemeth #endif
19706238d5faSjnemeth 					fdconf(fdc);
19716238d5faSjnemeth 				}
19726238d5faSjnemeth 				fdc->sc_overruns = 0;
19736238d5faSjnemeth 			}
19746238d5faSjnemeth 		}
19756238d5faSjnemeth 		fd->sc_blkno += fd->sc_nblks;
19766238d5faSjnemeth 		fd->sc_skip += fd->sc_nbytes;
19776238d5faSjnemeth 		fd->sc_bcount -= fd->sc_nbytes;
19786238d5faSjnemeth 		if (finfo == NULL && fd->sc_bcount > 0) {
19796238d5faSjnemeth 			bp->b_cylinder = fd->sc_blkno / fd->sc_type->seccyl;
19806238d5faSjnemeth 			goto doseek;
19816238d5faSjnemeth 		}
19826238d5faSjnemeth 		fdfinish(fd, bp);
19836238d5faSjnemeth 		goto loop;
19846238d5faSjnemeth 
19856238d5faSjnemeth 	case DORESET:
19866238d5faSjnemeth 		/* try a reset, keep motor on */
19876238d5faSjnemeth 		fd_set_motor(fdc);
19886238d5faSjnemeth 		delay(100);
19896238d5faSjnemeth 		fdc->sc_nstat = 0;
19906238d5faSjnemeth 		fdc->sc_itask = FDC_ITASK_SENSEI;
19916238d5faSjnemeth 		fdc->sc_state = RESETCOMPLETE;
19926238d5faSjnemeth 		callout_reset(&fdc->sc_timo_ch, hz / 2, fdctimeout, fdc);
19936238d5faSjnemeth 		fdc_reset(fdc);
19946238d5faSjnemeth 		return 1;			/* will return later */
19956238d5faSjnemeth 
19966238d5faSjnemeth 	case RESETCOMPLETE:
19976238d5faSjnemeth 		callout_stop(&fdc->sc_timo_ch);
19986238d5faSjnemeth 		fdconf(fdc);
19996238d5faSjnemeth 
20006238d5faSjnemeth 		/* FALLTHROUGH */
20016238d5faSjnemeth 	case DORECAL:
20026238d5faSjnemeth 		fdc->sc_state = RECALWAIT;
20036238d5faSjnemeth 		fdc->sc_itask = FDC_ITASK_SENSEI;
20046238d5faSjnemeth 		fdc->sc_nstat = 0;
20056238d5faSjnemeth 		callout_reset(&fdc->sc_timo_ch, 5 * hz, fdctimeout, fdc);
20066238d5faSjnemeth 		/* recalibrate function */
20076238d5faSjnemeth 		FDC_WRFIFO(fdc, NE7CMD_RECAL);
20086238d5faSjnemeth 		FDC_WRFIFO(fdc, fd->sc_drive);
20096238d5faSjnemeth 		return 1;			/* will return later */
20106238d5faSjnemeth 
20116238d5faSjnemeth 	case RECALWAIT:
20126238d5faSjnemeth 		callout_stop(&fdc->sc_timo_ch);
20136238d5faSjnemeth 		fdc->sc_state = RECALCOMPLETE;
2014b905acb3Smrg 		if ((fdc->sc_flags & FDC_NEEDHEADSETTLE) != 0) {
20156238d5faSjnemeth 			/* allow 1/30 second for heads to settle */
20166238d5faSjnemeth 			callout_reset(&fdc->sc_intr_ch, hz / 30,
20176238d5faSjnemeth 			    fdcpseudointr, fdc);
20186238d5faSjnemeth 			return 1;		/* will return later */
20196238d5faSjnemeth 		}
2020b905acb3Smrg 		/* FALLTHROUGH */
20216238d5faSjnemeth 
20226238d5faSjnemeth 	case RECALCOMPLETE:
20236238d5faSjnemeth 		if (fdc->sc_nstat != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
20246238d5faSjnemeth #ifdef FD_DEBUG
20256238d5faSjnemeth 			if (fdc_debug)
20266238d5faSjnemeth 				fdcstatus(fdc, "recalibrate failed");
20276238d5faSjnemeth #endif
20286238d5faSjnemeth 			fdcretry(fdc);
20296238d5faSjnemeth 			goto loop;
20306238d5faSjnemeth 		}
20316238d5faSjnemeth 		fd->sc_cylin = 0;
20326238d5faSjnemeth 		goto doseek;
20336238d5faSjnemeth 
20346238d5faSjnemeth 	case MOTORWAIT:
20356238d5faSjnemeth 		if (fd->sc_flags & FD_MOTOR_WAIT)
20366238d5faSjnemeth 			return 1;		/* time's not up yet */
20376238d5faSjnemeth 		goto doseek;
20386238d5faSjnemeth 
20396238d5faSjnemeth 	default:
20406238d5faSjnemeth 		fdcstatus(fdc, "stray interrupt");
20416238d5faSjnemeth 		return 1;
20426238d5faSjnemeth 	}
20436238d5faSjnemeth #ifdef DIAGNOSTIC
20446238d5faSjnemeth 	panic("fdcintr: impossible");
20456238d5faSjnemeth #endif
20466238d5faSjnemeth 
20476238d5faSjnemeth xxx:
20486238d5faSjnemeth 	/*
20496238d5faSjnemeth 	 * We get here if the chip locks up in FDC_WRFIFO()
20506238d5faSjnemeth 	 * Cancel any operation and schedule a reset
20516238d5faSjnemeth 	 */
20526238d5faSjnemeth 	callout_stop(&fdc->sc_timo_ch);
20536238d5faSjnemeth 	fdcretry(fdc);
20546238d5faSjnemeth 	fdc->sc_state = DORESET;
20556238d5faSjnemeth 	goto loop;
20566238d5faSjnemeth 
20576238d5faSjnemeth #undef	st0
20586238d5faSjnemeth #undef	st1
20596238d5faSjnemeth #undef	cyl
20606238d5faSjnemeth }
20616238d5faSjnemeth 
20626238d5faSjnemeth void
20636238d5faSjnemeth fdcretry(struct fdc_softc *fdc)
20646238d5faSjnemeth {
20656238d5faSjnemeth 	struct fd_softc *fd;
20666238d5faSjnemeth 	struct buf *bp;
20676238d5faSjnemeth 	int error = EIO;
20686238d5faSjnemeth 
20696238d5faSjnemeth 	fd = fdc->sc_drives.tqh_first;
207070de9736Syamt 	bp = bufq_peek(fd->sc_q);
20716238d5faSjnemeth 
20726238d5faSjnemeth 	fdc->sc_overruns = 0;
20736238d5faSjnemeth 	if (fd->sc_opts & FDOPT_NORETRY)
20746238d5faSjnemeth 		goto fail;
20756238d5faSjnemeth 
20766238d5faSjnemeth 	switch (fdc->sc_errors) {
20776238d5faSjnemeth 	case 0:
20786238d5faSjnemeth 		if (fdc->sc_nstat == 7 &&
20796238d5faSjnemeth 		    (fdc->sc_status[0] & 0xd8) == 0x40 &&
20806238d5faSjnemeth 		    (fdc->sc_status[1] & 0x2) == 0x2) {
2081ae8fd9fdSchristos 			aprint_error_dev(fdc->sc_dev, "read-only medium\n");
20826238d5faSjnemeth 			error = EROFS;
20836238d5faSjnemeth 			goto failsilent;
20846238d5faSjnemeth 		}
20856238d5faSjnemeth 		/* try again */
20866238d5faSjnemeth 		fdc->sc_state =
20876238d5faSjnemeth 			(fdc->sc_flags & FDC_EIS) ? DOIO : DOSEEK;
20886238d5faSjnemeth 		break;
20896238d5faSjnemeth 
20906238d5faSjnemeth 	case 1: case 2: case 3:
20916238d5faSjnemeth 		/* didn't work; try recalibrating */
20926238d5faSjnemeth 		fdc->sc_state = DORECAL;
20936238d5faSjnemeth 		break;
20946238d5faSjnemeth 
20956238d5faSjnemeth 	case 4:
20966238d5faSjnemeth 		if (fdc->sc_nstat == 7 &&
20976238d5faSjnemeth 		    fdc->sc_status[0] == 0 &&
20986238d5faSjnemeth 		    fdc->sc_status[1] == 0 &&
20996238d5faSjnemeth 		    fdc->sc_status[2] == 0) {
21006238d5faSjnemeth 			/*
21016238d5faSjnemeth 			 * We've retried a few times and we've got
21026238d5faSjnemeth 			 * valid status and all three status bytes
21036238d5faSjnemeth 			 * are zero.  Assume this condition is the
21046238d5faSjnemeth 			 * result of no disk loaded into the drive.
21056238d5faSjnemeth 			 */
2106ae8fd9fdSchristos 			aprint_error_dev(fdc->sc_dev, "no medium?\n");
21076238d5faSjnemeth 			error = ENODEV;
21086238d5faSjnemeth 			goto failsilent;
21096238d5faSjnemeth 		}
21106238d5faSjnemeth 
21116238d5faSjnemeth 		/* still no go; reset the bastard */
21126238d5faSjnemeth 		fdc->sc_state = DORESET;
21136238d5faSjnemeth 		break;
21146238d5faSjnemeth 
21156238d5faSjnemeth 	default:
21166238d5faSjnemeth 	fail:
21176238d5faSjnemeth 		if ((fd->sc_opts & FDOPT_SILENT) == 0) {
21186238d5faSjnemeth 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
21196238d5faSjnemeth 				fd->sc_skip / FD_BSIZE(fd),
21206238d5faSjnemeth 				(struct disklabel *)NULL);
21216238d5faSjnemeth 			printf("\n");
21226238d5faSjnemeth 			fdcstatus(fdc, "controller status");
21236238d5faSjnemeth 		}
21246238d5faSjnemeth 
21256238d5faSjnemeth 	failsilent:
21266238d5faSjnemeth 		bp->b_error = error;
21276238d5faSjnemeth 		fdfinish(fd, bp);
21286238d5faSjnemeth 	}
21296238d5faSjnemeth 	fdc->sc_errors++;
21306238d5faSjnemeth }
21316238d5faSjnemeth 
21326238d5faSjnemeth int
213353524e44Schristos fdioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
21346238d5faSjnemeth {
21356238d5faSjnemeth 	struct fd_softc *fd;
21366238d5faSjnemeth 	struct fdc_softc *fdc;
21376238d5faSjnemeth 	struct fdformat_parms *form_parms;
21386238d5faSjnemeth 	struct fdformat_cmd *form_cmd;
21396238d5faSjnemeth 	struct ne7_fd_formb *fd_formb;
21406238d5faSjnemeth 	int il[FD_MAX_NSEC + 1];
21416238d5faSjnemeth 	int unit;
21426238d5faSjnemeth 	int i, j;
21436238d5faSjnemeth 	int error;
21446238d5faSjnemeth 
21456238d5faSjnemeth 	unit = FDUNIT(dev);
21466238d5faSjnemeth 	if (unit >= fd_cd.cd_ndevs)
21476238d5faSjnemeth 		return ENXIO;
21486238d5faSjnemeth 
21492dea63feScegger 	fd = device_lookup_private(&fd_cd, FDUNIT(dev));
2150ae8fd9fdSchristos 	fdc = device_private(device_parent(fd->sc_dev));
21516238d5faSjnemeth 
21526238d5faSjnemeth 	switch (cmd) {
21536238d5faSjnemeth 	case DIOCGDINFO:
21546238d5faSjnemeth 		*(struct disklabel *)addr = *(fd->sc_dk.dk_label);
21556238d5faSjnemeth 		return 0;
21566238d5faSjnemeth 
21576238d5faSjnemeth 	case DIOCWLABEL:
21586238d5faSjnemeth 		if ((flag & FWRITE) == 0)
21596238d5faSjnemeth 			return EBADF;
21606238d5faSjnemeth 		/* XXX do something */
21616238d5faSjnemeth 		return 0;
21626238d5faSjnemeth 
21636238d5faSjnemeth 	case DIOCWDINFO:
21646238d5faSjnemeth 		if ((flag & FWRITE) == 0)
21656238d5faSjnemeth 			return EBADF;
21666238d5faSjnemeth 
21676238d5faSjnemeth 		error = setdisklabel(fd->sc_dk.dk_label,
21686238d5faSjnemeth 				    (struct disklabel *)addr, 0,
21696238d5faSjnemeth 				    fd->sc_dk.dk_cpulabel);
21706238d5faSjnemeth 		if (error)
21716238d5faSjnemeth 			return error;
21726238d5faSjnemeth 
21736238d5faSjnemeth 		error = writedisklabel(dev, fdstrategy,
21746238d5faSjnemeth 				       fd->sc_dk.dk_label,
21756238d5faSjnemeth 				       fd->sc_dk.dk_cpulabel);
21766238d5faSjnemeth 		return error;
21776238d5faSjnemeth 
21786238d5faSjnemeth 	case DIOCLOCK:
21796238d5faSjnemeth 		/*
21806238d5faSjnemeth 		 * Nothing to do here, really.
21816238d5faSjnemeth 		 */
21826238d5faSjnemeth 		return 0;
21836238d5faSjnemeth 
21846238d5faSjnemeth 	case DIOCEJECT:
21856238d5faSjnemeth 		if (*(int *)addr == 0) {
21866238d5faSjnemeth 			int part = DISKPART(dev);
21876238d5faSjnemeth 			/*
21886238d5faSjnemeth 			 * Don't force eject: check that we are the only
21896238d5faSjnemeth 			 * partition open. If so, unlock it.
21906238d5faSjnemeth 			 */
21916238d5faSjnemeth 			if ((fd->sc_dk.dk_openmask & ~(1 << part)) != 0 ||
21926238d5faSjnemeth 			    fd->sc_dk.dk_bopenmask + fd->sc_dk.dk_copenmask !=
21936238d5faSjnemeth 			    fd->sc_dk.dk_openmask) {
21946238d5faSjnemeth 				return EBUSY;
21956238d5faSjnemeth 			}
21966238d5faSjnemeth 		}
21976238d5faSjnemeth 		/* FALLTHROUGH */
21986238d5faSjnemeth 	case ODIOCEJECT:
21996238d5faSjnemeth 		if (fdc->sc_flags & FDC_NOEJECT)
22006238d5faSjnemeth 			return EINVAL;
22016238d5faSjnemeth 		fd_do_eject(fd);
22026238d5faSjnemeth 		return 0;
22036238d5faSjnemeth 
22046238d5faSjnemeth 	case FDIOCGETFORMAT:
22056238d5faSjnemeth 		form_parms = (struct fdformat_parms *)addr;
22066238d5faSjnemeth 		form_parms->fdformat_version = FDFORMAT_VERSION;
22076238d5faSjnemeth 		form_parms->nbps = 128 * (1 << fd->sc_type->secsize);
22086238d5faSjnemeth 		form_parms->ncyl = fd->sc_type->cylinders;
22096238d5faSjnemeth 		form_parms->nspt = fd->sc_type->sectrac;
22106238d5faSjnemeth 		form_parms->ntrk = fd->sc_type->heads;
22116238d5faSjnemeth 		form_parms->stepspercyl = fd->sc_type->step;
22126238d5faSjnemeth 		form_parms->gaplen = fd->sc_type->gap2;
22136238d5faSjnemeth 		form_parms->fillbyte = fd->sc_type->fillbyte;
22146238d5faSjnemeth 		form_parms->interleave = fd->sc_type->interleave;
22156238d5faSjnemeth 		switch (fd->sc_type->rate) {
22166238d5faSjnemeth 		case FDC_500KBPS:
22176238d5faSjnemeth 			form_parms->xfer_rate = 500 * 1024;
22186238d5faSjnemeth 			break;
22196238d5faSjnemeth 		case FDC_300KBPS:
22206238d5faSjnemeth 			form_parms->xfer_rate = 300 * 1024;
22216238d5faSjnemeth 			break;
22226238d5faSjnemeth 		case FDC_250KBPS:
22236238d5faSjnemeth 			form_parms->xfer_rate = 250 * 1024;
22246238d5faSjnemeth 			break;
22256238d5faSjnemeth 		default:
22266238d5faSjnemeth 			return EINVAL;
22276238d5faSjnemeth 		}
22286238d5faSjnemeth 		return 0;
22296238d5faSjnemeth 
22306238d5faSjnemeth 	case FDIOCSETFORMAT:
22316238d5faSjnemeth 		if ((flag & FWRITE) == 0)
22326238d5faSjnemeth 			return EBADF;	/* must be opened for writing */
22336238d5faSjnemeth 
22346238d5faSjnemeth 		form_parms = (struct fdformat_parms *)addr;
22356238d5faSjnemeth 		if (form_parms->fdformat_version != FDFORMAT_VERSION)
22366238d5faSjnemeth 			return EINVAL;/* wrong version of formatting prog */
22376238d5faSjnemeth 
22386238d5faSjnemeth 		i = form_parms->nbps >> 7;
22396238d5faSjnemeth 		if ((form_parms->nbps & 0x7f) || ffs(i) == 0 ||
22406238d5faSjnemeth 		    i & ~(1 << (ffs(i)-1)))
22416238d5faSjnemeth 			/* not a power-of-two multiple of 128 */
22426238d5faSjnemeth 			return EINVAL;
22436238d5faSjnemeth 
22446238d5faSjnemeth 		switch (form_parms->xfer_rate) {
22456238d5faSjnemeth 		case 500 * 1024:
22466238d5faSjnemeth 			fd->sc_type->rate = FDC_500KBPS;
22476238d5faSjnemeth 			break;
22486238d5faSjnemeth 		case 300 * 1024:
22496238d5faSjnemeth 			fd->sc_type->rate = FDC_300KBPS;
22506238d5faSjnemeth 			break;
22516238d5faSjnemeth 		case 250 * 1024:
22526238d5faSjnemeth 			fd->sc_type->rate = FDC_250KBPS;
22536238d5faSjnemeth 			break;
22546238d5faSjnemeth 		default:
22556238d5faSjnemeth 			return EINVAL;
22566238d5faSjnemeth 		}
22576238d5faSjnemeth 
22586238d5faSjnemeth 		if (form_parms->nspt > FD_MAX_NSEC ||
22596238d5faSjnemeth 		    form_parms->fillbyte > 0xff ||
22606238d5faSjnemeth 		    form_parms->interleave > 0xff)
22616238d5faSjnemeth 			return EINVAL;
22626238d5faSjnemeth 		fd->sc_type->sectrac = form_parms->nspt;
22636238d5faSjnemeth 		if (form_parms->ntrk != 2 && form_parms->ntrk != 1)
22646238d5faSjnemeth 			return EINVAL;
22656238d5faSjnemeth 		fd->sc_type->heads = form_parms->ntrk;
22666238d5faSjnemeth 		fd->sc_type->seccyl = form_parms->nspt * form_parms->ntrk;
22676238d5faSjnemeth 		fd->sc_type->secsize = ffs(i)-1;
22686238d5faSjnemeth 		fd->sc_type->gap2 = form_parms->gaplen;
22696238d5faSjnemeth 		fd->sc_type->cylinders = form_parms->ncyl;
22706238d5faSjnemeth 		fd->sc_type->size = fd->sc_type->seccyl * form_parms->ncyl *
22716238d5faSjnemeth 			form_parms->nbps / DEV_BSIZE;
22726238d5faSjnemeth 		fd->sc_type->step = form_parms->stepspercyl;
22736238d5faSjnemeth 		fd->sc_type->fillbyte = form_parms->fillbyte;
22746238d5faSjnemeth 		fd->sc_type->interleave = form_parms->interleave;
22756238d5faSjnemeth 		return 0;
22766238d5faSjnemeth 
22776238d5faSjnemeth 	case FDIOCFORMAT_TRACK:
22786238d5faSjnemeth 		if((flag & FWRITE) == 0)
22796238d5faSjnemeth 			/* must be opened for writing */
22806238d5faSjnemeth 			return EBADF;
22816238d5faSjnemeth 		form_cmd = (struct fdformat_cmd *)addr;
22826238d5faSjnemeth 		if (form_cmd->formatcmd_version != FDFORMAT_VERSION)
22836238d5faSjnemeth 			/* wrong version of formatting prog */
22846238d5faSjnemeth 			return EINVAL;
22856238d5faSjnemeth 
22866238d5faSjnemeth 		if (form_cmd->head >= fd->sc_type->heads ||
22876238d5faSjnemeth 		    form_cmd->cylinder >= fd->sc_type->cylinders) {
22886238d5faSjnemeth 			return EINVAL;
22896238d5faSjnemeth 		}
22906238d5faSjnemeth 
2291fc256c4aSthorpej 		fd_formb = kmem_alloc(sizeof(*fd_formb), KM_SLEEP);
22926238d5faSjnemeth 		fd_formb->head = form_cmd->head;
22936238d5faSjnemeth 		fd_formb->cyl = form_cmd->cylinder;
22946238d5faSjnemeth 		fd_formb->transfer_rate = fd->sc_type->rate;
22956238d5faSjnemeth 		fd_formb->fd_formb_secshift = fd->sc_type->secsize;
22966238d5faSjnemeth 		fd_formb->fd_formb_nsecs = fd->sc_type->sectrac;
22976238d5faSjnemeth 		fd_formb->fd_formb_gaplen = fd->sc_type->gap2;
22986238d5faSjnemeth 		fd_formb->fd_formb_fillbyte = fd->sc_type->fillbyte;
22996238d5faSjnemeth 
2300c363a9cbScegger 		memset(il, 0, sizeof il);
23016238d5faSjnemeth 		for (j = 0, i = 1; i <= fd_formb->fd_formb_nsecs; i++) {
23026238d5faSjnemeth 			while (il[(j % fd_formb->fd_formb_nsecs) + 1])
23036238d5faSjnemeth 				j++;
23046238d5faSjnemeth 			il[(j % fd_formb->fd_formb_nsecs) + 1] = i;
23056238d5faSjnemeth 			j += fd->sc_type->interleave;
23066238d5faSjnemeth 		}
23076238d5faSjnemeth 		for (i = 0; i < fd_formb->fd_formb_nsecs; i++) {
23086238d5faSjnemeth 			fd_formb->fd_formb_cylno(i) = form_cmd->cylinder;
23096238d5faSjnemeth 			fd_formb->fd_formb_headno(i) = form_cmd->head;
23106238d5faSjnemeth 			fd_formb->fd_formb_secno(i) = il[i + 1];
23116238d5faSjnemeth 			fd_formb->fd_formb_secsize(i) = fd->sc_type->secsize;
23126238d5faSjnemeth 		}
23136238d5faSjnemeth 
23146238d5faSjnemeth 		error = fdformat(dev, fd_formb, l->l_proc);
2315fc256c4aSthorpej 		kmem_free(fd_formb, sizeof(*fd_formb));
23166238d5faSjnemeth 		return error;
23176238d5faSjnemeth 
23186238d5faSjnemeth 	case FDIOCGETOPTS:		/* get drive options */
23196238d5faSjnemeth 		*(int *)addr = fd->sc_opts;
23206238d5faSjnemeth 		return 0;
23216238d5faSjnemeth 
23226238d5faSjnemeth 	case FDIOCSETOPTS:		/* set drive options */
23236238d5faSjnemeth 		fd->sc_opts = *(int *)addr;
23246238d5faSjnemeth 		return 0;
23256238d5faSjnemeth 
23266238d5faSjnemeth #ifdef FD_DEBUG
23276238d5faSjnemeth 	case _IO('f', 100):
23286238d5faSjnemeth 		fdc_wrfifo(fdc, NE7CMD_DUMPREG);
23296238d5faSjnemeth 		fdcresult(fdc);
23306238d5faSjnemeth 		printf("fdc: dumpreg(%d regs): <", fdc->sc_nstat);
23316238d5faSjnemeth 		for (i = 0; i < fdc->sc_nstat; i++)
23326238d5faSjnemeth 			printf(" 0x%x", fdc->sc_status[i]);
23336238d5faSjnemeth 		printf(">\n");
23346238d5faSjnemeth 		return 0;
23356238d5faSjnemeth 
23366238d5faSjnemeth 	case _IOW('f', 101, int):
23376238d5faSjnemeth 		fdc->sc_cfg &= ~CFG_THRHLD_MASK;
23386238d5faSjnemeth 		fdc->sc_cfg |= (*(int *)addr & CFG_THRHLD_MASK);
23396238d5faSjnemeth 		fdconf(fdc);
23406238d5faSjnemeth 		return 0;
23416238d5faSjnemeth 
23426238d5faSjnemeth 	case _IO('f', 102):
23436238d5faSjnemeth 		fdc_wrfifo(fdc, NE7CMD_SENSEI);
23446238d5faSjnemeth 		fdcresult(fdc);
23456238d5faSjnemeth 		printf("fdc: sensei(%d regs): <", fdc->sc_nstat);
23466238d5faSjnemeth 		for (i=0; i< fdc->sc_nstat; i++)
23476238d5faSjnemeth 			printf(" 0x%x", fdc->sc_status[i]);
23486238d5faSjnemeth 		printf(">\n");
23496238d5faSjnemeth 		return 0;
23506238d5faSjnemeth #endif
23516238d5faSjnemeth 	default:
23526238d5faSjnemeth 		return ENOTTY;
23536238d5faSjnemeth 	}
23546238d5faSjnemeth 
23556238d5faSjnemeth #ifdef DIAGNOSTIC
23566238d5faSjnemeth 	panic("fdioctl: impossible");
23576238d5faSjnemeth #endif
23586238d5faSjnemeth }
23596238d5faSjnemeth 
23606238d5faSjnemeth int
23616238d5faSjnemeth fdformat(dev_t dev, struct ne7_fd_formb *finfo, struct proc *p)
23626238d5faSjnemeth {
23636238d5faSjnemeth 	int rv = 0;
23642dea63feScegger 	struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(dev));
23656238d5faSjnemeth 	struct fd_type *type = fd->sc_type;
23666238d5faSjnemeth 	struct buf *bp;
23676238d5faSjnemeth 
23686238d5faSjnemeth 	/* set up a buffer header for fdstrategy() */
23694a780c9aSad 	bp = getiobuf(NULL, false);
23706238d5faSjnemeth 	if (bp == NULL)
23716238d5faSjnemeth 		return ENOBUFS;
23726238d5faSjnemeth 
23736238d5faSjnemeth 	bp->b_vp = NULL;
23744a780c9aSad 	bp->b_cflags = BC_BUSY;
23754a780c9aSad 	bp->b_flags = B_PHYS | B_FORMAT;
23766238d5faSjnemeth 	bp->b_proc = p;
23776238d5faSjnemeth 	bp->b_dev = dev;
23786238d5faSjnemeth 
23796238d5faSjnemeth 	/*
23806238d5faSjnemeth 	 * Calculate a fake blkno, so fdstrategy() would initiate a
23816238d5faSjnemeth 	 * seek to the requested cylinder.
23826238d5faSjnemeth 	 */
23836238d5faSjnemeth 	bp->b_blkno = ((finfo->cyl * (type->sectrac * type->heads)
23846238d5faSjnemeth 		       + finfo->head * type->sectrac) * FD_BSIZE(fd))
23856238d5faSjnemeth 		      / DEV_BSIZE;
23866238d5faSjnemeth 
23876238d5faSjnemeth 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
238853524e44Schristos 	bp->b_data = (void *)finfo;
23896238d5faSjnemeth 
23906238d5faSjnemeth #ifdef FD_DEBUG
23916238d5faSjnemeth 	if (fdc_debug) {
23926238d5faSjnemeth 		int i;
23936238d5faSjnemeth 
23946238d5faSjnemeth 		printf("fdformat: blkno 0x%llx count %d\n",
23956238d5faSjnemeth 			(unsigned long long)bp->b_blkno, bp->b_bcount);
23966238d5faSjnemeth 
23976238d5faSjnemeth 		printf("\tcyl:\t%d\n", finfo->cyl);
23986238d5faSjnemeth 		printf("\thead:\t%d\n", finfo->head);
23996238d5faSjnemeth 		printf("\tnsecs:\t%d\n", finfo->fd_formb_nsecs);
24006238d5faSjnemeth 		printf("\tsshft:\t%d\n", finfo->fd_formb_secshift);
24016238d5faSjnemeth 		printf("\tgaplen:\t%d\n", finfo->fd_formb_gaplen);
24026238d5faSjnemeth 		printf("\ttrack data:");
24036238d5faSjnemeth 		for (i = 0; i < finfo->fd_formb_nsecs; i++) {
24046238d5faSjnemeth 			printf(" [c%d h%d s%d]",
24056238d5faSjnemeth 					finfo->fd_formb_cylno(i),
24066238d5faSjnemeth 					finfo->fd_formb_headno(i),
24076238d5faSjnemeth 					finfo->fd_formb_secno(i) );
24086238d5faSjnemeth 			if (finfo->fd_formb_secsize(i) != 2)
24096238d5faSjnemeth 				printf("<sz:%d>", finfo->fd_formb_secsize(i));
24106238d5faSjnemeth 		}
24116238d5faSjnemeth 		printf("\n");
24126238d5faSjnemeth 	}
24136238d5faSjnemeth #endif
24146238d5faSjnemeth 
24156238d5faSjnemeth 	/* now do the format */
24166238d5faSjnemeth 	fdstrategy(bp);
24176238d5faSjnemeth 
24186238d5faSjnemeth 	/* ...and wait for it to complete */
24196238d5faSjnemeth 	rv = biowait(bp);
24206238d5faSjnemeth 	putiobuf(bp);
24216238d5faSjnemeth 	return rv;
24226238d5faSjnemeth }
24236238d5faSjnemeth 
24246238d5faSjnemeth void
24256238d5faSjnemeth fdgetdisklabel(dev_t dev)
24266238d5faSjnemeth {
24272dea63feScegger 	int i;
24282dea63feScegger 	struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(dev));
24296238d5faSjnemeth 	struct disklabel *lp = fd->sc_dk.dk_label;
24306238d5faSjnemeth 	struct cpu_disklabel *clp = fd->sc_dk.dk_cpulabel;
24316238d5faSjnemeth 
2432c363a9cbScegger 	memset(lp, 0, sizeof(struct disklabel));
24330e7c8bd0Sjnemeth 	memset(clp, 0, sizeof(struct cpu_disklabel));
24346238d5faSjnemeth 
2435c182898bSchristos 	lp->d_type = DKTYPE_FLOPPY;
24366238d5faSjnemeth 	lp->d_secsize = FD_BSIZE(fd);
24376238d5faSjnemeth 	lp->d_secpercyl = fd->sc_type->seccyl;
24386238d5faSjnemeth 	lp->d_nsectors = fd->sc_type->sectrac;
24396238d5faSjnemeth 	lp->d_ncylinders = fd->sc_type->cylinders;
24406238d5faSjnemeth 	lp->d_ntracks = fd->sc_type->heads;	/* Go figure... */
24416238d5faSjnemeth 	lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
24426238d5faSjnemeth 	lp->d_rpm = 300;	/* XXX like it matters... */
24436238d5faSjnemeth 
24446238d5faSjnemeth 	strncpy(lp->d_typename, "floppy disk", sizeof(lp->d_typename));
24456238d5faSjnemeth 	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
24466238d5faSjnemeth 	lp->d_interleave = 1;
24476238d5faSjnemeth 	lp->d_flags = D_REMOVABLE;
24486238d5faSjnemeth 
24496238d5faSjnemeth 	lp->d_partitions[RAW_PART].p_offset = 0;
24506238d5faSjnemeth 	lp->d_partitions[RAW_PART].p_size = lp->d_secpercyl * lp->d_ncylinders;
24516238d5faSjnemeth 	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
24526238d5faSjnemeth 	lp->d_npartitions = RAW_PART + 1;
24536238d5faSjnemeth 
24546238d5faSjnemeth 	lp->d_magic = DISKMAGIC;
24556238d5faSjnemeth 	lp->d_magic2 = DISKMAGIC;
24566238d5faSjnemeth 	lp->d_checksum = dkcksum(lp);
24576238d5faSjnemeth 
24586238d5faSjnemeth 	/*
24596238d5faSjnemeth 	 * Call the generic disklabel extraction routine.  If there's
24606238d5faSjnemeth 	 * not a label there, fake it.
24616238d5faSjnemeth 	 */
24626238d5faSjnemeth 	if (readdisklabel(dev, fdstrategy, lp, clp) != NULL) {
24636238d5faSjnemeth 		strncpy(lp->d_packname, "default label",
24646238d5faSjnemeth 		    sizeof(lp->d_packname));
24656238d5faSjnemeth 		/*
24666238d5faSjnemeth 		 * Reset the partition info; it might have gotten
24676238d5faSjnemeth 		 * trashed in readdisklabel().
24686238d5faSjnemeth 		 *
24696238d5faSjnemeth 		 * XXX Why do we have to do this?  readdisklabel()
24706238d5faSjnemeth 		 * should be safe...
24716238d5faSjnemeth 		 */
24726238d5faSjnemeth 		for (i = 0; i < MAXPARTITIONS; ++i) {
24736238d5faSjnemeth 			lp->d_partitions[i].p_offset = 0;
24746238d5faSjnemeth 			if (i == RAW_PART) {
24756238d5faSjnemeth 				lp->d_partitions[i].p_size =
24766238d5faSjnemeth 				    lp->d_secpercyl * lp->d_ncylinders;
24776238d5faSjnemeth 				lp->d_partitions[i].p_fstype = FS_BSDFFS;
24786238d5faSjnemeth 			} else {
24796238d5faSjnemeth 				lp->d_partitions[i].p_size = 0;
24806238d5faSjnemeth 				lp->d_partitions[i].p_fstype = FS_UNUSED;
24816238d5faSjnemeth 			}
24826238d5faSjnemeth 		}
24836238d5faSjnemeth 		lp->d_npartitions = RAW_PART + 1;
24846238d5faSjnemeth 	}
24856238d5faSjnemeth }
24866238d5faSjnemeth 
24876238d5faSjnemeth void
24886238d5faSjnemeth fd_do_eject(struct fd_softc *fd)
24896238d5faSjnemeth {
2490ae8fd9fdSchristos 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
24916238d5faSjnemeth 
2492d34b8a70Sjnemeth #ifdef SUN4
24936238d5faSjnemeth 	if (CPU_ISSUN4C) {
24946238d5faSjnemeth 		auxregbisc(AUXIO4C_FDS, AUXIO4C_FEJ);
24956238d5faSjnemeth 		delay(10);
24966238d5faSjnemeth 		auxregbisc(AUXIO4C_FEJ, AUXIO4C_FDS);
24976238d5faSjnemeth 		return;
24986238d5faSjnemeth 	}
24996238d5faSjnemeth 	if (CPU_ISSUN4M && (fdc->sc_flags & FDC_82077) != 0) {
25006238d5faSjnemeth #endif
25016238d5faSjnemeth 		bus_space_tag_t t = fdc->sc_bustag;
25026238d5faSjnemeth 		bus_space_handle_t h = fdc->sc_handle;
25036238d5faSjnemeth 		uint8_t dor = FDO_FRST | FDO_FDMAEN | FDO_MOEN(0);
25046238d5faSjnemeth 
25056238d5faSjnemeth 		bus_space_write_1(t, h, fdc->sc_reg_dor, dor | FDO_EJ);
25066238d5faSjnemeth 		delay(10);
25076238d5faSjnemeth 		bus_space_write_1(t, h, fdc->sc_reg_dor, FDO_FRST | FDO_DS);
25086238d5faSjnemeth 		return;
2509d34b8a70Sjnemeth #ifdef SUN4
25106238d5faSjnemeth 	}
25116238d5faSjnemeth #endif
25126238d5faSjnemeth }
25136238d5faSjnemeth 
25146238d5faSjnemeth /* ARGSUSED */
25156238d5faSjnemeth void
2516ae8fd9fdSchristos fd_mountroot_hook(device_t dev)
25176238d5faSjnemeth {
25186238d5faSjnemeth 	int c;
25196238d5faSjnemeth 
25206238d5faSjnemeth 	fd_do_eject((struct fd_softc *)dev);
25216238d5faSjnemeth 	printf("Insert filesystem floppy and press return.");
25226238d5faSjnemeth 	for (;;) {
25236238d5faSjnemeth 		c = cngetc();
25246238d5faSjnemeth 		if ((c == '\r') || (c == '\n')) {
25256238d5faSjnemeth 			printf("\n");
25266238d5faSjnemeth 			break;
25276238d5faSjnemeth 		}
25286238d5faSjnemeth 	}
25296238d5faSjnemeth }
25306238d5faSjnemeth 
25316238d5faSjnemeth #ifdef MEMORY_DISK_HOOKS
25326238d5faSjnemeth 
25336238d5faSjnemeth #define FDMICROROOTSIZE ((2*18*80) << DEV_BSHIFT)
25346238d5faSjnemeth 
25356238d5faSjnemeth int
253653524e44Schristos fd_read_md_image(size_t	*sizep, void **addrp)
25376238d5faSjnemeth {
25386238d5faSjnemeth 	struct buf buf, *bp = &buf;
25396238d5faSjnemeth 	dev_t dev;
25406238d5faSjnemeth 	off_t offset;
2541b9b4ca0bSdogcow 	char *addr;
25426238d5faSjnemeth 
25436238d5faSjnemeth 	dev = makedev(54,0);	/* XXX */
25446238d5faSjnemeth 
2545fc256c4aSthorpej 	addr = kmem_alloc(FDMICROROOTSIZE, KM_SLEEP);
25466238d5faSjnemeth 	*addrp = addr;
25476238d5faSjnemeth 
25486238d5faSjnemeth 	if (fdopen(dev, 0, S_IFCHR, NULL))
25496238d5faSjnemeth 		panic("fd: mountroot: fdopen");
25506238d5faSjnemeth 
25516238d5faSjnemeth 	offset = 0;
25526238d5faSjnemeth 
25536238d5faSjnemeth 	for (;;) {
25546238d5faSjnemeth 		bp->b_dev = dev;
25556238d5faSjnemeth 		bp->b_error = 0;
25566238d5faSjnemeth 		bp->b_resid = 0;
25576238d5faSjnemeth 		bp->b_proc = NULL;
25584a780c9aSad 		bp->b_cflags = BC_BUSY;
25594a780c9aSad 		bp->b_flags = B_PHYS | B_RAW | B_READ;
25606238d5faSjnemeth 		bp->b_blkno = btodb(offset);
25616238d5faSjnemeth 		bp->b_bcount = DEV_BSIZE;
25626238d5faSjnemeth 		bp->b_data = addr;
25636238d5faSjnemeth 		fdstrategy(bp);
25644a780c9aSad 		biowait(bp);
25656238d5faSjnemeth 		if (bp->b_error)
25666238d5faSjnemeth 			panic("fd: mountroot: fdread error %d", bp->b_error);
25676238d5faSjnemeth 
25686238d5faSjnemeth 		if (bp->b_resid != 0)
25696238d5faSjnemeth 			break;
25706238d5faSjnemeth 
25716238d5faSjnemeth 		addr += DEV_BSIZE;
25726238d5faSjnemeth 		offset += DEV_BSIZE;
25736238d5faSjnemeth 		if (offset + DEV_BSIZE > FDMICROROOTSIZE)
25746238d5faSjnemeth 			break;
25756238d5faSjnemeth 	}
25766238d5faSjnemeth 	(void)fdclose(dev, 0, S_IFCHR, NULL);
25776238d5faSjnemeth 	*sizep = offset;
25785b52b73cSdogcow 	fd_do_eject(device_lookup_private(&fd_cd, FDUNIT(dev)));
25796238d5faSjnemeth 	return 0;
25806238d5faSjnemeth }
25816238d5faSjnemeth #endif /* MEMORY_DISK_HOOKS */
25823d612181Sjnemeth 
25833d612181Sjnemeth static void
25847b845fa9Schristos fd_set_geometry(struct fd_softc *fd)
25853d612181Sjnemeth {
25867b845fa9Schristos 	const struct fd_type *fdt;
25873d612181Sjnemeth 
25887b845fa9Schristos 	fdt = fd->sc_type;
25897b845fa9Schristos 	if (fdt == NULL) {
25903d612181Sjnemeth 		fdt = fd->sc_deftype;
25917b845fa9Schristos 		if (fdt == NULL)
25927b845fa9Schristos 			return;
25933d612181Sjnemeth 	}
25943d612181Sjnemeth 
2595cf7199d0Smartin 	struct disk_geom *dg = &fd->sc_dk.dk_geom;
25963d612181Sjnemeth 
25977b845fa9Schristos 	memset(dg, 0, sizeof(*dg));
25987b845fa9Schristos 	dg->dg_secperunit = fdt->size;
25997b845fa9Schristos 	dg->dg_nsectors = fdt->sectrac;
26007b845fa9Schristos 	switch (fdt->secsize) {
26017b845fa9Schristos 	case 2:
26027b845fa9Schristos 		dg->dg_secsize = 512;
26037b845fa9Schristos 		break;
26047b845fa9Schristos 	case 3:
26057b845fa9Schristos 		dg->dg_secsize = 1024;
26067b845fa9Schristos 		break;
26077b845fa9Schristos 	default:
26087b845fa9Schristos 		break;
26097b845fa9Schristos 	}
26107b845fa9Schristos 	dg->dg_ntracks = fdt->heads;
2611cf7199d0Smartin 	dg->dg_ncylinders = fdt->cylinders;
2612cf7199d0Smartin 	disk_set_info(fd->sc_dev, &fd->sc_dk, NULL);
26133d612181Sjnemeth }
2614