1752d4735SNate Lawson /*- 25b81b6b3SRodney W. Grimes * Copyright (c) 1990 The Regents of the University of California. 35b81b6b3SRodney W. Grimes * All rights reserved. 45b81b6b3SRodney W. Grimes * 55b81b6b3SRodney W. Grimes * This code is derived from software contributed to Berkeley by 65b81b6b3SRodney W. Grimes * Don Ahn. 75b81b6b3SRodney W. Grimes * 869acd21dSWarner Losh * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) 969acd21dSWarner Losh * aided by the Linux floppy driver modifications from David Bateman 1069acd21dSWarner Losh * (dbateman@eng.uts.edu.au). 1169acd21dSWarner Losh * 12dc16046fSJoerg Wunsch * Copyright (c) 1993, 1994 by 133a2f7427SDavid Greenman * jc@irbs.UUCP (John Capo) 143a2f7427SDavid Greenman * vak@zebub.msk.su (Serge Vakulenko) 153a2f7427SDavid Greenman * ache@astral.msk.su (Andrew A. Chernov) 16dc16046fSJoerg Wunsch * 17dc16046fSJoerg Wunsch * Copyright (c) 1993, 1994, 1995 by 183a2f7427SDavid Greenman * joerg_wunsch@uriah.sax.de (Joerg Wunsch) 19dc5df763SJoerg Wunsch * dufault@hda.com (Peter Dufault) 203a2f7427SDavid Greenman * 212995d110SJoerg Wunsch * Copyright (c) 2001 Joerg Wunsch, 2206740f7bSJoerg Wunsch * joerg_wunsch@uriah.heep.sax.de (Joerg Wunsch) 232995d110SJoerg Wunsch * 245b81b6b3SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 255b81b6b3SRodney W. Grimes * modification, are permitted provided that the following conditions 265b81b6b3SRodney W. Grimes * are met: 275b81b6b3SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 285b81b6b3SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 295b81b6b3SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 305b81b6b3SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 315b81b6b3SRodney W. Grimes * documentation and/or other materials provided with the distribution. 3264860614SJoerg Wunsch * 4. Neither the name of the University nor the names of its contributors 3364860614SJoerg Wunsch * may be used to endorse or promote products derived from this software 3464860614SJoerg Wunsch * without specific prior written permission. 355b81b6b3SRodney W. Grimes * 365b81b6b3SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 375b81b6b3SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 385b81b6b3SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 395b81b6b3SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 405b81b6b3SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 415b81b6b3SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 425b81b6b3SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 435b81b6b3SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 445b81b6b3SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 455b81b6b3SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 465b81b6b3SRodney W. Grimes * SUCH DAMAGE. 475b81b6b3SRodney W. Grimes * 48dc4ff321SRodney W. Grimes * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 495b81b6b3SRodney W. Grimes */ 505b81b6b3SRodney W. Grimes 518c9bbf48SDavid E. O'Brien #include <sys/cdefs.h> 528c9bbf48SDavid E. O'Brien __FBSDID("$FreeBSD$"); 538c9bbf48SDavid E. O'Brien 54d2fb4892SJoerg Wunsch #include "opt_fdc.h" 555b81b6b3SRodney W. Grimes 56b99f0a4aSAndrew Moore #include <sys/param.h> 579626b608SPoul-Henning Kamp #include <sys/bio.h> 586182fdbdSPeter Wemm #include <sys/bus.h> 59b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h> 60f90c382cSPoul-Henning Kamp #include <sys/disk.h> 616182fdbdSPeter Wemm #include <sys/fcntl.h> 62e774b251SJoerg Wunsch #include <sys/fdcio.h> 631a6bed68SJoerg Wunsch #include <sys/filio.h> 64fb919e4dSMark Murray #include <sys/kernel.h> 65fb919e4dSMark Murray #include <sys/lock.h> 66b99f0a4aSAndrew Moore #include <sys/malloc.h> 676182fdbdSPeter Wemm #include <sys/module.h> 68fb919e4dSMark Murray #include <sys/mutex.h> 693a2f7427SDavid Greenman #include <sys/proc.h> 706182fdbdSPeter Wemm #include <sys/rman.h> 716d6fa4fdSWarner Losh #include <sys/systm.h> 726182fdbdSPeter Wemm 73c3ae4c40SWarner Losh #include <machine/bus.h> 746182fdbdSPeter Wemm #include <machine/clock.h> 75dc5df763SJoerg Wunsch #include <machine/stdarg.h> 766182fdbdSPeter Wemm 776182fdbdSPeter Wemm #include <isa/isavar.h> 78a97c75b7SDoug Rabson #include <isa/isareg.h> 79734e3cc5SWarner Losh #include <dev/fdc/fdcreg.h> 806d6fa4fdSWarner Losh #include <dev/fdc/fdcvar.h> 81a97c75b7SDoug Rabson #include <isa/rtc.h> 826182fdbdSPeter Wemm 83419f39ceSPoul-Henning Kamp #define FDBIO_FORMAT BIO_CMD2 84d306122dSPoul-Henning Kamp 851a6bed68SJoerg Wunsch /* configuration flags for fdc */ 86e34c71eaSJoerg Wunsch #define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */ 870722d6abSJoerg Wunsch 883fef646eSJoerg Wunsch /* 893fef646eSJoerg Wunsch * Stop retrying after this many DMA overruns. Since each retry takes 9083edbfa5SJoerg Wunsch * one revolution, with 300 rpm., 25 retries take approximately 5 913fef646eSJoerg Wunsch * seconds which the read attempt will block in case the DMA overrun 923fef646eSJoerg Wunsch * is persistent. 933fef646eSJoerg Wunsch */ 943fef646eSJoerg Wunsch #define FDC_DMAOV_MAX 25 95dc5df763SJoerg Wunsch 961a6bed68SJoerg Wunsch /* 97cb38bb6cSJoerg Wunsch * Timeout value for the PIO loops to wait until the FDC main status 98cb38bb6cSJoerg Wunsch * register matches our expectations (request for master, direction 99cb38bb6cSJoerg Wunsch * bit). This is supposed to be a number of microseconds, although 100cb38bb6cSJoerg Wunsch * timing might actually not be very accurate. 101cb38bb6cSJoerg Wunsch * 102cb38bb6cSJoerg Wunsch * Timeouts of 100 msec are believed to be required for some broken 103cb38bb6cSJoerg Wunsch * (old) hardware. 104cb38bb6cSJoerg Wunsch */ 105cb38bb6cSJoerg Wunsch #define FDSTS_TIMEOUT 100000 106cb38bb6cSJoerg Wunsch 107cb38bb6cSJoerg Wunsch /* 1081a6bed68SJoerg Wunsch * Number of subdevices that can be used for different density types. 1091a6bed68SJoerg Wunsch */ 1101a6bed68SJoerg Wunsch #define NUMDENS 16 111b99f0a4aSAndrew Moore 112419f39ceSPoul-Henning Kamp #define FDBIO_RDSECTID BIO_CMD1 1137ca0641bSAndrey A. Chernov 1141a6bed68SJoerg Wunsch /* 1151a6bed68SJoerg Wunsch * List of native drive densities. Order must match enum fd_drivetype 1161a6bed68SJoerg Wunsch * in <sys/fdcio.h>. Upon attaching the drive, each of the 1171a6bed68SJoerg Wunsch * programmable subdevices is initialized with the native density 1181a6bed68SJoerg Wunsch * definition. 1191a6bed68SJoerg Wunsch */ 1201a6bed68SJoerg Wunsch static struct fd_type fd_native_types[] = 1215b81b6b3SRodney W. Grimes { 1221a6bed68SJoerg Wunsch { 0 }, /* FDT_NONE */ 1231a6bed68SJoerg Wunsch { 9,2,0xFF,0x2A,40, 720,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* FDT_360K */ 1241a6bed68SJoerg Wunsch { 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* FDT_12M */ 1251a6bed68SJoerg Wunsch { 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* FDT_720K */ 1261a6bed68SJoerg Wunsch { 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* FDT_144M */ 1271a6bed68SJoerg Wunsch #if 0 /* we currently don't handle 2.88 MB */ 1281a6bed68SJoerg Wunsch { 36,2,0xFF,0x1B,80,5760,FDC_1MBPS, 2,0x4C,1,1,FL_MFM|FL_PERPND } /*FDT_288M*/ 1291a6bed68SJoerg Wunsch #else 1301a6bed68SJoerg Wunsch { 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* FDT_144M */ 1311a6bed68SJoerg Wunsch #endif 1325b81b6b3SRodney W. Grimes }; 1335b81b6b3SRodney W. Grimes 1341a6bed68SJoerg Wunsch /* 1351a6bed68SJoerg Wunsch * 360 KB 5.25" and 720 KB 3.5" drives don't have automatic density 1361a6bed68SJoerg Wunsch * selection, they just start out with their native density (or lose). 1371a6bed68SJoerg Wunsch * So 1.2 MB 5.25", 1.44 MB 3.5", and 2.88 MB 3.5" drives have their 1381a6bed68SJoerg Wunsch * respective lists of densities to search for. 1391a6bed68SJoerg Wunsch */ 1401a6bed68SJoerg Wunsch static struct fd_type fd_searchlist_12m[] = { 1411a6bed68SJoerg Wunsch { 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */ 1421a6bed68SJoerg Wunsch { 9,2,0xFF,0x23,40, 720,FDC_300KBPS,2,0x50,1,0,FL_MFM|FL_2STEP }, /* 360K */ 1431a6bed68SJoerg Wunsch { 9,2,0xFF,0x20,80,1440,FDC_300KBPS,2,0x50,1,0,FL_MFM }, /* 720K */ 1441a6bed68SJoerg Wunsch }; 1451a6bed68SJoerg Wunsch 1461a6bed68SJoerg Wunsch static struct fd_type fd_searchlist_144m[] = { 1471a6bed68SJoerg Wunsch { 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */ 1481a6bed68SJoerg Wunsch { 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */ 1491a6bed68SJoerg Wunsch }; 1501a6bed68SJoerg Wunsch 1511a6bed68SJoerg Wunsch /* We search for 1.44M first since this is the most common case. */ 1521a6bed68SJoerg Wunsch static struct fd_type fd_searchlist_288m[] = { 1531a6bed68SJoerg Wunsch { 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */ 1541a6bed68SJoerg Wunsch #if 0 1551a6bed68SJoerg Wunsch { 36,2,0xFF,0x1B,80,5760,FDC_1MBPS, 2,0x4C,1,1,FL_MFM|FL_PERPND } /* 2.88M */ 1561a6bed68SJoerg Wunsch #endif 1571a6bed68SJoerg Wunsch { 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */ 1581a6bed68SJoerg Wunsch }; 159dc16046fSJoerg Wunsch 160f8ce7dd5SJoerg Wunsch #define MAX_SEC_SIZE (128 << 3) 16164860614SJoerg Wunsch #define MAX_CYLINDER 85 /* some people really stress their drives 16264860614SJoerg Wunsch * up to cyl 82 */ 163250300ebSJoerg Wunsch #define MAX_HEAD 1 164f8ce7dd5SJoerg Wunsch 1656d6fa4fdSWarner Losh devclass_t fdc_devclass; 1665b81b6b3SRodney W. Grimes 167246ed35dSJoerg Wunsch /* 168246ed35dSJoerg Wunsch * Per drive structure (softc). 169246ed35dSJoerg Wunsch */ 1706182fdbdSPeter Wemm struct fd_data { 171b99f0a4aSAndrew Moore struct fdc_data *fdc; /* pointer to controller structure */ 1725b81b6b3SRodney W. Grimes int fdsu; /* this units number on this controller */ 1731a6bed68SJoerg Wunsch enum fd_drivetype type; /* drive type */ 1741a6bed68SJoerg Wunsch struct fd_type *ft; /* pointer to current type descriptor */ 1751a6bed68SJoerg Wunsch struct fd_type fts[NUMDENS]; /* type descriptors */ 1765b81b6b3SRodney W. Grimes int flags; 1775b81b6b3SRodney W. Grimes #define FD_OPEN 0x01 /* it's open */ 1781a6bed68SJoerg Wunsch #define FD_NONBLOCK 0x02 /* O_NONBLOCK set */ 1791a6bed68SJoerg Wunsch #define FD_ACTIVE 0x04 /* it's active */ 1801a6bed68SJoerg Wunsch #define FD_MOTOR 0x08 /* motor should be on */ 1811a6bed68SJoerg Wunsch #define FD_MOTOR_WAIT 0x10 /* motor coming up */ 1821a6bed68SJoerg Wunsch #define FD_UA 0x20 /* force unit attention */ 1835b81b6b3SRodney W. Grimes int skip; 1845b81b6b3SRodney W. Grimes int hddrv; 185dc5df763SJoerg Wunsch #define FD_NO_TRACK -2 1865b81b6b3SRodney W. Grimes int track; /* where we think the head is */ 1871a6bed68SJoerg Wunsch int options; /* user configurable options, see fdcio.h */ 18802a19910SJustin T. Gibbs struct callout_handle toffhandle; 18902a19910SJustin T. Gibbs struct callout_handle tohandle; 19080980460SPoul-Henning Kamp struct devstat *device_stats; 19189c9c53dSPoul-Henning Kamp struct cdev *masterdev; 1926182fdbdSPeter Wemm device_t dev; 1936182fdbdSPeter Wemm fdu_t fdu; 1946182fdbdSPeter Wemm }; 19537286586SPeter Wemm 19637286586SPeter Wemm struct fdc_ivars { 19737286586SPeter Wemm int fdunit; 198752d4735SNate Lawson int fdtype; 19937286586SPeter Wemm }; 200752d4735SNate Lawson 2016182fdbdSPeter Wemm static devclass_t fd_devclass; 2025b81b6b3SRodney W. Grimes 2031a6bed68SJoerg Wunsch /* configuration flags for fd */ 2041a6bed68SJoerg Wunsch #define FD_TYPEMASK 0x0f /* drive type, matches enum 2051a6bed68SJoerg Wunsch * fd_drivetype; on i386 machines, if 2061a6bed68SJoerg Wunsch * given as 0, use RTC type for fd0 2071a6bed68SJoerg Wunsch * and fd1 */ 2081a6bed68SJoerg Wunsch #define FD_DTYPE(flags) ((flags) & FD_TYPEMASK) 2091a6bed68SJoerg Wunsch #define FD_NO_CHLINE 0x10 /* drive does not support changeline 2101a6bed68SJoerg Wunsch * aka. unit attention */ 2111a6bed68SJoerg Wunsch #define FD_NO_PROBE 0x20 /* don't probe drive (seek test), just 2121a6bed68SJoerg Wunsch * assume it is there */ 2131a6bed68SJoerg Wunsch 214246ed35dSJoerg Wunsch /* 215246ed35dSJoerg Wunsch * Throughout this file the following conventions will be used: 216246ed35dSJoerg Wunsch * 217246ed35dSJoerg Wunsch * fd is a pointer to the fd_data struct for the drive in question 218246ed35dSJoerg Wunsch * fdc is a pointer to the fdc_data struct for the controller 219246ed35dSJoerg Wunsch * fdu is the floppy drive unit number 220246ed35dSJoerg Wunsch * fdcu is the floppy controller unit number 221246ed35dSJoerg Wunsch * fdsu is the floppy drive unit number on that controller. (sub-unit) 222246ed35dSJoerg Wunsch */ 223b99f0a4aSAndrew Moore 224246ed35dSJoerg Wunsch /* 225246ed35dSJoerg Wunsch * Function declarations, same (chaotic) order as they appear in the 226246ed35dSJoerg Wunsch * file. Re-ordering is too late now, it would only obfuscate the 227246ed35dSJoerg Wunsch * diffs against old and offspring versions (like the PC98 one). 228246ed35dSJoerg Wunsch * 229246ed35dSJoerg Wunsch * Anyone adding functions here, please keep this sequence the same 230246ed35dSJoerg Wunsch * as below -- makes locating a particular function in the body much 231246ed35dSJoerg Wunsch * easier. 232246ed35dSJoerg Wunsch */ 233246ed35dSJoerg Wunsch static u_int8_t fdsts_rd(fdc_p); 234246ed35dSJoerg Wunsch static void fddata_wr(fdc_p, u_int8_t); 235246ed35dSJoerg Wunsch static u_int8_t fddata_rd(fdc_p); 236246ed35dSJoerg Wunsch #if 0 237246ed35dSJoerg Wunsch static u_int8_t fdin_rd(fdc_p); 238246ed35dSJoerg Wunsch #endif 239246ed35dSJoerg Wunsch static int fdc_err(struct fdc_data *, const char *); 240246ed35dSJoerg Wunsch static int enable_fifo(fdc_p fdc); 241246ed35dSJoerg Wunsch static int fd_sense_drive_status(fdc_p, int *); 242246ed35dSJoerg Wunsch static int fd_sense_int(fdc_p, int *, int *); 243246ed35dSJoerg Wunsch static int fd_read_status(fdc_p); 244246ed35dSJoerg Wunsch static void fdc_add_child(device_t, const char *, int); 245246ed35dSJoerg Wunsch static int fd_probe(device_t); 246246ed35dSJoerg Wunsch static int fd_attach(device_t); 247246ed35dSJoerg Wunsch static int fd_detach(device_t); 2486182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int); 2493a2f7427SDavid Greenman # define TURNON 1 2503a2f7427SDavid Greenman # define TURNOFF 0 2513a2f7427SDavid Greenman static timeout_t fd_turnoff; 2523a2f7427SDavid Greenman static timeout_t fd_motor_on; 2536182fdbdSPeter Wemm static void fd_turnon(struct fd_data *); 2543a2f7427SDavid Greenman static void fdc_reset(fdc_p); 2556182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *); 25680909a7dSJoerg Wunsch static int out_fdc(struct fdc_data *, int); 257246ed35dSJoerg Wunsch /* 258f967c4f9SPoul-Henning Kamp * The open function is named fdopen() to avoid confusion with fdopen() 259246ed35dSJoerg Wunsch * in fd(4). The difference is now only meaningful for debuggers. 260246ed35dSJoerg Wunsch */ 261f967c4f9SPoul-Henning Kamp static d_open_t fdopen; 262246ed35dSJoerg Wunsch static d_close_t fdclose; 263246ed35dSJoerg Wunsch static d_strategy_t fdstrategy; 2646182fdbdSPeter Wemm static void fdstart(struct fdc_data *); 2655c1a1eaeSBruce Evans static timeout_t fd_iotimeout; 2663a2f7427SDavid Greenman static timeout_t fd_pseudointr; 267246ed35dSJoerg Wunsch static driver_intr_t fdc_intr; 268246ed35dSJoerg Wunsch static int fdcpio(fdc_p, long, caddr_t, u_int); 26989c9c53dSPoul-Henning Kamp static int fdautoselect(struct cdev *); 2706182fdbdSPeter Wemm static int fdstate(struct fdc_data *); 2716182fdbdSPeter Wemm static int retrier(struct fdc_data *); 272246ed35dSJoerg Wunsch static void fdbiodone(struct bio *); 27389c9c53dSPoul-Henning Kamp static int fdmisccmd(struct cdev *, u_int, void *); 274246ed35dSJoerg Wunsch static d_ioctl_t fdioctl; 275d66c374fSTor Egge 276d66c374fSTor Egge static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ 277d66c374fSTor Egge 278d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 2793a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */ 280cba2a7c6SBruce Evans static int volatile fd_debug = 0; 2810e17a5bcSJoerg Wunsch #define TRACE0(arg) do { if (fd_debug) printf(arg); } while (0) 2820e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { if (fd_debug) printf(arg1, arg2); } while (0) 283d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */ 2840e17a5bcSJoerg Wunsch #define TRACE0(arg) do { } while (0) 2850e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { } while (0) 286d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 2875b81b6b3SRodney W. Grimes 288246ed35dSJoerg Wunsch /* 289246ed35dSJoerg Wunsch * Bus space handling (access to low-level IO). 290246ed35dSJoerg Wunsch */ 2916d6fa4fdSWarner Losh void 292427ccf00SDoug Rabson fdout_wr(fdc_p fdc, u_int8_t v) 293427ccf00SDoug Rabson { 294427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); 295427ccf00SDoug Rabson } 296427ccf00SDoug Rabson 297427ccf00SDoug Rabson static u_int8_t 298427ccf00SDoug Rabson fdsts_rd(fdc_p fdc) 299427ccf00SDoug Rabson { 300427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); 301427ccf00SDoug Rabson } 302427ccf00SDoug Rabson 303427ccf00SDoug Rabson static void 304427ccf00SDoug Rabson fddata_wr(fdc_p fdc, u_int8_t v) 305427ccf00SDoug Rabson { 306427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); 307427ccf00SDoug Rabson } 308427ccf00SDoug Rabson 309427ccf00SDoug Rabson static u_int8_t 310427ccf00SDoug Rabson fddata_rd(fdc_p fdc) 311427ccf00SDoug Rabson { 312427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); 313427ccf00SDoug Rabson } 314427ccf00SDoug Rabson 315427ccf00SDoug Rabson static u_int8_t 316427ccf00SDoug Rabson fdin_rd(fdc_p fdc) 317427ccf00SDoug Rabson { 318427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDIN); 319427ccf00SDoug Rabson } 320427ccf00SDoug Rabson 3214e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = { 322dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 323f967c4f9SPoul-Henning Kamp .d_open = fdopen, 3247ac40f5fSPoul-Henning Kamp .d_close = fdclose, 3257ac40f5fSPoul-Henning Kamp .d_read = physread, 3267ac40f5fSPoul-Henning Kamp .d_write = physwrite, 3277ac40f5fSPoul-Henning Kamp .d_ioctl = fdioctl, 3287ac40f5fSPoul-Henning Kamp .d_strategy = fdstrategy, 3297ac40f5fSPoul-Henning Kamp .d_name = "fd", 330dc08ffecSPoul-Henning Kamp .d_flags = D_DISK | D_NEEDGIANT, 3314e2f199eSPoul-Henning Kamp }; 3324e2f199eSPoul-Henning Kamp 333246ed35dSJoerg Wunsch /* 334246ed35dSJoerg Wunsch * Auxiliary functions. Well, some only. Others are scattered 335246ed35dSJoerg Wunsch * throughout the entire file. 336246ed35dSJoerg Wunsch */ 337dc5df763SJoerg Wunsch static int 3386182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s) 339dc5df763SJoerg Wunsch { 3406182fdbdSPeter Wemm fdc->fdc_errs++; 34116b04b6aSJoerg Wunsch if (s) { 342b6e5f28eSPeter Wemm if (fdc->fdc_errs < FDC_ERRMAX) 343b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, "%s", s); 344b6e5f28eSPeter Wemm else if (fdc->fdc_errs == FDC_ERRMAX) 345b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, "too many errors, not " 346b6e5f28eSPeter Wemm "logging any more\n"); 34716b04b6aSJoerg Wunsch } 348dc5df763SJoerg Wunsch 349dc5df763SJoerg Wunsch return FD_FAILED; 350dc5df763SJoerg Wunsch } 351dc5df763SJoerg Wunsch 352dc5df763SJoerg Wunsch /* 353dc5df763SJoerg Wunsch * fd_cmd: Send a command to the chip. Takes a varargs with this structure: 354dc5df763SJoerg Wunsch * Unit number, 355dc5df763SJoerg Wunsch * # of output bytes, output bytes as ints ..., 356dc5df763SJoerg Wunsch * # of input bytes, input bytes as ints ... 357dc5df763SJoerg Wunsch */ 3586d6fa4fdSWarner Losh int 3596182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...) 360dc5df763SJoerg Wunsch { 361dc5df763SJoerg Wunsch u_char cmd; 362dc5df763SJoerg Wunsch int n_in; 363dc5df763SJoerg Wunsch int n; 364dc5df763SJoerg Wunsch va_list ap; 365dc5df763SJoerg Wunsch 366dc5df763SJoerg Wunsch va_start(ap, n_out); 367dc5df763SJoerg Wunsch cmd = (u_char)(va_arg(ap, int)); 368dc5df763SJoerg Wunsch va_end(ap); 369dc5df763SJoerg Wunsch va_start(ap, n_out); 370dc5df763SJoerg Wunsch for (n = 0; n < n_out; n++) 371dc5df763SJoerg Wunsch { 3726182fdbdSPeter Wemm if (out_fdc(fdc, va_arg(ap, int)) < 0) 373dc5df763SJoerg Wunsch { 374dc5df763SJoerg Wunsch char msg[50]; 3752127f260SArchie Cobbs snprintf(msg, sizeof(msg), 376dc5df763SJoerg Wunsch "cmd %x failed at out byte %d of %d\n", 377dc5df763SJoerg Wunsch cmd, n + 1, n_out); 3786182fdbdSPeter Wemm return fdc_err(fdc, msg); 379dc5df763SJoerg Wunsch } 380dc5df763SJoerg Wunsch } 381dc5df763SJoerg Wunsch n_in = va_arg(ap, int); 382dc5df763SJoerg Wunsch for (n = 0; n < n_in; n++) 383dc5df763SJoerg Wunsch { 384dc5df763SJoerg Wunsch int *ptr = va_arg(ap, int *); 3856182fdbdSPeter Wemm if (fd_in(fdc, ptr) < 0) 386dc5df763SJoerg Wunsch { 387dc5df763SJoerg Wunsch char msg[50]; 3882127f260SArchie Cobbs snprintf(msg, sizeof(msg), 389dc5df763SJoerg Wunsch "cmd %02x failed at in byte %d of %d\n", 390dc5df763SJoerg Wunsch cmd, n + 1, n_in); 3916182fdbdSPeter Wemm return fdc_err(fdc, msg); 392dc5df763SJoerg Wunsch } 393dc5df763SJoerg Wunsch } 394dc5df763SJoerg Wunsch 395dc5df763SJoerg Wunsch return 0; 396dc5df763SJoerg Wunsch } 397dc5df763SJoerg Wunsch 3986f4e0bebSPoul-Henning Kamp static int 399d66c374fSTor Egge enable_fifo(fdc_p fdc) 400d66c374fSTor Egge { 401d66c374fSTor Egge int i, j; 402d66c374fSTor Egge 403d66c374fSTor Egge if ((fdc->flags & FDC_HAS_FIFO) == 0) { 404d66c374fSTor Egge 405d66c374fSTor Egge /* 406d66c374fSTor Egge * Cannot use fd_cmd the normal way here, since 407d66c374fSTor Egge * this might be an invalid command. Thus we send the 408d66c374fSTor Egge * first byte, and check for an early turn of data directon. 409d66c374fSTor Egge */ 410d66c374fSTor Egge 4116182fdbdSPeter Wemm if (out_fdc(fdc, I8207X_CONFIGURE) < 0) 4126182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 413d66c374fSTor Egge 414d66c374fSTor Egge /* If command is invalid, return */ 415d0900d6bSJoerg Wunsch j = FDSTS_TIMEOUT; 416427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) 417d0900d6bSJoerg Wunsch != NE7_RQM && j-- > 0) { 418d66c374fSTor Egge if (i == (NE7_DIO | NE7_RQM)) { 419d66c374fSTor Egge fdc_reset(fdc); 420d66c374fSTor Egge return FD_FAILED; 421d66c374fSTor Egge } 422d0900d6bSJoerg Wunsch DELAY(1); 423d0900d6bSJoerg Wunsch } 424d66c374fSTor Egge if (j<0 || 4256182fdbdSPeter Wemm fd_cmd(fdc, 3, 426d66c374fSTor Egge 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { 427d66c374fSTor Egge fdc_reset(fdc); 4286182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 429d66c374fSTor Egge } 430d66c374fSTor Egge fdc->flags |= FDC_HAS_FIFO; 431d66c374fSTor Egge return 0; 432d66c374fSTor Egge } 4336182fdbdSPeter Wemm if (fd_cmd(fdc, 4, 434d66c374fSTor Egge I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) 4356182fdbdSPeter Wemm return fdc_err(fdc, "Re-enable FIFO failed\n"); 436d66c374fSTor Egge return 0; 437d66c374fSTor Egge } 438d66c374fSTor Egge 439d66c374fSTor Egge static int 440dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p) 441dc5df763SJoerg Wunsch { 442dc5df763SJoerg Wunsch int st3; 443dc5df763SJoerg Wunsch 4446182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) 445dc5df763SJoerg Wunsch { 4466182fdbdSPeter Wemm return fdc_err(fdc, "Sense Drive Status failed\n"); 447dc5df763SJoerg Wunsch } 448dc5df763SJoerg Wunsch if (st3p) 449dc5df763SJoerg Wunsch *st3p = st3; 450dc5df763SJoerg Wunsch 451dc5df763SJoerg Wunsch return 0; 452dc5df763SJoerg Wunsch } 453dc5df763SJoerg Wunsch 4546f4e0bebSPoul-Henning Kamp static int 455dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp) 456dc5df763SJoerg Wunsch { 4576182fdbdSPeter Wemm int cyl, st0, ret; 458dc5df763SJoerg Wunsch 4596182fdbdSPeter Wemm ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); 4606182fdbdSPeter Wemm if (ret) { 4616182fdbdSPeter Wemm (void)fdc_err(fdc, 462dc5df763SJoerg Wunsch "sense intr err reading stat reg 0\n"); 463dc5df763SJoerg Wunsch return ret; 464dc5df763SJoerg Wunsch } 465dc5df763SJoerg Wunsch 466dc5df763SJoerg Wunsch if (st0p) 467dc5df763SJoerg Wunsch *st0p = st0; 468dc5df763SJoerg Wunsch 4696182fdbdSPeter Wemm if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { 470dc5df763SJoerg Wunsch /* 471dc5df763SJoerg Wunsch * There doesn't seem to have been an interrupt. 472dc5df763SJoerg Wunsch */ 473dc5df763SJoerg Wunsch return FD_NOT_VALID; 474dc5df763SJoerg Wunsch } 475dc5df763SJoerg Wunsch 4766182fdbdSPeter Wemm if (fd_in(fdc, &cyl) < 0) { 4776182fdbdSPeter Wemm return fdc_err(fdc, "can't get cyl num\n"); 478dc5df763SJoerg Wunsch } 479dc5df763SJoerg Wunsch 480dc5df763SJoerg Wunsch if (cylp) 481dc5df763SJoerg Wunsch *cylp = cyl; 482dc5df763SJoerg Wunsch 483dc5df763SJoerg Wunsch return 0; 484dc5df763SJoerg Wunsch } 485dc5df763SJoerg Wunsch 486dc5df763SJoerg Wunsch 4876f4e0bebSPoul-Henning Kamp static int 48864860614SJoerg Wunsch fd_read_status(fdc_p fdc) 489dc5df763SJoerg Wunsch { 490dc5df763SJoerg Wunsch int i, ret; 491b5e8ce9fSBruce Evans 492246ed35dSJoerg Wunsch for (i = ret = 0; i < 7; i++) { 493b5e8ce9fSBruce Evans /* 49464860614SJoerg Wunsch * XXX types are poorly chosen. Only bytes can be read 495a838d83dSBruce Evans * from the hardware, but fdc->status[] wants u_ints and 496b5e8ce9fSBruce Evans * fd_in() gives ints. 497b5e8ce9fSBruce Evans */ 498b5e8ce9fSBruce Evans int status; 499b5e8ce9fSBruce Evans 5006182fdbdSPeter Wemm ret = fd_in(fdc, &status); 501b5e8ce9fSBruce Evans fdc->status[i] = status; 502b5e8ce9fSBruce Evans if (ret != 0) 503dc5df763SJoerg Wunsch break; 504dc5df763SJoerg Wunsch } 505dc5df763SJoerg Wunsch 506dc5df763SJoerg Wunsch if (ret == 0) 507dc5df763SJoerg Wunsch fdc->flags |= FDC_STAT_VALID; 508dc5df763SJoerg Wunsch else 509dc5df763SJoerg Wunsch fdc->flags &= ~FDC_STAT_VALID; 510dc5df763SJoerg Wunsch 511dc5df763SJoerg Wunsch return ret; 512dc5df763SJoerg Wunsch } 513dc5df763SJoerg Wunsch 5146d6fa4fdSWarner Losh int 51537286586SPeter Wemm fdc_alloc_resources(struct fdc_data *fdc) 5165b81b6b3SRodney W. Grimes { 51737286586SPeter Wemm device_t dev; 518e219897aSJoerg Wunsch int ispnp, ispcmcia, nports; 5195b81b6b3SRodney W. Grimes 52037286586SPeter Wemm dev = fdc->fdc_dev; 5215f830ea2SJoerg Wunsch ispnp = (fdc->flags & FDC_ISPNP) != 0; 5225f830ea2SJoerg Wunsch ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0; 5236182fdbdSPeter Wemm fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; 5246182fdbdSPeter Wemm fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; 52564746d06SNate Lawson fdc->rid_ctl = 1; 5266182fdbdSPeter Wemm 527b6e5f28eSPeter Wemm /* 5285f830ea2SJoerg Wunsch * On standard ISA, we don't just use an 8 port range 5295f830ea2SJoerg Wunsch * (e.g. 0x3f0-0x3f7) since that covers an IDE control 5305f830ea2SJoerg Wunsch * register at 0x3f6. 5315f830ea2SJoerg Wunsch * 532b6e5f28eSPeter Wemm * Isn't PC hardware wonderful. 5335f830ea2SJoerg Wunsch * 5345f830ea2SJoerg Wunsch * The Y-E Data PCMCIA FDC doesn't have this problem, it 5355f830ea2SJoerg Wunsch * uses the register with offset 6 for pseudo-DMA, and the 5365f830ea2SJoerg Wunsch * one with offset 7 as control register. 537b6e5f28eSPeter Wemm */ 538e219897aSJoerg Wunsch nports = ispcmcia ? 8 : (ispnp ? 1 : 6); 53964746d06SNate Lawson 54064746d06SNate Lawson /* 54164746d06SNate Lawson * Some ACPI BIOSen have _CRS objects for the floppy device that 54264746d06SNate Lawson * split the I/O port resource into several resources. We detect 54364746d06SNate Lawson * this case by checking if there are more than 2 IOPORT resources. 54464746d06SNate Lawson * If so, we use the resource with the smallest start address as 54564746d06SNate Lawson * the port RID and the largest start address as the control RID. 54664746d06SNate Lawson */ 54764746d06SNate Lawson if (bus_get_resource_count(dev, SYS_RES_IOPORT, 2) != 0) { 54864746d06SNate Lawson u_long min_start, max_start, tmp; 54964746d06SNate Lawson int i; 55064746d06SNate Lawson 55164746d06SNate Lawson /* Find the min/max start addresses and their RIDs. */ 55264746d06SNate Lawson max_start = 0ul; 55364746d06SNate Lawson min_start = ~0ul; 55464746d06SNate Lawson for (i = 0; bus_get_resource_count(dev, SYS_RES_IOPORT, i) > 0; 55564746d06SNate Lawson i++) { 55664746d06SNate Lawson tmp = bus_get_resource_start(dev, SYS_RES_IOPORT, i); 55764746d06SNate Lawson KASSERT(tmp != 0, ("bogus resource")); 55864746d06SNate Lawson if (tmp < min_start) { 55964746d06SNate Lawson min_start = tmp; 56064746d06SNate Lawson fdc->rid_ioport = i; 56164746d06SNate Lawson } 56264746d06SNate Lawson if (tmp > max_start) { 56364746d06SNate Lawson max_start = tmp; 56464746d06SNate Lawson fdc->rid_ctl = i; 56564746d06SNate Lawson } 56664746d06SNate Lawson } 56764746d06SNate Lawson if (min_start + 7 != max_start) { 56864746d06SNate Lawson device_printf(dev, "I/O to control range incorrect\n"); 56964746d06SNate Lawson return (ENXIO); 57064746d06SNate Lawson } 57164746d06SNate Lawson } 57264746d06SNate Lawson 5736182fdbdSPeter Wemm fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, 5746182fdbdSPeter Wemm &fdc->rid_ioport, 0ul, ~0ul, 575e219897aSJoerg Wunsch nports, RF_ACTIVE); 5766182fdbdSPeter Wemm if (fdc->res_ioport == 0) { 577e219897aSJoerg Wunsch device_printf(dev, "cannot reserve I/O port range (%d ports)\n", 578e219897aSJoerg Wunsch nports); 57937286586SPeter Wemm return ENXIO; 5806182fdbdSPeter Wemm } 581427ccf00SDoug Rabson fdc->portt = rman_get_bustag(fdc->res_ioport); 582427ccf00SDoug Rabson fdc->porth = rman_get_bushandle(fdc->res_ioport); 583427ccf00SDoug Rabson 5845f830ea2SJoerg Wunsch if (!ispcmcia) { 585427ccf00SDoug Rabson /* 5865f830ea2SJoerg Wunsch * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7 5875f830ea2SJoerg Wunsch * and some at 0x3f0-0x3f5,0x3f7. We detect the former 5885f830ea2SJoerg Wunsch * by checking the size and adjust the port address 5895f830ea2SJoerg Wunsch * accordingly. 590b6e5f28eSPeter Wemm */ 591b6e5f28eSPeter Wemm if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) 592b6e5f28eSPeter Wemm fdc->port_off = -2; 593b6e5f28eSPeter Wemm 594b6e5f28eSPeter Wemm /* 5955f830ea2SJoerg Wunsch * Register the control port range as rid 1 if it 5965f830ea2SJoerg Wunsch * isn't there already. Most PnP BIOSen will have 5975f830ea2SJoerg Wunsch * already done this but non-PnP configurations don't. 598a2639a18SPeter Wemm * 5995f830ea2SJoerg Wunsch * And some (!!) report 0x3f2-0x3f5 and completely 6005f830ea2SJoerg Wunsch * leave out the control register! It seems that some 6015f830ea2SJoerg Wunsch * non-antique controller chips have a different 6025f830ea2SJoerg Wunsch * method of programming the transfer speed which 6035f830ea2SJoerg Wunsch * doesn't require the control register, but it's 6045f830ea2SJoerg Wunsch * mighty bogus as the chip still responds to the 6055f830ea2SJoerg Wunsch * address for the control register. 606427ccf00SDoug Rabson */ 607b6e5f28eSPeter Wemm if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { 608b9da888fSPeter Wemm u_long ctlstart; 609b9da888fSPeter Wemm 610b6e5f28eSPeter Wemm /* Find the control port, usually 0x3f7 */ 6115f830ea2SJoerg Wunsch ctlstart = rman_get_start(fdc->res_ioport) + 6125f830ea2SJoerg Wunsch fdc->port_off + 7; 613b6e5f28eSPeter Wemm 614b6e5f28eSPeter Wemm bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1); 615b9da888fSPeter Wemm } 616b6e5f28eSPeter Wemm 617b6e5f28eSPeter Wemm /* 618b6e5f28eSPeter Wemm * Now (finally!) allocate the control port. 619b6e5f28eSPeter Wemm */ 6205f96beb9SNate Lawson fdc->res_ctl = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 6215f96beb9SNate Lawson &fdc->rid_ctl, RF_ACTIVE); 622427ccf00SDoug Rabson if (fdc->res_ctl == 0) { 6235f830ea2SJoerg Wunsch device_printf(dev, 624e219897aSJoerg Wunsch "cannot reserve control I/O port range (control port)\n"); 62537286586SPeter Wemm return ENXIO; 626427ccf00SDoug Rabson } 627427ccf00SDoug Rabson fdc->ctlt = rman_get_bustag(fdc->res_ctl); 628427ccf00SDoug Rabson fdc->ctlh = rman_get_bushandle(fdc->res_ctl); 6295f830ea2SJoerg Wunsch } 6306182fdbdSPeter Wemm 6316d6fa4fdSWarner Losh fdc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fdc->rid_irq, 632de0b7a63SPoul-Henning Kamp RF_ACTIVE | RF_SHAREABLE); 6336182fdbdSPeter Wemm if (fdc->res_irq == 0) { 634427ccf00SDoug Rabson device_printf(dev, "cannot reserve interrupt line\n"); 63537286586SPeter Wemm return ENXIO; 6366182fdbdSPeter Wemm } 6375f830ea2SJoerg Wunsch 6385f830ea2SJoerg Wunsch if ((fdc->flags & FDC_NODMA) == 0) { 6395f96beb9SNate Lawson fdc->res_drq = bus_alloc_resource_any(dev, SYS_RES_DRQ, 640de0b7a63SPoul-Henning Kamp &fdc->rid_drq, RF_ACTIVE | RF_SHAREABLE); 6416182fdbdSPeter Wemm if (fdc->res_drq == 0) { 642427ccf00SDoug Rabson device_printf(dev, "cannot reserve DMA request line\n"); 6436fba12f2SPoul-Henning Kamp fdc->flags |= FDC_NODMA; 6446fba12f2SPoul-Henning Kamp } else 645bc8d2181SWarner Losh fdc->dmachan = rman_get_start(fdc->res_drq); 6465f830ea2SJoerg Wunsch } 64737286586SPeter Wemm 64837286586SPeter Wemm return 0; 64937286586SPeter Wemm } 65037286586SPeter Wemm 6516d6fa4fdSWarner Losh void 65237286586SPeter Wemm fdc_release_resources(struct fdc_data *fdc) 65337286586SPeter Wemm { 65437286586SPeter Wemm device_t dev; 65537286586SPeter Wemm 65637286586SPeter Wemm dev = fdc->fdc_dev; 65737286586SPeter Wemm if (fdc->res_irq != 0) { 65837286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 65937286586SPeter Wemm fdc->res_irq); 66037286586SPeter Wemm bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 66137286586SPeter Wemm fdc->res_irq); 66237286586SPeter Wemm } 66337286586SPeter Wemm if (fdc->res_ctl != 0) { 66437286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, 66537286586SPeter Wemm fdc->res_ctl); 66637286586SPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, 66737286586SPeter Wemm fdc->res_ctl); 66837286586SPeter Wemm } 66937286586SPeter Wemm if (fdc->res_ioport != 0) { 67037286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 67137286586SPeter Wemm fdc->res_ioport); 67237286586SPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 67337286586SPeter Wemm fdc->res_ioport); 67437286586SPeter Wemm } 67537286586SPeter Wemm if (fdc->res_drq != 0) { 67637286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 67737286586SPeter Wemm fdc->res_drq); 67837286586SPeter Wemm bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 67937286586SPeter Wemm fdc->res_drq); 68037286586SPeter Wemm } 68137286586SPeter Wemm } 68237286586SPeter Wemm 683246ed35dSJoerg Wunsch /* 684246ed35dSJoerg Wunsch * Configuration/initialization stuff, per controller. 685246ed35dSJoerg Wunsch */ 68637286586SPeter Wemm 6876d6fa4fdSWarner Losh int 6883aae7b16SBruce Evans fdc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 68937286586SPeter Wemm { 69037286586SPeter Wemm struct fdc_ivars *ivars = device_get_ivars(child); 69137286586SPeter Wemm 69237286586SPeter Wemm switch (which) { 69337286586SPeter Wemm case FDC_IVAR_FDUNIT: 69437286586SPeter Wemm *result = ivars->fdunit; 69537286586SPeter Wemm break; 696752d4735SNate Lawson case FDC_IVAR_FDTYPE: 697752d4735SNate Lawson *result = ivars->fdtype; 698752d4735SNate Lawson break; 69937286586SPeter Wemm default: 700752d4735SNate Lawson return (ENOENT); 70137286586SPeter Wemm } 702752d4735SNate Lawson return (0); 703752d4735SNate Lawson } 704752d4735SNate Lawson 705752d4735SNate Lawson int 706752d4735SNate Lawson fdc_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 707752d4735SNate Lawson { 708752d4735SNate Lawson struct fdc_ivars *ivars = device_get_ivars(child); 709752d4735SNate Lawson 710752d4735SNate Lawson switch (which) { 711752d4735SNate Lawson case FDC_IVAR_FDUNIT: 712752d4735SNate Lawson ivars->fdunit = value; 713752d4735SNate Lawson break; 714752d4735SNate Lawson case FDC_IVAR_FDTYPE: 715752d4735SNate Lawson ivars->fdtype = value; 716752d4735SNate Lawson break; 717752d4735SNate Lawson default: 718752d4735SNate Lawson return (ENOENT); 719752d4735SNate Lawson } 720752d4735SNate Lawson return (0); 721752d4735SNate Lawson } 722752d4735SNate Lawson 723752d4735SNate Lawson int 724752d4735SNate Lawson fdc_initial_reset(struct fdc_data *fdc) 725752d4735SNate Lawson { 726752d4735SNate Lawson /* First, reset the floppy controller. */ 727752d4735SNate Lawson fdout_wr(fdc, 0); 728752d4735SNate Lawson DELAY(100); 729752d4735SNate Lawson fdout_wr(fdc, FDO_FRST); 730752d4735SNate Lawson 731752d4735SNate Lawson /* Then, see if it can handle a command. */ 732752d4735SNate Lawson if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), 733752d4735SNate Lawson NE7_SPEC_2(2, 0), 0)) 734752d4735SNate Lawson return (ENXIO); 735752d4735SNate Lawson return (0); 73637286586SPeter Wemm } 73737286586SPeter Wemm 7386d6fa4fdSWarner Losh int 739e219897aSJoerg Wunsch fdc_detach(device_t dev) 7405f830ea2SJoerg Wunsch { 7415f830ea2SJoerg Wunsch struct fdc_data *fdc; 7425f830ea2SJoerg Wunsch int error; 7435f830ea2SJoerg Wunsch 7445f830ea2SJoerg Wunsch fdc = device_get_softc(dev); 7455f830ea2SJoerg Wunsch 7465f830ea2SJoerg Wunsch /* have our children detached first */ 7475f830ea2SJoerg Wunsch if ((error = bus_generic_detach(dev))) 7485f830ea2SJoerg Wunsch return (error); 7495f830ea2SJoerg Wunsch 750e219897aSJoerg Wunsch /* reset controller, turn motor off */ 751e219897aSJoerg Wunsch fdout_wr(fdc, 0); 752e219897aSJoerg Wunsch 7535f830ea2SJoerg Wunsch if ((fdc->flags & FDC_ATTACHED) == 0) { 7545f830ea2SJoerg Wunsch device_printf(dev, "already unloaded\n"); 7555f830ea2SJoerg Wunsch return (0); 7565f830ea2SJoerg Wunsch } 7575f830ea2SJoerg Wunsch fdc->flags &= ~FDC_ATTACHED; 7585f830ea2SJoerg Wunsch 7595f830ea2SJoerg Wunsch BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, 7605f830ea2SJoerg Wunsch fdc->fdc_intr); 7615f830ea2SJoerg Wunsch fdc_release_resources(fdc); 7625f830ea2SJoerg Wunsch return (0); 7635f830ea2SJoerg Wunsch } 7645f830ea2SJoerg Wunsch 7655b81b6b3SRodney W. Grimes /* 76637286586SPeter Wemm * Add a child device to the fdc controller. It will then be probed etc. 7675b81b6b3SRodney W. Grimes */ 7686182fdbdSPeter Wemm static void 76937286586SPeter Wemm fdc_add_child(device_t dev, const char *name, int unit) 7705b81b6b3SRodney W. Grimes { 771752d4735SNate Lawson int fdu, flags; 77237286586SPeter Wemm struct fdc_ivars *ivar; 7736182fdbdSPeter Wemm device_t child; 77492200632SGarrett Wollman 7757cc0979fSDavid Malone ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT | M_ZERO); 77637286586SPeter Wemm if (ivar == NULL) 7776182fdbdSPeter Wemm return; 778fe0d4089SMatthew N. Dodd child = device_add_child(dev, name, unit); 77942117b6cSPoul-Henning Kamp if (child == NULL) { 78042117b6cSPoul-Henning Kamp free(ivar, M_DEVBUF); 7816182fdbdSPeter Wemm return; 78242117b6cSPoul-Henning Kamp } 78337286586SPeter Wemm device_set_ivars(child, ivar); 784752d4735SNate Lawson if (resource_int_value(name, unit, "drive", &fdu) != 0) 785752d4735SNate Lawson fdu = 0; 786752d4735SNate Lawson fdc_set_fdunit(child, fdu); 787752d4735SNate Lawson fdc_set_fdtype(child, FDT_NONE); 7881a6bed68SJoerg Wunsch if (resource_int_value(name, unit, "flags", &flags) == 0) 7891a6bed68SJoerg Wunsch device_set_flags(child, flags); 7908a9bc9c0SJohn Baldwin if (resource_disabled(name, unit)) 7916182fdbdSPeter Wemm device_disable(child); 7926182fdbdSPeter Wemm } 7936182fdbdSPeter Wemm 7946d6fa4fdSWarner Losh int 7956182fdbdSPeter Wemm fdc_attach(device_t dev) 7966182fdbdSPeter Wemm { 79737286586SPeter Wemm struct fdc_data *fdc; 7982398f0cdSPeter Wemm const char *name, *dname; 7995d54fe91SJoerg Wunsch int i, error, dunit; 800427ccf00SDoug Rabson 80137286586SPeter Wemm fdc = device_get_softc(dev); 802b0c2b925SWarner Losh fdc->fdc_dev = dev; 80337286586SPeter Wemm error = fdc_alloc_resources(fdc); 80437286586SPeter Wemm if (error) { 805f8ce7dd5SJoerg Wunsch device_printf(dev, "cannot re-acquire resources\n"); 80637286586SPeter Wemm return error; 80737286586SPeter Wemm } 80837286586SPeter Wemm error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, 8093c36743eSMark Murray INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc, 8103c36743eSMark Murray &fdc->fdc_intr); 81137286586SPeter Wemm if (error) { 81237286586SPeter Wemm device_printf(dev, "cannot setup interrupt\n"); 81337286586SPeter Wemm return error; 81437286586SPeter Wemm } 81537286586SPeter Wemm fdc->fdcu = device_get_unit(dev); 816fb35bd37SJoerg Wunsch fdc->flags |= FDC_ATTACHED | FDC_NEEDS_RESET; 8176182fdbdSPeter Wemm 8185b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 8196182fdbdSPeter Wemm 8203a2f7427SDavid Greenman /* reset controller, turn motor off, clear fdout mirror reg */ 82164860614SJoerg Wunsch fdout_wr(fdc, fdc->fdout = 0); 8228177437dSPoul-Henning Kamp bioq_init(&fdc->head); 8235b81b6b3SRodney W. Grimes 8246182fdbdSPeter Wemm /* 82537286586SPeter Wemm * Probe and attach any children. We should probably detect 82637286586SPeter Wemm * devices from the BIOS unless overridden. 8276182fdbdSPeter Wemm */ 828ada54f9eSPeter Wemm name = device_get_nameunit(dev); 8292398f0cdSPeter Wemm i = 0; 8302398f0cdSPeter Wemm while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) 8312398f0cdSPeter Wemm fdc_add_child(dev, dname, dunit); 83237286586SPeter Wemm 83360444853SJoerg Wunsch if ((error = bus_generic_attach(dev)) != 0) 83460444853SJoerg Wunsch return (error); 83560444853SJoerg Wunsch 83660444853SJoerg Wunsch return (0); 8376182fdbdSPeter Wemm } 8386182fdbdSPeter Wemm 8396d6fa4fdSWarner Losh int 8406182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child) 8416182fdbdSPeter Wemm { 8421a6bed68SJoerg Wunsch int retval = 0, flags; 84315317dd8SMatthew N. Dodd 84415317dd8SMatthew N. Dodd retval += bus_print_child_header(me, child); 8451a6bed68SJoerg Wunsch retval += printf(" on %s drive %d", device_get_nameunit(me), 84637286586SPeter Wemm fdc_get_fdunit(child)); 8471a6bed68SJoerg Wunsch if ((flags = device_get_flags(me)) != 0) 8481a6bed68SJoerg Wunsch retval += printf(" flags %#x", flags); 8491a6bed68SJoerg Wunsch retval += printf("\n"); 85015317dd8SMatthew N. Dodd 85115317dd8SMatthew N. Dodd return (retval); 8526182fdbdSPeter Wemm } 8536182fdbdSPeter Wemm 85416e68fc6SPeter Wemm /* 855246ed35dSJoerg Wunsch * Configuration/initialization, per drive. 85616e68fc6SPeter Wemm */ 8576182fdbdSPeter Wemm static int 8586182fdbdSPeter Wemm fd_probe(device_t dev) 8596182fdbdSPeter Wemm { 8606182fdbdSPeter Wemm int i; 8611a6bed68SJoerg Wunsch u_int st0, st3; 8626182fdbdSPeter Wemm struct fd_data *fd; 8636182fdbdSPeter Wemm struct fdc_data *fdc; 8646182fdbdSPeter Wemm fdsu_t fdsu; 865752d4735SNate Lawson int flags, type; 8666182fdbdSPeter Wemm 867752d4735SNate Lawson fdsu = fdc_get_fdunit(dev); 8686182fdbdSPeter Wemm fd = device_get_softc(dev); 8696182fdbdSPeter Wemm fdc = device_get_softc(device_get_parent(dev)); 8701a6bed68SJoerg Wunsch flags = device_get_flags(dev); 8716182fdbdSPeter Wemm 8726182fdbdSPeter Wemm fd->dev = dev; 8736182fdbdSPeter Wemm fd->fdc = fdc; 8746182fdbdSPeter Wemm fd->fdsu = fdsu; 8756182fdbdSPeter Wemm fd->fdu = device_get_unit(dev); 8766182fdbdSPeter Wemm 877752d4735SNate Lawson type = FD_DTYPE(flags); 878752d4735SNate Lawson 879752d4735SNate Lawson /* Auto-probe if fdinfo is present, but always allow override. */ 880752d4735SNate Lawson if (type == FDT_NONE && (type = fdc_get_fdtype(dev)) != FDT_NONE) { 881752d4735SNate Lawson fd->type = type; 882752d4735SNate Lawson goto done; 883752d4735SNate Lawson } else { 884752d4735SNate Lawson /* make sure fdautoselect() will be called */ 885752d4735SNate Lawson fd->flags = FD_UA; 886752d4735SNate Lawson fd->type = type; 887752d4735SNate Lawson } 888752d4735SNate Lawson 889038d1bbdSJoerg Wunsch /* 890038d1bbdSJoerg Wunsch * XXX I think using __i386__ is wrong here since we actually want to probe 891038d1bbdSJoerg Wunsch * for the machine type, not the CPU type (so non-PC arch's like the PC98 will 892038d1bbdSJoerg Wunsch * fail the probe). However, for whatever reason, testing for _MACHINE_ARCH 893038d1bbdSJoerg Wunsch * == i386 breaks the test on FreeBSD/Alpha. 894038d1bbdSJoerg Wunsch */ 89504209354SPeter Wemm #if defined(__i386__) || defined(__amd64__) 8961a6bed68SJoerg Wunsch if (fd->type == FDT_NONE && (fd->fdu == 0 || fd->fdu == 1)) { 8971a6bed68SJoerg Wunsch /* Look up what the BIOS thinks we have. */ 8981a6bed68SJoerg Wunsch if (fd->fdu == 0) { 8995f830ea2SJoerg Wunsch if ((fdc->flags & FDC_ISPCMCIA)) 9001a6bed68SJoerg Wunsch /* 9011a6bed68SJoerg Wunsch * Somewhat special. No need to force the 9021a6bed68SJoerg Wunsch * user to set device flags, since the Y-E 9031a6bed68SJoerg Wunsch * Data PCMCIA floppy is always a 1.44 MB 9041a6bed68SJoerg Wunsch * device. 9051a6bed68SJoerg Wunsch */ 9061a6bed68SJoerg Wunsch fd->type = FDT_144M; 9070722d6abSJoerg Wunsch else 9081a6bed68SJoerg Wunsch fd->type = (rtcin(RTC_FDISKETTE) & 0xf0) >> 4; 9091a6bed68SJoerg Wunsch } else { 9101a6bed68SJoerg Wunsch fd->type = rtcin(RTC_FDISKETTE) & 0x0f; 9116b7bd95bSJoerg Wunsch } 9121a6bed68SJoerg Wunsch if (fd->type == FDT_288M_1) 9131a6bed68SJoerg Wunsch fd->type = FDT_288M; 9141a6bed68SJoerg Wunsch } 91504209354SPeter Wemm #endif /* __i386__ || __amd64__ */ 9166182fdbdSPeter Wemm /* is there a unit? */ 9171a6bed68SJoerg Wunsch if (fd->type == FDT_NONE) 9186182fdbdSPeter Wemm return (ENXIO); 9196182fdbdSPeter Wemm 9206182fdbdSPeter Wemm /* select it */ 9216182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNON); 922fb35bd37SJoerg Wunsch fdc_reset(fdc); /* XXX reset, then unreset, etc. */ 9236182fdbdSPeter Wemm DELAY(1000000); /* 1 sec */ 9246182fdbdSPeter Wemm 9251a6bed68SJoerg Wunsch if ((flags & FD_NO_PROBE) == 0) { 926dc5df763SJoerg Wunsch /* If we're at track 0 first seek inwards. */ 9271a6bed68SJoerg Wunsch if ((fd_sense_drive_status(fdc, &st3) == 0) && 9281a6bed68SJoerg Wunsch (st3 & NE7_ST3_T0)) { 929dc5df763SJoerg Wunsch /* Seek some steps... */ 9306182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 931dc5df763SJoerg Wunsch /* ...wait a moment... */ 932dc5df763SJoerg Wunsch DELAY(300000); 933dc5df763SJoerg Wunsch /* make ctrlr happy: */ 9346182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); 935dc5df763SJoerg Wunsch } 936dc5df763SJoerg Wunsch } 937dc5df763SJoerg Wunsch 9386b7bd95bSJoerg Wunsch for (i = 0; i < 2; i++) { 9396b7bd95bSJoerg Wunsch /* 9406b7bd95bSJoerg Wunsch * we must recalibrate twice, just in case the 9411a6bed68SJoerg Wunsch * heads have been beyond cylinder 76, since 9421a6bed68SJoerg Wunsch * most FDCs still barf when attempting to 9431a6bed68SJoerg Wunsch * recalibrate more than 77 steps 9446b7bd95bSJoerg Wunsch */ 945dc5df763SJoerg Wunsch /* go back to 0: */ 9466182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 9476b7bd95bSJoerg Wunsch /* a second being enough for full stroke seek*/ 9486b7bd95bSJoerg Wunsch DELAY(i == 0 ? 1000000 : 300000); 9495b81b6b3SRodney W. Grimes 9506b7bd95bSJoerg Wunsch /* anything responding? */ 951dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, 0) == 0 && 952dc5df763SJoerg Wunsch (st0 & NE7_ST0_EC) == 0) 9536b7bd95bSJoerg Wunsch break; /* already probed succesfully */ 9546b7bd95bSJoerg Wunsch } 955dc5df763SJoerg Wunsch } 9561a6bed68SJoerg Wunsch } 9576b7bd95bSJoerg Wunsch 9586182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNOFF); 9593a2f7427SDavid Greenman 9601a6bed68SJoerg Wunsch if ((flags & FD_NO_PROBE) == 0 && 9611a6bed68SJoerg Wunsch (st0 & NE7_ST0_EC) != 0) /* no track 0 -> no drive present */ 9626182fdbdSPeter Wemm return (ENXIO); 9635b81b6b3SRodney W. Grimes 964752d4735SNate Lawson done: 965752d4735SNate Lawson /* This doesn't work before the first reset. Or set_motor?? */ 966752d4735SNate Lawson if ((fdc->flags & FDC_HAS_FIFO) == 0 && 967752d4735SNate Lawson fdc->fdct == FDC_ENHANCED && 968752d4735SNate Lawson (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 && 969752d4735SNate Lawson enable_fifo(fdc) == 0) { 970752d4735SNate Lawson device_printf(device_get_parent(dev), 971752d4735SNate Lawson "FIFO enabled, %d bytes threshold\n", fifo_threshold); 972752d4735SNate Lawson } 973752d4735SNate Lawson 9741a6bed68SJoerg Wunsch switch (fd->type) { 9751a6bed68SJoerg Wunsch case FDT_12M: 9761a6bed68SJoerg Wunsch device_set_desc(dev, "1200-KB 5.25\" drive"); 9771a6bed68SJoerg Wunsch break; 9781a6bed68SJoerg Wunsch case FDT_144M: 9791a6bed68SJoerg Wunsch device_set_desc(dev, "1440-KB 3.5\" drive"); 9801a6bed68SJoerg Wunsch break; 9811a6bed68SJoerg Wunsch case FDT_288M: 9821a6bed68SJoerg Wunsch device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); 9831a6bed68SJoerg Wunsch break; 9841a6bed68SJoerg Wunsch case FDT_360K: 9851a6bed68SJoerg Wunsch device_set_desc(dev, "360-KB 5.25\" drive"); 9861a6bed68SJoerg Wunsch break; 9871a6bed68SJoerg Wunsch case FDT_720K: 9881a6bed68SJoerg Wunsch device_set_desc(dev, "720-KB 3.5\" drive"); 9891a6bed68SJoerg Wunsch break; 9901a6bed68SJoerg Wunsch default: 9911a6bed68SJoerg Wunsch return (ENXIO); 9921a6bed68SJoerg Wunsch } 993dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 994b99f0a4aSAndrew Moore fd->fdc = fdc; 995b99f0a4aSAndrew Moore fd->fdsu = fdsu; 9963a2f7427SDavid Greenman fd->options = 0; 99702a19910SJustin T. Gibbs callout_handle_init(&fd->toffhandle); 99802a19910SJustin T. Gibbs callout_handle_init(&fd->tohandle); 9995b81b6b3SRodney W. Grimes 10001a6bed68SJoerg Wunsch /* initialize densities for subdevices */ 10011a6bed68SJoerg Wunsch for (i = 0; i < NUMDENS; i++) 10021a6bed68SJoerg Wunsch memcpy(fd->fts + i, fd_native_types + fd->type, 10031a6bed68SJoerg Wunsch sizeof(struct fd_type)); 10046182fdbdSPeter Wemm return (0); 10056182fdbdSPeter Wemm } 10066182fdbdSPeter Wemm 10076182fdbdSPeter Wemm static int 10086182fdbdSPeter Wemm fd_attach(device_t dev) 10096182fdbdSPeter Wemm { 10106182fdbdSPeter Wemm struct fd_data *fd; 10116182fdbdSPeter Wemm 101264860614SJoerg Wunsch fd = device_get_softc(dev); 1013503799eaSPoul-Henning Kamp fd->masterdev = make_dev(&fd_cdevsw, fd->fdu, 10143f54a085SPoul-Henning Kamp UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu); 1015a2f19df9SPoul-Henning Kamp fd->masterdev->si_drv1 = fd; 101680980460SPoul-Henning Kamp fd->device_stats = devstat_new_entry(device_get_name(dev), 1017f8ce7dd5SJoerg Wunsch device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS, 10182a888f93SKenneth D. Merry DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, 10192a888f93SKenneth D. Merry DEVSTAT_PRIORITY_FD); 10206182fdbdSPeter Wemm return (0); 10215b81b6b3SRodney W. Grimes } 10225b81b6b3SRodney W. Grimes 10235f830ea2SJoerg Wunsch static int 10245f830ea2SJoerg Wunsch fd_detach(device_t dev) 10255f830ea2SJoerg Wunsch { 10265f830ea2SJoerg Wunsch struct fd_data *fd; 10275f830ea2SJoerg Wunsch 10285f830ea2SJoerg Wunsch fd = device_get_softc(dev); 102960444853SJoerg Wunsch untimeout(fd_turnoff, fd, fd->toffhandle); 103080980460SPoul-Henning Kamp devstat_remove_entry(fd->device_stats); 1031e219897aSJoerg Wunsch destroy_dev(fd->masterdev); 10325f830ea2SJoerg Wunsch 10335f830ea2SJoerg Wunsch return (0); 10345f830ea2SJoerg Wunsch } 10355f830ea2SJoerg Wunsch 103616e68fc6SPeter Wemm static device_method_t fd_methods[] = { 103716e68fc6SPeter Wemm /* Device interface */ 103816e68fc6SPeter Wemm DEVMETHOD(device_probe, fd_probe), 103916e68fc6SPeter Wemm DEVMETHOD(device_attach, fd_attach), 10405f830ea2SJoerg Wunsch DEVMETHOD(device_detach, fd_detach), 104116e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 104216e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ 104316e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ 104416e68fc6SPeter Wemm 104516e68fc6SPeter Wemm { 0, 0 } 104616e68fc6SPeter Wemm }; 104716e68fc6SPeter Wemm 104816e68fc6SPeter Wemm static driver_t fd_driver = { 104916e68fc6SPeter Wemm "fd", 105016e68fc6SPeter Wemm fd_methods, 105116e68fc6SPeter Wemm sizeof(struct fd_data) 105216e68fc6SPeter Wemm }; 105316e68fc6SPeter Wemm 1054475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); 105516e68fc6SPeter Wemm 1056246ed35dSJoerg Wunsch /* 1057246ed35dSJoerg Wunsch * More auxiliary functions. 1058246ed35dSJoerg Wunsch */ 1059246ed35dSJoerg Wunsch /* 1060246ed35dSJoerg Wunsch * Motor control stuff. 1061246ed35dSJoerg Wunsch * Remember to not deselect the drive we're working on. 1062246ed35dSJoerg Wunsch */ 10633a2f7427SDavid Greenman static void 10646182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon) 10655b81b6b3SRodney W. Grimes { 1066fb35bd37SJoerg Wunsch int fdout; 10673a2f7427SDavid Greenman 1068fb35bd37SJoerg Wunsch fdout = fdc->fdout; 10693a2f7427SDavid Greenman if (turnon) { 10703a2f7427SDavid Greenman fdout &= ~FDO_FDSEL; 1071fb35bd37SJoerg Wunsch fdout |= (FDO_MOEN0 << fdsu) | FDO_FDMAEN | FDO_FRST | fdsu; 10723a2f7427SDavid Greenman } else 10733a2f7427SDavid Greenman fdout &= ~(FDO_MOEN0 << fdsu); 10746182fdbdSPeter Wemm fdc->fdout = fdout; 1075fb35bd37SJoerg Wunsch fdout_wr(fdc, fdout); 10763a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdout); 10773a2f7427SDavid Greenman } 10783a2f7427SDavid Greenman 1079381fe1aaSGarrett Wollman static void 10806182fdbdSPeter Wemm fd_turnoff(void *xfd) 10815b81b6b3SRodney W. Grimes { 1082f5f7ba03SJordan K. Hubbard int s; 10836182fdbdSPeter Wemm fd_p fd = xfd; 1084dc16046fSJoerg Wunsch 10856182fdbdSPeter Wemm TRACE1("[fd%d: turnoff]", fd->fdu); 10868335c1b8SBruce Evans 10875f830ea2SJoerg Wunsch s = splbio(); 10888335c1b8SBruce Evans /* 10898335c1b8SBruce Evans * Don't turn off the motor yet if the drive is active. 10905f830ea2SJoerg Wunsch * 10915f830ea2SJoerg Wunsch * If we got here, this could only mean we missed an interrupt. 10925f830ea2SJoerg Wunsch * This can e. g. happen on the Y-E Date PCMCIA floppy controller 10935f830ea2SJoerg Wunsch * after a controller reset. Just schedule a pseudo-interrupt 10945f830ea2SJoerg Wunsch * so the state machine gets re-entered. 10958335c1b8SBruce Evans */ 10966182fdbdSPeter Wemm if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { 10975f830ea2SJoerg Wunsch fdc_intr(fd->fdc); 10985f830ea2SJoerg Wunsch splx(s); 10998335c1b8SBruce Evans return; 11008335c1b8SBruce Evans } 11018335c1b8SBruce Evans 11025b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR; 11036182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNOFF); 1104f5f7ba03SJordan K. Hubbard splx(s); 11055b81b6b3SRodney W. Grimes } 11065b81b6b3SRodney W. Grimes 11073a2f7427SDavid Greenman static void 11086182fdbdSPeter Wemm fd_motor_on(void *xfd) 11095b81b6b3SRodney W. Grimes { 1110f5f7ba03SJordan K. Hubbard int s; 11116182fdbdSPeter Wemm fd_p fd = xfd; 1112f5f7ba03SJordan K. Hubbard 1113f5f7ba03SJordan K. Hubbard s = splbio(); 11145b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR_WAIT; 11155b81b6b3SRodney W. Grimes if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) 11165b81b6b3SRodney W. Grimes { 11176182fdbdSPeter Wemm fdc_intr(fd->fdc); 11185b81b6b3SRodney W. Grimes } 1119f5f7ba03SJordan K. Hubbard splx(s); 11205b81b6b3SRodney W. Grimes } 11215b81b6b3SRodney W. Grimes 11223a2f7427SDavid Greenman static void 11236182fdbdSPeter Wemm fd_turnon(fd_p fd) 11245b81b6b3SRodney W. Grimes { 11255b81b6b3SRodney W. Grimes if(!(fd->flags & FD_MOTOR)) 11265b81b6b3SRodney W. Grimes { 11273a2f7427SDavid Greenman fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); 11286182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNON); 11296182fdbdSPeter Wemm timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ 11305b81b6b3SRodney W. Grimes } 11315b81b6b3SRodney W. Grimes } 11325b81b6b3SRodney W. Grimes 1133381fe1aaSGarrett Wollman static void 1134dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc) 11355b81b6b3SRodney W. Grimes { 11363a2f7427SDavid Greenman /* Try a reset, keep motor on */ 1137427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 11383a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 11393a2f7427SDavid Greenman DELAY(100); 11403a2f7427SDavid Greenman /* enable FDC, but defer interrupts a moment */ 1141427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); 11423a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); 11433a2f7427SDavid Greenman DELAY(100); 1144427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout); 11453a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout); 11463a2f7427SDavid Greenman 114764860614SJoerg Wunsch /* XXX after a reset, silently believe the FDC will accept commands */ 11486182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1149dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1150dc5df763SJoerg Wunsch 0); 1151d66c374fSTor Egge if (fdc->flags & FDC_HAS_FIFO) 1152d66c374fSTor Egge (void) enable_fifo(fdc); 11535b81b6b3SRodney W. Grimes } 11545b81b6b3SRodney W. Grimes 1155246ed35dSJoerg Wunsch /* 1156246ed35dSJoerg Wunsch * FDC IO functions, take care of the main status register, timeout 1157246ed35dSJoerg Wunsch * in case the desired status bits are never set. 1158a2642c4dSJoerg Wunsch * 1159a2642c4dSJoerg Wunsch * These PIO loops initially start out with short delays between 1160a2642c4dSJoerg Wunsch * each iteration in the expectation that the required condition 1161a2642c4dSJoerg Wunsch * is usually met quickly, so it can be handled immediately. After 1162a2642c4dSJoerg Wunsch * about 1 ms, stepping is increased to achieve a better timing 1163a2642c4dSJoerg Wunsch * accuracy in the calls to DELAY(). 1164246ed35dSJoerg Wunsch */ 1165b5e8ce9fSBruce Evans static int 11666182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr) 1167dc5df763SJoerg Wunsch { 1168a2642c4dSJoerg Wunsch int i, j, step; 1169a2642c4dSJoerg Wunsch 1170a2642c4dSJoerg Wunsch for (j = 0, step = 1; 1171a2642c4dSJoerg Wunsch (i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && 1172a2642c4dSJoerg Wunsch j < FDSTS_TIMEOUT; 1173a2642c4dSJoerg Wunsch j += step) { 1174dc5df763SJoerg Wunsch if (i == NE7_RQM) 11758a0ba818SJoerg Wunsch return (fdc_err(fdc, "ready for output in input\n")); 1176a2642c4dSJoerg Wunsch if (j == 1000) 1177a2642c4dSJoerg Wunsch step = 1000; 1178a2642c4dSJoerg Wunsch DELAY(step); 1179d0900d6bSJoerg Wunsch } 1180a2642c4dSJoerg Wunsch if (j >= FDSTS_TIMEOUT) 11818a0ba818SJoerg Wunsch return (fdc_err(fdc, bootverbose? "input ready timeout\n": 0)); 1182d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 1183427ccf00SDoug Rabson i = fddata_rd(fdc); 1184dc5df763SJoerg Wunsch TRACE1("[FDDATA->0x%x]", (unsigned char)i); 1185dc5df763SJoerg Wunsch *ptr = i; 11868a0ba818SJoerg Wunsch return (0); 1187d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 1188427ccf00SDoug Rabson i = fddata_rd(fdc); 1189dc5df763SJoerg Wunsch if (ptr) 1190dc5df763SJoerg Wunsch *ptr = i; 11918a0ba818SJoerg Wunsch return (0); 1192d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 1193dc5df763SJoerg Wunsch } 1194dc5df763SJoerg Wunsch 119537c84183SPoul-Henning Kamp static int 11966182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x) 11975b81b6b3SRodney W. Grimes { 1198a2642c4dSJoerg Wunsch int i, j, step; 11995b81b6b3SRodney W. Grimes 1200a2642c4dSJoerg Wunsch for (j = 0, step = 1; 1201a2642c4dSJoerg Wunsch (i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != NE7_RQM && 1202a2642c4dSJoerg Wunsch j < FDSTS_TIMEOUT; 1203a2642c4dSJoerg Wunsch j += step) { 1204a2642c4dSJoerg Wunsch if (i == (NE7_DIO|NE7_RQM)) 1205a2642c4dSJoerg Wunsch return (fdc_err(fdc, "ready for input in output\n")); 1206a2642c4dSJoerg Wunsch if (j == 1000) 1207a2642c4dSJoerg Wunsch step = 1000; 1208a2642c4dSJoerg Wunsch DELAY(step); 1209a2642c4dSJoerg Wunsch } 1210a2642c4dSJoerg Wunsch if (j >= FDSTS_TIMEOUT) 12118a0ba818SJoerg Wunsch return (fdc_err(fdc, bootverbose? "output ready timeout\n": 0)); 12123b3837dbSRodney W. Grimes 12133b3837dbSRodney W. Grimes /* Send the command and return */ 1214427ccf00SDoug Rabson fddata_wr(fdc, x); 12153a2f7427SDavid Greenman TRACE1("[0x%x->FDDATA]", x); 12165b81b6b3SRodney W. Grimes return (0); 12175b81b6b3SRodney W. Grimes } 12185b81b6b3SRodney W. Grimes 1219246ed35dSJoerg Wunsch /* 1220246ed35dSJoerg Wunsch * Block device driver interface functions (interspersed with even more 1221246ed35dSJoerg Wunsch * auxiliary functions). 1222246ed35dSJoerg Wunsch */ 122337c84183SPoul-Henning Kamp static int 122489c9c53dSPoul-Henning Kamp fdopen(struct cdev *dev, int flags, int mode, struct thread *td) 12255b81b6b3SRodney W. Grimes { 12266182fdbdSPeter Wemm fd_p fd; 1227b99f0a4aSAndrew Moore fdc_p fdc; 12281a6bed68SJoerg Wunsch int rv, unitattn, dflags; 12295b81b6b3SRodney W. Grimes 1230a2f19df9SPoul-Henning Kamp fd = dev->si_drv1; 1231a2f19df9SPoul-Henning Kamp if (fd == NULL) 1232b99f0a4aSAndrew Moore return (ENXIO); 12336182fdbdSPeter Wemm fdc = fd->fdc; 12341a6bed68SJoerg Wunsch if ((fdc == NULL) || (fd->type == FDT_NONE)) 1235b99f0a4aSAndrew Moore return (ENXIO); 12361a6bed68SJoerg Wunsch dflags = device_get_flags(fd->dev); 12373e425b96SJulian Elischer /* 12381a6bed68SJoerg Wunsch * This is a bit bogus. It's still possible that e. g. a 12391a6bed68SJoerg Wunsch * descriptor gets inherited to a child, but then it's at 12401a6bed68SJoerg Wunsch * least for the same subdevice. By checking FD_OPEN here, we 12411a6bed68SJoerg Wunsch * can ensure that a device isn't attempted to be opened with 12421a6bed68SJoerg Wunsch * different densities at the same time where the second open 12431a6bed68SJoerg Wunsch * could clobber the settings from the first one. 12443e425b96SJulian Elischer */ 12451a6bed68SJoerg Wunsch if (fd->flags & FD_OPEN) 12461a6bed68SJoerg Wunsch return (EBUSY); 12471a6bed68SJoerg Wunsch 12481a6bed68SJoerg Wunsch if (flags & FNONBLOCK) { 12491a6bed68SJoerg Wunsch /* 12501a6bed68SJoerg Wunsch * Unfortunately, physio(9) discards its ioflag 12511a6bed68SJoerg Wunsch * argument, thus preventing us from seeing the 12521a6bed68SJoerg Wunsch * IO_NDELAY bit. So we need to keep track 12531a6bed68SJoerg Wunsch * ourselves. 12541a6bed68SJoerg Wunsch */ 12551a6bed68SJoerg Wunsch fd->flags |= FD_NONBLOCK; 12561a6bed68SJoerg Wunsch fd->ft = 0; 12571a6bed68SJoerg Wunsch } else { 12581a6bed68SJoerg Wunsch /* 12591a6bed68SJoerg Wunsch * Figure out a unit attention condition. 12601a6bed68SJoerg Wunsch * 12611a6bed68SJoerg Wunsch * If UA has been forced, proceed. 12621a6bed68SJoerg Wunsch * 126362cc1e05SYaroslav Tykhiy * If the drive has no changeline support, 126462cc1e05SYaroslav Tykhiy * or if the drive parameters have been lost 126562cc1e05SYaroslav Tykhiy * due to previous non-blocking access, 126662cc1e05SYaroslav Tykhiy * assume a forced UA condition. 126762cc1e05SYaroslav Tykhiy * 12681a6bed68SJoerg Wunsch * If motor is off, turn it on for a moment 12691a6bed68SJoerg Wunsch * and select our drive, in order to read the 12701a6bed68SJoerg Wunsch * UA hardware signal. 12711a6bed68SJoerg Wunsch * 12721a6bed68SJoerg Wunsch * If motor is on, and our drive is currently 12731a6bed68SJoerg Wunsch * selected, just read the hardware bit. 12741a6bed68SJoerg Wunsch * 12751a6bed68SJoerg Wunsch * If motor is on, but active for another 12761a6bed68SJoerg Wunsch * drive on that controller, we are lost. We 12771a6bed68SJoerg Wunsch * cannot risk to deselect the other drive, so 12781a6bed68SJoerg Wunsch * we just assume a forced UA condition to be 12791a6bed68SJoerg Wunsch * on the safe side. 12801a6bed68SJoerg Wunsch */ 12811a6bed68SJoerg Wunsch unitattn = 0; 12821a6bed68SJoerg Wunsch if ((dflags & FD_NO_CHLINE) != 0 || 128362cc1e05SYaroslav Tykhiy (fd->flags & FD_UA) != 0 || 128462cc1e05SYaroslav Tykhiy fd->ft == 0) { 12851a6bed68SJoerg Wunsch unitattn = 1; 12861a6bed68SJoerg Wunsch fd->flags &= ~FD_UA; 12871a6bed68SJoerg Wunsch } else if (fdc->fdout & (FDO_MOEN0 | FDO_MOEN1 | 12881a6bed68SJoerg Wunsch FDO_MOEN2 | FDO_MOEN3)) { 12891a6bed68SJoerg Wunsch if ((fdc->fdout & FDO_FDSEL) == fd->fdsu) 12901a6bed68SJoerg Wunsch unitattn = fdin_rd(fdc) & FDI_DCHG; 12911a6bed68SJoerg Wunsch else 12921a6bed68SJoerg Wunsch unitattn = 1; 12931a6bed68SJoerg Wunsch } else { 12941a6bed68SJoerg Wunsch set_motor(fdc, fd->fdsu, TURNON); 12951a6bed68SJoerg Wunsch unitattn = fdin_rd(fdc) & FDI_DCHG; 12961a6bed68SJoerg Wunsch set_motor(fdc, fd->fdsu, TURNOFF); 1297b39c878eSAndrey A. Chernov } 12981a6bed68SJoerg Wunsch if (unitattn && (rv = fdautoselect(dev)) != 0) 12991a6bed68SJoerg Wunsch return (rv); 13007ca0641bSAndrey A. Chernov } 13016182fdbdSPeter Wemm fd->flags |= FD_OPEN; 1302de0b7a63SPoul-Henning Kamp 1303de0b7a63SPoul-Henning Kamp if ((fdc->flags & FDC_NODMA) == 0) { 1304de0b7a63SPoul-Henning Kamp if (fdc->dmacnt++ == 0) { 1305de0b7a63SPoul-Henning Kamp isa_dma_acquire(fdc->dmachan); 1306de0b7a63SPoul-Henning Kamp isa_dmainit(fdc->dmachan, MAX_SEC_SIZE); 1307de0b7a63SPoul-Henning Kamp } 1308de0b7a63SPoul-Henning Kamp } 1309de0b7a63SPoul-Henning Kamp 13103fef646eSJoerg Wunsch /* 13113fef646eSJoerg Wunsch * Clearing the DMA overrun counter at open time is a bit messy. 13123fef646eSJoerg Wunsch * Since we're only managing one counter per controller, opening 13133fef646eSJoerg Wunsch * the second drive could mess it up. Anyway, if the DMA overrun 13143fef646eSJoerg Wunsch * condition is really persistent, it will eventually time out 13153fef646eSJoerg Wunsch * still. OTOH, clearing it here will ensure we'll at least start 13163fef646eSJoerg Wunsch * trying again after a previous (maybe even long ago) failure. 13173fef646eSJoerg Wunsch * Also, this is merely a stop-gap measure only that should not 13183fef646eSJoerg Wunsch * happen during normal operation, so we can tolerate it to be a 13193fef646eSJoerg Wunsch * bit sloppy about this. 13203fef646eSJoerg Wunsch */ 13213fef646eSJoerg Wunsch fdc->dma_overruns = 0; 13225f830ea2SJoerg Wunsch 13235b81b6b3SRodney W. Grimes return 0; 13245b81b6b3SRodney W. Grimes } 13255b81b6b3SRodney W. Grimes 132637c84183SPoul-Henning Kamp static int 132789c9c53dSPoul-Henning Kamp fdclose(struct cdev *dev, int flags, int mode, struct thread *td) 13285b81b6b3SRodney W. Grimes { 13296182fdbdSPeter Wemm struct fd_data *fd; 1330de0b7a63SPoul-Henning Kamp fdc_p fdc; 1331b99f0a4aSAndrew Moore 1332a2f19df9SPoul-Henning Kamp fd = dev->si_drv1; 1333de0b7a63SPoul-Henning Kamp fdc = fd->fdc; 13341a6bed68SJoerg Wunsch fd->flags &= ~(FD_OPEN | FD_NONBLOCK); 13352995d110SJoerg Wunsch fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR); 1336dc16046fSJoerg Wunsch 1337de0b7a63SPoul-Henning Kamp if ((fdc->flags & FDC_NODMA) == 0) 1338de0b7a63SPoul-Henning Kamp if (--fdc->dmacnt == 0) 1339de0b7a63SPoul-Henning Kamp isa_dma_release(fdc->dmachan); 1340de0b7a63SPoul-Henning Kamp 13415b81b6b3SRodney W. Grimes return (0); 13425b81b6b3SRodney W. Grimes } 13435b81b6b3SRodney W. Grimes 134437c84183SPoul-Henning Kamp static void 13458177437dSPoul-Henning Kamp fdstrategy(struct bio *bp) 13463a2f7427SDavid Greenman { 1347fb35bd37SJoerg Wunsch long blknum, nblocks; 13483a2f7427SDavid Greenman int s; 13493a2f7427SDavid Greenman fdu_t fdu; 13503a2f7427SDavid Greenman fdc_p fdc; 13513a2f7427SDavid Greenman fd_p fd; 13523a2f7427SDavid Greenman size_t fdblk; 13533a2f7427SDavid Greenman 1354a2f19df9SPoul-Henning Kamp fd = bp->bio_dev->si_drv1; 1355503799eaSPoul-Henning Kamp fdu = fd->fdu; 13563a2f7427SDavid Greenman fdc = fd->fdc; 135783efe35aSYaroslav Tykhiy bp->bio_resid = bp->bio_bcount; 13581a6bed68SJoerg Wunsch if (fd->type == FDT_NONE || fd->ft == 0) { 1359f79981acSYaroslav Tykhiy if (fd->type != FDT_NONE && (fd->flags & FD_NONBLOCK)) 1360f79981acSYaroslav Tykhiy bp->bio_error = EAGAIN; 1361f79981acSYaroslav Tykhiy else 13628177437dSPoul-Henning Kamp bp->bio_error = ENXIO; 13638177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 13643b178206SWarner Losh goto bad; 136564860614SJoerg Wunsch } 1366d3628763SRodney W. Grimes fdblk = 128 << (fd->ft->secsize); 1367419f39ceSPoul-Henning Kamp if (bp->bio_cmd != FDBIO_FORMAT && bp->bio_cmd != FDBIO_RDSECTID) { 13681a6bed68SJoerg Wunsch if (fd->flags & FD_NONBLOCK) { 13691a6bed68SJoerg Wunsch bp->bio_error = EAGAIN; 13701a6bed68SJoerg Wunsch bp->bio_flags |= BIO_ERROR; 13711a6bed68SJoerg Wunsch goto bad; 13721a6bed68SJoerg Wunsch } 1373b52b7f46SPoul-Henning Kamp if (bp->bio_offset < 0) { 1374dc5df763SJoerg Wunsch printf( 1375b52b7f46SPoul-Henning Kamp "fd%d: fdstrat: bad request offset = %ju, bcount = %ld\n", 1376b52b7f46SPoul-Henning Kamp fdu, (intmax_t)bp->bio_offset, bp->bio_bcount); 13778177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 13788177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 13793a2f7427SDavid Greenman goto bad; 13803a2f7427SDavid Greenman } 13818177437dSPoul-Henning Kamp if ((bp->bio_bcount % fdblk) != 0) { 13828177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 13838177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 13843a2f7427SDavid Greenman goto bad; 13853a2f7427SDavid Greenman } 13863a2f7427SDavid Greenman } 13873a2f7427SDavid Greenman 13883a2f7427SDavid Greenman /* 13893a2f7427SDavid Greenman * Set up block calculations. 13903a2f7427SDavid Greenman */ 1391b52b7f46SPoul-Henning Kamp if (bp->bio_offset >= ((off_t)128 << fd->ft->secsize) * fd->ft->size) { 13928177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 13938177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 13943a2f7427SDavid Greenman goto bad; 13953a2f7427SDavid Greenman } 1396b52b7f46SPoul-Henning Kamp blknum = bp->bio_offset / fdblk; 1397bb6382faSJoerg Wunsch nblocks = fd->ft->size; 1398fb35bd37SJoerg Wunsch if (blknum + bp->bio_bcount / fdblk > nblocks) { 1399fb35bd37SJoerg Wunsch if (blknum >= nblocks) { 140083efe35aSYaroslav Tykhiy if (bp->bio_cmd != BIO_READ) { 1401fb35bd37SJoerg Wunsch bp->bio_error = ENOSPC; 14028177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 1403bb6382faSJoerg Wunsch } 1404fb35bd37SJoerg Wunsch goto bad; /* not always bad, but EOF */ 1405fb35bd37SJoerg Wunsch } 1406fb35bd37SJoerg Wunsch bp->bio_bcount = (nblocks - blknum) * fdblk; 1407bb6382faSJoerg Wunsch } 14089a5e3ddbSJoerg Wunsch bp->bio_pblkno = blknum; 14093a2f7427SDavid Greenman s = splbio(); 1410891619a6SPoul-Henning Kamp bioq_disksort(&fdc->head, bp); 14116182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ 14121ecc485cSPoul-Henning Kamp devstat_start_transaction_bio(fd->device_stats, bp); 14135f830ea2SJoerg Wunsch device_busy(fd->dev); 14146182fdbdSPeter Wemm fdstart(fdc); 14153a2f7427SDavid Greenman splx(s); 14163a2f7427SDavid Greenman return; 14173a2f7427SDavid Greenman 14183a2f7427SDavid Greenman bad: 14193a2f7427SDavid Greenman biodone(bp); 14203a2f7427SDavid Greenman } 14213a2f7427SDavid Greenman 1422246ed35dSJoerg Wunsch /* 1423246ed35dSJoerg Wunsch * fdstart 1424246ed35dSJoerg Wunsch * 1425246ed35dSJoerg Wunsch * We have just queued something. If the controller is not busy 1426246ed35dSJoerg Wunsch * then simulate the case where it has just finished a command 1427246ed35dSJoerg Wunsch * So that it (the interrupt routine) looks on the queue for more 1428246ed35dSJoerg Wunsch * work to do and picks up what we just added. 1429246ed35dSJoerg Wunsch * 1430246ed35dSJoerg Wunsch * If the controller is already busy, we need do nothing, as it 1431246ed35dSJoerg Wunsch * will pick up our work when the present work completes. 1432246ed35dSJoerg Wunsch */ 1433381fe1aaSGarrett Wollman static void 14346182fdbdSPeter Wemm fdstart(struct fdc_data *fdc) 14355b81b6b3SRodney W. Grimes { 14365b81b6b3SRodney W. Grimes int s; 14375b81b6b3SRodney W. Grimes 14385b81b6b3SRodney W. Grimes s = splbio(); 14396182fdbdSPeter Wemm if(fdc->state == DEVIDLE) 14405b81b6b3SRodney W. Grimes { 14416182fdbdSPeter Wemm fdc_intr(fdc); 14425b81b6b3SRodney W. Grimes } 14435b81b6b3SRodney W. Grimes splx(s); 14445b81b6b3SRodney W. Grimes } 14455b81b6b3SRodney W. Grimes 1446381fe1aaSGarrett Wollman static void 14476182fdbdSPeter Wemm fd_iotimeout(void *xfdc) 14485b81b6b3SRodney W. Grimes { 14495c1a1eaeSBruce Evans fdc_p fdc; 1450f5f7ba03SJordan K. Hubbard int s; 14515b81b6b3SRodney W. Grimes 14526182fdbdSPeter Wemm fdc = xfdc; 14535c1a1eaeSBruce Evans TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); 14545b81b6b3SRodney W. Grimes 14553a2f7427SDavid Greenman /* 14563a2f7427SDavid Greenman * Due to IBM's brain-dead design, the FDC has a faked ready 14573a2f7427SDavid Greenman * signal, hardwired to ready == true. Thus, any command 14583a2f7427SDavid Greenman * issued if there's no diskette in the drive will _never_ 14593a2f7427SDavid Greenman * complete, and must be aborted by resetting the FDC. 14603a2f7427SDavid Greenman * Many thanks, Big Blue! 14615c1a1eaeSBruce Evans * The FDC must not be reset directly, since that would 14625c1a1eaeSBruce Evans * interfere with the state machine. Instead, pretend that 14635c1a1eaeSBruce Evans * the command completed but was invalid. The state machine 14645c1a1eaeSBruce Evans * will reset the FDC and retry once. 14653a2f7427SDavid Greenman */ 14663a2f7427SDavid Greenman s = splbio(); 14675c1a1eaeSBruce Evans fdc->status[0] = NE7_ST0_IC_IV; 14685c1a1eaeSBruce Evans fdc->flags &= ~FDC_STAT_VALID; 14695c1a1eaeSBruce Evans fdc->state = IOTIMEDOUT; 14706182fdbdSPeter Wemm fdc_intr(fdc); 1471f5f7ba03SJordan K. Hubbard splx(s); 14725b81b6b3SRodney W. Grimes } 14735b81b6b3SRodney W. Grimes 1474246ed35dSJoerg Wunsch /* Just ensure it has the right spl. */ 1475381fe1aaSGarrett Wollman static void 14766182fdbdSPeter Wemm fd_pseudointr(void *xfdc) 14775b81b6b3SRodney W. Grimes { 14785b81b6b3SRodney W. Grimes int s; 14793a2f7427SDavid Greenman 14805b81b6b3SRodney W. Grimes s = splbio(); 14816182fdbdSPeter Wemm fdc_intr(xfdc); 14825b81b6b3SRodney W. Grimes splx(s); 14835b81b6b3SRodney W. Grimes } 14845b81b6b3SRodney W. Grimes 1485246ed35dSJoerg Wunsch /* 1486246ed35dSJoerg Wunsch * fdc_intr 1487246ed35dSJoerg Wunsch * 1488246ed35dSJoerg Wunsch * Keep calling the state machine until it returns a 0. 1489246ed35dSJoerg Wunsch * Always called at splbio. 1490246ed35dSJoerg Wunsch */ 1491fe310de8SBruce Evans static void 14926182fdbdSPeter Wemm fdc_intr(void *xfdc) 14935b81b6b3SRodney W. Grimes { 14946182fdbdSPeter Wemm fdc_p fdc = xfdc; 14956182fdbdSPeter Wemm while(fdstate(fdc)) 1496381fe1aaSGarrett Wollman ; 14975b81b6b3SRodney W. Grimes } 14985b81b6b3SRodney W. Grimes 149969acd21dSWarner Losh /* 1500246ed35dSJoerg Wunsch * Magic pseudo-DMA initialization for YE FDC. Sets count and 1501246ed35dSJoerg Wunsch * direction. 150269acd21dSWarner Losh */ 15033b178206SWarner Losh #define SET_BCDR(fdc,wr,cnt,port) \ 15043b178206SWarner Losh bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port, \ 15053b178206SWarner Losh ((cnt)-1) & 0xff); \ 15063b178206SWarner Losh bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \ 15073b178206SWarner Losh ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))); 150869acd21dSWarner Losh 150969acd21dSWarner Losh /* 1510246ed35dSJoerg Wunsch * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy. 151169acd21dSWarner Losh */ 15124c34deeeSJoerg Wunsch static int 15134c34deeeSJoerg Wunsch fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) 151469acd21dSWarner Losh { 151569acd21dSWarner Losh u_char *cptr = (u_char *)addr; 151669acd21dSWarner Losh 151721144e3bSPoul-Henning Kamp if (flags == BIO_READ) { 151869acd21dSWarner Losh if (fdc->state != PIOREAD) { 151969acd21dSWarner Losh fdc->state = PIOREAD; 152069acd21dSWarner Losh return(0); 152164860614SJoerg Wunsch } 15223b178206SWarner Losh SET_BCDR(fdc, 0, count, 0); 15233b178206SWarner Losh bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off + 15243b178206SWarner Losh FDC_YE_DATAPORT, cptr, count); 152569acd21dSWarner Losh } else { 15263b178206SWarner Losh bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off + 15273b178206SWarner Losh FDC_YE_DATAPORT, cptr, count); 15283b178206SWarner Losh SET_BCDR(fdc, 0, count, 0); 152964860614SJoerg Wunsch } 153069acd21dSWarner Losh return(1); 153169acd21dSWarner Losh } 153269acd21dSWarner Losh 1533246ed35dSJoerg Wunsch /* 15341a6bed68SJoerg Wunsch * Try figuring out the density of the media present in our device. 15351a6bed68SJoerg Wunsch */ 15361a6bed68SJoerg Wunsch static int 153789c9c53dSPoul-Henning Kamp fdautoselect(struct cdev *dev) 15381a6bed68SJoerg Wunsch { 15391a6bed68SJoerg Wunsch fd_p fd; 15401a6bed68SJoerg Wunsch struct fd_type *fdtp; 15411a6bed68SJoerg Wunsch struct fdc_readid id; 15421a6bed68SJoerg Wunsch int i, n, oopts, rv; 15431a6bed68SJoerg Wunsch 1544a2f19df9SPoul-Henning Kamp fd = dev->si_drv1; 15451a6bed68SJoerg Wunsch 15461a6bed68SJoerg Wunsch switch (fd->type) { 15471a6bed68SJoerg Wunsch default: 15481a6bed68SJoerg Wunsch return (ENXIO); 15491a6bed68SJoerg Wunsch 15501a6bed68SJoerg Wunsch case FDT_360K: 15511a6bed68SJoerg Wunsch case FDT_720K: 15521a6bed68SJoerg Wunsch /* no autoselection on those drives */ 15531a6bed68SJoerg Wunsch fd->ft = fd_native_types + fd->type; 15541a6bed68SJoerg Wunsch return (0); 15551a6bed68SJoerg Wunsch 15561a6bed68SJoerg Wunsch case FDT_12M: 15571a6bed68SJoerg Wunsch fdtp = fd_searchlist_12m; 15581a6bed68SJoerg Wunsch n = sizeof fd_searchlist_12m / sizeof(struct fd_type); 15591a6bed68SJoerg Wunsch break; 15601a6bed68SJoerg Wunsch 15611a6bed68SJoerg Wunsch case FDT_144M: 15621a6bed68SJoerg Wunsch fdtp = fd_searchlist_144m; 15631a6bed68SJoerg Wunsch n = sizeof fd_searchlist_144m / sizeof(struct fd_type); 15641a6bed68SJoerg Wunsch break; 15651a6bed68SJoerg Wunsch 15661a6bed68SJoerg Wunsch case FDT_288M: 15671a6bed68SJoerg Wunsch fdtp = fd_searchlist_288m; 15681a6bed68SJoerg Wunsch n = sizeof fd_searchlist_288m / sizeof(struct fd_type); 15691a6bed68SJoerg Wunsch break; 15701a6bed68SJoerg Wunsch } 15711a6bed68SJoerg Wunsch 15721a6bed68SJoerg Wunsch /* 15731a6bed68SJoerg Wunsch * Try reading sector ID fields, first at cylinder 0, head 0, 15741a6bed68SJoerg Wunsch * then at cylinder 2, head N. We don't probe cylinder 1, 15751a6bed68SJoerg Wunsch * since for 5.25in DD media in a HD drive, there are no data 15761a6bed68SJoerg Wunsch * to read (2 step pulses per media cylinder required). For 15771a6bed68SJoerg Wunsch * two-sided media, the second probe always goes to head 1, so 15781a6bed68SJoerg Wunsch * we can tell them apart from single-sided media. As a 15791a6bed68SJoerg Wunsch * side-effect this means that single-sided media should be 15801a6bed68SJoerg Wunsch * mentioned in the search list after two-sided media of an 15811a6bed68SJoerg Wunsch * otherwise identical density. Media with a different number 15821a6bed68SJoerg Wunsch * of sectors per track but otherwise identical parameters 15831a6bed68SJoerg Wunsch * cannot be distinguished at all. 15841a6bed68SJoerg Wunsch * 15851a6bed68SJoerg Wunsch * If we successfully read an ID field on both cylinders where 15861a6bed68SJoerg Wunsch * the recorded values match our expectation, we are done. 15871a6bed68SJoerg Wunsch * Otherwise, we try the next density entry from the table. 15881a6bed68SJoerg Wunsch * 15891a6bed68SJoerg Wunsch * Stepping to cylinder 2 has the side-effect of clearing the 15901a6bed68SJoerg Wunsch * unit attention bit. 15911a6bed68SJoerg Wunsch */ 15921a6bed68SJoerg Wunsch oopts = fd->options; 15931a6bed68SJoerg Wunsch fd->options |= FDOPT_NOERRLOG | FDOPT_NORETRY; 15941a6bed68SJoerg Wunsch for (i = 0; i < n; i++, fdtp++) { 15951a6bed68SJoerg Wunsch fd->ft = fdtp; 15961a6bed68SJoerg Wunsch 15971a6bed68SJoerg Wunsch id.cyl = id.head = 0; 1598419f39ceSPoul-Henning Kamp rv = fdmisccmd(dev, FDBIO_RDSECTID, &id); 15991a6bed68SJoerg Wunsch if (rv != 0) 16001a6bed68SJoerg Wunsch continue; 16011a6bed68SJoerg Wunsch if (id.cyl != 0 || id.head != 0 || 16021a6bed68SJoerg Wunsch id.secshift != fdtp->secsize) 16031a6bed68SJoerg Wunsch continue; 16041a6bed68SJoerg Wunsch id.cyl = 2; 16051a6bed68SJoerg Wunsch id.head = fd->ft->heads - 1; 1606419f39ceSPoul-Henning Kamp rv = fdmisccmd(dev, FDBIO_RDSECTID, &id); 16071a6bed68SJoerg Wunsch if (id.cyl != 2 || id.head != fdtp->heads - 1 || 16081a6bed68SJoerg Wunsch id.secshift != fdtp->secsize) 16091a6bed68SJoerg Wunsch continue; 16101a6bed68SJoerg Wunsch if (rv == 0) 16111a6bed68SJoerg Wunsch break; 16121a6bed68SJoerg Wunsch } 16131a6bed68SJoerg Wunsch 16141a6bed68SJoerg Wunsch fd->options = oopts; 16151a6bed68SJoerg Wunsch if (i == n) { 16165613959dSJoerg Wunsch if (bootverbose) 16171a6bed68SJoerg Wunsch device_printf(fd->dev, "autoselection failed\n"); 16181a6bed68SJoerg Wunsch fd->ft = 0; 16191a6bed68SJoerg Wunsch return (EIO); 16201a6bed68SJoerg Wunsch } else { 16215613959dSJoerg Wunsch if (bootverbose) 16221a6bed68SJoerg Wunsch device_printf(fd->dev, "autoselected %d KB medium\n", 16231a6bed68SJoerg Wunsch fd->ft->size / 2); 16241a6bed68SJoerg Wunsch return (0); 16251a6bed68SJoerg Wunsch } 16261a6bed68SJoerg Wunsch } 16271a6bed68SJoerg Wunsch 16281a6bed68SJoerg Wunsch 16291a6bed68SJoerg Wunsch /* 1630246ed35dSJoerg Wunsch * The controller state machine. 1631246ed35dSJoerg Wunsch * 1632246ed35dSJoerg Wunsch * If it returns a non zero value, it should be called again immediately. 1633246ed35dSJoerg Wunsch */ 16343a2f7427SDavid Greenman static int 16356182fdbdSPeter Wemm fdstate(fdc_p fdc) 16365b81b6b3SRodney W. Grimes { 163764860614SJoerg Wunsch struct fdc_readid *idp; 1638fb35bd37SJoerg Wunsch int read, format, rdsectid, cylinder, head, i, sec = 0, sectrac; 16391a6bed68SJoerg Wunsch int st0, cyl, st3, idf, ne7cmd, mfm, steptrac; 1640fb35bd37SJoerg Wunsch unsigned long blknum; 16415b81b6b3SRodney W. Grimes fdu_t fdu = fdc->fdu; 16425b81b6b3SRodney W. Grimes fd_p fd; 16438177437dSPoul-Henning Kamp register struct bio *bp; 1644b39c878eSAndrey A. Chernov struct fd_formb *finfo = NULL; 16453a2f7427SDavid Greenman size_t fdblk; 16465b81b6b3SRodney W. Grimes 1647e93e63cbSBruce Evans bp = fdc->bp; 1648e93e63cbSBruce Evans if (bp == NULL) { 16498177437dSPoul-Henning Kamp bp = bioq_first(&fdc->head); 1650e93e63cbSBruce Evans if (bp != NULL) { 16518177437dSPoul-Henning Kamp bioq_remove(&fdc->head, bp); 1652e93e63cbSBruce Evans fdc->bp = bp; 1653e93e63cbSBruce Evans } 1654e93e63cbSBruce Evans } 1655e93e63cbSBruce Evans if (bp == NULL) { 1656246ed35dSJoerg Wunsch /* 1657246ed35dSJoerg Wunsch * Nothing left for this controller to do, 1658246ed35dSJoerg Wunsch * force into the IDLE state. 1659246ed35dSJoerg Wunsch */ 16605b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 16616182fdbdSPeter Wemm if (fdc->fd) { 1662b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, 1663b6e5f28eSPeter Wemm "unexpected valid fd pointer\n"); 16645b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 16655b81b6b3SRodney W. Grimes fdc->fdu = -1; 16665b81b6b3SRodney W. Grimes } 16676182fdbdSPeter Wemm TRACE1("[fdc%d IDLE]", fdc->fdcu); 16685b81b6b3SRodney W. Grimes return (0); 16695b81b6b3SRodney W. Grimes } 1670a2f19df9SPoul-Henning Kamp fd = bp->bio_dev->si_drv1; 1671503799eaSPoul-Henning Kamp fdu = fd->fdu; 16723a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 1673b6e5f28eSPeter Wemm if (fdc->fd && (fd != fdc->fd)) 1674b6e5f28eSPeter Wemm device_printf(fd->dev, "confused fd pointers\n"); 16758177437dSPoul-Henning Kamp read = bp->bio_cmd == BIO_READ; 16761a6bed68SJoerg Wunsch mfm = (fd->ft->flags & FL_MFM)? NE7CMD_MFM: 0; 16771a6bed68SJoerg Wunsch steptrac = (fd->ft->flags & FL_2STEP)? 2: 1; 167856a23089SPoul-Henning Kamp if (read) 167956a23089SPoul-Henning Kamp idf = ISADMA_READ; 168056a23089SPoul-Henning Kamp else 168156a23089SPoul-Henning Kamp idf = ISADMA_WRITE; 1682419f39ceSPoul-Henning Kamp format = bp->bio_cmd == FDBIO_FORMAT; 1683419f39ceSPoul-Henning Kamp rdsectid = bp->bio_cmd == FDBIO_RDSECTID; 1684fb35bd37SJoerg Wunsch if (format) 16858177437dSPoul-Henning Kamp finfo = (struct fd_formb *)bp->bio_data; 16865b81b6b3SRodney W. Grimes TRACE1("fd%d", fdu); 16875b81b6b3SRodney W. Grimes TRACE1("[%s]", fdstates[fdc->state]); 16885b81b6b3SRodney W. Grimes TRACE1("(0x%x)", fd->flags); 16896182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); 16906182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 16915b81b6b3SRodney W. Grimes switch (fdc->state) 16925b81b6b3SRodney W. Grimes { 16935b81b6b3SRodney W. Grimes case DEVIDLE: 16945b81b6b3SRodney W. Grimes case FINDWORK: /* we have found new work */ 16955b81b6b3SRodney W. Grimes fdc->retry = 0; 16965b81b6b3SRodney W. Grimes fd->skip = 0; 16975b81b6b3SRodney W. Grimes fdc->fd = fd; 16985b81b6b3SRodney W. Grimes fdc->fdu = fdu; 16995f830ea2SJoerg Wunsch fdc->fdctl_wr(fdc, fd->ft->trans); 17003a2f7427SDavid Greenman TRACE1("[0x%x->FDCTL]", fd->ft->trans); 1701246ed35dSJoerg Wunsch /* 1702246ed35dSJoerg Wunsch * If the next drive has a motor startup pending, then 1703246ed35dSJoerg Wunsch * it will start up in its own good time. 1704246ed35dSJoerg Wunsch */ 17056182fdbdSPeter Wemm if(fd->flags & FD_MOTOR_WAIT) { 17065b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 1707246ed35dSJoerg Wunsch return (0); /* will return later */ 17085b81b6b3SRodney W. Grimes } 1709246ed35dSJoerg Wunsch /* 1710246ed35dSJoerg Wunsch * Maybe if it's not starting, it SHOULD be starting. 1711246ed35dSJoerg Wunsch */ 17125b81b6b3SRodney W. Grimes if (!(fd->flags & FD_MOTOR)) 17135b81b6b3SRodney W. Grimes { 17145b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 17156182fdbdSPeter Wemm fd_turnon(fd); 1716246ed35dSJoerg Wunsch return (0); /* will return later */ 17175b81b6b3SRodney W. Grimes } 17185b81b6b3SRodney W. Grimes else /* at least make sure we are selected */ 17195b81b6b3SRodney W. Grimes { 17206182fdbdSPeter Wemm set_motor(fdc, fd->fdsu, TURNON); 17215b81b6b3SRodney W. Grimes } 17225c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 17235c1a1eaeSBruce Evans fdc->state = RESETCTLR; 17245c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 17255c1a1eaeSBruce Evans } else 17265e235068SJordan K. Hubbard fdc->state = DOSEEK; 1727246ed35dSJoerg Wunsch return (1); /* will return immediately */ 1728fb35bd37SJoerg Wunsch 17295b81b6b3SRodney W. Grimes case DOSEEK: 1730fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 1731fb35bd37SJoerg Wunsch cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1732fb35bd37SJoerg Wunsch if (cylinder == fd->track) 17335b81b6b3SRodney W. Grimes { 17345b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 1735246ed35dSJoerg Wunsch return (1); /* will return immediately */ 17365b81b6b3SRodney W. Grimes } 17376182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, 17381a6bed68SJoerg Wunsch fd->fdsu, cylinder * steptrac, 0)) 1739dc8603e3SJoerg Wunsch { 1740dc8603e3SJoerg Wunsch /* 1741246ed35dSJoerg Wunsch * Seek command not accepted, looks like 1742dc8603e3SJoerg Wunsch * the FDC went off to the Saints... 1743dc8603e3SJoerg Wunsch */ 1744dc8603e3SJoerg Wunsch fdc->retry = 6; /* try a reset */ 17456182fdbdSPeter Wemm return(retrier(fdc)); 1746dc8603e3SJoerg Wunsch } 1747dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 17485b81b6b3SRodney W. Grimes fdc->state = SEEKWAIT; 17495b81b6b3SRodney W. Grimes return(0); /* will return later */ 1750fb35bd37SJoerg Wunsch 17515b81b6b3SRodney W. Grimes case SEEKWAIT: 17525b81b6b3SRodney W. Grimes /* allow heads to settle */ 17536182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 16); 17545b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 17555b81b6b3SRodney W. Grimes return(0); /* will return later */ 1756fb35bd37SJoerg Wunsch 1757246ed35dSJoerg Wunsch case SEEKCOMPLETE : /* seek done, start DMA */ 1758fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 1759fb35bd37SJoerg Wunsch cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1760fb35bd37SJoerg Wunsch 1761246ed35dSJoerg Wunsch /* Make sure seek really happened. */ 17626182fdbdSPeter Wemm if(fd->track == FD_NO_TRACK) { 17631a6bed68SJoerg Wunsch int descyl = cylinder * steptrac; 17643a2f7427SDavid Greenman do { 17653a2f7427SDavid Greenman /* 1766dc5df763SJoerg Wunsch * This might be a "ready changed" interrupt, 1767dc5df763SJoerg Wunsch * which cannot really happen since the 1768dc5df763SJoerg Wunsch * RDY pin is hardwired to + 5 volts. This 1769dc5df763SJoerg Wunsch * generally indicates a "bouncing" intr 1770dc5df763SJoerg Wunsch * line, so do one of the following: 1771dc5df763SJoerg Wunsch * 1772dc5df763SJoerg Wunsch * When running on an enhanced FDC that is 1773dc5df763SJoerg Wunsch * known to not go stuck after responding 1774dc5df763SJoerg Wunsch * with INVALID, fetch all interrupt states 1775dc5df763SJoerg Wunsch * until seeing either an INVALID or a 1776dc5df763SJoerg Wunsch * real interrupt condition. 1777dc5df763SJoerg Wunsch * 1778dc5df763SJoerg Wunsch * When running on a dumb old NE765, give 1779dc5df763SJoerg Wunsch * up immediately. The controller will 1780dc5df763SJoerg Wunsch * provide up to four dummy RC interrupt 1781dc5df763SJoerg Wunsch * conditions right after reset (for the 1782dc5df763SJoerg Wunsch * corresponding four drives), so this is 1783dc5df763SJoerg Wunsch * our only chance to get notice that it 1784dc5df763SJoerg Wunsch * was not the FDC that caused the interrupt. 17853a2f7427SDavid Greenman */ 1786dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) 1787dc5df763SJoerg Wunsch == FD_NOT_VALID) 1788246ed35dSJoerg Wunsch return (0); /* will return later */ 1789dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 1790dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 1791246ed35dSJoerg Wunsch return (0); /* hope for a real intr */ 17923a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 1793dc5df763SJoerg Wunsch 17946182fdbdSPeter Wemm if (0 == descyl) { 1795dc5df763SJoerg Wunsch int failed = 0; 17963a2f7427SDavid Greenman /* 17973a2f7427SDavid Greenman * seek to cyl 0 requested; make sure we are 17983a2f7427SDavid Greenman * really there 17993a2f7427SDavid Greenman */ 1800dc5df763SJoerg Wunsch if (fd_sense_drive_status(fdc, &st3)) 1801dc5df763SJoerg Wunsch failed = 1; 18023a2f7427SDavid Greenman if ((st3 & NE7_ST3_T0) == 0) { 18033a2f7427SDavid Greenman printf( 18043a2f7427SDavid Greenman "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", 18053a2f7427SDavid Greenman fdu, st3, NE7_ST3BITS); 1806dc5df763SJoerg Wunsch failed = 1; 1807dc5df763SJoerg Wunsch } 1808dc5df763SJoerg Wunsch 18096182fdbdSPeter Wemm if (failed) { 18103a2f7427SDavid Greenman if(fdc->retry < 3) 18113a2f7427SDavid Greenman fdc->retry = 3; 18126182fdbdSPeter Wemm return (retrier(fdc)); 18133a2f7427SDavid Greenman } 18143a2f7427SDavid Greenman } 1815dc5df763SJoerg Wunsch 18166182fdbdSPeter Wemm if (cyl != descyl) { 18173a2f7427SDavid Greenman printf( 18183a2f7427SDavid Greenman "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", 18192d9d0204SRodney W. Grimes fdu, descyl, cyl, st0); 1820e5d7d243SBruce Evans if (fdc->retry < 3) 1821e5d7d243SBruce Evans fdc->retry = 3; 18226182fdbdSPeter Wemm return (retrier(fdc)); 18235b81b6b3SRodney W. Grimes } 18245b81b6b3SRodney W. Grimes } 18255b81b6b3SRodney W. Grimes 1826fb35bd37SJoerg Wunsch fd->track = cylinder; 1827fb35bd37SJoerg Wunsch if (format) 1828fb35bd37SJoerg Wunsch fd->skip = (char *)&(finfo->fd_formb_cylno(0)) 1829fb35bd37SJoerg Wunsch - (char *)finfo; 1830250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 18318177437dSPoul-Henning Kamp isa_dmastart(idf, bp->bio_data+fd->skip, 18328177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, fdc->dmachan); 1833fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 18345b81b6b3SRodney W. Grimes sectrac = fd->ft->sectrac; 18355b81b6b3SRodney W. Grimes sec = blknum % (sectrac * fd->ft->heads); 18365b81b6b3SRodney W. Grimes head = sec / sectrac; 18375b81b6b3SRodney W. Grimes sec = sec % sectrac + 1; 18381a6bed68SJoerg Wunsch if (head != 0 && fd->ft->offset_side2 != 0) 18391a6bed68SJoerg Wunsch sec += fd->ft->offset_side2; 18403a2f7427SDavid Greenman fd->hddrv = ((head&1)<<2)+fdu; 18413a2f7427SDavid Greenman 1842250300ebSJoerg Wunsch if(format || !(read || rdsectid)) 18433a2f7427SDavid Greenman { 18443a2f7427SDavid Greenman /* make sure the drive is writable */ 1845dc5df763SJoerg Wunsch if(fd_sense_drive_status(fdc, &st3) != 0) 1846dc8603e3SJoerg Wunsch { 1847dc8603e3SJoerg Wunsch /* stuck controller? */ 18485f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 184956a23089SPoul-Henning Kamp isa_dmadone(idf, 18508177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 18518177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 18525c1a1eaeSBruce Evans fdc->dmachan); 1853dc8603e3SJoerg Wunsch fdc->retry = 6; /* reset the beast */ 18546182fdbdSPeter Wemm return (retrier(fdc)); 1855dc8603e3SJoerg Wunsch } 18563a2f7427SDavid Greenman if(st3 & NE7_ST3_WP) 18573a2f7427SDavid Greenman { 18583a2f7427SDavid Greenman /* 18593a2f7427SDavid Greenman * XXX YES! this is ugly. 18603a2f7427SDavid Greenman * in order to force the current operation 18613a2f7427SDavid Greenman * to fail, we will have to fake an FDC 18623a2f7427SDavid Greenman * error - all error handling is done 18633a2f7427SDavid Greenman * by the retrier() 18643a2f7427SDavid Greenman */ 18653a2f7427SDavid Greenman fdc->status[0] = NE7_ST0_IC_AT; 18663a2f7427SDavid Greenman fdc->status[1] = NE7_ST1_NW; 18673a2f7427SDavid Greenman fdc->status[2] = 0; 18683a2f7427SDavid Greenman fdc->status[3] = fd->track; 18693a2f7427SDavid Greenman fdc->status[4] = head; 18703a2f7427SDavid Greenman fdc->status[5] = sec; 18713a2f7427SDavid Greenman fdc->retry = 8; /* break out immediately */ 18723a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; /* not really... */ 1873246ed35dSJoerg Wunsch return (1); /* will return immediately */ 18743a2f7427SDavid Greenman } 18753a2f7427SDavid Greenman } 18765b81b6b3SRodney W. Grimes 18776182fdbdSPeter Wemm if (format) { 18781a6bed68SJoerg Wunsch ne7cmd = NE7CMD_FORMAT | mfm; 18795f830ea2SJoerg Wunsch if (fdc->flags & FDC_NODMA) { 18805f830ea2SJoerg Wunsch /* 18815f830ea2SJoerg Wunsch * This seems to be necessary for 18825f830ea2SJoerg Wunsch * whatever obscure reason; if we omit 18835f830ea2SJoerg Wunsch * it, we end up filling the sector ID 18845f830ea2SJoerg Wunsch * fields of the newly formatted track 18855f830ea2SJoerg Wunsch * entirely with garbage, causing 18865f830ea2SJoerg Wunsch * `wrong cylinder' errors all over 18875f830ea2SJoerg Wunsch * the place when trying to read them 18885f830ea2SJoerg Wunsch * back. 18895f830ea2SJoerg Wunsch * 18905f830ea2SJoerg Wunsch * Umpf. 18915f830ea2SJoerg Wunsch */ 18928177437dSPoul-Henning Kamp SET_BCDR(fdc, 1, bp->bio_bcount, 0); 18935f830ea2SJoerg Wunsch 18948177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd, 18958177437dSPoul-Henning Kamp bp->bio_data+fd->skip, 18968177437dSPoul-Henning Kamp bp->bio_bcount); 18975f830ea2SJoerg Wunsch 18985f830ea2SJoerg Wunsch } 1899b39c878eSAndrey A. Chernov /* formatting */ 19001a6bed68SJoerg Wunsch if(fd_cmd(fdc, 6, ne7cmd, head << 2 | fdu, 1901dc5df763SJoerg Wunsch finfo->fd_formb_secshift, 1902dc5df763SJoerg Wunsch finfo->fd_formb_nsecs, 1903dc5df763SJoerg Wunsch finfo->fd_formb_gaplen, 19046182fdbdSPeter Wemm finfo->fd_formb_fillbyte, 0)) { 1905dc8603e3SJoerg Wunsch /* controller fell over */ 19065f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 190756a23089SPoul-Henning Kamp isa_dmadone(idf, 19088177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 19098177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 19105c1a1eaeSBruce Evans fdc->dmachan); 1911dc8603e3SJoerg Wunsch fdc->retry = 6; 19126182fdbdSPeter Wemm return (retrier(fdc)); 1913dc8603e3SJoerg Wunsch } 1914250300ebSJoerg Wunsch } else if (rdsectid) { 19151a6bed68SJoerg Wunsch ne7cmd = NE7CMD_READID | mfm; 19161a6bed68SJoerg Wunsch if (fd_cmd(fdc, 2, ne7cmd, head << 2 | fdu, 0)) { 1917250300ebSJoerg Wunsch /* controller jamming */ 1918250300ebSJoerg Wunsch fdc->retry = 6; 1919250300ebSJoerg Wunsch return (retrier(fdc)); 1920250300ebSJoerg Wunsch } 19216182fdbdSPeter Wemm } else { 1922250300ebSJoerg Wunsch /* read or write operation */ 19231a6bed68SJoerg Wunsch ne7cmd = (read ? NE7CMD_READ | NE7CMD_SK : NE7CMD_WRITE) | mfm; 19243b178206SWarner Losh if (fdc->flags & FDC_NODMA) { 192569acd21dSWarner Losh /* 1926246ed35dSJoerg Wunsch * This seems to be necessary even when 1927246ed35dSJoerg Wunsch * reading data. 192869acd21dSWarner Losh */ 19293b178206SWarner Losh SET_BCDR(fdc, 1, fdblk, 0); 193069acd21dSWarner Losh 193169acd21dSWarner Losh /* 1932246ed35dSJoerg Wunsch * Perform the write pseudo-DMA before 1933246ed35dSJoerg Wunsch * the WRITE command is sent. 193469acd21dSWarner Losh */ 193569acd21dSWarner Losh if (!read) 19368177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd, 19378177437dSPoul-Henning Kamp bp->bio_data+fd->skip, 193869acd21dSWarner Losh fdblk); 193969acd21dSWarner Losh } 19406182fdbdSPeter Wemm if (fd_cmd(fdc, 9, 19411a6bed68SJoerg Wunsch ne7cmd, 1942dc5df763SJoerg Wunsch head << 2 | fdu, /* head & unit */ 1943dc5df763SJoerg Wunsch fd->track, /* track */ 1944dc5df763SJoerg Wunsch head, 1945dc5df763SJoerg Wunsch sec, /* sector + 1 */ 1946dc5df763SJoerg Wunsch fd->ft->secsize, /* sector size */ 1947dc5df763SJoerg Wunsch sectrac, /* sectors/track */ 1948dc5df763SJoerg Wunsch fd->ft->gap, /* gap size */ 1949dc5df763SJoerg Wunsch fd->ft->datalen, /* data length */ 19506182fdbdSPeter Wemm 0)) { 1951dc8603e3SJoerg Wunsch /* the beast is sleeping again */ 19525f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 195356a23089SPoul-Henning Kamp isa_dmadone(idf, 19548177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 19558177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 19565c1a1eaeSBruce Evans fdc->dmachan); 1957dc8603e3SJoerg Wunsch fdc->retry = 6; 19586182fdbdSPeter Wemm return (retrier(fdc)); 19595b81b6b3SRodney W. Grimes } 1960b39c878eSAndrey A. Chernov } 1961250300ebSJoerg Wunsch if (!rdsectid && (fdc->flags & FDC_NODMA)) 196269acd21dSWarner Losh /* 1963246ed35dSJoerg Wunsch * If this is a read, then simply await interrupt 1964246ed35dSJoerg Wunsch * before performing PIO. 196569acd21dSWarner Losh */ 19668177437dSPoul-Henning Kamp if (read && !fdcpio(fdc,bp->bio_cmd, 19678177437dSPoul-Henning Kamp bp->bio_data+fd->skip,fdblk)) { 19683b178206SWarner Losh fd->tohandle = timeout(fd_iotimeout, fdc, hz); 196969acd21dSWarner Losh return(0); /* will return later */ 197064860614SJoerg Wunsch } 197169acd21dSWarner Losh 197269acd21dSWarner Losh /* 1973246ed35dSJoerg Wunsch * Write (or format) operation will fall through and 1974246ed35dSJoerg Wunsch * await completion interrupt. 197569acd21dSWarner Losh */ 19765b81b6b3SRodney W. Grimes fdc->state = IOCOMPLETE; 19776182fdbdSPeter Wemm fd->tohandle = timeout(fd_iotimeout, fdc, hz); 19785b81b6b3SRodney W. Grimes return (0); /* will return later */ 1979fb35bd37SJoerg Wunsch 198069acd21dSWarner Losh case PIOREAD: 198169acd21dSWarner Losh /* 1982246ed35dSJoerg Wunsch * Actually perform the PIO read. The IOCOMPLETE case 198369acd21dSWarner Losh * removes the timeout for us. 198469acd21dSWarner Losh */ 19858177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk); 198669acd21dSWarner Losh fdc->state = IOCOMPLETE; 198769acd21dSWarner Losh /* FALLTHROUGH */ 1988246ed35dSJoerg Wunsch case IOCOMPLETE: /* IO done, post-analyze */ 19896182fdbdSPeter Wemm untimeout(fd_iotimeout, fdc, fd->tohandle); 1990dc5df763SJoerg Wunsch 199164860614SJoerg Wunsch if (fd_read_status(fdc)) { 1992250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 19938177437dSPoul-Henning Kamp isa_dmadone(idf, bp->bio_data + fd->skip, 19948177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 19955c1a1eaeSBruce Evans fdc->dmachan); 1996dc5df763SJoerg Wunsch if (fdc->retry < 6) 1997dc5df763SJoerg Wunsch fdc->retry = 6; /* force a reset */ 19986182fdbdSPeter Wemm return (retrier(fdc)); 19995b81b6b3SRodney W. Grimes } 2000dc5df763SJoerg Wunsch 20013a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; 2002dc5df763SJoerg Wunsch 20033a2f7427SDavid Greenman /* FALLTHROUGH */ 20043a2f7427SDavid Greenman case IOTIMEDOUT: 2005250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 20068177437dSPoul-Henning Kamp isa_dmadone(idf, bp->bio_data + fd->skip, 20078177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, fdc->dmachan); 20086182fdbdSPeter Wemm if (fdc->status[0] & NE7_ST0_IC) { 20093a2f7427SDavid Greenman if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 20103a2f7427SDavid Greenman && fdc->status[1] & NE7_ST1_OR) { 2011b39c878eSAndrey A. Chernov /* 20123fef646eSJoerg Wunsch * DMA overrun. Someone hogged the bus and 20133fef646eSJoerg Wunsch * didn't release it in time for the next 20143fef646eSJoerg Wunsch * FDC transfer. 20153fef646eSJoerg Wunsch * 20163fef646eSJoerg Wunsch * We normally restart this without bumping 20173fef646eSJoerg Wunsch * the retry counter. However, in case 20183fef646eSJoerg Wunsch * something is seriously messed up (like 20193fef646eSJoerg Wunsch * broken hardware), we rather limit the 20203fef646eSJoerg Wunsch * number of retries so the IO operation 20213fef646eSJoerg Wunsch * doesn't block indefinately. 2022b39c878eSAndrey A. Chernov */ 20233fef646eSJoerg Wunsch if (fdc->dma_overruns++ < FDC_DMAOV_MAX) { 2024b39c878eSAndrey A. Chernov fdc->state = SEEKCOMPLETE; 2025246ed35dSJoerg Wunsch return (1);/* will return immediately */ 20263fef646eSJoerg Wunsch } /* else fall through */ 2027b39c878eSAndrey A. Chernov } 20283fef646eSJoerg Wunsch if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV 20293a2f7427SDavid Greenman && fdc->retry < 6) 20303a2f7427SDavid Greenman fdc->retry = 6; /* force a reset */ 20313a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 20323a2f7427SDavid Greenman && fdc->status[2] & NE7_ST2_WC 20333a2f7427SDavid Greenman && fdc->retry < 3) 20343a2f7427SDavid Greenman fdc->retry = 3; /* force recalibrate */ 20356182fdbdSPeter Wemm return (retrier(fdc)); 20365b81b6b3SRodney W. Grimes } 20375b81b6b3SRodney W. Grimes /* All OK */ 2038250300ebSJoerg Wunsch if (rdsectid) { 2039250300ebSJoerg Wunsch /* copy out ID field contents */ 204064860614SJoerg Wunsch idp = (struct fdc_readid *)bp->bio_data; 2041250300ebSJoerg Wunsch idp->cyl = fdc->status[3]; 2042250300ebSJoerg Wunsch idp->head = fdc->status[4]; 2043250300ebSJoerg Wunsch idp->sec = fdc->status[5]; 2044250300ebSJoerg Wunsch idp->secshift = fdc->status[6]; 2045250300ebSJoerg Wunsch } 20463fef646eSJoerg Wunsch /* Operation successful, retry DMA overruns again next time. */ 20473fef646eSJoerg Wunsch fdc->dma_overruns = 0; 20483a2f7427SDavid Greenman fd->skip += fdblk; 2049fb35bd37SJoerg Wunsch if (!rdsectid && !format && fd->skip < bp->bio_bcount) { 20505b81b6b3SRodney W. Grimes /* set up next transfer */ 20515b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 20526182fdbdSPeter Wemm } else { 20535b81b6b3SRodney W. Grimes /* ALL DONE */ 20545b81b6b3SRodney W. Grimes fd->skip = 0; 2055fb35bd37SJoerg Wunsch bp->bio_resid = 0; 2056e93e63cbSBruce Evans fdc->bp = NULL; 20575f830ea2SJoerg Wunsch device_unbusy(fd->dev); 205880980460SPoul-Henning Kamp biofinish(bp, fd->device_stats, 0); 20595b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 20605b81b6b3SRodney W. Grimes fdc->fdu = -1; 20615b81b6b3SRodney W. Grimes fdc->state = FINDWORK; 20625b81b6b3SRodney W. Grimes } 2063246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2064fb35bd37SJoerg Wunsch 20655b81b6b3SRodney W. Grimes case RESETCTLR: 20663a2f7427SDavid Greenman fdc_reset(fdc); 20675b81b6b3SRodney W. Grimes fdc->retry++; 20685c1a1eaeSBruce Evans fdc->state = RESETCOMPLETE; 2069246ed35dSJoerg Wunsch return (0); /* will return later */ 2070fb35bd37SJoerg Wunsch 20715c1a1eaeSBruce Evans case RESETCOMPLETE: 20725c1a1eaeSBruce Evans /* 20735c1a1eaeSBruce Evans * Discard all the results from the reset so that they 20745c1a1eaeSBruce Evans * can't cause an unexpected interrupt later. 20755c1a1eaeSBruce Evans */ 20760e317d05SJoerg Wunsch for (i = 0; i < 4; i++) 20770e317d05SJoerg Wunsch (void)fd_sense_int(fdc, &st0, &cyl); 20785c1a1eaeSBruce Evans fdc->state = STARTRECAL; 2079fb35bd37SJoerg Wunsch /* FALLTHROUGH */ 20805c1a1eaeSBruce Evans case STARTRECAL: 20816182fdbdSPeter Wemm if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { 2082dc8603e3SJoerg Wunsch /* arrgl */ 2083dc8603e3SJoerg Wunsch fdc->retry = 6; 20846182fdbdSPeter Wemm return (retrier(fdc)); 2085dc8603e3SJoerg Wunsch } 20865b81b6b3SRodney W. Grimes fdc->state = RECALWAIT; 20875b81b6b3SRodney W. Grimes return (0); /* will return later */ 2088fb35bd37SJoerg Wunsch 20895b81b6b3SRodney W. Grimes case RECALWAIT: 20905b81b6b3SRodney W. Grimes /* allow heads to settle */ 20916182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 8); 20925b81b6b3SRodney W. Grimes fdc->state = RECALCOMPLETE; 20935b81b6b3SRodney W. Grimes return (0); /* will return later */ 2094fb35bd37SJoerg Wunsch 20955b81b6b3SRodney W. Grimes case RECALCOMPLETE: 20963a2f7427SDavid Greenman do { 20973a2f7427SDavid Greenman /* 2098dc5df763SJoerg Wunsch * See SEEKCOMPLETE for a comment on this: 20993a2f7427SDavid Greenman */ 2100dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) 2101246ed35dSJoerg Wunsch return (0); /* will return later */ 2102dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 2103dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 2104246ed35dSJoerg Wunsch return (0); /* hope for a real intr */ 21053a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 21063a2f7427SDavid Greenman if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) 21075b81b6b3SRodney W. Grimes { 2108dc8603e3SJoerg Wunsch if(fdc->retry > 3) 2109dc8603e3SJoerg Wunsch /* 2110246ed35dSJoerg Wunsch * A recalibrate from beyond cylinder 77 2111dc8603e3SJoerg Wunsch * will "fail" due to the FDC limitations; 2112dc8603e3SJoerg Wunsch * since people used to complain much about 2113dc8603e3SJoerg Wunsch * the failure message, try not logging 2114dc8603e3SJoerg Wunsch * this one if it seems to be the first 2115246ed35dSJoerg Wunsch * time in a line. 2116dc8603e3SJoerg Wunsch */ 2117dc8603e3SJoerg Wunsch printf("fd%d: recal failed ST0 %b cyl %d\n", 2118dc8603e3SJoerg Wunsch fdu, st0, NE7_ST0BITS, cyl); 21193a2f7427SDavid Greenman if(fdc->retry < 3) fdc->retry = 3; 21206182fdbdSPeter Wemm return (retrier(fdc)); 21215b81b6b3SRodney W. Grimes } 21225b81b6b3SRodney W. Grimes fd->track = 0; 21235b81b6b3SRodney W. Grimes /* Seek (probably) necessary */ 21245b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 2125246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2126fb35bd37SJoerg Wunsch 21275b81b6b3SRodney W. Grimes case MOTORWAIT: 21285b81b6b3SRodney W. Grimes if(fd->flags & FD_MOTOR_WAIT) 21295b81b6b3SRodney W. Grimes { 21305b81b6b3SRodney W. Grimes return (0); /* time's not up yet */ 21315b81b6b3SRodney W. Grimes } 21325c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 21335c1a1eaeSBruce Evans fdc->state = RESETCTLR; 21345c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 2135fb35bd37SJoerg Wunsch } else 2136fb35bd37SJoerg Wunsch fdc->state = DOSEEK; 2137246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2138fb35bd37SJoerg Wunsch 21395b81b6b3SRodney W. Grimes default: 2140b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, "unexpected FD int->"); 214164860614SJoerg Wunsch if (fd_read_status(fdc) == 0) 2142a838d83dSBruce Evans printf("FDC status :%x %x %x %x %x %x %x ", 21435b81b6b3SRodney W. Grimes fdc->status[0], 21445b81b6b3SRodney W. Grimes fdc->status[1], 21455b81b6b3SRodney W. Grimes fdc->status[2], 21465b81b6b3SRodney W. Grimes fdc->status[3], 21475b81b6b3SRodney W. Grimes fdc->status[4], 21485b81b6b3SRodney W. Grimes fdc->status[5], 21495b81b6b3SRodney W. Grimes fdc->status[6] ); 21503a2f7427SDavid Greenman else 2151dac0f2dbSJoerg Wunsch printf("No status available "); 2152dac0f2dbSJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) != 0) 2153dac0f2dbSJoerg Wunsch { 2154dac0f2dbSJoerg Wunsch printf("[controller is dead now]\n"); 2155246ed35dSJoerg Wunsch return (0); /* will return later */ 21565b81b6b3SRodney W. Grimes } 2157dac0f2dbSJoerg Wunsch printf("ST0 = %x, PCN = %x\n", st0, cyl); 2158246ed35dSJoerg Wunsch return (0); /* will return later */ 2159dac0f2dbSJoerg Wunsch } 2160246ed35dSJoerg Wunsch /* noone should ever get here */ 21615b81b6b3SRodney W. Grimes } 21625b81b6b3SRodney W. Grimes 2163aaf08d94SGarrett Wollman static int 21646182fdbdSPeter Wemm retrier(struct fdc_data *fdc) 21655b81b6b3SRodney W. Grimes { 21668177437dSPoul-Henning Kamp struct bio *bp; 21676182fdbdSPeter Wemm struct fd_data *fd; 21686182fdbdSPeter Wemm int fdu; 21695b81b6b3SRodney W. Grimes 2170e93e63cbSBruce Evans bp = fdc->bp; 21715b81b6b3SRodney W. Grimes 21726182fdbdSPeter Wemm /* XXX shouldn't this be cached somewhere? */ 2173a2f19df9SPoul-Henning Kamp fd = bp->bio_dev->si_drv1; 2174503799eaSPoul-Henning Kamp fdu = fd->fdu; 21756182fdbdSPeter Wemm if (fd->options & FDOPT_NORETRY) 21763a2f7427SDavid Greenman goto fail; 21776182fdbdSPeter Wemm 21786182fdbdSPeter Wemm switch (fdc->retry) { 21795b81b6b3SRodney W. Grimes case 0: case 1: case 2: 21805b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 21815b81b6b3SRodney W. Grimes break; 21825b81b6b3SRodney W. Grimes case 3: case 4: case 5: 21835b81b6b3SRodney W. Grimes fdc->state = STARTRECAL; 21845b81b6b3SRodney W. Grimes break; 21855b81b6b3SRodney W. Grimes case 6: 21865b81b6b3SRodney W. Grimes fdc->state = RESETCTLR; 21875b81b6b3SRodney W. Grimes break; 21885b81b6b3SRodney W. Grimes case 7: 21895b81b6b3SRodney W. Grimes break; 21905b81b6b3SRodney W. Grimes default: 21913a2f7427SDavid Greenman fail: 2192fb35bd37SJoerg Wunsch if ((fd->options & FDOPT_NOERRLOG) == 0) { 2193f90c382cSPoul-Henning Kamp disk_err(bp, "hard error", 2194f90c382cSPoul-Henning Kamp fdc->fd->skip / DEV_BSIZE, 0); 2195fb35bd37SJoerg Wunsch if (fdc->flags & FDC_STAT_VALID) { 2196dc5df763SJoerg Wunsch printf( 2197a838d83dSBruce Evans " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", 2198dc5df763SJoerg Wunsch fdc->status[0], NE7_ST0BITS, 2199dc5df763SJoerg Wunsch fdc->status[1], NE7_ST1BITS, 2200dc5df763SJoerg Wunsch fdc->status[2], NE7_ST2BITS, 2201dc5df763SJoerg Wunsch fdc->status[3], fdc->status[4], 2202dc5df763SJoerg Wunsch fdc->status[5]); 2203dc5df763SJoerg Wunsch } 2204dc5df763SJoerg Wunsch else 2205dc5df763SJoerg Wunsch printf(" (No status)\n"); 22065b81b6b3SRodney W. Grimes } 22072995d110SJoerg Wunsch if ((fd->options & FDOPT_NOERROR) == 0) { 22088177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 22098177437dSPoul-Henning Kamp bp->bio_error = EIO; 2210fb35bd37SJoerg Wunsch bp->bio_resid = bp->bio_bcount - fdc->fd->skip; 2211fb35bd37SJoerg Wunsch } else 2212fb35bd37SJoerg Wunsch bp->bio_resid = 0; 2213e93e63cbSBruce Evans fdc->bp = NULL; 22145b81b6b3SRodney W. Grimes fdc->fd->skip = 0; 22155f830ea2SJoerg Wunsch device_unbusy(fd->dev); 221680980460SPoul-Henning Kamp biofinish(bp, fdc->fd->device_stats, 0); 221792ed385aSRodney W. Grimes fdc->state = FINDWORK; 22185c1a1eaeSBruce Evans fdc->flags |= FDC_NEEDS_RESET; 22195b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 22205b81b6b3SRodney W. Grimes fdc->fdu = -1; 222192ed385aSRodney W. Grimes return (1); 22225b81b6b3SRodney W. Grimes } 22235b81b6b3SRodney W. Grimes fdc->retry++; 22245b81b6b3SRodney W. Grimes return (1); 22255b81b6b3SRodney W. Grimes } 22265b81b6b3SRodney W. Grimes 22271fdb6e6cSPoul-Henning Kamp static void 22281fdb6e6cSPoul-Henning Kamp fdbiodone(struct bio *bp) 22291fdb6e6cSPoul-Henning Kamp { 22301fdb6e6cSPoul-Henning Kamp wakeup(bp); 22311fdb6e6cSPoul-Henning Kamp } 22321fdb6e6cSPoul-Henning Kamp 2233b39c878eSAndrey A. Chernov static int 223489c9c53dSPoul-Henning Kamp fdmisccmd(struct cdev *dev, u_int cmd, void *data) 2235b39c878eSAndrey A. Chernov { 2236b39c878eSAndrey A. Chernov fdu_t fdu; 2237b39c878eSAndrey A. Chernov fd_p fd; 22381fdb6e6cSPoul-Henning Kamp struct bio *bp; 2239f664aeeeSJoerg Wunsch struct fd_formb *finfo; 2240f664aeeeSJoerg Wunsch struct fdc_readid *idfield; 22413a2f7427SDavid Greenman size_t fdblk; 224202aad38cSPoul-Henning Kamp int error; 2243b39c878eSAndrey A. Chernov 2244a2f19df9SPoul-Henning Kamp fd = dev->si_drv1; 2245503799eaSPoul-Henning Kamp fdu = fd->fdu; 22463a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2247f664aeeeSJoerg Wunsch finfo = (struct fd_formb *)data; 2248f664aeeeSJoerg Wunsch idfield = (struct fdc_readid *)data; 2249b39c878eSAndrey A. Chernov 22508595de4aSPoul-Henning Kamp bp = malloc(sizeof(struct bio), M_TEMP, M_WAITOK | M_ZERO); 2251b39c878eSAndrey A. Chernov 2252b39c878eSAndrey A. Chernov /* 2253b52b7f46SPoul-Henning Kamp * Set up a bio request for fdstrategy(). bio_offset is faked 2254f664aeeeSJoerg Wunsch * so that fdstrategy() will seek to the the requested 2255817988beSPoul-Henning Kamp * cylinder, and use the desired head. 2256b39c878eSAndrey A. Chernov */ 2257f664aeeeSJoerg Wunsch bp->bio_cmd = cmd; 2258419f39ceSPoul-Henning Kamp if (cmd == FDBIO_FORMAT) { 2259b52b7f46SPoul-Henning Kamp bp->bio_offset = 2260f664aeeeSJoerg Wunsch (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + 2261b52b7f46SPoul-Henning Kamp finfo->head * fd->ft->sectrac) * fdblk; 2262f664aeeeSJoerg Wunsch bp->bio_bcount = sizeof(struct fd_idfield_data) * 2263f664aeeeSJoerg Wunsch finfo->fd_formb_nsecs; 2264419f39ceSPoul-Henning Kamp } else if (cmd == FDBIO_RDSECTID) { 2265b52b7f46SPoul-Henning Kamp bp->bio_offset = 2266f664aeeeSJoerg Wunsch (idfield->cyl * (fd->ft->sectrac * fd->ft->heads) + 2267b52b7f46SPoul-Henning Kamp idfield->head * fd->ft->sectrac) * fdblk; 2268250300ebSJoerg Wunsch bp->bio_bcount = sizeof(struct fdc_readid); 2269f664aeeeSJoerg Wunsch } else 2270f664aeeeSJoerg Wunsch panic("wrong cmd in fdmisccmd()"); 2271f664aeeeSJoerg Wunsch bp->bio_data = data; 2272250300ebSJoerg Wunsch bp->bio_dev = dev; 2273250300ebSJoerg Wunsch bp->bio_done = fdbiodone; 2274817988beSPoul-Henning Kamp bp->bio_flags = 0; 2275250300ebSJoerg Wunsch 2276c3bdb2f7SPoul-Henning Kamp /* Now run the command. */ 2277f664aeeeSJoerg Wunsch fdstrategy(bp); 227802aad38cSPoul-Henning Kamp error = biowait(bp, "fdcmd"); 2279c3bdb2f7SPoul-Henning Kamp 2280f664aeeeSJoerg Wunsch free(bp, M_TEMP); 228102aad38cSPoul-Henning Kamp return (error); 2282f664aeeeSJoerg Wunsch } 22835b81b6b3SRodney W. Grimes 22843e425b96SJulian Elischer static int 228589c9c53dSPoul-Henning Kamp fdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 2286f5f7ba03SJordan K. Hubbard { 2287f664aeeeSJoerg Wunsch fdu_t fdu; 2288f664aeeeSJoerg Wunsch fd_p fd; 22892995d110SJoerg Wunsch struct fdc_status *fsp; 2290250300ebSJoerg Wunsch struct fdc_readid *rid; 2291503799eaSPoul-Henning Kamp int error; 2292f5f7ba03SJordan K. Hubbard 2293a2f19df9SPoul-Henning Kamp fd = dev->si_drv1; 2294503799eaSPoul-Henning Kamp fdu = fd->fdu; 2295fb35bd37SJoerg Wunsch 22961a6bed68SJoerg Wunsch /* 22971a6bed68SJoerg Wunsch * First, handle everything that could be done with 22981a6bed68SJoerg Wunsch * FD_NONBLOCK still being set. 22991a6bed68SJoerg Wunsch */ 23001a6bed68SJoerg Wunsch switch (cmd) { 2301c91a63aaSPoul-Henning Kamp 2302c91a63aaSPoul-Henning Kamp case DIOCGMEDIASIZE: 230329ec21d1SYaroslav Tykhiy if (fd->ft == 0) 230429ec21d1SYaroslav Tykhiy return ((fd->flags & FD_NONBLOCK) ? EAGAIN : ENXIO); 2305c91a63aaSPoul-Henning Kamp *(off_t *)addr = (128 << (fd->ft->secsize)) * fd->ft->size; 2306c91a63aaSPoul-Henning Kamp return (0); 2307c91a63aaSPoul-Henning Kamp 2308c91a63aaSPoul-Henning Kamp case DIOCGSECTORSIZE: 230929ec21d1SYaroslav Tykhiy if (fd->ft == 0) 231029ec21d1SYaroslav Tykhiy return ((fd->flags & FD_NONBLOCK) ? EAGAIN : ENXIO); 2311c91a63aaSPoul-Henning Kamp *(u_int *)addr = 128 << (fd->ft->secsize); 2312c91a63aaSPoul-Henning Kamp return (0); 2313c91a63aaSPoul-Henning Kamp 23141a6bed68SJoerg Wunsch case FIONBIO: 23151a6bed68SJoerg Wunsch if (*(int *)addr != 0) 23161a6bed68SJoerg Wunsch fd->flags |= FD_NONBLOCK; 23171a6bed68SJoerg Wunsch else { 23181a6bed68SJoerg Wunsch if (fd->ft == 0) { 23191a6bed68SJoerg Wunsch /* 23201a6bed68SJoerg Wunsch * No drive type has been selected yet, 23211a6bed68SJoerg Wunsch * cannot turn FNONBLOCK off. 23221a6bed68SJoerg Wunsch */ 23231a6bed68SJoerg Wunsch return (EINVAL); 23241a6bed68SJoerg Wunsch } 23251a6bed68SJoerg Wunsch fd->flags &= ~FD_NONBLOCK; 23261a6bed68SJoerg Wunsch } 23271a6bed68SJoerg Wunsch return (0); 2328fb35bd37SJoerg Wunsch 23291a6bed68SJoerg Wunsch case FIOASYNC: 23301a6bed68SJoerg Wunsch /* keep the generic fcntl() code happy */ 23311a6bed68SJoerg Wunsch return (0); 23321a6bed68SJoerg Wunsch 23331a6bed68SJoerg Wunsch case FD_GTYPE: /* get drive type */ 23341a6bed68SJoerg Wunsch if (fd->ft == 0) 23351a6bed68SJoerg Wunsch /* no type known yet, return the native type */ 23361a6bed68SJoerg Wunsch *(struct fd_type *)addr = fd_native_types[fd->type]; 23371a6bed68SJoerg Wunsch else 23381a6bed68SJoerg Wunsch *(struct fd_type *)addr = *fd->ft; 23391a6bed68SJoerg Wunsch return (0); 23401a6bed68SJoerg Wunsch 23411a6bed68SJoerg Wunsch case FD_STYPE: /* set drive type */ 2342a3a10d1cSBruce Evans /* 2343a3a10d1cSBruce Evans * Allow setting drive type temporarily iff 2344a3a10d1cSBruce Evans * currently unset. Used for fdformat so any 2345a3a10d1cSBruce Evans * user can set it, and then start formatting. 2346a3a10d1cSBruce Evans */ 2347a3a10d1cSBruce Evans if (fd->ft) 2348a3a10d1cSBruce Evans return (EINVAL); /* already set */ 2349503799eaSPoul-Henning Kamp fd->fts[0] = *(struct fd_type *)addr; 2350a3a10d1cSBruce Evans fd->ft = &fd->fts[0]; 2351a3a10d1cSBruce Evans fd->flags |= FD_UA; 23521a6bed68SJoerg Wunsch return (0); 23531a6bed68SJoerg Wunsch 23541a6bed68SJoerg Wunsch case FD_GOPTS: /* get drive options */ 2355503799eaSPoul-Henning Kamp *(int *)addr = fd->options + FDOPT_AUTOSEL; 23561a6bed68SJoerg Wunsch return (0); 23571a6bed68SJoerg Wunsch 23581a6bed68SJoerg Wunsch case FD_SOPTS: /* set drive options */ 23591a6bed68SJoerg Wunsch fd->options = *(int *)addr & ~FDOPT_AUTOSEL; 23601a6bed68SJoerg Wunsch return (0); 23611a6bed68SJoerg Wunsch 23621a6bed68SJoerg Wunsch #ifdef FDC_DEBUG 23631a6bed68SJoerg Wunsch case FD_DEBUG: 23641a6bed68SJoerg Wunsch if ((fd_debug != 0) != (*(int *)addr != 0)) { 23651a6bed68SJoerg Wunsch fd_debug = (*(int *)addr != 0); 23661a6bed68SJoerg Wunsch printf("fd%d: debugging turned %s\n", 23671a6bed68SJoerg Wunsch fd->fdu, fd_debug ? "on" : "off"); 23681a6bed68SJoerg Wunsch } 23691a6bed68SJoerg Wunsch return (0); 23701a6bed68SJoerg Wunsch #endif 23711a6bed68SJoerg Wunsch 23721a6bed68SJoerg Wunsch case FD_CLRERR: 237344731cabSJohn Baldwin if (suser(td) != 0) 23741a6bed68SJoerg Wunsch return (EPERM); 23751a6bed68SJoerg Wunsch fd->fdc->fdc_errs = 0; 23761a6bed68SJoerg Wunsch return (0); 23771a6bed68SJoerg Wunsch 23781a6bed68SJoerg Wunsch case FD_GSTAT: 23791a6bed68SJoerg Wunsch fsp = (struct fdc_status *)addr; 23801a6bed68SJoerg Wunsch if ((fd->fdc->flags & FDC_STAT_VALID) == 0) 23811a6bed68SJoerg Wunsch return (EINVAL); 23821a6bed68SJoerg Wunsch memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); 23831a6bed68SJoerg Wunsch return (0); 23841a6bed68SJoerg Wunsch 23851a6bed68SJoerg Wunsch case FD_GDTYPE: 23861a6bed68SJoerg Wunsch *(enum fd_drivetype *)addr = fd->type; 23871a6bed68SJoerg Wunsch return (0); 23881a6bed68SJoerg Wunsch } 23891a6bed68SJoerg Wunsch 23901a6bed68SJoerg Wunsch /* 23911a6bed68SJoerg Wunsch * Now handle everything else. Make sure we have a valid 23921a6bed68SJoerg Wunsch * drive type. 23931a6bed68SJoerg Wunsch */ 23941a6bed68SJoerg Wunsch if (fd->flags & FD_NONBLOCK) 23951a6bed68SJoerg Wunsch return (EAGAIN); 23961a6bed68SJoerg Wunsch if (fd->ft == 0) 23971a6bed68SJoerg Wunsch return (ENXIO); 2398fb35bd37SJoerg Wunsch error = 0; 2399f5f7ba03SJordan K. Hubbard 24006182fdbdSPeter Wemm switch (cmd) { 2401f664aeeeSJoerg Wunsch 2402b39c878eSAndrey A. Chernov case FD_FORM: 2403b39c878eSAndrey A. Chernov if ((flag & FWRITE) == 0) 2404fb35bd37SJoerg Wunsch return (EBADF); /* must be opened for writing */ 2405fb35bd37SJoerg Wunsch if (((struct fd_formb *)addr)->format_version != 2406b39c878eSAndrey A. Chernov FD_FORMAT_VERSION) 2407fb35bd37SJoerg Wunsch return (EINVAL); /* wrong version of formatting prog */ 2408419f39ceSPoul-Henning Kamp error = fdmisccmd(dev, FDBIO_FORMAT, addr); 2409b39c878eSAndrey A. Chernov break; 2410b39c878eSAndrey A. Chernov 2411b39c878eSAndrey A. Chernov case FD_GTYPE: /* get drive type */ 24123e425b96SJulian Elischer *(struct fd_type *)addr = *fd->ft; 2413f5f7ba03SJordan K. Hubbard break; 2414f5f7ba03SJordan K. Hubbard 24153a2f7427SDavid Greenman case FD_STYPE: /* set drive type */ 24163a2f7427SDavid Greenman /* this is considered harmful; only allow for superuser */ 241744731cabSJohn Baldwin if (suser(td) != 0) 2418fb35bd37SJoerg Wunsch return (EPERM); 24193e425b96SJulian Elischer *fd->ft = *(struct fd_type *)addr; 24203a2f7427SDavid Greenman break; 24213a2f7427SDavid Greenman 24223a2f7427SDavid Greenman case FD_GOPTS: /* get drive options */ 24233e425b96SJulian Elischer *(int *)addr = fd->options; 24243a2f7427SDavid Greenman break; 24253a2f7427SDavid Greenman 24263a2f7427SDavid Greenman case FD_SOPTS: /* set drive options */ 24273e425b96SJulian Elischer fd->options = *(int *)addr; 24283a2f7427SDavid Greenman break; 24293a2f7427SDavid Greenman 2430f664aeeeSJoerg Wunsch #ifdef FDC_DEBUG 2431f664aeeeSJoerg Wunsch case FD_DEBUG: 24320e17a5bcSJoerg Wunsch if ((fd_debug != 0) != (*(int *)addr != 0)) { 24330e17a5bcSJoerg Wunsch fd_debug = (*(int *)addr != 0); 24340e17a5bcSJoerg Wunsch printf("fd%d: debugging turned %s\n", 24350e17a5bcSJoerg Wunsch fd->fdu, fd_debug ? "on" : "off"); 24360e17a5bcSJoerg Wunsch } 2437f664aeeeSJoerg Wunsch break; 2438f664aeeeSJoerg Wunsch #endif 2439f664aeeeSJoerg Wunsch 24402995d110SJoerg Wunsch case FD_CLRERR: 244144731cabSJohn Baldwin if (suser(td) != 0) 2442fb35bd37SJoerg Wunsch return (EPERM); 24432995d110SJoerg Wunsch fd->fdc->fdc_errs = 0; 24442995d110SJoerg Wunsch break; 24452995d110SJoerg Wunsch 24462995d110SJoerg Wunsch case FD_GSTAT: 24472995d110SJoerg Wunsch fsp = (struct fdc_status *)addr; 24482995d110SJoerg Wunsch if ((fd->fdc->flags & FDC_STAT_VALID) == 0) 2449fb35bd37SJoerg Wunsch return (EINVAL); 24502995d110SJoerg Wunsch memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); 24512995d110SJoerg Wunsch break; 24522995d110SJoerg Wunsch 2453250300ebSJoerg Wunsch case FD_READID: 2454250300ebSJoerg Wunsch rid = (struct fdc_readid *)addr; 2455250300ebSJoerg Wunsch if (rid->cyl > MAX_CYLINDER || rid->head > MAX_HEAD) 2456fb35bd37SJoerg Wunsch return (EINVAL); 2457419f39ceSPoul-Henning Kamp error = fdmisccmd(dev, FDBIO_RDSECTID, addr); 2458250300ebSJoerg Wunsch break; 2459250300ebSJoerg Wunsch 2460f5f7ba03SJordan K. Hubbard default: 24613a2f7427SDavid Greenman error = ENOTTY; 2462f5f7ba03SJordan K. Hubbard break; 2463f5f7ba03SJordan K. Hubbard } 2464f5f7ba03SJordan K. Hubbard return (error); 2465f5f7ba03SJordan K. Hubbard } 2466