diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2005-03-09 18:41:51 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2005-03-09 18:41:51 +0000 |
commit | 4eaca50d846d95e18b1f4b8bfac26b5043bd3eaf (patch) | |
tree | 4ae984f5c65bb10ccafd67d33474d3c954766c71 /sys/arch/sparc64/dev | |
parent | 1da8f26758611ca48a0d63bbc6ac0af99badda46 (diff) |
Preliminary support for the floppy drive on Ultrasparcs. Only tested on
SBus machines so far, although EBus attachment glue is provided but not enabled
by default.
Also, fdformat(8) does not work correctly yet, although reading and writing
is safe; this will hopefully be fixed in the near future.
ok deraadt@
Diffstat (limited to 'sys/arch/sparc64/dev')
-rw-r--r-- | sys/arch/sparc64/dev/auxio.c | 32 | ||||
-rw-r--r-- | sys/arch/sparc64/dev/auxioreg.h | 17 | ||||
-rw-r--r-- | sys/arch/sparc64/dev/auxiovar.h | 6 | ||||
-rw-r--r-- | sys/arch/sparc64/dev/fd.c | 2062 | ||||
-rw-r--r-- | sys/arch/sparc64/dev/fdreg.h | 93 | ||||
-rw-r--r-- | sys/arch/sparc64/dev/fdvar.h | 73 |
6 files changed, 2272 insertions, 11 deletions
diff --git a/sys/arch/sparc64/dev/auxio.c b/sys/arch/sparc64/dev/auxio.c index daf8ee3bbe5..e084b205452 100644 --- a/sys/arch/sparc64/dev/auxio.c +++ b/sys/arch/sparc64/dev/auxio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auxio.c,v 1.6 2004/10/01 18:18:49 jason Exp $ */ +/* $OpenBSD: auxio.c,v 1.7 2005/03/09 18:41:48 miod Exp $ */ /* $NetBSD: auxio.c,v 1.1 2000/04/15 03:08:13 mrg Exp $ */ /* @@ -213,3 +213,33 @@ auxio_led_blink(void *vsc, int on) splx(s); } + +int +auxio_fd_control(u_int32_t bits) +{ + struct auxio_softc *sc; + u_int32_t led; + + if (auxio_cd.cd_ndevs == 0) { + return ENXIO; + } + + /* + * XXX This does not handle > 1 auxio correctly. + * We'll assume the floppy drive is tied to first auxio found. + */ + sc = (struct auxio_softc *)auxio_cd.cd_devs[0]; + if (sc->sc_flags & AUXIO_EBUS) + led = letoh32(bus_space_read_4(sc->sc_tag, sc->sc_led, 0)); + else + led = bus_space_read_1(sc->sc_tag, sc->sc_led, 0); + + led = (led & ~AUXIO_LED_FLOPPY_MASK) | bits; + + if (sc->sc_flags & AUXIO_EBUS) + bus_space_write_4(sc->sc_tag, sc->sc_led, 0, htole32(led)); + else + bus_space_write_1(sc->sc_tag, sc->sc_led, 0, led); + + return 0; +} diff --git a/sys/arch/sparc64/dev/auxioreg.h b/sys/arch/sparc64/dev/auxioreg.h index d69858f6759..472140fa60b 100644 --- a/sys/arch/sparc64/dev/auxioreg.h +++ b/sys/arch/sparc64/dev/auxioreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auxioreg.h,v 1.3 2002/02/01 21:48:23 jason Exp $ */ +/* $OpenBSD: auxioreg.h,v 1.4 2005/03/09 18:41:48 miod Exp $ */ /* $NetBSD: auxioreg.h,v 1.3 2000/04/15 03:08:13 mrg Exp $ */ /* @@ -32,7 +32,7 @@ /* * The AUXIO registers; their offset in the Ebus2 address space, plus the * bits for each register. Note that the fdthree (FD), SUNW,CS4231 (AUDIO) - * and power (POWER) devices on the Ebus2 have their AUXIO regsiters mapped + * and power (POWER) devices on the Ebus2 have their AUXIO registers mapped * into their own "reg" properties, not the "auxio" device's "reg" properties. */ #define AUXIO_FD 0x00720000 @@ -47,14 +47,13 @@ #define AUXIO_POWER_COURTESY_OFF 0x1 #define AUXIO_LED 0x00726000 -#define AUXIO_LED_MB1 0xf0 /* must be set on write */ /* XXX: these may be useless on Ebus2 auxio! find out! */ -#define AUXIO_LED_FHD 0x20 /* floppy: high density (unreliable?)*/ -#define AUXIO_LED_FDC 0x10 /* floppy: diskette was changed */ -#define AUXIO_LED_FDS 0x08 /* floppy: drive select */ -#define AUXIO_LED_FTC 0x04 /* floppy: drives Terminal Count pin */ -#define AUXIO_LED_FEJ 0x02 /* floppy: eject disk */ -#define AUXIO_LED_LED 0x01 /* front panel LED */ +#define AUXIO_LED_FHD 0x20 /* floppy: high density (unreliable?)*/ +#define AUXIO_LED_LTE 0x08 /* link-test enable */ +#define AUXIO_LED_MMUX 0x04 /* Monitor/Mouse MUX; what is it? */ +#define AUXIO_LED_FTC 0x02 /* floppy: drives Terminal Count pin */ +#define AUXIO_LED_LED 0x01 /* front panel LED */ +#define AUXIO_LED_FLOPPY_MASK (AUXIO_LED_FTC) #define AUXIO_PCI 0x00728000 #define AUXIO_PCI_SLOT0 0x0 /* two bits each */ diff --git a/sys/arch/sparc64/dev/auxiovar.h b/sys/arch/sparc64/dev/auxiovar.h index 2bfc1973f53..91633f262db 100644 --- a/sys/arch/sparc64/dev/auxiovar.h +++ b/sys/arch/sparc64/dev/auxiovar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auxiovar.h,v 1.6 2004/10/01 18:18:49 jason Exp $ */ +/* $OpenBSD: auxiovar.h,v 1.7 2005/03/09 18:41:48 miod Exp $ */ /* $NetBSD: auxiovar.h,v 1.4 2000/04/15 03:08:13 mrg Exp $ */ /* @@ -53,3 +53,7 @@ struct auxio_softc { #define AUXIO_SBUS 0x4 struct blink_led sc_blink; }; + +#ifndef _LOCORE +int auxio_fd_control(u_int32_t); +#endif diff --git a/sys/arch/sparc64/dev/fd.c b/sys/arch/sparc64/dev/fd.c new file mode 100644 index 00000000000..e3ea4d5503e --- /dev/null +++ b/sys/arch/sparc64/dev/fd.c @@ -0,0 +1,2062 @@ +/* $OpenBSD: fd.c,v 1.1 2005/03/09 18:41:49 miod Exp $ */ +/* $NetBSD: fd.c,v 1.112 2003/08/07 16:29:35 agc Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Kranenburg. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Don Ahn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fd.c 7.4 (Berkeley) 5/25/91 + */ + +/*- + * Copyright (c) 1993, 1994, 1995 Charles M. Hannum. + * Copyright (c) 1995 Paul Kranenburg. + * + * This code is derived from software contributed to Berkeley by + * Don Ahn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fd.c 7.4 (Berkeley) 5/25/91 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/timeout.h> +#include <sys/kernel.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/disklabel.h> +#include <sys/disk.h> +#include <sys/buf.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/uio.h> +#include <sys/mtio.h> +#include <sys/stat.h> +#include <sys/syslog.h> +#include <sys/queue.h> + +#include <dev/cons.h> + +#include <uvm/uvm_extern.h> + +#include <machine/autoconf.h> +#include <machine/conf.h> +#include <machine/intr.h> +#include <machine/ioctl_fd.h> + +#include <sparc64/dev/auxioreg.h> +#include <sparc64/dev/auxiovar.h> +#include <sparc64/dev/ebusreg.h> +#include <sparc64/dev/ebusvar.h> +#include <sparc64/dev/fdreg.h> +#include <sparc64/dev/fdvar.h> + +#define FDUNIT(dev) ((minor(dev) / MAXPARTITIONS) / 8) +#define FDTYPE(dev) ((minor(dev) / MAXPARTITIONS) % 8) + +#define FTC_FLIP \ + do { \ + auxio_fd_control(AUXIO_LED_FTC); \ + auxio_fd_control(0); \ + } while (0) + +/* XXX misuse a flag to identify format operation */ +#define B_FORMAT B_XXX + +#define b_cylin b_resid + +#ifdef FD_DEBUG +int fdc_debug = 0; +#endif + +enum fdc_state { + DEVIDLE = 0, + MOTORWAIT, /* 1 */ + DOSEEK, /* 2 */ + SEEKWAIT, /* 3 */ + SEEKTIMEDOUT, /* 4 */ + SEEKCOMPLETE, /* 5 */ + DOIO, /* 6 */ + IOCOMPLETE, /* 7 */ + IOTIMEDOUT, /* 8 */ + IOCLEANUPWAIT, /* 9 */ + IOCLEANUPTIMEDOUT,/*10 */ + DORESET, /* 11 */ + RESETCOMPLETE, /* 12 */ + RESETTIMEDOUT, /* 13 */ + DORECAL, /* 14 */ + RECALWAIT, /* 15 */ + RECALTIMEDOUT, /* 16 */ + RECALCOMPLETE, /* 17 */ + DODSKCHG, /* 18 */ + DSKCHGWAIT, /* 19 */ + DSKCHGTIMEDOUT, /* 20 */ +}; + +/* software state, per controller */ +struct fdc_softc { + struct device sc_dev; /* boilerplate */ + bus_space_tag_t sc_bustag; + + struct timeout fdctimeout_to; + struct timeout fdcpseudointr_to; + + struct fd_softc *sc_fd[4]; /* pointers to children */ + TAILQ_HEAD(drivehead, fd_softc) sc_drives; + enum fdc_state sc_state; + int sc_flags; +#define FDC_EBUS 0x01 +#define FDC_NEEDHEADSETTLE 0x02 +#define FDC_EIS 0x04 +#define FDC_NEEDMOTORWAIT 0x08 +#define FDC_NOEJECT 0x10 + int sc_errors; /* number of retries so far */ + int sc_overruns; /* number of DMA overruns */ + int sc_cfg; /* current configuration */ + struct fdcio sc_io; +#define sc_handle sc_io.fdcio_handle +#define sc_itask sc_io.fdcio_itask +#define sc_istatus sc_io.fdcio_istatus +#define sc_data sc_io.fdcio_data +#define sc_tc sc_io.fdcio_tc +#define sc_nstat sc_io.fdcio_nstat +#define sc_status sc_io.fdcio_status + + void *sc_sicookie; /* softintr(9) cookie */ +}; + +/* controller driver configuration */ +int fdcmatch_sbus(struct device *, void *, void *); +int fdcmatch_ebus(struct device *, void *, void *); +void fdcattach_sbus(struct device *, struct device *, void *); +void fdcattach_ebus(struct device *, struct device *, void *); + +int fdcattach(struct fdc_softc *, int); + +struct cfattach fdc_sbus_ca = { + sizeof(struct fdc_softc), fdcmatch_sbus, fdcattach_sbus +}; + +struct cfattach fdc_ebus_ca = { + sizeof(struct fdc_softc), fdcmatch_ebus, fdcattach_ebus +}; + +struct cfdriver fdc_cd = { + NULL, "fdc", DV_DULL +}; + +__inline struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t); + +/* The order of entries in the following table is important -- BEWARE! */ +struct fd_type fd_types[] = { + { 18,2,36,2,0xff,0xcf,0x1b,0x54,80,2880,1,FDC_500KBPS, "1.44MB" }, /* 1.44MB diskette */ + { 9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, "720KB" }, /* 3.5" 720kB diskette */ + { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, "360KB/x" }, /* 360kB in 720kB drive */ + { 8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS, "1.2MB/NEC" } /* 1.2 MB japanese format */ +}; + +/* software state, per disk (with up to 4 disks per ctlr) */ +struct fd_softc { + struct device sc_dv; /* generic device info */ + struct disk sc_dk; /* generic disk info */ + + struct fd_type *sc_deftype; /* default type descriptor */ + struct fd_type *sc_type; /* current type descriptor */ + + struct timeout sc_motoron_to; + struct timeout sc_motoroff_to; + + daddr_t sc_blkno; /* starting block number */ + int sc_bcount; /* byte count left */ + int sc_skip; /* bytes already transferred */ + int sc_nblks; /* number of blocks currently transferring */ + int sc_nbytes; /* number of bytes currently transferring */ + + int sc_drive; /* physical unit number */ + int sc_flags; +#define FD_OPEN 0x01 /* it's open */ +#define FD_MOTOR 0x02 /* motor should be on */ +#define FD_MOTOR_WAIT 0x04 /* motor coming up */ + int sc_cylin; /* where we think the head is */ + int sc_opts; /* user-set options */ + + void *sc_sdhook; /* shutdownhook cookie */ + + TAILQ_ENTRY(fd_softc) sc_drivechain; + int sc_ops; /* I/O ops since last switch */ + struct buf sc_q; /* pending I/O requests */ +}; + +/* floppy driver configuration */ +int fdmatch(struct device *, void *, void *); +void fdattach(struct device *, struct device *, void *); + +struct cfattach fd_ca = { + sizeof(struct fd_softc), fdmatch, fdattach +}; + +struct cfdriver fd_cd = { + NULL, "fd", DV_DISK +}; + +void fdgetdisklabel(dev_t); +int fd_get_parms(struct fd_softc *); +void fdstrategy(struct buf *); +void fdstart(struct fd_softc *); +int fdprint(void *, const char *); + +struct dkdriver fddkdriver = { fdstrategy }; + +struct fd_type *fd_nvtotype(char *, int, int); +void fd_set_motor(struct fdc_softc *fdc); +void fd_motor_off(void *arg); +void fd_motor_on(void *arg); +int fdcresult(struct fdc_softc *fdc); +int fdc_wrfifo(struct fdc_softc *fdc, u_char x); +void fdcstart(struct fdc_softc *fdc); +void fdcstatus(struct fdc_softc *fdc, char *s); +void fdc_reset(struct fdc_softc *fdc); +int fdc_diskchange(struct fdc_softc *fdc); +void fdctimeout(void *arg); +void fdcpseudointr(void *arg); +int fdchwintr(void *); +void fdcswintr(void *); +int fdcstate(struct fdc_softc *); +void fdcretry(struct fdc_softc *fdc); +void fdfinish(struct fd_softc *fd, struct buf *bp); +int fdformat(dev_t, struct fd_formb *, struct proc *); +void fd_do_eject(struct fd_softc *); +static int fdconf(struct fdc_softc *); + +int +fdcmatch_sbus(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct sbus_attach_args *sa = aux; + + return (strcmp("SUNW,fdtwo", sa->sa_name) == 0); +} + +void +fdcattach_sbus(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct fdc_softc *fdc = (void *)self; + struct sbus_attach_args *sa = aux; + + if (sa->sa_nintr == 0) { + printf(": no interrupt line configured\n"); + return; + } + + if (auxio_fd_control(0) != 0) { + printf(": can't attach before auxio\n"); + return; + } + + fdc->sc_bustag = sa->sa_bustag; + + if (sbus_bus_map(sa->sa_bustag, + sa->sa_slot, sa->sa_offset, sa->sa_size, + BUS_SPACE_MAP_LINEAR, 0, &fdc->sc_handle) != 0) { + printf(": cannot map control registers\n"); + return; + } + + if (strcmp(getpropstring(sa->sa_node, "status"), "disabled") == 0) { + printf(": no drives attached\n"); + return; + } + + if (getproplen(sa->sa_node, "manual") >= 0) { + printf(": manual eject"); + fdc->sc_flags |= FDC_NOEJECT; + } + + if (fdcattach(fdc, sa->sa_pri) != 0) + bus_space_unmap(sa->sa_bustag, fdc->sc_handle, sa->sa_size); +} + +int +fdcmatch_ebus(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct ebus_attach_args *ea = aux; + + return (strcmp("fdthree", ea->ea_name) == 0); +} + +void +fdcattach_ebus(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct fdc_softc *fdc = (void *)self; + struct ebus_attach_args *ea = aux; + + if (ea->ea_nintrs == 0) { + printf(": no interrupt line configured\n"); + return; + } + + if (ea->ea_nregs < 3) { + printf(": expected 3 register, got only %d\n", + ea->ea_nregs); + return; + } + + if (ea->ea_nvaddrs > 0) { + if (bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0, + BUS_SPACE_MAP_PROMADDRESS, &fdc->sc_handle) != 0) { + printf(": can't map control registers\n"); + return; + } + fdc->sc_bustag = ea->ea_memtag; + } else if (ebus_bus_map(ea->ea_memtag, 0, + EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), + ea->ea_regs[0].size, 0, 0, &fdc->sc_handle) == 0) { + fdc->sc_bustag = ea->ea_memtag; + } else if (ebus_bus_map(ea->ea_iotag, 0, + EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), + ea->ea_regs[0].size, 0, 0, &fdc->sc_handle) == 0) { + fdc->sc_bustag = ea->ea_iotag; + } else { + printf(": can't map control registers\n"); + return; + } + + if (strcmp(getpropstring(ea->ea_node, "status"), "disabled") == 0) { + printf(": no drives attached\n"); + return; + } + + fdc->sc_flags |= FDC_EBUS; + + if (getproplen(ea->ea_node, "manual") >= 0) { + printf(": manual eject"); + fdc->sc_flags |= FDC_NOEJECT; + } + + /* XXX unmapping if it fails */ + fdcattach(fdc, ea->ea_intrs[0]); +} + +/* + * Arguments passed between fdcattach and fdprobe. + */ +struct fdc_attach_args { + int fa_drive; + struct fd_type *fa_deftype; +}; + +/* + * Print the location of a disk drive (called just before attaching the + * the drive). If `fdc' is not NULL, the drive was found but was not + * in the system config file; print the drive name as well. + * Return QUIET (config_find ignores this if the device was configured) to + * avoid printing `fdN not configured' messages. + */ +int +fdprint(aux, fdc) + void *aux; + const char *fdc; +{ + register struct fdc_attach_args *fa = aux; + + if (!fdc) + printf(" drive %d", fa->fa_drive); + return (QUIET); +} + +/* + * Configure several parameters and features on the FDC. + * Return 0 on success. + */ +static int +fdconf(fdc) + struct fdc_softc *fdc; +{ + int vroom; + + if (fdc_wrfifo(fdc, NE7CMD_DUMPREG) || fdcresult(fdc) != 10) + return (-1); + + /* + * dumpreg[7] seems to be a motor-off timeout; set it to whatever + * the PROM thinks is appropriate. + */ + if ((vroom = fdc->sc_status[7]) == 0) + vroom = 0x64; + + /* Configure controller to use FIFO and Implied Seek */ + if (fdc_wrfifo(fdc, NE7CMD_CFG) != 0) + return (-1); + if (fdc_wrfifo(fdc, vroom) != 0) + return (-1); + if (fdc_wrfifo(fdc, fdc->sc_cfg) != 0) + return (-1); + if (fdc_wrfifo(fdc, 0) != 0) /* PRETRK */ + return (-1); + /* No result phase for the NE7CMD_CFG command */ + + /* Lock configuration across soft resets. */ + if (fdc_wrfifo(fdc, NE7CMD_LOCK | CFG_LOCK) != 0 || + fdcresult(fdc) != 1) { +#ifdef FD_DEBUG + printf("fdconf: CFGLOCK failed"); +#endif + return (-1); + } + + return (0); +#if 0 + if (fdc_wrfifo(fdc, NE7CMD_VERSION) == 0 && + fdcresult(fdc) == 1 && fdc->sc_status[0] == 0x90) { + if (fdc_debug) + printf("[version cmd]"); + } +#endif +} + +int +fdcattach(fdc, pri) + struct fdc_softc *fdc; + int pri; +{ + struct fdc_attach_args fa; + int drive_attached; + + timeout_set(&fdc->fdctimeout_to, fdctimeout, fdc); + timeout_set(&fdc->fdcpseudointr_to, fdcpseudointr, fdc); + + fdc->sc_state = DEVIDLE; + fdc->sc_itask = FDC_ITASK_NONE; + fdc->sc_istatus = FDC_ISTATUS_NONE; + fdc->sc_flags |= FDC_EIS | FDC_NEEDMOTORWAIT; + TAILQ_INIT(&fdc->sc_drives); + + /* + * Configure controller; enable FIFO, Implied seek, no POLL mode?. + * Note: CFG_EFIFO is active-low, initial threshold value: 8 + */ + fdc->sc_cfg = CFG_EIS|/*CFG_EFIFO|*/CFG_POLL|(8 & CFG_THRHLD_MASK); + if (fdconf(fdc) != 0) { + printf("\n%s: no drives attached\n", fdc->sc_dev.dv_xname); + return (-1); + } + + if (bus_intr_establish(fdc->sc_bustag, pri, IPL_BIO, + 0, fdchwintr, fdc, fdc->sc_dev.dv_xname) == NULL) { + printf("\n%s: cannot register interrupt handler\n", + fdc->sc_dev.dv_xname); + return (-1); + } + + fdc->sc_sicookie = softintr_establish(IPL_BIO, fdcswintr, fdc); + if (fdc->sc_sicookie == NULL) { + printf("\n%s: cannot register soft interrupt handler\n", + fdc->sc_dev.dv_xname); + return (-1); + } + printf(" softpri %d\n", PIL_FDSOFT); + + /* physical limit: four drives per controller. */ + drive_attached = 0; + for (fa.fa_drive = 0; fa.fa_drive < 4; fa.fa_drive++) { + fa.fa_deftype = NULL; /* unknown */ + fa.fa_deftype = &fd_types[0]; /* XXX */ + if (config_found(&fdc->sc_dev, (void *)&fa, fdprint) != NULL) + drive_attached = 1; + } + + if (drive_attached == 0) { + /* XXX - dis-establish interrupts here */ + /* return (-1); */ + } + + return (0); +} + +int +fdmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct fdc_softc *fdc = (void *)parent; + bus_space_tag_t t = fdc->sc_bustag; + bus_space_handle_t h = fdc->sc_handle; + struct fdc_attach_args *fa = aux; + int drive = fa->fa_drive; + int n, ok; + + if (drive > 0) + /* XXX - for now, punt on more than one drive */ + return (0); + + /* select drive and turn on motor */ + bus_space_write_1(t, h, FDREG77_DOR, + drive | FDO_FRST | FDO_MOEN(drive)); + /* wait for motor to spin up */ + delay(250000); + + fdc->sc_nstat = 0; + fdc_wrfifo(fdc, NE7CMD_RECAL); + fdc_wrfifo(fdc, drive); + + /* Wait for recalibration to complete */ + for (n = 0; n < 10000; n++) { + u_int8_t v; + + delay(1000); + v = bus_space_read_1(t, h, FDREG77_MSR); + if ((v & (NE7_RQM|NE7_DIO|NE7_CB)) == NE7_RQM) { + /* wait a bit longer till device *really* is ready */ + delay(100000); + if (fdc_wrfifo(fdc, NE7CMD_SENSEI)) + break; + if (fdcresult(fdc) == 1 && fdc->sc_status[0] == 0x80) + /* + * Got `invalid command'; we interpret it + * to mean that the re-calibrate hasn't in + * fact finished yet + */ + continue; + break; + } + } + n = fdc->sc_nstat; +#ifdef FD_DEBUG + if (fdc_debug) { + int i; + printf("fdprobe: %d stati:", n); + for (i = 0; i < n; i++) + printf(" 0x%x", fdc->sc_status[i]); + printf("\n"); + } +#endif + ok = (n == 2 && (fdc->sc_status[0] & 0xf8) == 0x20) ? 1 : 0; + + /* deselect drive and turn motor off */ + bus_space_write_1(t, h, FDREG77_DOR, FDO_FRST | FDO_DS); + + return (ok); +} + +/* + * Controller is working, and drive responded. Attach it. + */ +void +fdattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct fdc_softc *fdc = (void *)parent; + struct fd_softc *fd = (void *)self; + struct fdc_attach_args *fa = aux; + struct fd_type *type = fa->fa_deftype; + int drive = fa->fa_drive; + + timeout_set(&fd->sc_motoron_to, fd_motor_on, fd); + timeout_set(&fd->sc_motoroff_to, fd_motor_off, fd); + + /* XXX Allow `flags' to override device type? */ + + if (type) + printf(": %s %d cyl, %d head, %d sec\n", type->name, + type->tracks, type->heads, type->sectrac); + else + printf(": density unknown\n"); + + fd->sc_cylin = -1; + fd->sc_drive = drive; + fd->sc_deftype = type; + fdc->sc_fd[drive] = fd; + + fdc_wrfifo(fdc, NE7CMD_SPECIFY); + fdc_wrfifo(fdc, type->steprate); + /* XXX head load time == 6ms */ + fdc_wrfifo(fdc, 6 | NE7_SPECIFY_NODMA); + + /* + * Initialize and attach the disk structure. + */ + fd->sc_dk.dk_name = fd->sc_dv.dv_xname; + fd->sc_dk.dk_driver = &fddkdriver; + disk_attach(&fd->sc_dk); + + /* Make sure the drive motor gets turned off at shutdown time. */ + fd->sc_sdhook = shutdownhook_establish(fd_motor_off, fd); + + /* XXX Need to do some more fiddling with sc_dk. */ + dk_establish(&fd->sc_dk, &fd->sc_dv); +} + +__inline struct fd_type * +fd_dev_to_type(fd, dev) + struct fd_softc *fd; + dev_t dev; +{ + int type = FDTYPE(dev); + + if (type > (sizeof(fd_types) / sizeof(fd_types[0]))) + return (NULL); + return (type ? &fd_types[type - 1] : fd->sc_deftype); +} + +void +fdstrategy(bp) + register struct buf *bp; /* IO operation to perform */ +{ + struct fd_softc *fd; + int unit = FDUNIT(bp->b_dev); + int sz; + int s; + + /* Valid unit, controller, and request? */ + if (unit >= fd_cd.cd_ndevs || + (fd = fd_cd.cd_devs[unit]) == 0 || + bp->b_blkno < 0 || + (((bp->b_bcount % FD_BSIZE(fd)) != 0 || + (bp->b_blkno * DEV_BSIZE) % FD_BSIZE(fd) != 0) && + (bp->b_flags & B_FORMAT) == 0)) { + bp->b_error = EINVAL; + goto bad; + } + + /* If it's a null transfer, return immediately. */ + if (bp->b_bcount == 0) + goto done; + + sz = howmany(bp->b_bcount, DEV_BSIZE); + + if (bp->b_blkno + sz > (fd->sc_type->size * DEV_BSIZE) / FD_BSIZE(fd)) { + sz = (fd->sc_type->size * DEV_BSIZE) / FD_BSIZE(fd) + - bp->b_blkno; + if (sz == 0) { + /* If exactly at end of disk, return EOF. */ + bp->b_resid = bp->b_bcount; + goto done; + } + if (sz < 0) { + /* If past end of disk, return EINVAL. */ + bp->b_error = EINVAL; + goto bad; + } + /* Otherwise, truncate request. */ + bp->b_bcount = sz << DEV_BSHIFT; + } + + bp->b_cylin = (bp->b_blkno * DEV_BSIZE) / + (FD_BSIZE(fd) * fd->sc_type->seccyl); + +#ifdef FD_DEBUG + if (fdc_debug > 1) + printf("fdstrategy: b_blkno %lld b_bcount %ld blkno %lld cylin %ld\n", + (long long)bp->b_blkno, bp->b_bcount, + (long long)fd->sc_blkno, bp->b_cylin); +#endif + + /* Queue transfer on drive, activate drive and controller if idle. */ + s = splbio(); + disksort(&fd->sc_q, bp); + timeout_del(&fd->sc_motoroff_to); /* a good idea */ + if (!fd->sc_q.b_active) + fdstart(fd); +#ifdef DIAGNOSTIC + else { + struct fdc_softc *fdc = (void *)fd->sc_dv.dv_parent; + if (fdc->sc_state == DEVIDLE) { + printf("fdstrategy: controller inactive\n"); + fdcstart(fdc); + } + } +#endif + splx(s); + return; + +bad: + bp->b_flags |= B_ERROR; +done: + /* Toss transfer; we're done early. */ + s = splbio(); + biodone(bp); + splx(s); +} + +void +fdstart(fd) + struct fd_softc *fd; +{ + struct fdc_softc *fdc = (void *)fd->sc_dv.dv_parent; + int active = !TAILQ_EMPTY(&fdc->sc_drives); + + /* Link into controller queue. */ + fd->sc_q.b_active = 1; + TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain); + + /* If controller not already active, start it. */ + if (!active) + fdcstart(fdc); +} + +void +fdfinish(fd, bp) + struct fd_softc *fd; + struct buf *bp; +{ + struct fdc_softc *fdc = (void *)fd->sc_dv.dv_parent; + + /* + * Move this drive to the end of the queue to give others a `fair' + * chance. We only force a switch if N operations are completed while + * another drive is waiting to be serviced, since there is a long motor + * startup delay whenever we switch. + */ + if (TAILQ_NEXT(fd, sc_drivechain) != NULL && ++fd->sc_ops >= 8) { + fd->sc_ops = 0; + TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain); + if (bp->b_actf) { + TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain); + } else + fd->sc_q.b_active = 0; + } + bp->b_resid = fd->sc_bcount; + fd->sc_skip = 0; + fd->sc_q.b_actf = bp->b_actf; + + biodone(bp); + /* turn off motor 5s from now */ + timeout_add(&fd->sc_motoroff_to, 5 * hz); + fdc->sc_state = DEVIDLE; +} + +void +fdc_reset(fdc) + struct fdc_softc *fdc; +{ + bus_space_tag_t t = fdc->sc_bustag; + bus_space_handle_t h = fdc->sc_handle; + + bus_space_write_1(t, h, FDREG77_DOR, FDO_FDMAEN | FDO_MOEN(0)); + + bus_space_write_1(t, h, FDREG77_DRS, DRS_RESET); + delay(10); + bus_space_write_1(t, h, FDREG77_DRS, 0); + + bus_space_write_1(t, h, FDREG77_DOR, + FDO_FRST | FDO_FDMAEN | FDO_DS); +#ifdef FD_DEBUG + if (fdc_debug) + printf("fdc reset\n"); +#endif +} + +void +fd_set_motor(fdc) + struct fdc_softc *fdc; +{ + struct fd_softc *fd; + u_char status; + int n; + + status = FDO_FRST | FDO_FDMAEN; + if ((fd = TAILQ_FIRST(&fdc->sc_drives)) != NULL) + status |= fd->sc_drive; + + for (n = 0; n < 4; n++) + if ((fd = fdc->sc_fd[n]) && (fd->sc_flags & FD_MOTOR)) + status |= FDO_MOEN(n); + bus_space_write_1(fdc->sc_bustag, fdc->sc_handle, + FDREG77_DOR, status); +} + +void +fd_motor_off(arg) + void *arg; +{ + struct fd_softc *fd = arg; + int s; + + s = splbio(); + fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); + fd_set_motor((struct fdc_softc *)fd->sc_dv.dv_parent); + splx(s); +} + +void +fd_motor_on(arg) + void *arg; +{ + struct fd_softc *fd = arg; + struct fdc_softc *fdc = (void *)fd->sc_dv.dv_parent; + int s; + + s = splbio(); + fd->sc_flags &= ~FD_MOTOR_WAIT; + if (fd == TAILQ_FIRST(&fdc->sc_drives) && fdc->sc_state == MOTORWAIT) + (void) fdcstate(fdc); + splx(s); +} + +/* + * Get status bytes off the FDC after a command has finished + * Returns the number of status bytes read; -1 on error. + * The return value is also stored in `sc_nstat'. + */ +int +fdcresult(fdc) + struct fdc_softc *fdc; +{ + bus_space_tag_t t = fdc->sc_bustag; + bus_space_handle_t h = fdc->sc_handle; + int j, n = 0; + + for (j = 10000; j; j--) { + u_int8_t v = bus_space_read_1(t, h, FDREG77_MSR); + v &= (NE7_DIO | NE7_RQM | NE7_CB); + if (v == NE7_RQM) + return (fdc->sc_nstat = n); + if (v == (NE7_DIO | NE7_RQM | NE7_CB)) { + if (n >= sizeof(fdc->sc_status)) { + log(LOG_ERR, "fdcresult: overrun\n"); + return (-1); + } + fdc->sc_status[n++] = + bus_space_read_1(t, h, FDREG77_FIFO); + } else + delay(1); + } + + log(LOG_ERR, "fdcresult: timeout\n"); + return (fdc->sc_nstat = -1); +} + +/* + * Write a command byte to the FDC. + * Returns 0 on success; -1 on failure (i.e. timeout) + */ +int +fdc_wrfifo(fdc, x) + struct fdc_softc *fdc; + u_int8_t x; +{ + bus_space_tag_t t = fdc->sc_bustag; + bus_space_handle_t h = fdc->sc_handle; + int i; + + for (i = 100000; i-- != 0;) { + u_int8_t v = bus_space_read_1(t, h, FDREG77_MSR); + if ((v & (NE7_DIO|NE7_RQM)) == NE7_RQM) { + /* The chip is ready */ + bus_space_write_1(t, h, FDREG77_FIFO, x); + return (0); + } + delay(1); + } + return (-1); +} + +int +fdc_diskchange(fdc) + struct fdc_softc *fdc; +{ + bus_space_tag_t t = fdc->sc_bustag; + bus_space_handle_t h = fdc->sc_handle; + + u_int8_t v = bus_space_read_1(t, h, FDREG77_DIR); + return ((v & FDI_DCHG) != 0); +} + +int +fdopen(dev, flags, fmt, p) + dev_t dev; + int flags, fmt; + struct proc *p; +{ + int unit, pmask; + struct fd_softc *fd; + struct fd_type *type; + + unit = FDUNIT(dev); + if (unit >= fd_cd.cd_ndevs) + return (ENXIO); + fd = fd_cd.cd_devs[unit]; + if (fd == NULL) + return (ENXIO); + type = fd_dev_to_type(fd, dev); + if (type == NULL) + return (ENXIO); + + if ((fd->sc_flags & FD_OPEN) != 0 && + fd->sc_type != type) + return (EBUSY); + + fd->sc_type = type; + fd->sc_cylin = -1; + fd->sc_flags |= FD_OPEN; + + /* + * Only update the disklabel if we're not open anywhere else. + */ + if (fd->sc_dk.dk_openmask == 0) + fdgetdisklabel(dev); + + pmask = (1 << DISKPART(dev)); + + switch (fmt) { + case S_IFCHR: + fd->sc_dk.dk_copenmask |= pmask; + break; + + case S_IFBLK: + fd->sc_dk.dk_bopenmask |= pmask; + break; + } + fd->sc_dk.dk_openmask = + fd->sc_dk.dk_copenmask | fd->sc_dk.dk_bopenmask; + + return (0); +} + +int +fdclose(dev, flags, fmt, p) + dev_t dev; + int flags, fmt; + struct proc *p; +{ + struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; + int pmask = (1 << DISKPART(dev)); + + fd->sc_flags &= ~FD_OPEN; + fd->sc_opts &= ~(FDOPT_NORETRY|FDOPT_SILENT); + + switch (fmt) { + case S_IFCHR: + fd->sc_dk.dk_copenmask &= ~pmask; + break; + + case S_IFBLK: + fd->sc_dk.dk_bopenmask &= ~pmask; + break; + } + fd->sc_dk.dk_openmask = + fd->sc_dk.dk_copenmask | fd->sc_dk.dk_bopenmask; + + return (0); +} + +int +fdread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + + return (physio(fdstrategy, NULL, dev, B_READ, minphys, uio)); +} + +int +fdwrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + + return (physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio)); +} + +void +fdcstart(fdc) + struct fdc_softc *fdc; +{ + +#ifdef DIAGNOSTIC + /* only got here if controller's drive queue was inactive; should + be in idle state */ + if (fdc->sc_state != DEVIDLE) { + printf("fdcstart: not idle\n"); + return; + } +#endif + (void) fdcstate(fdc); +} + +void +fdcstatus(fdc, s) + struct fdc_softc *fdc; + char *s; +{ + struct fd_softc *fd = fdc->sc_drives.tqh_first; + int n; + + /* Just print last status */ + n = fdc->sc_nstat; + +#if 0 + if (n == 0) { + fdc_wrfifo(fdc, NE7CMD_SENSEI); + (void) fdcresult(fdc); + n = 2; + } +#endif + + printf("%s: %s: state %d", + fd ? fd->sc_dv.dv_xname : "fdc", s, fdc->sc_state); + + switch (n) { + case 0: + printf("\n"); + break; + case 2: + printf(" (st0 %b cyl %d)\n", + fdc->sc_status[0], NE7_ST0BITS, + fdc->sc_status[1]); + break; + case 7: + printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n", + fdc->sc_status[0], NE7_ST0BITS, + fdc->sc_status[1], NE7_ST1BITS, + fdc->sc_status[2], NE7_ST2BITS, + fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]); + break; +#ifdef DIAGNOSTIC + default: + printf(" fdcstatus: weird size: %d\n", n); + break; +#endif + } +} + +void +fdctimeout(arg) + void *arg; +{ + struct fdc_softc *fdc = arg; + struct fd_softc *fd; + int s; + + s = splbio(); + fd = TAILQ_FIRST(&fdc->sc_drives); + if (fd == NULL) { + printf("%s: timeout but no I/O pending: state %d, istatus=%d\n", + fdc->sc_dev.dv_xname, fdc->sc_state, fdc->sc_istatus); + fdc->sc_state = DEVIDLE; + goto out; + } + + if (fd->sc_q.b_actf) + fdc->sc_state++; + else + fdc->sc_state = DEVIDLE; + + (void) fdcstate(fdc); +out: + splx(s); + +} + +void +fdcpseudointr(arg) + void *arg; +{ + struct fdc_softc *fdc = arg; + int s; + + /* Just ensure it has the right spl. */ + s = splbio(); + (void) fdcstate(fdc); + splx(s); +} + + +/* + * Hardware interrupt entry point. + * Unfortunately, we have no reliable way to determine that the + * interrupt really came from the floppy controller; just hope + * that the other devices that share this interrupt can do better.. + */ +int +fdchwintr(arg) + void *arg; +{ + struct fdc_softc *fdc = arg; + bus_space_tag_t t = fdc->sc_bustag; + bus_space_handle_t h = fdc->sc_handle; + + switch (fdc->sc_itask) { + case FDC_ITASK_NONE: + return (0); + case FDC_ITASK_SENSEI: + if (fdc_wrfifo(fdc, NE7CMD_SENSEI) != 0 || fdcresult(fdc) == -1) + fdc->sc_istatus = FDC_ISTATUS_ERROR; + else + fdc->sc_istatus = FDC_ISTATUS_DONE; + softintr_schedule(fdc->sc_sicookie); + return (1); + case FDC_ITASK_RESULT: + if (fdcresult(fdc) == -1) + fdc->sc_istatus = FDC_ISTATUS_ERROR; + else + fdc->sc_istatus = FDC_ISTATUS_DONE; + softintr_schedule(fdc->sc_sicookie); + return (1); + case FDC_ITASK_DMA: + /* Proceed with pseudo-DMA below */ + break; + default: + printf("fdc: stray hard interrupt: itask=%d\n", fdc->sc_itask); + fdc->sc_istatus = FDC_ISTATUS_SPURIOUS; + softintr_schedule(fdc->sc_sicookie); + return (1); + } + + /* + * Pseudo DMA in progress + */ + for (;;) { + u_int8_t msr; + + msr = bus_space_read_1(t, h, FDREG77_MSR); + + if ((msr & NE7_RQM) == 0) + /* That's all this round */ + break; + + if ((msr & NE7_NDM) == 0) { + fdcresult(fdc); + fdc->sc_istatus = FDC_ISTATUS_DONE; + softintr_schedule(fdc->sc_sicookie); +#ifdef FD_DEBUG + if (fdc_debug > 1) + printf("fdc: overrun: msr = %x, tc = %d\n", + msr, fdc->sc_tc); +#endif + break; + } + + /* Another byte can be transferred */ + if ((msr & NE7_DIO) != 0) + *fdc->sc_data = + bus_space_read_1(t, h, FDREG77_FIFO); + else + bus_space_write_1(t, h, FDREG77_FIFO, + *fdc->sc_data); + + fdc->sc_data++; + if (--fdc->sc_tc == 0) { + fdc->sc_istatus = FDC_ISTATUS_DONE; + FTC_FLIP; + fdcresult(fdc); + softintr_schedule(fdc->sc_sicookie); + break; + } + } + return (1); +} + +void +fdcswintr(arg) + void *arg; +{ + struct fdc_softc *fdc = arg; + int s; + + if (fdc->sc_istatus == FDC_ISTATUS_NONE) + /* This (software) interrupt is not for us */ + return; + + switch (fdc->sc_istatus) { + case FDC_ISTATUS_ERROR: + printf("fdc: ierror status: state %d\n", fdc->sc_state); + break; + case FDC_ISTATUS_SPURIOUS: + printf("fdc: spurious interrupt: state %d\n", fdc->sc_state); + break; + } + + s = splbio(); + fdcstate(fdc); + splx(s); + return; +} + +int +fdcstate(fdc) + struct fdc_softc *fdc; +{ +#define st0 fdc->sc_status[0] +#define st1 fdc->sc_status[1] +#define cyl fdc->sc_status[1] +#define FDC_WRFIFO(fdc, c) \ + do { \ + if (fdc_wrfifo(fdc, (c))) { \ + goto xxx; \ + } \ + } while(0) + + struct fd_softc *fd; + struct buf *bp; + int read, head, sec, nblks; + struct fd_type *type; + struct fd_formb *finfo = NULL; + + if (fdc->sc_istatus == FDC_ISTATUS_ERROR) { + /* Prevent loop if the reset sequence produces errors */ + if (fdc->sc_state != RESETCOMPLETE && + fdc->sc_state != RECALWAIT && + fdc->sc_state != RECALCOMPLETE) + fdc->sc_state = DORESET; + } + + /* Clear I task/status field */ + fdc->sc_istatus = FDC_ISTATUS_NONE; + fdc->sc_itask = FDC_ITASK_NONE; + +loop: + /* Is there a drive for the controller to do a transfer with? */ + fd = TAILQ_FIRST(&fdc->sc_drives); + if (fd == NULL) { + fdc->sc_state = DEVIDLE; + return (0); + } + + /* Is there a transfer to this drive? If not, deactivate drive. */ + bp = fd->sc_q.b_actf; + if (bp == NULL) { + fd->sc_ops = 0; + TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain); + fd->sc_q.b_active = 0; + goto loop; + } + + if (bp->b_flags & B_FORMAT) + finfo = (struct fd_formb *)bp->b_data; + + switch (fdc->sc_state) { + case DEVIDLE: + fdc->sc_errors = 0; + fd->sc_skip = 0; + fd->sc_bcount = bp->b_bcount; + fd->sc_blkno = (bp->b_blkno * DEV_BSIZE) / FD_BSIZE(fd); + timeout_del(&fd->sc_motoroff_to); + if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) { + fdc->sc_state = MOTORWAIT; + return (1); + } + if ((fd->sc_flags & FD_MOTOR) == 0) { + /* Turn on the motor, being careful about pairing. */ + struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1]; + if (ofd && ofd->sc_flags & FD_MOTOR) { + timeout_del(&ofd->sc_motoroff_to); + ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); + } + fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT; + fd_set_motor(fdc); + fdc->sc_state = MOTORWAIT; + if ((fdc->sc_flags & FDC_NEEDMOTORWAIT) != 0) { /*XXX*/ + /* Allow .25s for motor to stabilize. */ + timeout_add(&fd->sc_motoron_to, hz / 4); + } else { + fd->sc_flags &= ~FD_MOTOR_WAIT; + goto loop; + } + return (1); + } + /* Make sure the right drive is selected. */ + fd_set_motor(fdc); + + if (fdc_diskchange(fdc)) + goto dodskchg; + + /*FALLTHROUGH*/ + case DOSEEK: + doseek: + if ((fdc->sc_flags & FDC_EIS) && + (bp->b_flags & B_FORMAT) == 0) { + fd->sc_cylin = bp->b_cylin; + /* We use implied seek */ + goto doio; + } + + if (fd->sc_cylin == bp->b_cylin) + goto doio; + + fd->sc_cylin = -1; + fdc->sc_state = SEEKWAIT; + fdc->sc_nstat = 0; + + fd->sc_dk.dk_seek++; + + disk_busy(&fd->sc_dk); + timeout_add(&fdc->fdctimeout_to, 4 * hz); + + /* specify command */ + FDC_WRFIFO(fdc, NE7CMD_SPECIFY); + FDC_WRFIFO(fdc, fd->sc_type->steprate); + /* XXX head load time == 6ms */ + FDC_WRFIFO(fdc, 6 | NE7_SPECIFY_NODMA); + + fdc->sc_itask = FDC_ITASK_SENSEI; + /* seek function */ + FDC_WRFIFO(fdc, NE7CMD_SEEK); + FDC_WRFIFO(fdc, fd->sc_drive); /* drive number */ + FDC_WRFIFO(fdc, bp->b_cylin * fd->sc_type->step); + return (1); + + case DODSKCHG: + dodskchg: + /* + * Disk change: force a seek operation by going to cyl 1 + * followed by a recalibrate. + */ + disk_busy(&fd->sc_dk); + timeout_add(&fdc->fdctimeout_to, 4 * hz); + fd->sc_cylin = -1; + fdc->sc_nstat = 0; + fdc->sc_state = DSKCHGWAIT; + + fdc->sc_itask = FDC_ITASK_SENSEI; + /* seek function */ + FDC_WRFIFO(fdc, NE7CMD_SEEK); + FDC_WRFIFO(fdc, fd->sc_drive); /* drive number */ + FDC_WRFIFO(fdc, 1 * fd->sc_type->step); + return (1); + + case DSKCHGWAIT: + timeout_del(&fdc->fdctimeout_to); + disk_unbusy(&fd->sc_dk, 0, 0); + if (fdc->sc_nstat != 2 || (st0 & 0xf8) != 0x20 || + cyl != 1 * fd->sc_type->step) { + fdcstatus(fdc, "dskchg seek failed"); + fdc->sc_state = DORESET; + } else + fdc->sc_state = DORECAL; + + if (fdc_diskchange(fdc)) { + printf("%s: cannot clear disk change status\n", + fdc->sc_dev.dv_xname); + fdc->sc_state = DORESET; + } + goto loop; + + case DOIO: + doio: + if (finfo != NULL) + fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) - + (char *)finfo; + type = fd->sc_type; + sec = fd->sc_blkno % type->seccyl; + nblks = type->seccyl - sec; + nblks = min(nblks, fd->sc_bcount / FD_BSIZE(fd)); + nblks = min(nblks, FDC_MAXIOSIZE / FD_BSIZE(fd)); + fd->sc_nblks = nblks; + fd->sc_nbytes = finfo ? bp->b_bcount : nblks * FD_BSIZE(fd); + head = sec / type->sectrac; + sec -= head * type->sectrac; +#ifdef DIAGNOSTIC + {int block; + block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec; + if (block != fd->sc_blkno) { + printf("fdcintr: block %d != blkno %d\n", block, (int)fd->sc_blkno); +#ifdef DDB + Debugger(); +#endif + }} +#endif + read = bp->b_flags & B_READ; + + /* Setup for pseudo DMA */ + fdc->sc_data = bp->b_data + fd->sc_skip; + fdc->sc_tc = fd->sc_nbytes; + + bus_space_write_1(fdc->sc_bustag, fdc->sc_handle, + FDREG77_DRS, type->rate); +#ifdef FD_DEBUG + if (fdc_debug > 1) + printf("fdcstate: doio: %s drive %d " + "track %d head %d sec %d nblks %d\n", + finfo ? "format" : + (read ? "read" : "write"), + fd->sc_drive, fd->sc_cylin, head, sec, nblks); +#endif + fdc->sc_state = IOCOMPLETE; + fdc->sc_itask = FDC_ITASK_DMA; + fdc->sc_nstat = 0; + + disk_busy(&fd->sc_dk); + + /* allow 3 seconds for operation */ + timeout_add(&fdc->fdctimeout_to, 3 * hz); + + if (finfo != NULL) { + /* formatting */ + FDC_WRFIFO(fdc, NE7CMD_FORMAT); + FDC_WRFIFO(fdc, (head << 2) | fd->sc_drive); + FDC_WRFIFO(fdc, finfo->fd_formb_secshift); + FDC_WRFIFO(fdc, finfo->fd_formb_nsecs); + FDC_WRFIFO(fdc, finfo->fd_formb_gaplen); + FDC_WRFIFO(fdc, finfo->fd_formb_fillbyte); + } else { + if (read) + FDC_WRFIFO(fdc, NE7CMD_READ); + else + FDC_WRFIFO(fdc, NE7CMD_WRITE); + FDC_WRFIFO(fdc, (head << 2) | fd->sc_drive); + FDC_WRFIFO(fdc, fd->sc_cylin); /*track*/ + FDC_WRFIFO(fdc, head); + FDC_WRFIFO(fdc, sec + 1); /*sector+1*/ + FDC_WRFIFO(fdc, type->secsize); /*sector size*/ + FDC_WRFIFO(fdc, type->sectrac); /*secs/track*/ + FDC_WRFIFO(fdc, type->gap1); /*gap1 size*/ + FDC_WRFIFO(fdc, type->datalen); /*data length*/ + } + + return (1); /* will return later */ + + case SEEKWAIT: + timeout_del(&fdc->fdctimeout_to); + fdc->sc_state = SEEKCOMPLETE; + if (fdc->sc_flags & FDC_NEEDHEADSETTLE) { + /* allow 1/50 second for heads to settle */ + timeout_add(&fdc->fdcpseudointr_to, hz / 50); + return (1); /* will return later */ + } + /*FALLTHROUGH*/ + case SEEKCOMPLETE: + /* no data on seek */ + disk_unbusy(&fd->sc_dk, 0, 0); + + /* Make sure seek really happened. */ + if (fdc->sc_nstat != 2 || (st0 & 0xf8) != 0x20 || + cyl != bp->b_cylin * fd->sc_type->step) { +#ifdef FD_DEBUG + if (fdc_debug) + fdcstatus(fdc, "seek failed"); +#endif + fdcretry(fdc); + goto loop; + } + fd->sc_cylin = bp->b_cylin; + goto doio; + + case IOTIMEDOUT: + /* + * Try to abort the I/O operation without resetting + * the chip first. Poke TC and arrange to pick up + * the timed out I/O command's status. + */ + fdc->sc_itask = FDC_ITASK_RESULT; + fdc->sc_state = IOCLEANUPWAIT; + fdc->sc_nstat = 0; + /* 1/10 second should be enough */ + timeout_add(&fdc->fdctimeout_to, hz / 10); + return (1); + + case IOCLEANUPTIMEDOUT: + case SEEKTIMEDOUT: + case RECALTIMEDOUT: + case RESETTIMEDOUT: + case DSKCHGTIMEDOUT: + fdcstatus(fdc, "timeout"); + + /* All other timeouts always roll through to a chip reset */ + fdcretry(fdc); + + /* Force reset, no matter what fdcretry() says */ + fdc->sc_state = DORESET; + goto loop; + + case IOCLEANUPWAIT: /* IO FAILED, cleanup succeeded */ + timeout_del(&fdc->fdctimeout_to); + disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid), + (bp->b_flags & B_READ)); + fdcretry(fdc); + goto loop; + + case IOCOMPLETE: /* IO DONE, post-analyze */ + timeout_del(&fdc->fdctimeout_to); + + disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid), + (bp->b_flags & B_READ)); + + if (fdc->sc_nstat != 7 || st1 != 0 || + ((st0 & 0xf8) != 0 && + ((st0 & 0xf8) != 0x20 || (fdc->sc_cfg & CFG_EIS) == 0))) { +#ifdef FD_DEBUG + if (fdc_debug) { + fdcstatus(fdc, + bp->b_flags & B_READ + ? "read failed" : "write failed"); + printf("blkno %lld nblks %d nstat %d tc %d\n", + (long long)fd->sc_blkno, fd->sc_nblks, + fdc->sc_nstat, fdc->sc_tc); + } +#endif + if (fdc->sc_nstat == 7 && + (st1 & ST1_OVERRUN) == ST1_OVERRUN) { + + /* + * Silently retry overruns if no other + * error bit is set. Adjust threshold. + */ + int thr = fdc->sc_cfg & CFG_THRHLD_MASK; + if (thr < 15) { + thr++; + fdc->sc_cfg &= ~CFG_THRHLD_MASK; + fdc->sc_cfg |= (thr & CFG_THRHLD_MASK); +#ifdef FD_DEBUG + if (fdc_debug) + printf("fdc: %d -> threshold\n", thr); +#endif + fdconf(fdc); + fdc->sc_overruns = 0; + } + if (++fdc->sc_overruns < 3) { + fdc->sc_state = DOIO; + goto loop; + } + } + fdcretry(fdc); + goto loop; + } + if (fdc->sc_errors) { + diskerr(bp, "fd", "soft error", LOG_PRINTF, + fd->sc_skip / FD_BSIZE(fd), + (struct disklabel *)NULL); + printf("\n"); + fdc->sc_errors = 0; + } else { + if (--fdc->sc_overruns < -20) { + int thr = fdc->sc_cfg & CFG_THRHLD_MASK; + if (thr > 0) { + thr--; + fdc->sc_cfg &= ~CFG_THRHLD_MASK; + fdc->sc_cfg |= (thr & CFG_THRHLD_MASK); +#ifdef FD_DEBUG + if (fdc_debug) + printf("fdc: %d -> threshold\n", thr); +#endif + fdconf(fdc); + } + fdc->sc_overruns = 0; + } + } + fd->sc_blkno += fd->sc_nblks; + fd->sc_skip += fd->sc_nbytes; + fd->sc_bcount -= fd->sc_nbytes; + if (finfo == NULL && fd->sc_bcount > 0) { + bp->b_cylin = fd->sc_blkno / fd->sc_type->seccyl; + goto doseek; + } + fdfinish(fd, bp); + goto loop; + + case DORESET: + /* try a reset, keep motor on */ + fd_set_motor(fdc); + delay(100); + fdc->sc_nstat = 0; + fdc->sc_itask = FDC_ITASK_SENSEI; + fdc->sc_state = RESETCOMPLETE; + timeout_add(&fdc->fdctimeout_to, hz / 2); + fdc_reset(fdc); + return (1); /* will return later */ + + case RESETCOMPLETE: + timeout_del(&fdc->fdctimeout_to); + fdconf(fdc); + + /* FALLTHROUGH */ + case DORECAL: + fdc->sc_state = RECALWAIT; + fdc->sc_itask = FDC_ITASK_SENSEI; + fdc->sc_nstat = 0; + timeout_add(&fdc->fdctimeout_to, 5 * hz); + /* recalibrate function */ + FDC_WRFIFO(fdc, NE7CMD_RECAL); + FDC_WRFIFO(fdc, fd->sc_drive); + return (1); /* will return later */ + + case RECALWAIT: + timeout_del(&fdc->fdctimeout_to); + fdc->sc_state = RECALCOMPLETE; + if (fdc->sc_flags & FDC_NEEDHEADSETTLE) { + /* allow 1/30 second for heads to settle */ + timeout_add(&fdc->fdcpseudointr_to, hz / 30); + return (1); /* will return later */ + } + + case RECALCOMPLETE: + if (fdc->sc_nstat != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) { +#ifdef FD_DEBUG + if (fdc_debug) + fdcstatus(fdc, "recalibrate failed"); +#endif + fdcretry(fdc); + goto loop; + } + fd->sc_cylin = 0; + goto doseek; + + case MOTORWAIT: + if (fd->sc_flags & FD_MOTOR_WAIT) + return (1); /* time's not up yet */ + goto doseek; + + default: + fdcstatus(fdc, "stray interrupt"); + return (1); + } +#ifdef DIAGNOSTIC + panic("fdcintr: impossible"); +#endif + +xxx: + /* + * We get here if the chip locks up in FDC_WRFIFO() + * Cancel any operation and schedule a reset + */ + timeout_del(&fdc->fdctimeout_to); + fdcretry(fdc); + fdc->sc_state = DORESET; + goto loop; + +#undef st0 +#undef st1 +#undef cyl +} + +void +fdcretry(fdc) + struct fdc_softc *fdc; +{ + struct fd_softc *fd; + struct buf *bp; + int error = EIO; + + fd = TAILQ_FIRST(&fdc->sc_drives); + bp = fd->sc_q.b_actf; + + fdc->sc_overruns = 0; + if (fd->sc_opts & FDOPT_NORETRY) + goto fail; + + switch (fdc->sc_errors) { + case 0: + if (fdc->sc_nstat == 7 && + (fdc->sc_status[0] & 0xd8) == 0x40 && + (fdc->sc_status[1] & 0x2) == 0x2) { + printf("%s: read-only medium\n", fd->sc_dv.dv_xname); + error = EROFS; + goto failsilent; + } + /* try again */ + fdc->sc_state = + (fdc->sc_flags & FDC_EIS) ? DOIO : DOSEEK; + break; + + case 1: case 2: case 3: + /* didn't work; try recalibrating */ + fdc->sc_state = DORECAL; + break; + + case 4: + if (fdc->sc_nstat == 7 && + fdc->sc_status[0] == 0 && + fdc->sc_status[1] == 0 && + fdc->sc_status[2] == 0) { + /* + * We've retried a few times and we've got + * valid status and all three status bytes + * are zero. Assume this condition is the + * result of no disk loaded into the drive. + */ + printf("%s: no medium?\n", fd->sc_dv.dv_xname); + error = ENODEV; + goto failsilent; + } + + /* still no go; reset the bastard */ + fdc->sc_state = DORESET; + break; + + default: + fail: + if ((fd->sc_opts & FDOPT_SILENT) == 0) { + diskerr(bp, "fd", "hard error", LOG_PRINTF, + fd->sc_skip / FD_BSIZE(fd), + (struct disklabel *)NULL); + printf("\n"); + fdcstatus(fdc, "controller status"); + } + + failsilent: + bp->b_flags |= B_ERROR; + bp->b_error = error; + fdfinish(fd, bp); + } + fdc->sc_errors++; +} + +int +fdsize(dev) + dev_t dev; +{ + + /* Swapping to floppies would not make sense. */ + return (-1); +} + +int +fddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + + /* Not implemented. */ + return (EINVAL); +} + +int +fdioctl(dev, cmd, addr, flag, p) + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + struct fd_softc *fd; + struct fdc_softc *fdc; + int unit; + int error; +#ifdef FD_DEBUG + int i; +#endif + + unit = FDUNIT(dev); + if (unit >= fd_cd.cd_ndevs) + return (ENXIO); + + fd = fd_cd.cd_devs[FDUNIT(dev)]; + fdc = (struct fdc_softc *)fd->sc_dv.dv_parent; + + switch (cmd) { + case DIOCGDINFO: + *(struct disklabel *)addr = *(fd->sc_dk.dk_label); + return 0; + + case DIOCWLABEL: + if ((flag & FWRITE) == 0) + return EBADF; + /* XXX do something */ + return (0); + + case DIOCWDINFO: + if ((flag & FWRITE) == 0) + return (EBADF); + + error = setdisklabel(fd->sc_dk.dk_label, + (struct disklabel *)addr, 0, + fd->sc_dk.dk_cpulabel); + if (error) + return (error); + + error = writedisklabel(dev, fdstrategy, + fd->sc_dk.dk_label, + fd->sc_dk.dk_cpulabel); + return (error); + + case DIOCLOCK: + /* + * Nothing to do here, really. + */ + return (0); + + case MTIOCTOP: + if (((struct mtop *)addr)->mt_op != MTOFFL) + return (EIO); + /* FALLTHROUGH */ + case DIOCEJECT: + if (fdc->sc_flags & FDC_NOEJECT) + return (ENODEV); + fd_do_eject(fd); + return (0); + + case FD_FORM: + if ((flag & FWRITE) == 0) + return (EBADF); /* must be opened for writing */ + else if (((struct fd_formb *)addr)->format_version != + FD_FORMAT_VERSION) + return (EINVAL);/* wrong version of formatting prog */ + else + return fdformat(dev, (struct fd_formb *)addr, p); + break; + + case FD_GTYPE: /* get drive options */ + *(struct fd_type *)addr = *fd->sc_type; + return (0); + + case FD_GOPTS: /* get drive options */ + *(int *)addr = fd->sc_opts; + return (0); + + case FD_SOPTS: /* set drive options */ + fd->sc_opts = *(int *)addr; + return (0); + +#ifdef FD_DEBUG + case _IO('f', 100): + fdc_wrfifo(fdc, NE7CMD_DUMPREG); + fdcresult(fdc); + printf("fdc: dumpreg(%d regs): <", fdc->sc_nstat); + for (i = 0; i < fdc->sc_nstat; i++) + printf(" 0x%x", fdc->sc_status[i]); + printf(">\n"); + return (0); + + case _IOW('f', 101, int): + fdc->sc_cfg &= ~CFG_THRHLD_MASK; + fdc->sc_cfg |= (*(int *)addr & CFG_THRHLD_MASK); + fdconf(fdc); + return (0); + + case _IO('f', 102): + fdc_wrfifo(fdc, NE7CMD_SENSEI); + fdcresult(fdc); + printf("fdc: sensei(%d regs): <", fdc->sc_nstat); + for (i=0; i< fdc->sc_nstat; i++) + printf(" 0x%x", fdc->sc_status[i]); + printf(">\n"); + return (0); +#endif + default: + return (ENOTTY); + } +} + +int +fdformat(dev, finfo, p) + dev_t dev; + struct fd_formb *finfo; + struct proc *p; +{ + int rv = 0; + struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; + struct fd_type *type = fd->sc_type; + struct buf *bp; + + /* set up a buffer header for fdstrategy() */ + bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); + if (bp == NULL) + return (ENOBUFS); + + PHOLD(p); + bzero((void *)bp, sizeof(struct buf)); + bp->b_flags = B_BUSY | B_PHYS | B_FORMAT; + bp->b_proc = p; + bp->b_dev = dev; + + /* + * Calculate a fake blkno, so fdstrategy() would initiate a + * seek to the requested cylinder. + */ + bp->b_blkno = ((finfo->cyl * (type->sectrac * type->heads) + + finfo->head * type->sectrac) * FD_BSIZE(fd)) + / DEV_BSIZE; + + bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; + bp->b_data = (caddr_t)finfo; + +#ifdef FD_DEBUG + if (fdc_debug) { + int i; + + printf("fdformat: blkno 0x%llx count %ld\n", + (unsigned long long)bp->b_blkno, bp->b_bcount); + + printf("\tcyl:\t%d\n", finfo->cyl); + printf("\thead:\t%d\n", finfo->head); + printf("\tnsecs:\t%d\n", finfo->fd_formb_nsecs); + printf("\tsshft:\t%d\n", finfo->fd_formb_secshift); + printf("\tgaplen:\t%d\n", finfo->fd_formb_gaplen); + printf("\ttrack data:"); + for (i = 0; i < finfo->fd_formb_nsecs; i++) { + printf(" [c%d h%d s%d]", + finfo->fd_formb_cylno(i), + finfo->fd_formb_headno(i), + finfo->fd_formb_secno(i) ); + if (finfo->fd_formb_secsize(i) != 2) + printf("<sz:%d>", finfo->fd_formb_secsize(i)); + } + printf("\n"); + } +#endif + + /* now do the format */ + fdstrategy(bp); + + /* ...and wait for it to complete */ + rv = biowait(bp); + PRELE(p); + free(bp, M_TEMP); + return (rv); +} + +void +fdgetdisklabel(dev) + dev_t dev; +{ + int unit = FDUNIT(dev); + struct fd_softc *fd = fd_cd.cd_devs[unit]; + struct disklabel *lp = fd->sc_dk.dk_label; + struct cpu_disklabel *clp = fd->sc_dk.dk_cpulabel; + char *errstring; + + bzero(lp, sizeof(struct disklabel)); + bzero(lp, sizeof(struct cpu_disklabel)); + + lp->d_type = DTYPE_FLOPPY; + lp->d_secsize = FD_BSIZE(fd); + lp->d_secpercyl = fd->sc_type->seccyl; + lp->d_nsectors = fd->sc_type->sectrac; + lp->d_ncylinders = fd->sc_type->tracks; + lp->d_ntracks = fd->sc_type->heads; /* Go figure... */ + lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; + lp->d_rpm = 300; /* XXX like it matters... */ + + strncpy(lp->d_typename, "floppy disk", sizeof(lp->d_typename)); + strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); + lp->d_interleave = 1; + lp->d_flags = D_REMOVABLE; + + lp->d_partitions[RAW_PART].p_offset = 0; + lp->d_partitions[RAW_PART].p_size = lp->d_secpercyl * lp->d_ncylinders; + lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; + lp->d_npartitions = RAW_PART + 1; + + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = dkcksum(lp); + + /* + * Call the generic disklabel extraction routine. If there's + * not a label there, fake it. + */ + errstring = readdisklabel(dev, fdstrategy, lp, clp, 0); + if (errstring) { + printf("%s: %s\n", fd->sc_dv.dv_xname, errstring); + } +} + +void +fd_do_eject(fd) + struct fd_softc *fd; +{ + struct fdc_softc *fdc = (void *)fd->sc_dv.dv_parent; + bus_space_tag_t t = fdc->sc_bustag; + bus_space_handle_t h = fdc->sc_handle; + u_int8_t dor = FDO_FRST | FDO_FDMAEN | FDO_MOEN(0); + + bus_space_write_1(t, h, FDREG77_DOR, dor | FDO_EJ); + delay(10); + bus_space_write_1(t, h, FDREG77_DOR, FDO_FRST | FDO_DS); +} diff --git a/sys/arch/sparc64/dev/fdreg.h b/sys/arch/sparc64/dev/fdreg.h new file mode 100644 index 00000000000..228f49f677d --- /dev/null +++ b/sys/arch/sparc64/dev/fdreg.h @@ -0,0 +1,93 @@ +/* $OpenBSD: fdreg.h,v 1.1 2005/03/09 18:41:49 miod Exp $ */ +/* $NetBSD: fdreg.h,v 1.9 2003/08/07 16:29:35 agc Exp $ */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fdreg.h 7.1 (Berkeley) 5/9/91 + */ + +/* + * AT floppy controller registers and bitfields + */ + +/* uses NEC765 controller */ +#include <dev/ic/nec765reg.h> + +/* + * Register offsets for the 82077 controller. + */ +#define FDREG77_STATUSA 0 +#define FDREG77_STATUSB 1 +#define FDREG77_DOR 2 /* Digital Output Register (R/W) */ +#define FDREG77_TDR 3 /* Tape Control Register (R/W) */ +#define FDREG77_MSR 4 /* Main Status Register (R) */ +#define FDREG77_DRS 4 /* Data Rate Select Register (W) */ +#define FDREG77_FIFO 5 /* Data (FIFO) register (R/W) */ +#define FDREG77_DIR 7 /* Digital Input Register (R) */ +#define FDREG77_CCR 7 /* Configuration Control (W) */ + +/* Data Select Register bits */ +#define DRS_RESET 0x80 +#define DRS_POWER 0x40 +#define DRS_PLL 0x20 +#define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */ +#define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */ +#define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */ +#define FDC_125KBPS 0x03 /* 125KBPS FM drive transfer rate */ + +/* Digital Output Register bits (modified on suns) */ +#define FDO_DS 0x01 /* floppy device select (neg) */ +#define FDO_FRST 0x04 /* floppy controller reset (neg) */ +#define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */ +#define FDO_MOEN(n) ((1 << n) << 4) /* motor enable */ +#define FDO_DEN 0x40 /* Density select */ +#define FDO_EJ 0x80 /* Eject disk */ + +/* Digital Input Register bits */ +#define FDI_DCHG 0x80 /* diskette has been changed */ + +/* XXX - find a place for these... */ +#define NE7CMD_CFG 0x13 +#define CFG_EIS 0x40 +#define CFG_EFIFO 0x20 +#define CFG_POLL 0x10 +#define CFG_THRHLD_MASK 0x0f + +#define NE7CMD_LOCK 0x14 +#define CFG_LOCK 0x80 + +#define NE7CMD_MOTOR 0x0b +#define MOTOR_ON 0x80 + +#define NE7CMD_DUMPREG 0x0e +#define NE7CMD_VERSION 0x10 + +#define ST1_OVERRUN 0x10 + +#define NE7_SPECIFY_NODMA 0x01 diff --git a/sys/arch/sparc64/dev/fdvar.h b/sys/arch/sparc64/dev/fdvar.h new file mode 100644 index 00000000000..838482dc164 --- /dev/null +++ b/sys/arch/sparc64/dev/fdvar.h @@ -0,0 +1,73 @@ +/* $OpenBSD: fdvar.h,v 1.1 2005/03/09 18:41:49 miod Exp $ */ +/* $NetBSD: fdvar.h,v 1.12 2003/07/11 12:09:13 pk Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Kranenburg. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define FD_BSIZE(fd) (128 * (1 << fd->sc_type->secsize)) +#define FDC_MAXIOSIZE NBPG /* XXX should be MAXBSIZE */ + +#define FDC_NSTATUS 10 + +struct fdcio { + bus_space_handle_t fdcio_handle; + + /* + * Interrupt state. + */ + int fdcio_itask; + int fdcio_istatus; + + /* + * IO state. + */ + char *fdcio_data; /* pseudo-DMA data */ + int fdcio_tc; /* pseudo-DMA Terminal Count */ + u_char fdcio_status[FDC_NSTATUS]; /* copy of registers */ + int fdcio_nstat; /* # of valid status bytes */ +}; + +/* itask values */ +#define FDC_ITASK_NONE 0 /* No HW interrupt expected */ +#define FDC_ITASK_SENSEI 1 /* Do SENSEI on next HW interrupt */ +#define FDC_ITASK_DMA 2 /* Do Pseudo-DMA */ +#define FDC_ITASK_RESULT 3 /* Pick up command results */ + +/* istatus values */ +#define FDC_ISTATUS_NONE 0 /* No status available */ +#define FDC_ISTATUS_SPURIOUS 1 /* Spurious HW interrupt detected */ +#define FDC_ISTATUS_ERROR 2 /* Operation completed abnormally */ +#define FDC_ISTATUS_DONE 3 /* Operation completed normally */ |