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