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); 80237286586SPeter Wemm error = fdc_alloc_resources(fdc); 80337286586SPeter Wemm if (error) { 804f8ce7dd5SJoerg Wunsch device_printf(dev, "cannot re-acquire resources\n"); 80537286586SPeter Wemm return error; 80637286586SPeter Wemm } 80737286586SPeter Wemm error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, 8083c36743eSMark Murray INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc, 8093c36743eSMark Murray &fdc->fdc_intr); 81037286586SPeter Wemm if (error) { 81137286586SPeter Wemm device_printf(dev, "cannot setup interrupt\n"); 81237286586SPeter Wemm return error; 81337286586SPeter Wemm } 81437286586SPeter Wemm fdc->fdcu = device_get_unit(dev); 815fb35bd37SJoerg Wunsch fdc->flags |= FDC_ATTACHED | FDC_NEEDS_RESET; 8166182fdbdSPeter Wemm 8175b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 8186182fdbdSPeter Wemm 8193a2f7427SDavid Greenman /* reset controller, turn motor off, clear fdout mirror reg */ 82064860614SJoerg Wunsch fdout_wr(fdc, fdc->fdout = 0); 8218177437dSPoul-Henning Kamp bioq_init(&fdc->head); 8225b81b6b3SRodney W. Grimes 8236182fdbdSPeter Wemm /* 82437286586SPeter Wemm * Probe and attach any children. We should probably detect 82537286586SPeter Wemm * devices from the BIOS unless overridden. 8266182fdbdSPeter Wemm */ 827ada54f9eSPeter Wemm name = device_get_nameunit(dev); 8282398f0cdSPeter Wemm i = 0; 8292398f0cdSPeter Wemm while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) 8302398f0cdSPeter Wemm fdc_add_child(dev, dname, dunit); 83137286586SPeter Wemm 83260444853SJoerg Wunsch if ((error = bus_generic_attach(dev)) != 0) 83360444853SJoerg Wunsch return (error); 83460444853SJoerg Wunsch 83560444853SJoerg Wunsch return (0); 8366182fdbdSPeter Wemm } 8376182fdbdSPeter Wemm 8386d6fa4fdSWarner Losh int 8396182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child) 8406182fdbdSPeter Wemm { 8411a6bed68SJoerg Wunsch int retval = 0, flags; 84215317dd8SMatthew N. Dodd 84315317dd8SMatthew N. Dodd retval += bus_print_child_header(me, child); 8441a6bed68SJoerg Wunsch retval += printf(" on %s drive %d", device_get_nameunit(me), 84537286586SPeter Wemm fdc_get_fdunit(child)); 8461a6bed68SJoerg Wunsch if ((flags = device_get_flags(me)) != 0) 8471a6bed68SJoerg Wunsch retval += printf(" flags %#x", flags); 8481a6bed68SJoerg Wunsch retval += printf("\n"); 84915317dd8SMatthew N. Dodd 85015317dd8SMatthew N. Dodd return (retval); 8516182fdbdSPeter Wemm } 8526182fdbdSPeter Wemm 85316e68fc6SPeter Wemm /* 854246ed35dSJoerg Wunsch * Configuration/initialization, per drive. 85516e68fc6SPeter Wemm */ 8566182fdbdSPeter Wemm static int 8576182fdbdSPeter Wemm fd_probe(device_t dev) 8586182fdbdSPeter Wemm { 8596182fdbdSPeter Wemm int i; 8601a6bed68SJoerg Wunsch u_int st0, st3; 8616182fdbdSPeter Wemm struct fd_data *fd; 8626182fdbdSPeter Wemm struct fdc_data *fdc; 8636182fdbdSPeter Wemm fdsu_t fdsu; 864752d4735SNate Lawson int flags, type; 8656182fdbdSPeter Wemm 866752d4735SNate Lawson fdsu = fdc_get_fdunit(dev); 8676182fdbdSPeter Wemm fd = device_get_softc(dev); 8686182fdbdSPeter Wemm fdc = device_get_softc(device_get_parent(dev)); 8691a6bed68SJoerg Wunsch flags = device_get_flags(dev); 8706182fdbdSPeter Wemm 8716182fdbdSPeter Wemm fd->dev = dev; 8726182fdbdSPeter Wemm fd->fdc = fdc; 8736182fdbdSPeter Wemm fd->fdsu = fdsu; 8746182fdbdSPeter Wemm fd->fdu = device_get_unit(dev); 8756182fdbdSPeter Wemm 876752d4735SNate Lawson type = FD_DTYPE(flags); 877752d4735SNate Lawson 878752d4735SNate Lawson /* Auto-probe if fdinfo is present, but always allow override. */ 879752d4735SNate Lawson if (type == FDT_NONE && (type = fdc_get_fdtype(dev)) != FDT_NONE) { 880752d4735SNate Lawson fd->type = type; 881752d4735SNate Lawson goto done; 882752d4735SNate Lawson } else { 883752d4735SNate Lawson /* make sure fdautoselect() will be called */ 884752d4735SNate Lawson fd->flags = FD_UA; 885752d4735SNate Lawson fd->type = type; 886752d4735SNate Lawson } 887752d4735SNate Lawson 888038d1bbdSJoerg Wunsch /* 889038d1bbdSJoerg Wunsch * XXX I think using __i386__ is wrong here since we actually want to probe 890038d1bbdSJoerg Wunsch * for the machine type, not the CPU type (so non-PC arch's like the PC98 will 891038d1bbdSJoerg Wunsch * fail the probe). However, for whatever reason, testing for _MACHINE_ARCH 892038d1bbdSJoerg Wunsch * == i386 breaks the test on FreeBSD/Alpha. 893038d1bbdSJoerg Wunsch */ 89404209354SPeter Wemm #if defined(__i386__) || defined(__amd64__) 8951a6bed68SJoerg Wunsch if (fd->type == FDT_NONE && (fd->fdu == 0 || fd->fdu == 1)) { 8961a6bed68SJoerg Wunsch /* Look up what the BIOS thinks we have. */ 8971a6bed68SJoerg Wunsch if (fd->fdu == 0) { 8985f830ea2SJoerg Wunsch if ((fdc->flags & FDC_ISPCMCIA)) 8991a6bed68SJoerg Wunsch /* 9001a6bed68SJoerg Wunsch * Somewhat special. No need to force the 9011a6bed68SJoerg Wunsch * user to set device flags, since the Y-E 9021a6bed68SJoerg Wunsch * Data PCMCIA floppy is always a 1.44 MB 9031a6bed68SJoerg Wunsch * device. 9041a6bed68SJoerg Wunsch */ 9051a6bed68SJoerg Wunsch fd->type = FDT_144M; 9060722d6abSJoerg Wunsch else 9071a6bed68SJoerg Wunsch fd->type = (rtcin(RTC_FDISKETTE) & 0xf0) >> 4; 9081a6bed68SJoerg Wunsch } else { 9091a6bed68SJoerg Wunsch fd->type = rtcin(RTC_FDISKETTE) & 0x0f; 9106b7bd95bSJoerg Wunsch } 9111a6bed68SJoerg Wunsch if (fd->type == FDT_288M_1) 9121a6bed68SJoerg Wunsch fd->type = FDT_288M; 9131a6bed68SJoerg Wunsch } 91404209354SPeter Wemm #endif /* __i386__ || __amd64__ */ 9156182fdbdSPeter Wemm /* is there a unit? */ 9161a6bed68SJoerg Wunsch if (fd->type == FDT_NONE) 9176182fdbdSPeter Wemm return (ENXIO); 9186182fdbdSPeter Wemm 9196182fdbdSPeter Wemm /* select it */ 9206182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNON); 921fb35bd37SJoerg Wunsch fdc_reset(fdc); /* XXX reset, then unreset, etc. */ 9226182fdbdSPeter Wemm DELAY(1000000); /* 1 sec */ 9236182fdbdSPeter Wemm 9241a6bed68SJoerg Wunsch if ((flags & FD_NO_PROBE) == 0) { 925dc5df763SJoerg Wunsch /* If we're at track 0 first seek inwards. */ 9261a6bed68SJoerg Wunsch if ((fd_sense_drive_status(fdc, &st3) == 0) && 9271a6bed68SJoerg Wunsch (st3 & NE7_ST3_T0)) { 928dc5df763SJoerg Wunsch /* Seek some steps... */ 9296182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 930dc5df763SJoerg Wunsch /* ...wait a moment... */ 931dc5df763SJoerg Wunsch DELAY(300000); 932dc5df763SJoerg Wunsch /* make ctrlr happy: */ 9336182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); 934dc5df763SJoerg Wunsch } 935dc5df763SJoerg Wunsch } 936dc5df763SJoerg Wunsch 9376b7bd95bSJoerg Wunsch for (i = 0; i < 2; i++) { 9386b7bd95bSJoerg Wunsch /* 9396b7bd95bSJoerg Wunsch * we must recalibrate twice, just in case the 9401a6bed68SJoerg Wunsch * heads have been beyond cylinder 76, since 9411a6bed68SJoerg Wunsch * most FDCs still barf when attempting to 9421a6bed68SJoerg Wunsch * recalibrate more than 77 steps 9436b7bd95bSJoerg Wunsch */ 944dc5df763SJoerg Wunsch /* go back to 0: */ 9456182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 9466b7bd95bSJoerg Wunsch /* a second being enough for full stroke seek*/ 9476b7bd95bSJoerg Wunsch DELAY(i == 0 ? 1000000 : 300000); 9485b81b6b3SRodney W. Grimes 9496b7bd95bSJoerg Wunsch /* anything responding? */ 950dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, 0) == 0 && 951dc5df763SJoerg Wunsch (st0 & NE7_ST0_EC) == 0) 9526b7bd95bSJoerg Wunsch break; /* already probed succesfully */ 9536b7bd95bSJoerg Wunsch } 954dc5df763SJoerg Wunsch } 9551a6bed68SJoerg Wunsch } 9566b7bd95bSJoerg Wunsch 9576182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNOFF); 9583a2f7427SDavid Greenman 9591a6bed68SJoerg Wunsch if ((flags & FD_NO_PROBE) == 0 && 9601a6bed68SJoerg Wunsch (st0 & NE7_ST0_EC) != 0) /* no track 0 -> no drive present */ 9616182fdbdSPeter Wemm return (ENXIO); 9625b81b6b3SRodney W. Grimes 963752d4735SNate Lawson done: 964752d4735SNate Lawson /* This doesn't work before the first reset. Or set_motor?? */ 965752d4735SNate Lawson if ((fdc->flags & FDC_HAS_FIFO) == 0 && 966752d4735SNate Lawson fdc->fdct == FDC_ENHANCED && 967752d4735SNate Lawson (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 && 968752d4735SNate Lawson enable_fifo(fdc) == 0) { 969752d4735SNate Lawson device_printf(device_get_parent(dev), 970752d4735SNate Lawson "FIFO enabled, %d bytes threshold\n", fifo_threshold); 971752d4735SNate Lawson } 972752d4735SNate Lawson 9731a6bed68SJoerg Wunsch switch (fd->type) { 9741a6bed68SJoerg Wunsch case FDT_12M: 9751a6bed68SJoerg Wunsch device_set_desc(dev, "1200-KB 5.25\" drive"); 9761a6bed68SJoerg Wunsch break; 9771a6bed68SJoerg Wunsch case FDT_144M: 9781a6bed68SJoerg Wunsch device_set_desc(dev, "1440-KB 3.5\" drive"); 9791a6bed68SJoerg Wunsch break; 9801a6bed68SJoerg Wunsch case FDT_288M: 9811a6bed68SJoerg Wunsch device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); 9821a6bed68SJoerg Wunsch break; 9831a6bed68SJoerg Wunsch case FDT_360K: 9841a6bed68SJoerg Wunsch device_set_desc(dev, "360-KB 5.25\" drive"); 9851a6bed68SJoerg Wunsch break; 9861a6bed68SJoerg Wunsch case FDT_720K: 9871a6bed68SJoerg Wunsch device_set_desc(dev, "720-KB 3.5\" drive"); 9881a6bed68SJoerg Wunsch break; 9891a6bed68SJoerg Wunsch default: 9901a6bed68SJoerg Wunsch return (ENXIO); 9911a6bed68SJoerg Wunsch } 992dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 993b99f0a4aSAndrew Moore fd->fdc = fdc; 994b99f0a4aSAndrew Moore fd->fdsu = fdsu; 9953a2f7427SDavid Greenman fd->options = 0; 99602a19910SJustin T. Gibbs callout_handle_init(&fd->toffhandle); 99702a19910SJustin T. Gibbs callout_handle_init(&fd->tohandle); 9985b81b6b3SRodney W. Grimes 9991a6bed68SJoerg Wunsch /* initialize densities for subdevices */ 10001a6bed68SJoerg Wunsch for (i = 0; i < NUMDENS; i++) 10011a6bed68SJoerg Wunsch memcpy(fd->fts + i, fd_native_types + fd->type, 10021a6bed68SJoerg Wunsch sizeof(struct fd_type)); 10036182fdbdSPeter Wemm return (0); 10046182fdbdSPeter Wemm } 10056182fdbdSPeter Wemm 10066182fdbdSPeter Wemm static int 10076182fdbdSPeter Wemm fd_attach(device_t dev) 10086182fdbdSPeter Wemm { 10096182fdbdSPeter Wemm struct fd_data *fd; 10106182fdbdSPeter Wemm 101164860614SJoerg Wunsch fd = device_get_softc(dev); 1012503799eaSPoul-Henning Kamp fd->masterdev = make_dev(&fd_cdevsw, fd->fdu, 10133f54a085SPoul-Henning Kamp UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu); 1014a2f19df9SPoul-Henning Kamp fd->masterdev->si_drv1 = fd; 101580980460SPoul-Henning Kamp fd->device_stats = devstat_new_entry(device_get_name(dev), 1016f8ce7dd5SJoerg Wunsch device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS, 10172a888f93SKenneth D. Merry DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, 10182a888f93SKenneth D. Merry DEVSTAT_PRIORITY_FD); 10196182fdbdSPeter Wemm return (0); 10205b81b6b3SRodney W. Grimes } 10215b81b6b3SRodney W. Grimes 10225f830ea2SJoerg Wunsch static int 10235f830ea2SJoerg Wunsch fd_detach(device_t dev) 10245f830ea2SJoerg Wunsch { 10255f830ea2SJoerg Wunsch struct fd_data *fd; 10265f830ea2SJoerg Wunsch 10275f830ea2SJoerg Wunsch fd = device_get_softc(dev); 102860444853SJoerg Wunsch untimeout(fd_turnoff, fd, fd->toffhandle); 102980980460SPoul-Henning Kamp devstat_remove_entry(fd->device_stats); 1030e219897aSJoerg Wunsch destroy_dev(fd->masterdev); 10315f830ea2SJoerg Wunsch 10325f830ea2SJoerg Wunsch return (0); 10335f830ea2SJoerg Wunsch } 10345f830ea2SJoerg Wunsch 103516e68fc6SPeter Wemm static device_method_t fd_methods[] = { 103616e68fc6SPeter Wemm /* Device interface */ 103716e68fc6SPeter Wemm DEVMETHOD(device_probe, fd_probe), 103816e68fc6SPeter Wemm DEVMETHOD(device_attach, fd_attach), 10395f830ea2SJoerg Wunsch DEVMETHOD(device_detach, fd_detach), 104016e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 104116e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ 104216e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ 104316e68fc6SPeter Wemm 104416e68fc6SPeter Wemm { 0, 0 } 104516e68fc6SPeter Wemm }; 104616e68fc6SPeter Wemm 104716e68fc6SPeter Wemm static driver_t fd_driver = { 104816e68fc6SPeter Wemm "fd", 104916e68fc6SPeter Wemm fd_methods, 105016e68fc6SPeter Wemm sizeof(struct fd_data) 105116e68fc6SPeter Wemm }; 105216e68fc6SPeter Wemm 1053475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); 105416e68fc6SPeter Wemm 1055246ed35dSJoerg Wunsch /* 1056246ed35dSJoerg Wunsch * More auxiliary functions. 1057246ed35dSJoerg Wunsch */ 1058246ed35dSJoerg Wunsch /* 1059246ed35dSJoerg Wunsch * Motor control stuff. 1060246ed35dSJoerg Wunsch * Remember to not deselect the drive we're working on. 1061246ed35dSJoerg Wunsch */ 10623a2f7427SDavid Greenman static void 10636182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon) 10645b81b6b3SRodney W. Grimes { 1065fb35bd37SJoerg Wunsch int fdout; 10663a2f7427SDavid Greenman 1067fb35bd37SJoerg Wunsch fdout = fdc->fdout; 10683a2f7427SDavid Greenman if (turnon) { 10693a2f7427SDavid Greenman fdout &= ~FDO_FDSEL; 1070fb35bd37SJoerg Wunsch fdout |= (FDO_MOEN0 << fdsu) | FDO_FDMAEN | FDO_FRST | fdsu; 10713a2f7427SDavid Greenman } else 10723a2f7427SDavid Greenman fdout &= ~(FDO_MOEN0 << fdsu); 10736182fdbdSPeter Wemm fdc->fdout = fdout; 1074fb35bd37SJoerg Wunsch fdout_wr(fdc, fdout); 10753a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdout); 10763a2f7427SDavid Greenman } 10773a2f7427SDavid Greenman 1078381fe1aaSGarrett Wollman static void 10796182fdbdSPeter Wemm fd_turnoff(void *xfd) 10805b81b6b3SRodney W. Grimes { 1081f5f7ba03SJordan K. Hubbard int s; 10826182fdbdSPeter Wemm fd_p fd = xfd; 1083dc16046fSJoerg Wunsch 10846182fdbdSPeter Wemm TRACE1("[fd%d: turnoff]", fd->fdu); 10858335c1b8SBruce Evans 10865f830ea2SJoerg Wunsch s = splbio(); 10878335c1b8SBruce Evans /* 10888335c1b8SBruce Evans * Don't turn off the motor yet if the drive is active. 10895f830ea2SJoerg Wunsch * 10905f830ea2SJoerg Wunsch * If we got here, this could only mean we missed an interrupt. 10915f830ea2SJoerg Wunsch * This can e. g. happen on the Y-E Date PCMCIA floppy controller 10925f830ea2SJoerg Wunsch * after a controller reset. Just schedule a pseudo-interrupt 10935f830ea2SJoerg Wunsch * so the state machine gets re-entered. 10948335c1b8SBruce Evans */ 10956182fdbdSPeter Wemm if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { 10965f830ea2SJoerg Wunsch fdc_intr(fd->fdc); 10975f830ea2SJoerg Wunsch splx(s); 10988335c1b8SBruce Evans return; 10998335c1b8SBruce Evans } 11008335c1b8SBruce Evans 11015b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR; 11026182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNOFF); 1103f5f7ba03SJordan K. Hubbard splx(s); 11045b81b6b3SRodney W. Grimes } 11055b81b6b3SRodney W. Grimes 11063a2f7427SDavid Greenman static void 11076182fdbdSPeter Wemm fd_motor_on(void *xfd) 11085b81b6b3SRodney W. Grimes { 1109f5f7ba03SJordan K. Hubbard int s; 11106182fdbdSPeter Wemm fd_p fd = xfd; 1111f5f7ba03SJordan K. Hubbard 1112f5f7ba03SJordan K. Hubbard s = splbio(); 11135b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR_WAIT; 11145b81b6b3SRodney W. Grimes if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) 11155b81b6b3SRodney W. Grimes { 11166182fdbdSPeter Wemm fdc_intr(fd->fdc); 11175b81b6b3SRodney W. Grimes } 1118f5f7ba03SJordan K. Hubbard splx(s); 11195b81b6b3SRodney W. Grimes } 11205b81b6b3SRodney W. Grimes 11213a2f7427SDavid Greenman static void 11226182fdbdSPeter Wemm fd_turnon(fd_p fd) 11235b81b6b3SRodney W. Grimes { 11245b81b6b3SRodney W. Grimes if(!(fd->flags & FD_MOTOR)) 11255b81b6b3SRodney W. Grimes { 11263a2f7427SDavid Greenman fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); 11276182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNON); 11286182fdbdSPeter Wemm timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ 11295b81b6b3SRodney W. Grimes } 11305b81b6b3SRodney W. Grimes } 11315b81b6b3SRodney W. Grimes 1132381fe1aaSGarrett Wollman static void 1133dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc) 11345b81b6b3SRodney W. Grimes { 11353a2f7427SDavid Greenman /* Try a reset, keep motor on */ 1136427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 11373a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 11383a2f7427SDavid Greenman DELAY(100); 11393a2f7427SDavid Greenman /* enable FDC, but defer interrupts a moment */ 1140427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); 11413a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); 11423a2f7427SDavid Greenman DELAY(100); 1143427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout); 11443a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout); 11453a2f7427SDavid Greenman 114664860614SJoerg Wunsch /* XXX after a reset, silently believe the FDC will accept commands */ 11476182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1148dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1149dc5df763SJoerg Wunsch 0); 1150d66c374fSTor Egge if (fdc->flags & FDC_HAS_FIFO) 1151d66c374fSTor Egge (void) enable_fifo(fdc); 11525b81b6b3SRodney W. Grimes } 11535b81b6b3SRodney W. Grimes 1154246ed35dSJoerg Wunsch /* 1155246ed35dSJoerg Wunsch * FDC IO functions, take care of the main status register, timeout 1156246ed35dSJoerg Wunsch * in case the desired status bits are never set. 1157a2642c4dSJoerg Wunsch * 1158a2642c4dSJoerg Wunsch * These PIO loops initially start out with short delays between 1159a2642c4dSJoerg Wunsch * each iteration in the expectation that the required condition 1160a2642c4dSJoerg Wunsch * is usually met quickly, so it can be handled immediately. After 1161a2642c4dSJoerg Wunsch * about 1 ms, stepping is increased to achieve a better timing 1162a2642c4dSJoerg Wunsch * accuracy in the calls to DELAY(). 1163246ed35dSJoerg Wunsch */ 1164b5e8ce9fSBruce Evans static int 11656182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr) 1166dc5df763SJoerg Wunsch { 1167a2642c4dSJoerg Wunsch int i, j, step; 1168a2642c4dSJoerg Wunsch 1169a2642c4dSJoerg Wunsch for (j = 0, step = 1; 1170a2642c4dSJoerg Wunsch (i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && 1171a2642c4dSJoerg Wunsch j < FDSTS_TIMEOUT; 1172a2642c4dSJoerg Wunsch j += step) { 1173dc5df763SJoerg Wunsch if (i == NE7_RQM) 11748a0ba818SJoerg Wunsch return (fdc_err(fdc, "ready for output in input\n")); 1175a2642c4dSJoerg Wunsch if (j == 1000) 1176a2642c4dSJoerg Wunsch step = 1000; 1177a2642c4dSJoerg Wunsch DELAY(step); 1178d0900d6bSJoerg Wunsch } 1179a2642c4dSJoerg Wunsch if (j >= FDSTS_TIMEOUT) 11808a0ba818SJoerg Wunsch return (fdc_err(fdc, bootverbose? "input ready timeout\n": 0)); 1181d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 1182427ccf00SDoug Rabson i = fddata_rd(fdc); 1183dc5df763SJoerg Wunsch TRACE1("[FDDATA->0x%x]", (unsigned char)i); 1184dc5df763SJoerg Wunsch *ptr = i; 11858a0ba818SJoerg Wunsch return (0); 1186d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 1187427ccf00SDoug Rabson i = fddata_rd(fdc); 1188dc5df763SJoerg Wunsch if (ptr) 1189dc5df763SJoerg Wunsch *ptr = i; 11908a0ba818SJoerg Wunsch return (0); 1191d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 1192dc5df763SJoerg Wunsch } 1193dc5df763SJoerg Wunsch 119437c84183SPoul-Henning Kamp static int 11956182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x) 11965b81b6b3SRodney W. Grimes { 1197a2642c4dSJoerg Wunsch int i, j, step; 11985b81b6b3SRodney W. Grimes 1199a2642c4dSJoerg Wunsch for (j = 0, step = 1; 1200a2642c4dSJoerg Wunsch (i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != NE7_RQM && 1201a2642c4dSJoerg Wunsch j < FDSTS_TIMEOUT; 1202a2642c4dSJoerg Wunsch j += step) { 1203a2642c4dSJoerg Wunsch if (i == (NE7_DIO|NE7_RQM)) 1204a2642c4dSJoerg Wunsch return (fdc_err(fdc, "ready for input in output\n")); 1205a2642c4dSJoerg Wunsch if (j == 1000) 1206a2642c4dSJoerg Wunsch step = 1000; 1207a2642c4dSJoerg Wunsch DELAY(step); 1208a2642c4dSJoerg Wunsch } 1209a2642c4dSJoerg Wunsch if (j >= FDSTS_TIMEOUT) 12108a0ba818SJoerg Wunsch return (fdc_err(fdc, bootverbose? "output ready timeout\n": 0)); 12113b3837dbSRodney W. Grimes 12123b3837dbSRodney W. Grimes /* Send the command and return */ 1213427ccf00SDoug Rabson fddata_wr(fdc, x); 12143a2f7427SDavid Greenman TRACE1("[0x%x->FDDATA]", x); 12155b81b6b3SRodney W. Grimes return (0); 12165b81b6b3SRodney W. Grimes } 12175b81b6b3SRodney W. Grimes 1218246ed35dSJoerg Wunsch /* 1219246ed35dSJoerg Wunsch * Block device driver interface functions (interspersed with even more 1220246ed35dSJoerg Wunsch * auxiliary functions). 1221246ed35dSJoerg Wunsch */ 122237c84183SPoul-Henning Kamp static int 122389c9c53dSPoul-Henning Kamp fdopen(struct cdev *dev, int flags, int mode, struct thread *td) 12245b81b6b3SRodney W. Grimes { 12256182fdbdSPeter Wemm fd_p fd; 1226b99f0a4aSAndrew Moore fdc_p fdc; 12271a6bed68SJoerg Wunsch int rv, unitattn, dflags; 12285b81b6b3SRodney W. Grimes 1229a2f19df9SPoul-Henning Kamp fd = dev->si_drv1; 1230a2f19df9SPoul-Henning Kamp if (fd == NULL) 1231b99f0a4aSAndrew Moore return (ENXIO); 12326182fdbdSPeter Wemm fdc = fd->fdc; 12331a6bed68SJoerg Wunsch if ((fdc == NULL) || (fd->type == FDT_NONE)) 1234b99f0a4aSAndrew Moore return (ENXIO); 12351a6bed68SJoerg Wunsch dflags = device_get_flags(fd->dev); 12363e425b96SJulian Elischer /* 12371a6bed68SJoerg Wunsch * This is a bit bogus. It's still possible that e. g. a 12381a6bed68SJoerg Wunsch * descriptor gets inherited to a child, but then it's at 12391a6bed68SJoerg Wunsch * least for the same subdevice. By checking FD_OPEN here, we 12401a6bed68SJoerg Wunsch * can ensure that a device isn't attempted to be opened with 12411a6bed68SJoerg Wunsch * different densities at the same time where the second open 12421a6bed68SJoerg Wunsch * could clobber the settings from the first one. 12433e425b96SJulian Elischer */ 12441a6bed68SJoerg Wunsch if (fd->flags & FD_OPEN) 12451a6bed68SJoerg Wunsch return (EBUSY); 12461a6bed68SJoerg Wunsch 12471a6bed68SJoerg Wunsch if (flags & FNONBLOCK) { 12481a6bed68SJoerg Wunsch /* 12491a6bed68SJoerg Wunsch * Unfortunately, physio(9) discards its ioflag 12501a6bed68SJoerg Wunsch * argument, thus preventing us from seeing the 12511a6bed68SJoerg Wunsch * IO_NDELAY bit. So we need to keep track 12521a6bed68SJoerg Wunsch * ourselves. 12531a6bed68SJoerg Wunsch */ 12541a6bed68SJoerg Wunsch fd->flags |= FD_NONBLOCK; 12551a6bed68SJoerg Wunsch fd->ft = 0; 12561a6bed68SJoerg Wunsch } else { 12571a6bed68SJoerg Wunsch /* 12581a6bed68SJoerg Wunsch * Figure out a unit attention condition. 12591a6bed68SJoerg Wunsch * 12601a6bed68SJoerg Wunsch * If UA has been forced, proceed. 12611a6bed68SJoerg Wunsch * 126262cc1e05SYaroslav Tykhiy * If the drive has no changeline support, 126362cc1e05SYaroslav Tykhiy * or if the drive parameters have been lost 126462cc1e05SYaroslav Tykhiy * due to previous non-blocking access, 126562cc1e05SYaroslav Tykhiy * assume a forced UA condition. 126662cc1e05SYaroslav Tykhiy * 12671a6bed68SJoerg Wunsch * If motor is off, turn it on for a moment 12681a6bed68SJoerg Wunsch * and select our drive, in order to read the 12691a6bed68SJoerg Wunsch * UA hardware signal. 12701a6bed68SJoerg Wunsch * 12711a6bed68SJoerg Wunsch * If motor is on, and our drive is currently 12721a6bed68SJoerg Wunsch * selected, just read the hardware bit. 12731a6bed68SJoerg Wunsch * 12741a6bed68SJoerg Wunsch * If motor is on, but active for another 12751a6bed68SJoerg Wunsch * drive on that controller, we are lost. We 12761a6bed68SJoerg Wunsch * cannot risk to deselect the other drive, so 12771a6bed68SJoerg Wunsch * we just assume a forced UA condition to be 12781a6bed68SJoerg Wunsch * on the safe side. 12791a6bed68SJoerg Wunsch */ 12801a6bed68SJoerg Wunsch unitattn = 0; 12811a6bed68SJoerg Wunsch if ((dflags & FD_NO_CHLINE) != 0 || 128262cc1e05SYaroslav Tykhiy (fd->flags & FD_UA) != 0 || 128362cc1e05SYaroslav Tykhiy fd->ft == 0) { 12841a6bed68SJoerg Wunsch unitattn = 1; 12851a6bed68SJoerg Wunsch fd->flags &= ~FD_UA; 12861a6bed68SJoerg Wunsch } else if (fdc->fdout & (FDO_MOEN0 | FDO_MOEN1 | 12871a6bed68SJoerg Wunsch FDO_MOEN2 | FDO_MOEN3)) { 12881a6bed68SJoerg Wunsch if ((fdc->fdout & FDO_FDSEL) == fd->fdsu) 12891a6bed68SJoerg Wunsch unitattn = fdin_rd(fdc) & FDI_DCHG; 12901a6bed68SJoerg Wunsch else 12911a6bed68SJoerg Wunsch unitattn = 1; 12921a6bed68SJoerg Wunsch } else { 12931a6bed68SJoerg Wunsch set_motor(fdc, fd->fdsu, TURNON); 12941a6bed68SJoerg Wunsch unitattn = fdin_rd(fdc) & FDI_DCHG; 12951a6bed68SJoerg Wunsch set_motor(fdc, fd->fdsu, TURNOFF); 1296b39c878eSAndrey A. Chernov } 12971a6bed68SJoerg Wunsch if (unitattn && (rv = fdautoselect(dev)) != 0) 12981a6bed68SJoerg Wunsch return (rv); 12997ca0641bSAndrey A. Chernov } 13006182fdbdSPeter Wemm fd->flags |= FD_OPEN; 1301de0b7a63SPoul-Henning Kamp 1302de0b7a63SPoul-Henning Kamp if ((fdc->flags & FDC_NODMA) == 0) { 1303de0b7a63SPoul-Henning Kamp if (fdc->dmacnt++ == 0) { 1304de0b7a63SPoul-Henning Kamp isa_dma_acquire(fdc->dmachan); 1305de0b7a63SPoul-Henning Kamp isa_dmainit(fdc->dmachan, MAX_SEC_SIZE); 1306de0b7a63SPoul-Henning Kamp } 1307de0b7a63SPoul-Henning Kamp } 1308de0b7a63SPoul-Henning Kamp 13093fef646eSJoerg Wunsch /* 13103fef646eSJoerg Wunsch * Clearing the DMA overrun counter at open time is a bit messy. 13113fef646eSJoerg Wunsch * Since we're only managing one counter per controller, opening 13123fef646eSJoerg Wunsch * the second drive could mess it up. Anyway, if the DMA overrun 13133fef646eSJoerg Wunsch * condition is really persistent, it will eventually time out 13143fef646eSJoerg Wunsch * still. OTOH, clearing it here will ensure we'll at least start 13153fef646eSJoerg Wunsch * trying again after a previous (maybe even long ago) failure. 13163fef646eSJoerg Wunsch * Also, this is merely a stop-gap measure only that should not 13173fef646eSJoerg Wunsch * happen during normal operation, so we can tolerate it to be a 13183fef646eSJoerg Wunsch * bit sloppy about this. 13193fef646eSJoerg Wunsch */ 13203fef646eSJoerg Wunsch fdc->dma_overruns = 0; 13215f830ea2SJoerg Wunsch 13225b81b6b3SRodney W. Grimes return 0; 13235b81b6b3SRodney W. Grimes } 13245b81b6b3SRodney W. Grimes 132537c84183SPoul-Henning Kamp static int 132689c9c53dSPoul-Henning Kamp fdclose(struct cdev *dev, int flags, int mode, struct thread *td) 13275b81b6b3SRodney W. Grimes { 13286182fdbdSPeter Wemm struct fd_data *fd; 1329de0b7a63SPoul-Henning Kamp fdc_p fdc; 1330b99f0a4aSAndrew Moore 1331a2f19df9SPoul-Henning Kamp fd = dev->si_drv1; 1332de0b7a63SPoul-Henning Kamp fdc = fd->fdc; 13331a6bed68SJoerg Wunsch fd->flags &= ~(FD_OPEN | FD_NONBLOCK); 13342995d110SJoerg Wunsch fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR); 1335dc16046fSJoerg Wunsch 1336de0b7a63SPoul-Henning Kamp if ((fdc->flags & FDC_NODMA) == 0) 1337de0b7a63SPoul-Henning Kamp if (--fdc->dmacnt == 0) 1338de0b7a63SPoul-Henning Kamp isa_dma_release(fdc->dmachan); 1339de0b7a63SPoul-Henning Kamp 13405b81b6b3SRodney W. Grimes return (0); 13415b81b6b3SRodney W. Grimes } 13425b81b6b3SRodney W. Grimes 134337c84183SPoul-Henning Kamp static void 13448177437dSPoul-Henning Kamp fdstrategy(struct bio *bp) 13453a2f7427SDavid Greenman { 1346fb35bd37SJoerg Wunsch long blknum, nblocks; 13473a2f7427SDavid Greenman int s; 13483a2f7427SDavid Greenman fdu_t fdu; 13493a2f7427SDavid Greenman fdc_p fdc; 13503a2f7427SDavid Greenman fd_p fd; 13513a2f7427SDavid Greenman size_t fdblk; 13523a2f7427SDavid Greenman 1353a2f19df9SPoul-Henning Kamp fd = bp->bio_dev->si_drv1; 1354503799eaSPoul-Henning Kamp fdu = fd->fdu; 13553a2f7427SDavid Greenman fdc = fd->fdc; 135683efe35aSYaroslav Tykhiy bp->bio_resid = bp->bio_bcount; 13571a6bed68SJoerg Wunsch if (fd->type == FDT_NONE || fd->ft == 0) { 1358f79981acSYaroslav Tykhiy if (fd->type != FDT_NONE && (fd->flags & FD_NONBLOCK)) 1359f79981acSYaroslav Tykhiy bp->bio_error = EAGAIN; 1360f79981acSYaroslav Tykhiy else 13618177437dSPoul-Henning Kamp bp->bio_error = ENXIO; 13628177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 13633b178206SWarner Losh goto bad; 136464860614SJoerg Wunsch } 1365d3628763SRodney W. Grimes fdblk = 128 << (fd->ft->secsize); 1366419f39ceSPoul-Henning Kamp if (bp->bio_cmd != FDBIO_FORMAT && bp->bio_cmd != FDBIO_RDSECTID) { 13671a6bed68SJoerg Wunsch if (fd->flags & FD_NONBLOCK) { 13681a6bed68SJoerg Wunsch bp->bio_error = EAGAIN; 13691a6bed68SJoerg Wunsch bp->bio_flags |= BIO_ERROR; 13701a6bed68SJoerg Wunsch goto bad; 13711a6bed68SJoerg Wunsch } 1372b52b7f46SPoul-Henning Kamp if (bp->bio_offset < 0) { 1373dc5df763SJoerg Wunsch printf( 1374b52b7f46SPoul-Henning Kamp "fd%d: fdstrat: bad request offset = %ju, bcount = %ld\n", 1375b52b7f46SPoul-Henning Kamp fdu, (intmax_t)bp->bio_offset, bp->bio_bcount); 13768177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 13778177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 13783a2f7427SDavid Greenman goto bad; 13793a2f7427SDavid Greenman } 13808177437dSPoul-Henning Kamp if ((bp->bio_bcount % fdblk) != 0) { 13818177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 13828177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 13833a2f7427SDavid Greenman goto bad; 13843a2f7427SDavid Greenman } 13853a2f7427SDavid Greenman } 13863a2f7427SDavid Greenman 13873a2f7427SDavid Greenman /* 13883a2f7427SDavid Greenman * Set up block calculations. 13893a2f7427SDavid Greenman */ 1390b52b7f46SPoul-Henning Kamp if (bp->bio_offset >= ((off_t)128 << fd->ft->secsize) * fd->ft->size) { 13918177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 13928177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 13933a2f7427SDavid Greenman goto bad; 13943a2f7427SDavid Greenman } 1395b52b7f46SPoul-Henning Kamp blknum = bp->bio_offset / fdblk; 1396bb6382faSJoerg Wunsch nblocks = fd->ft->size; 1397fb35bd37SJoerg Wunsch if (blknum + bp->bio_bcount / fdblk > nblocks) { 1398fb35bd37SJoerg Wunsch if (blknum >= nblocks) { 139983efe35aSYaroslav Tykhiy if (bp->bio_cmd != BIO_READ) { 1400fb35bd37SJoerg Wunsch bp->bio_error = ENOSPC; 14018177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 1402bb6382faSJoerg Wunsch } 1403fb35bd37SJoerg Wunsch goto bad; /* not always bad, but EOF */ 1404fb35bd37SJoerg Wunsch } 1405fb35bd37SJoerg Wunsch bp->bio_bcount = (nblocks - blknum) * fdblk; 1406bb6382faSJoerg Wunsch } 14079a5e3ddbSJoerg Wunsch bp->bio_pblkno = blknum; 14083a2f7427SDavid Greenman s = splbio(); 1409891619a6SPoul-Henning Kamp bioq_disksort(&fdc->head, bp); 14106182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ 14111ecc485cSPoul-Henning Kamp devstat_start_transaction_bio(fd->device_stats, bp); 14125f830ea2SJoerg Wunsch device_busy(fd->dev); 14136182fdbdSPeter Wemm fdstart(fdc); 14143a2f7427SDavid Greenman splx(s); 14153a2f7427SDavid Greenman return; 14163a2f7427SDavid Greenman 14173a2f7427SDavid Greenman bad: 14183a2f7427SDavid Greenman biodone(bp); 14193a2f7427SDavid Greenman } 14203a2f7427SDavid Greenman 1421246ed35dSJoerg Wunsch /* 1422246ed35dSJoerg Wunsch * fdstart 1423246ed35dSJoerg Wunsch * 1424246ed35dSJoerg Wunsch * We have just queued something. If the controller is not busy 1425246ed35dSJoerg Wunsch * then simulate the case where it has just finished a command 1426246ed35dSJoerg Wunsch * So that it (the interrupt routine) looks on the queue for more 1427246ed35dSJoerg Wunsch * work to do and picks up what we just added. 1428246ed35dSJoerg Wunsch * 1429246ed35dSJoerg Wunsch * If the controller is already busy, we need do nothing, as it 1430246ed35dSJoerg Wunsch * will pick up our work when the present work completes. 1431246ed35dSJoerg Wunsch */ 1432381fe1aaSGarrett Wollman static void 14336182fdbdSPeter Wemm fdstart(struct fdc_data *fdc) 14345b81b6b3SRodney W. Grimes { 14355b81b6b3SRodney W. Grimes int s; 14365b81b6b3SRodney W. Grimes 14375b81b6b3SRodney W. Grimes s = splbio(); 14386182fdbdSPeter Wemm if(fdc->state == DEVIDLE) 14395b81b6b3SRodney W. Grimes { 14406182fdbdSPeter Wemm fdc_intr(fdc); 14415b81b6b3SRodney W. Grimes } 14425b81b6b3SRodney W. Grimes splx(s); 14435b81b6b3SRodney W. Grimes } 14445b81b6b3SRodney W. Grimes 1445381fe1aaSGarrett Wollman static void 14466182fdbdSPeter Wemm fd_iotimeout(void *xfdc) 14475b81b6b3SRodney W. Grimes { 14485c1a1eaeSBruce Evans fdc_p fdc; 1449f5f7ba03SJordan K. Hubbard int s; 14505b81b6b3SRodney W. Grimes 14516182fdbdSPeter Wemm fdc = xfdc; 14525c1a1eaeSBruce Evans TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); 14535b81b6b3SRodney W. Grimes 14543a2f7427SDavid Greenman /* 14553a2f7427SDavid Greenman * Due to IBM's brain-dead design, the FDC has a faked ready 14563a2f7427SDavid Greenman * signal, hardwired to ready == true. Thus, any command 14573a2f7427SDavid Greenman * issued if there's no diskette in the drive will _never_ 14583a2f7427SDavid Greenman * complete, and must be aborted by resetting the FDC. 14593a2f7427SDavid Greenman * Many thanks, Big Blue! 14605c1a1eaeSBruce Evans * The FDC must not be reset directly, since that would 14615c1a1eaeSBruce Evans * interfere with the state machine. Instead, pretend that 14625c1a1eaeSBruce Evans * the command completed but was invalid. The state machine 14635c1a1eaeSBruce Evans * will reset the FDC and retry once. 14643a2f7427SDavid Greenman */ 14653a2f7427SDavid Greenman s = splbio(); 14665c1a1eaeSBruce Evans fdc->status[0] = NE7_ST0_IC_IV; 14675c1a1eaeSBruce Evans fdc->flags &= ~FDC_STAT_VALID; 14685c1a1eaeSBruce Evans fdc->state = IOTIMEDOUT; 14696182fdbdSPeter Wemm fdc_intr(fdc); 1470f5f7ba03SJordan K. Hubbard splx(s); 14715b81b6b3SRodney W. Grimes } 14725b81b6b3SRodney W. Grimes 1473246ed35dSJoerg Wunsch /* Just ensure it has the right spl. */ 1474381fe1aaSGarrett Wollman static void 14756182fdbdSPeter Wemm fd_pseudointr(void *xfdc) 14765b81b6b3SRodney W. Grimes { 14775b81b6b3SRodney W. Grimes int s; 14783a2f7427SDavid Greenman 14795b81b6b3SRodney W. Grimes s = splbio(); 14806182fdbdSPeter Wemm fdc_intr(xfdc); 14815b81b6b3SRodney W. Grimes splx(s); 14825b81b6b3SRodney W. Grimes } 14835b81b6b3SRodney W. Grimes 1484246ed35dSJoerg Wunsch /* 1485246ed35dSJoerg Wunsch * fdc_intr 1486246ed35dSJoerg Wunsch * 1487246ed35dSJoerg Wunsch * Keep calling the state machine until it returns a 0. 1488246ed35dSJoerg Wunsch * Always called at splbio. 1489246ed35dSJoerg Wunsch */ 1490fe310de8SBruce Evans static void 14916182fdbdSPeter Wemm fdc_intr(void *xfdc) 14925b81b6b3SRodney W. Grimes { 14936182fdbdSPeter Wemm fdc_p fdc = xfdc; 14946182fdbdSPeter Wemm while(fdstate(fdc)) 1495381fe1aaSGarrett Wollman ; 14965b81b6b3SRodney W. Grimes } 14975b81b6b3SRodney W. Grimes 149869acd21dSWarner Losh /* 1499246ed35dSJoerg Wunsch * Magic pseudo-DMA initialization for YE FDC. Sets count and 1500246ed35dSJoerg Wunsch * direction. 150169acd21dSWarner Losh */ 15023b178206SWarner Losh #define SET_BCDR(fdc,wr,cnt,port) \ 15033b178206SWarner Losh bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port, \ 15043b178206SWarner Losh ((cnt)-1) & 0xff); \ 15053b178206SWarner Losh bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \ 15063b178206SWarner Losh ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))); 150769acd21dSWarner Losh 150869acd21dSWarner Losh /* 1509246ed35dSJoerg Wunsch * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy. 151069acd21dSWarner Losh */ 15114c34deeeSJoerg Wunsch static int 15124c34deeeSJoerg Wunsch fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) 151369acd21dSWarner Losh { 151469acd21dSWarner Losh u_char *cptr = (u_char *)addr; 151569acd21dSWarner Losh 151621144e3bSPoul-Henning Kamp if (flags == BIO_READ) { 151769acd21dSWarner Losh if (fdc->state != PIOREAD) { 151869acd21dSWarner Losh fdc->state = PIOREAD; 151969acd21dSWarner Losh return(0); 152064860614SJoerg Wunsch } 15213b178206SWarner Losh SET_BCDR(fdc, 0, count, 0); 15223b178206SWarner Losh bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off + 15233b178206SWarner Losh FDC_YE_DATAPORT, cptr, count); 152469acd21dSWarner Losh } else { 15253b178206SWarner Losh bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off + 15263b178206SWarner Losh FDC_YE_DATAPORT, cptr, count); 15273b178206SWarner Losh SET_BCDR(fdc, 0, count, 0); 152864860614SJoerg Wunsch } 152969acd21dSWarner Losh return(1); 153069acd21dSWarner Losh } 153169acd21dSWarner Losh 1532246ed35dSJoerg Wunsch /* 15331a6bed68SJoerg Wunsch * Try figuring out the density of the media present in our device. 15341a6bed68SJoerg Wunsch */ 15351a6bed68SJoerg Wunsch static int 153689c9c53dSPoul-Henning Kamp fdautoselect(struct cdev *dev) 15371a6bed68SJoerg Wunsch { 15381a6bed68SJoerg Wunsch fd_p fd; 15391a6bed68SJoerg Wunsch struct fd_type *fdtp; 15401a6bed68SJoerg Wunsch struct fdc_readid id; 15411a6bed68SJoerg Wunsch int i, n, oopts, rv; 15421a6bed68SJoerg Wunsch 1543a2f19df9SPoul-Henning Kamp fd = dev->si_drv1; 15441a6bed68SJoerg Wunsch 15451a6bed68SJoerg Wunsch switch (fd->type) { 15461a6bed68SJoerg Wunsch default: 15471a6bed68SJoerg Wunsch return (ENXIO); 15481a6bed68SJoerg Wunsch 15491a6bed68SJoerg Wunsch case FDT_360K: 15501a6bed68SJoerg Wunsch case FDT_720K: 15511a6bed68SJoerg Wunsch /* no autoselection on those drives */ 15521a6bed68SJoerg Wunsch fd->ft = fd_native_types + fd->type; 15531a6bed68SJoerg Wunsch return (0); 15541a6bed68SJoerg Wunsch 15551a6bed68SJoerg Wunsch case FDT_12M: 15561a6bed68SJoerg Wunsch fdtp = fd_searchlist_12m; 15571a6bed68SJoerg Wunsch n = sizeof fd_searchlist_12m / sizeof(struct fd_type); 15581a6bed68SJoerg Wunsch break; 15591a6bed68SJoerg Wunsch 15601a6bed68SJoerg Wunsch case FDT_144M: 15611a6bed68SJoerg Wunsch fdtp = fd_searchlist_144m; 15621a6bed68SJoerg Wunsch n = sizeof fd_searchlist_144m / sizeof(struct fd_type); 15631a6bed68SJoerg Wunsch break; 15641a6bed68SJoerg Wunsch 15651a6bed68SJoerg Wunsch case FDT_288M: 15661a6bed68SJoerg Wunsch fdtp = fd_searchlist_288m; 15671a6bed68SJoerg Wunsch n = sizeof fd_searchlist_288m / sizeof(struct fd_type); 15681a6bed68SJoerg Wunsch break; 15691a6bed68SJoerg Wunsch } 15701a6bed68SJoerg Wunsch 15711a6bed68SJoerg Wunsch /* 15721a6bed68SJoerg Wunsch * Try reading sector ID fields, first at cylinder 0, head 0, 15731a6bed68SJoerg Wunsch * then at cylinder 2, head N. We don't probe cylinder 1, 15741a6bed68SJoerg Wunsch * since for 5.25in DD media in a HD drive, there are no data 15751a6bed68SJoerg Wunsch * to read (2 step pulses per media cylinder required). For 15761a6bed68SJoerg Wunsch * two-sided media, the second probe always goes to head 1, so 15771a6bed68SJoerg Wunsch * we can tell them apart from single-sided media. As a 15781a6bed68SJoerg Wunsch * side-effect this means that single-sided media should be 15791a6bed68SJoerg Wunsch * mentioned in the search list after two-sided media of an 15801a6bed68SJoerg Wunsch * otherwise identical density. Media with a different number 15811a6bed68SJoerg Wunsch * of sectors per track but otherwise identical parameters 15821a6bed68SJoerg Wunsch * cannot be distinguished at all. 15831a6bed68SJoerg Wunsch * 15841a6bed68SJoerg Wunsch * If we successfully read an ID field on both cylinders where 15851a6bed68SJoerg Wunsch * the recorded values match our expectation, we are done. 15861a6bed68SJoerg Wunsch * Otherwise, we try the next density entry from the table. 15871a6bed68SJoerg Wunsch * 15881a6bed68SJoerg Wunsch * Stepping to cylinder 2 has the side-effect of clearing the 15891a6bed68SJoerg Wunsch * unit attention bit. 15901a6bed68SJoerg Wunsch */ 15911a6bed68SJoerg Wunsch oopts = fd->options; 15921a6bed68SJoerg Wunsch fd->options |= FDOPT_NOERRLOG | FDOPT_NORETRY; 15931a6bed68SJoerg Wunsch for (i = 0; i < n; i++, fdtp++) { 15941a6bed68SJoerg Wunsch fd->ft = fdtp; 15951a6bed68SJoerg Wunsch 15961a6bed68SJoerg Wunsch id.cyl = id.head = 0; 1597419f39ceSPoul-Henning Kamp rv = fdmisccmd(dev, FDBIO_RDSECTID, &id); 15981a6bed68SJoerg Wunsch if (rv != 0) 15991a6bed68SJoerg Wunsch continue; 16001a6bed68SJoerg Wunsch if (id.cyl != 0 || id.head != 0 || 16011a6bed68SJoerg Wunsch id.secshift != fdtp->secsize) 16021a6bed68SJoerg Wunsch continue; 16031a6bed68SJoerg Wunsch id.cyl = 2; 16041a6bed68SJoerg Wunsch id.head = fd->ft->heads - 1; 1605419f39ceSPoul-Henning Kamp rv = fdmisccmd(dev, FDBIO_RDSECTID, &id); 16061a6bed68SJoerg Wunsch if (id.cyl != 2 || id.head != fdtp->heads - 1 || 16071a6bed68SJoerg Wunsch id.secshift != fdtp->secsize) 16081a6bed68SJoerg Wunsch continue; 16091a6bed68SJoerg Wunsch if (rv == 0) 16101a6bed68SJoerg Wunsch break; 16111a6bed68SJoerg Wunsch } 16121a6bed68SJoerg Wunsch 16131a6bed68SJoerg Wunsch fd->options = oopts; 16141a6bed68SJoerg Wunsch if (i == n) { 16155613959dSJoerg Wunsch if (bootverbose) 16161a6bed68SJoerg Wunsch device_printf(fd->dev, "autoselection failed\n"); 16171a6bed68SJoerg Wunsch fd->ft = 0; 16181a6bed68SJoerg Wunsch return (EIO); 16191a6bed68SJoerg Wunsch } else { 16205613959dSJoerg Wunsch if (bootverbose) 16211a6bed68SJoerg Wunsch device_printf(fd->dev, "autoselected %d KB medium\n", 16221a6bed68SJoerg Wunsch fd->ft->size / 2); 16231a6bed68SJoerg Wunsch return (0); 16241a6bed68SJoerg Wunsch } 16251a6bed68SJoerg Wunsch } 16261a6bed68SJoerg Wunsch 16271a6bed68SJoerg Wunsch 16281a6bed68SJoerg Wunsch /* 1629246ed35dSJoerg Wunsch * The controller state machine. 1630246ed35dSJoerg Wunsch * 1631246ed35dSJoerg Wunsch * If it returns a non zero value, it should be called again immediately. 1632246ed35dSJoerg Wunsch */ 16333a2f7427SDavid Greenman static int 16346182fdbdSPeter Wemm fdstate(fdc_p fdc) 16355b81b6b3SRodney W. Grimes { 163664860614SJoerg Wunsch struct fdc_readid *idp; 1637fb35bd37SJoerg Wunsch int read, format, rdsectid, cylinder, head, i, sec = 0, sectrac; 16381a6bed68SJoerg Wunsch int st0, cyl, st3, idf, ne7cmd, mfm, steptrac; 1639fb35bd37SJoerg Wunsch unsigned long blknum; 16405b81b6b3SRodney W. Grimes fdu_t fdu = fdc->fdu; 16415b81b6b3SRodney W. Grimes fd_p fd; 16428177437dSPoul-Henning Kamp register struct bio *bp; 1643b39c878eSAndrey A. Chernov struct fd_formb *finfo = NULL; 16443a2f7427SDavid Greenman size_t fdblk; 16455b81b6b3SRodney W. Grimes 1646e93e63cbSBruce Evans bp = fdc->bp; 1647e93e63cbSBruce Evans if (bp == NULL) { 16488177437dSPoul-Henning Kamp bp = bioq_first(&fdc->head); 1649e93e63cbSBruce Evans if (bp != NULL) { 16508177437dSPoul-Henning Kamp bioq_remove(&fdc->head, bp); 1651e93e63cbSBruce Evans fdc->bp = bp; 1652e93e63cbSBruce Evans } 1653e93e63cbSBruce Evans } 1654e93e63cbSBruce Evans if (bp == NULL) { 1655246ed35dSJoerg Wunsch /* 1656246ed35dSJoerg Wunsch * Nothing left for this controller to do, 1657246ed35dSJoerg Wunsch * force into the IDLE state. 1658246ed35dSJoerg Wunsch */ 16595b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 16606182fdbdSPeter Wemm if (fdc->fd) { 1661b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, 1662b6e5f28eSPeter Wemm "unexpected valid fd pointer\n"); 16635b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 16645b81b6b3SRodney W. Grimes fdc->fdu = -1; 16655b81b6b3SRodney W. Grimes } 16666182fdbdSPeter Wemm TRACE1("[fdc%d IDLE]", fdc->fdcu); 16675b81b6b3SRodney W. Grimes return (0); 16685b81b6b3SRodney W. Grimes } 1669a2f19df9SPoul-Henning Kamp fd = bp->bio_dev->si_drv1; 1670503799eaSPoul-Henning Kamp fdu = fd->fdu; 16713a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 1672b6e5f28eSPeter Wemm if (fdc->fd && (fd != fdc->fd)) 1673b6e5f28eSPeter Wemm device_printf(fd->dev, "confused fd pointers\n"); 16748177437dSPoul-Henning Kamp read = bp->bio_cmd == BIO_READ; 16751a6bed68SJoerg Wunsch mfm = (fd->ft->flags & FL_MFM)? NE7CMD_MFM: 0; 16761a6bed68SJoerg Wunsch steptrac = (fd->ft->flags & FL_2STEP)? 2: 1; 167756a23089SPoul-Henning Kamp if (read) 167856a23089SPoul-Henning Kamp idf = ISADMA_READ; 167956a23089SPoul-Henning Kamp else 168056a23089SPoul-Henning Kamp idf = ISADMA_WRITE; 1681419f39ceSPoul-Henning Kamp format = bp->bio_cmd == FDBIO_FORMAT; 1682419f39ceSPoul-Henning Kamp rdsectid = bp->bio_cmd == FDBIO_RDSECTID; 1683fb35bd37SJoerg Wunsch if (format) 16848177437dSPoul-Henning Kamp finfo = (struct fd_formb *)bp->bio_data; 16855b81b6b3SRodney W. Grimes TRACE1("fd%d", fdu); 16865b81b6b3SRodney W. Grimes TRACE1("[%s]", fdstates[fdc->state]); 16875b81b6b3SRodney W. Grimes TRACE1("(0x%x)", fd->flags); 16886182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); 16896182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 16905b81b6b3SRodney W. Grimes switch (fdc->state) 16915b81b6b3SRodney W. Grimes { 16925b81b6b3SRodney W. Grimes case DEVIDLE: 16935b81b6b3SRodney W. Grimes case FINDWORK: /* we have found new work */ 16945b81b6b3SRodney W. Grimes fdc->retry = 0; 16955b81b6b3SRodney W. Grimes fd->skip = 0; 16965b81b6b3SRodney W. Grimes fdc->fd = fd; 16975b81b6b3SRodney W. Grimes fdc->fdu = fdu; 16985f830ea2SJoerg Wunsch fdc->fdctl_wr(fdc, fd->ft->trans); 16993a2f7427SDavid Greenman TRACE1("[0x%x->FDCTL]", fd->ft->trans); 1700246ed35dSJoerg Wunsch /* 1701246ed35dSJoerg Wunsch * If the next drive has a motor startup pending, then 1702246ed35dSJoerg Wunsch * it will start up in its own good time. 1703246ed35dSJoerg Wunsch */ 17046182fdbdSPeter Wemm if(fd->flags & FD_MOTOR_WAIT) { 17055b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 1706246ed35dSJoerg Wunsch return (0); /* will return later */ 17075b81b6b3SRodney W. Grimes } 1708246ed35dSJoerg Wunsch /* 1709246ed35dSJoerg Wunsch * Maybe if it's not starting, it SHOULD be starting. 1710246ed35dSJoerg Wunsch */ 17115b81b6b3SRodney W. Grimes if (!(fd->flags & FD_MOTOR)) 17125b81b6b3SRodney W. Grimes { 17135b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 17146182fdbdSPeter Wemm fd_turnon(fd); 1715246ed35dSJoerg Wunsch return (0); /* will return later */ 17165b81b6b3SRodney W. Grimes } 17175b81b6b3SRodney W. Grimes else /* at least make sure we are selected */ 17185b81b6b3SRodney W. Grimes { 17196182fdbdSPeter Wemm set_motor(fdc, fd->fdsu, TURNON); 17205b81b6b3SRodney W. Grimes } 17215c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 17225c1a1eaeSBruce Evans fdc->state = RESETCTLR; 17235c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 17245c1a1eaeSBruce Evans } else 17255e235068SJordan K. Hubbard fdc->state = DOSEEK; 1726246ed35dSJoerg Wunsch return (1); /* will return immediately */ 1727fb35bd37SJoerg Wunsch 17285b81b6b3SRodney W. Grimes case DOSEEK: 1729fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 1730fb35bd37SJoerg Wunsch cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1731fb35bd37SJoerg Wunsch if (cylinder == fd->track) 17325b81b6b3SRodney W. Grimes { 17335b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 1734246ed35dSJoerg Wunsch return (1); /* will return immediately */ 17355b81b6b3SRodney W. Grimes } 17366182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, 17371a6bed68SJoerg Wunsch fd->fdsu, cylinder * steptrac, 0)) 1738dc8603e3SJoerg Wunsch { 1739dc8603e3SJoerg Wunsch /* 1740246ed35dSJoerg Wunsch * Seek command not accepted, looks like 1741dc8603e3SJoerg Wunsch * the FDC went off to the Saints... 1742dc8603e3SJoerg Wunsch */ 1743dc8603e3SJoerg Wunsch fdc->retry = 6; /* try a reset */ 17446182fdbdSPeter Wemm return(retrier(fdc)); 1745dc8603e3SJoerg Wunsch } 1746dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 17475b81b6b3SRodney W. Grimes fdc->state = SEEKWAIT; 17485b81b6b3SRodney W. Grimes return(0); /* will return later */ 1749fb35bd37SJoerg Wunsch 17505b81b6b3SRodney W. Grimes case SEEKWAIT: 17515b81b6b3SRodney W. Grimes /* allow heads to settle */ 17526182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 16); 17535b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 17545b81b6b3SRodney W. Grimes return(0); /* will return later */ 1755fb35bd37SJoerg Wunsch 1756246ed35dSJoerg Wunsch case SEEKCOMPLETE : /* seek done, start DMA */ 1757fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 1758fb35bd37SJoerg Wunsch cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1759fb35bd37SJoerg Wunsch 1760246ed35dSJoerg Wunsch /* Make sure seek really happened. */ 17616182fdbdSPeter Wemm if(fd->track == FD_NO_TRACK) { 17621a6bed68SJoerg Wunsch int descyl = cylinder * steptrac; 17633a2f7427SDavid Greenman do { 17643a2f7427SDavid Greenman /* 1765dc5df763SJoerg Wunsch * This might be a "ready changed" interrupt, 1766dc5df763SJoerg Wunsch * which cannot really happen since the 1767dc5df763SJoerg Wunsch * RDY pin is hardwired to + 5 volts. This 1768dc5df763SJoerg Wunsch * generally indicates a "bouncing" intr 1769dc5df763SJoerg Wunsch * line, so do one of the following: 1770dc5df763SJoerg Wunsch * 1771dc5df763SJoerg Wunsch * When running on an enhanced FDC that is 1772dc5df763SJoerg Wunsch * known to not go stuck after responding 1773dc5df763SJoerg Wunsch * with INVALID, fetch all interrupt states 1774dc5df763SJoerg Wunsch * until seeing either an INVALID or a 1775dc5df763SJoerg Wunsch * real interrupt condition. 1776dc5df763SJoerg Wunsch * 1777dc5df763SJoerg Wunsch * When running on a dumb old NE765, give 1778dc5df763SJoerg Wunsch * up immediately. The controller will 1779dc5df763SJoerg Wunsch * provide up to four dummy RC interrupt 1780dc5df763SJoerg Wunsch * conditions right after reset (for the 1781dc5df763SJoerg Wunsch * corresponding four drives), so this is 1782dc5df763SJoerg Wunsch * our only chance to get notice that it 1783dc5df763SJoerg Wunsch * was not the FDC that caused the interrupt. 17843a2f7427SDavid Greenman */ 1785dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) 1786dc5df763SJoerg Wunsch == FD_NOT_VALID) 1787246ed35dSJoerg Wunsch return (0); /* will return later */ 1788dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 1789dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 1790246ed35dSJoerg Wunsch return (0); /* hope for a real intr */ 17913a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 1792dc5df763SJoerg Wunsch 17936182fdbdSPeter Wemm if (0 == descyl) { 1794dc5df763SJoerg Wunsch int failed = 0; 17953a2f7427SDavid Greenman /* 17963a2f7427SDavid Greenman * seek to cyl 0 requested; make sure we are 17973a2f7427SDavid Greenman * really there 17983a2f7427SDavid Greenman */ 1799dc5df763SJoerg Wunsch if (fd_sense_drive_status(fdc, &st3)) 1800dc5df763SJoerg Wunsch failed = 1; 18013a2f7427SDavid Greenman if ((st3 & NE7_ST3_T0) == 0) { 18023a2f7427SDavid Greenman printf( 18033a2f7427SDavid Greenman "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", 18043a2f7427SDavid Greenman fdu, st3, NE7_ST3BITS); 1805dc5df763SJoerg Wunsch failed = 1; 1806dc5df763SJoerg Wunsch } 1807dc5df763SJoerg Wunsch 18086182fdbdSPeter Wemm if (failed) { 18093a2f7427SDavid Greenman if(fdc->retry < 3) 18103a2f7427SDavid Greenman fdc->retry = 3; 18116182fdbdSPeter Wemm return (retrier(fdc)); 18123a2f7427SDavid Greenman } 18133a2f7427SDavid Greenman } 1814dc5df763SJoerg Wunsch 18156182fdbdSPeter Wemm if (cyl != descyl) { 18163a2f7427SDavid Greenman printf( 18173a2f7427SDavid Greenman "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", 18182d9d0204SRodney W. Grimes fdu, descyl, cyl, st0); 1819e5d7d243SBruce Evans if (fdc->retry < 3) 1820e5d7d243SBruce Evans fdc->retry = 3; 18216182fdbdSPeter Wemm return (retrier(fdc)); 18225b81b6b3SRodney W. Grimes } 18235b81b6b3SRodney W. Grimes } 18245b81b6b3SRodney W. Grimes 1825fb35bd37SJoerg Wunsch fd->track = cylinder; 1826fb35bd37SJoerg Wunsch if (format) 1827fb35bd37SJoerg Wunsch fd->skip = (char *)&(finfo->fd_formb_cylno(0)) 1828fb35bd37SJoerg Wunsch - (char *)finfo; 1829250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 18308177437dSPoul-Henning Kamp isa_dmastart(idf, bp->bio_data+fd->skip, 18318177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, fdc->dmachan); 1832fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 18335b81b6b3SRodney W. Grimes sectrac = fd->ft->sectrac; 18345b81b6b3SRodney W. Grimes sec = blknum % (sectrac * fd->ft->heads); 18355b81b6b3SRodney W. Grimes head = sec / sectrac; 18365b81b6b3SRodney W. Grimes sec = sec % sectrac + 1; 18371a6bed68SJoerg Wunsch if (head != 0 && fd->ft->offset_side2 != 0) 18381a6bed68SJoerg Wunsch sec += fd->ft->offset_side2; 18393a2f7427SDavid Greenman fd->hddrv = ((head&1)<<2)+fdu; 18403a2f7427SDavid Greenman 1841250300ebSJoerg Wunsch if(format || !(read || rdsectid)) 18423a2f7427SDavid Greenman { 18433a2f7427SDavid Greenman /* make sure the drive is writable */ 1844dc5df763SJoerg Wunsch if(fd_sense_drive_status(fdc, &st3) != 0) 1845dc8603e3SJoerg Wunsch { 1846dc8603e3SJoerg Wunsch /* stuck controller? */ 18475f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 184856a23089SPoul-Henning Kamp isa_dmadone(idf, 18498177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 18508177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 18515c1a1eaeSBruce Evans fdc->dmachan); 1852dc8603e3SJoerg Wunsch fdc->retry = 6; /* reset the beast */ 18536182fdbdSPeter Wemm return (retrier(fdc)); 1854dc8603e3SJoerg Wunsch } 18553a2f7427SDavid Greenman if(st3 & NE7_ST3_WP) 18563a2f7427SDavid Greenman { 18573a2f7427SDavid Greenman /* 18583a2f7427SDavid Greenman * XXX YES! this is ugly. 18593a2f7427SDavid Greenman * in order to force the current operation 18603a2f7427SDavid Greenman * to fail, we will have to fake an FDC 18613a2f7427SDavid Greenman * error - all error handling is done 18623a2f7427SDavid Greenman * by the retrier() 18633a2f7427SDavid Greenman */ 18643a2f7427SDavid Greenman fdc->status[0] = NE7_ST0_IC_AT; 18653a2f7427SDavid Greenman fdc->status[1] = NE7_ST1_NW; 18663a2f7427SDavid Greenman fdc->status[2] = 0; 18673a2f7427SDavid Greenman fdc->status[3] = fd->track; 18683a2f7427SDavid Greenman fdc->status[4] = head; 18693a2f7427SDavid Greenman fdc->status[5] = sec; 18703a2f7427SDavid Greenman fdc->retry = 8; /* break out immediately */ 18713a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; /* not really... */ 1872246ed35dSJoerg Wunsch return (1); /* will return immediately */ 18733a2f7427SDavid Greenman } 18743a2f7427SDavid Greenman } 18755b81b6b3SRodney W. Grimes 18766182fdbdSPeter Wemm if (format) { 18771a6bed68SJoerg Wunsch ne7cmd = NE7CMD_FORMAT | mfm; 18785f830ea2SJoerg Wunsch if (fdc->flags & FDC_NODMA) { 18795f830ea2SJoerg Wunsch /* 18805f830ea2SJoerg Wunsch * This seems to be necessary for 18815f830ea2SJoerg Wunsch * whatever obscure reason; if we omit 18825f830ea2SJoerg Wunsch * it, we end up filling the sector ID 18835f830ea2SJoerg Wunsch * fields of the newly formatted track 18845f830ea2SJoerg Wunsch * entirely with garbage, causing 18855f830ea2SJoerg Wunsch * `wrong cylinder' errors all over 18865f830ea2SJoerg Wunsch * the place when trying to read them 18875f830ea2SJoerg Wunsch * back. 18885f830ea2SJoerg Wunsch * 18895f830ea2SJoerg Wunsch * Umpf. 18905f830ea2SJoerg Wunsch */ 18918177437dSPoul-Henning Kamp SET_BCDR(fdc, 1, bp->bio_bcount, 0); 18925f830ea2SJoerg Wunsch 18938177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd, 18948177437dSPoul-Henning Kamp bp->bio_data+fd->skip, 18958177437dSPoul-Henning Kamp bp->bio_bcount); 18965f830ea2SJoerg Wunsch 18975f830ea2SJoerg Wunsch } 1898b39c878eSAndrey A. Chernov /* formatting */ 18991a6bed68SJoerg Wunsch if(fd_cmd(fdc, 6, ne7cmd, head << 2 | fdu, 1900dc5df763SJoerg Wunsch finfo->fd_formb_secshift, 1901dc5df763SJoerg Wunsch finfo->fd_formb_nsecs, 1902dc5df763SJoerg Wunsch finfo->fd_formb_gaplen, 19036182fdbdSPeter Wemm finfo->fd_formb_fillbyte, 0)) { 1904dc8603e3SJoerg Wunsch /* controller fell over */ 19055f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 190656a23089SPoul-Henning Kamp isa_dmadone(idf, 19078177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 19088177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 19095c1a1eaeSBruce Evans fdc->dmachan); 1910dc8603e3SJoerg Wunsch fdc->retry = 6; 19116182fdbdSPeter Wemm return (retrier(fdc)); 1912dc8603e3SJoerg Wunsch } 1913250300ebSJoerg Wunsch } else if (rdsectid) { 19141a6bed68SJoerg Wunsch ne7cmd = NE7CMD_READID | mfm; 19151a6bed68SJoerg Wunsch if (fd_cmd(fdc, 2, ne7cmd, head << 2 | fdu, 0)) { 1916250300ebSJoerg Wunsch /* controller jamming */ 1917250300ebSJoerg Wunsch fdc->retry = 6; 1918250300ebSJoerg Wunsch return (retrier(fdc)); 1919250300ebSJoerg Wunsch } 19206182fdbdSPeter Wemm } else { 1921250300ebSJoerg Wunsch /* read or write operation */ 19221a6bed68SJoerg Wunsch ne7cmd = (read ? NE7CMD_READ | NE7CMD_SK : NE7CMD_WRITE) | mfm; 19233b178206SWarner Losh if (fdc->flags & FDC_NODMA) { 192469acd21dSWarner Losh /* 1925246ed35dSJoerg Wunsch * This seems to be necessary even when 1926246ed35dSJoerg Wunsch * reading data. 192769acd21dSWarner Losh */ 19283b178206SWarner Losh SET_BCDR(fdc, 1, fdblk, 0); 192969acd21dSWarner Losh 193069acd21dSWarner Losh /* 1931246ed35dSJoerg Wunsch * Perform the write pseudo-DMA before 1932246ed35dSJoerg Wunsch * the WRITE command is sent. 193369acd21dSWarner Losh */ 193469acd21dSWarner Losh if (!read) 19358177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd, 19368177437dSPoul-Henning Kamp bp->bio_data+fd->skip, 193769acd21dSWarner Losh fdblk); 193869acd21dSWarner Losh } 19396182fdbdSPeter Wemm if (fd_cmd(fdc, 9, 19401a6bed68SJoerg Wunsch ne7cmd, 1941dc5df763SJoerg Wunsch head << 2 | fdu, /* head & unit */ 1942dc5df763SJoerg Wunsch fd->track, /* track */ 1943dc5df763SJoerg Wunsch head, 1944dc5df763SJoerg Wunsch sec, /* sector + 1 */ 1945dc5df763SJoerg Wunsch fd->ft->secsize, /* sector size */ 1946dc5df763SJoerg Wunsch sectrac, /* sectors/track */ 1947dc5df763SJoerg Wunsch fd->ft->gap, /* gap size */ 1948dc5df763SJoerg Wunsch fd->ft->datalen, /* data length */ 19496182fdbdSPeter Wemm 0)) { 1950dc8603e3SJoerg Wunsch /* the beast is sleeping again */ 19515f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 195256a23089SPoul-Henning Kamp isa_dmadone(idf, 19538177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 19548177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 19555c1a1eaeSBruce Evans fdc->dmachan); 1956dc8603e3SJoerg Wunsch fdc->retry = 6; 19576182fdbdSPeter Wemm return (retrier(fdc)); 19585b81b6b3SRodney W. Grimes } 1959b39c878eSAndrey A. Chernov } 1960250300ebSJoerg Wunsch if (!rdsectid && (fdc->flags & FDC_NODMA)) 196169acd21dSWarner Losh /* 1962246ed35dSJoerg Wunsch * If this is a read, then simply await interrupt 1963246ed35dSJoerg Wunsch * before performing PIO. 196469acd21dSWarner Losh */ 19658177437dSPoul-Henning Kamp if (read && !fdcpio(fdc,bp->bio_cmd, 19668177437dSPoul-Henning Kamp bp->bio_data+fd->skip,fdblk)) { 19673b178206SWarner Losh fd->tohandle = timeout(fd_iotimeout, fdc, hz); 196869acd21dSWarner Losh return(0); /* will return later */ 196964860614SJoerg Wunsch } 197069acd21dSWarner Losh 197169acd21dSWarner Losh /* 1972246ed35dSJoerg Wunsch * Write (or format) operation will fall through and 1973246ed35dSJoerg Wunsch * await completion interrupt. 197469acd21dSWarner Losh */ 19755b81b6b3SRodney W. Grimes fdc->state = IOCOMPLETE; 19766182fdbdSPeter Wemm fd->tohandle = timeout(fd_iotimeout, fdc, hz); 19775b81b6b3SRodney W. Grimes return (0); /* will return later */ 1978fb35bd37SJoerg Wunsch 197969acd21dSWarner Losh case PIOREAD: 198069acd21dSWarner Losh /* 1981246ed35dSJoerg Wunsch * Actually perform the PIO read. The IOCOMPLETE case 198269acd21dSWarner Losh * removes the timeout for us. 198369acd21dSWarner Losh */ 19848177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk); 198569acd21dSWarner Losh fdc->state = IOCOMPLETE; 198669acd21dSWarner Losh /* FALLTHROUGH */ 1987246ed35dSJoerg Wunsch case IOCOMPLETE: /* IO done, post-analyze */ 19886182fdbdSPeter Wemm untimeout(fd_iotimeout, fdc, fd->tohandle); 1989dc5df763SJoerg Wunsch 199064860614SJoerg Wunsch if (fd_read_status(fdc)) { 1991250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 19928177437dSPoul-Henning Kamp isa_dmadone(idf, bp->bio_data + fd->skip, 19938177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 19945c1a1eaeSBruce Evans fdc->dmachan); 1995dc5df763SJoerg Wunsch if (fdc->retry < 6) 1996dc5df763SJoerg Wunsch fdc->retry = 6; /* force a reset */ 19976182fdbdSPeter Wemm return (retrier(fdc)); 19985b81b6b3SRodney W. Grimes } 1999dc5df763SJoerg Wunsch 20003a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; 2001dc5df763SJoerg Wunsch 20023a2f7427SDavid Greenman /* FALLTHROUGH */ 20033a2f7427SDavid Greenman case IOTIMEDOUT: 2004250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 20058177437dSPoul-Henning Kamp isa_dmadone(idf, bp->bio_data + fd->skip, 20068177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, fdc->dmachan); 20076182fdbdSPeter Wemm if (fdc->status[0] & NE7_ST0_IC) { 20083a2f7427SDavid Greenman if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 20093a2f7427SDavid Greenman && fdc->status[1] & NE7_ST1_OR) { 2010b39c878eSAndrey A. Chernov /* 20113fef646eSJoerg Wunsch * DMA overrun. Someone hogged the bus and 20123fef646eSJoerg Wunsch * didn't release it in time for the next 20133fef646eSJoerg Wunsch * FDC transfer. 20143fef646eSJoerg Wunsch * 20153fef646eSJoerg Wunsch * We normally restart this without bumping 20163fef646eSJoerg Wunsch * the retry counter. However, in case 20173fef646eSJoerg Wunsch * something is seriously messed up (like 20183fef646eSJoerg Wunsch * broken hardware), we rather limit the 20193fef646eSJoerg Wunsch * number of retries so the IO operation 20203fef646eSJoerg Wunsch * doesn't block indefinately. 2021b39c878eSAndrey A. Chernov */ 20223fef646eSJoerg Wunsch if (fdc->dma_overruns++ < FDC_DMAOV_MAX) { 2023b39c878eSAndrey A. Chernov fdc->state = SEEKCOMPLETE; 2024246ed35dSJoerg Wunsch return (1);/* will return immediately */ 20253fef646eSJoerg Wunsch } /* else fall through */ 2026b39c878eSAndrey A. Chernov } 20273fef646eSJoerg Wunsch if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV 20283a2f7427SDavid Greenman && fdc->retry < 6) 20293a2f7427SDavid Greenman fdc->retry = 6; /* force a reset */ 20303a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 20313a2f7427SDavid Greenman && fdc->status[2] & NE7_ST2_WC 20323a2f7427SDavid Greenman && fdc->retry < 3) 20333a2f7427SDavid Greenman fdc->retry = 3; /* force recalibrate */ 20346182fdbdSPeter Wemm return (retrier(fdc)); 20355b81b6b3SRodney W. Grimes } 20365b81b6b3SRodney W. Grimes /* All OK */ 2037250300ebSJoerg Wunsch if (rdsectid) { 2038250300ebSJoerg Wunsch /* copy out ID field contents */ 203964860614SJoerg Wunsch idp = (struct fdc_readid *)bp->bio_data; 2040250300ebSJoerg Wunsch idp->cyl = fdc->status[3]; 2041250300ebSJoerg Wunsch idp->head = fdc->status[4]; 2042250300ebSJoerg Wunsch idp->sec = fdc->status[5]; 2043250300ebSJoerg Wunsch idp->secshift = fdc->status[6]; 2044250300ebSJoerg Wunsch } 20453fef646eSJoerg Wunsch /* Operation successful, retry DMA overruns again next time. */ 20463fef646eSJoerg Wunsch fdc->dma_overruns = 0; 20473a2f7427SDavid Greenman fd->skip += fdblk; 2048fb35bd37SJoerg Wunsch if (!rdsectid && !format && fd->skip < bp->bio_bcount) { 20495b81b6b3SRodney W. Grimes /* set up next transfer */ 20505b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 20516182fdbdSPeter Wemm } else { 20525b81b6b3SRodney W. Grimes /* ALL DONE */ 20535b81b6b3SRodney W. Grimes fd->skip = 0; 2054fb35bd37SJoerg Wunsch bp->bio_resid = 0; 2055e93e63cbSBruce Evans fdc->bp = NULL; 20565f830ea2SJoerg Wunsch device_unbusy(fd->dev); 205780980460SPoul-Henning Kamp biofinish(bp, fd->device_stats, 0); 20585b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 20595b81b6b3SRodney W. Grimes fdc->fdu = -1; 20605b81b6b3SRodney W. Grimes fdc->state = FINDWORK; 20615b81b6b3SRodney W. Grimes } 2062246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2063fb35bd37SJoerg Wunsch 20645b81b6b3SRodney W. Grimes case RESETCTLR: 20653a2f7427SDavid Greenman fdc_reset(fdc); 20665b81b6b3SRodney W. Grimes fdc->retry++; 20675c1a1eaeSBruce Evans fdc->state = RESETCOMPLETE; 2068246ed35dSJoerg Wunsch return (0); /* will return later */ 2069fb35bd37SJoerg Wunsch 20705c1a1eaeSBruce Evans case RESETCOMPLETE: 20715c1a1eaeSBruce Evans /* 20725c1a1eaeSBruce Evans * Discard all the results from the reset so that they 20735c1a1eaeSBruce Evans * can't cause an unexpected interrupt later. 20745c1a1eaeSBruce Evans */ 20750e317d05SJoerg Wunsch for (i = 0; i < 4; i++) 20760e317d05SJoerg Wunsch (void)fd_sense_int(fdc, &st0, &cyl); 20775c1a1eaeSBruce Evans fdc->state = STARTRECAL; 2078fb35bd37SJoerg Wunsch /* FALLTHROUGH */ 20795c1a1eaeSBruce Evans case STARTRECAL: 20806182fdbdSPeter Wemm if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { 2081dc8603e3SJoerg Wunsch /* arrgl */ 2082dc8603e3SJoerg Wunsch fdc->retry = 6; 20836182fdbdSPeter Wemm return (retrier(fdc)); 2084dc8603e3SJoerg Wunsch } 20855b81b6b3SRodney W. Grimes fdc->state = RECALWAIT; 20865b81b6b3SRodney W. Grimes return (0); /* will return later */ 2087fb35bd37SJoerg Wunsch 20885b81b6b3SRodney W. Grimes case RECALWAIT: 20895b81b6b3SRodney W. Grimes /* allow heads to settle */ 20906182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 8); 20915b81b6b3SRodney W. Grimes fdc->state = RECALCOMPLETE; 20925b81b6b3SRodney W. Grimes return (0); /* will return later */ 2093fb35bd37SJoerg Wunsch 20945b81b6b3SRodney W. Grimes case RECALCOMPLETE: 20953a2f7427SDavid Greenman do { 20963a2f7427SDavid Greenman /* 2097dc5df763SJoerg Wunsch * See SEEKCOMPLETE for a comment on this: 20983a2f7427SDavid Greenman */ 2099dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) 2100246ed35dSJoerg Wunsch return (0); /* will return later */ 2101dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 2102dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 2103246ed35dSJoerg Wunsch return (0); /* hope for a real intr */ 21043a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 21053a2f7427SDavid Greenman if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) 21065b81b6b3SRodney W. Grimes { 2107dc8603e3SJoerg Wunsch if(fdc->retry > 3) 2108dc8603e3SJoerg Wunsch /* 2109246ed35dSJoerg Wunsch * A recalibrate from beyond cylinder 77 2110dc8603e3SJoerg Wunsch * will "fail" due to the FDC limitations; 2111dc8603e3SJoerg Wunsch * since people used to complain much about 2112dc8603e3SJoerg Wunsch * the failure message, try not logging 2113dc8603e3SJoerg Wunsch * this one if it seems to be the first 2114246ed35dSJoerg Wunsch * time in a line. 2115dc8603e3SJoerg Wunsch */ 2116dc8603e3SJoerg Wunsch printf("fd%d: recal failed ST0 %b cyl %d\n", 2117dc8603e3SJoerg Wunsch fdu, st0, NE7_ST0BITS, cyl); 21183a2f7427SDavid Greenman if(fdc->retry < 3) fdc->retry = 3; 21196182fdbdSPeter Wemm return (retrier(fdc)); 21205b81b6b3SRodney W. Grimes } 21215b81b6b3SRodney W. Grimes fd->track = 0; 21225b81b6b3SRodney W. Grimes /* Seek (probably) necessary */ 21235b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 2124246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2125fb35bd37SJoerg Wunsch 21265b81b6b3SRodney W. Grimes case MOTORWAIT: 21275b81b6b3SRodney W. Grimes if(fd->flags & FD_MOTOR_WAIT) 21285b81b6b3SRodney W. Grimes { 21295b81b6b3SRodney W. Grimes return (0); /* time's not up yet */ 21305b81b6b3SRodney W. Grimes } 21315c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 21325c1a1eaeSBruce Evans fdc->state = RESETCTLR; 21335c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 2134fb35bd37SJoerg Wunsch } else 2135fb35bd37SJoerg Wunsch fdc->state = DOSEEK; 2136246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2137fb35bd37SJoerg Wunsch 21385b81b6b3SRodney W. Grimes default: 2139b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, "unexpected FD int->"); 214064860614SJoerg Wunsch if (fd_read_status(fdc) == 0) 2141a838d83dSBruce Evans printf("FDC status :%x %x %x %x %x %x %x ", 21425b81b6b3SRodney W. Grimes fdc->status[0], 21435b81b6b3SRodney W. Grimes fdc->status[1], 21445b81b6b3SRodney W. Grimes fdc->status[2], 21455b81b6b3SRodney W. Grimes fdc->status[3], 21465b81b6b3SRodney W. Grimes fdc->status[4], 21475b81b6b3SRodney W. Grimes fdc->status[5], 21485b81b6b3SRodney W. Grimes fdc->status[6] ); 21493a2f7427SDavid Greenman else 2150dac0f2dbSJoerg Wunsch printf("No status available "); 2151dac0f2dbSJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) != 0) 2152dac0f2dbSJoerg Wunsch { 2153dac0f2dbSJoerg Wunsch printf("[controller is dead now]\n"); 2154246ed35dSJoerg Wunsch return (0); /* will return later */ 21555b81b6b3SRodney W. Grimes } 2156dac0f2dbSJoerg Wunsch printf("ST0 = %x, PCN = %x\n", st0, cyl); 2157246ed35dSJoerg Wunsch return (0); /* will return later */ 2158dac0f2dbSJoerg Wunsch } 2159246ed35dSJoerg Wunsch /* noone should ever get here */ 21605b81b6b3SRodney W. Grimes } 21615b81b6b3SRodney W. Grimes 2162aaf08d94SGarrett Wollman static int 21636182fdbdSPeter Wemm retrier(struct fdc_data *fdc) 21645b81b6b3SRodney W. Grimes { 21658177437dSPoul-Henning Kamp struct bio *bp; 21666182fdbdSPeter Wemm struct fd_data *fd; 21676182fdbdSPeter Wemm int fdu; 21685b81b6b3SRodney W. Grimes 2169e93e63cbSBruce Evans bp = fdc->bp; 21705b81b6b3SRodney W. Grimes 21716182fdbdSPeter Wemm /* XXX shouldn't this be cached somewhere? */ 2172a2f19df9SPoul-Henning Kamp fd = bp->bio_dev->si_drv1; 2173503799eaSPoul-Henning Kamp fdu = fd->fdu; 21746182fdbdSPeter Wemm if (fd->options & FDOPT_NORETRY) 21753a2f7427SDavid Greenman goto fail; 21766182fdbdSPeter Wemm 21776182fdbdSPeter Wemm switch (fdc->retry) { 21785b81b6b3SRodney W. Grimes case 0: case 1: case 2: 21795b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 21805b81b6b3SRodney W. Grimes break; 21815b81b6b3SRodney W. Grimes case 3: case 4: case 5: 21825b81b6b3SRodney W. Grimes fdc->state = STARTRECAL; 21835b81b6b3SRodney W. Grimes break; 21845b81b6b3SRodney W. Grimes case 6: 21855b81b6b3SRodney W. Grimes fdc->state = RESETCTLR; 21865b81b6b3SRodney W. Grimes break; 21875b81b6b3SRodney W. Grimes case 7: 21885b81b6b3SRodney W. Grimes break; 21895b81b6b3SRodney W. Grimes default: 21903a2f7427SDavid Greenman fail: 2191fb35bd37SJoerg Wunsch if ((fd->options & FDOPT_NOERRLOG) == 0) { 2192f90c382cSPoul-Henning Kamp disk_err(bp, "hard error", 2193f90c382cSPoul-Henning Kamp fdc->fd->skip / DEV_BSIZE, 0); 2194fb35bd37SJoerg Wunsch if (fdc->flags & FDC_STAT_VALID) { 2195dc5df763SJoerg Wunsch printf( 2196a838d83dSBruce Evans " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", 2197dc5df763SJoerg Wunsch fdc->status[0], NE7_ST0BITS, 2198dc5df763SJoerg Wunsch fdc->status[1], NE7_ST1BITS, 2199dc5df763SJoerg Wunsch fdc->status[2], NE7_ST2BITS, 2200dc5df763SJoerg Wunsch fdc->status[3], fdc->status[4], 2201dc5df763SJoerg Wunsch fdc->status[5]); 2202dc5df763SJoerg Wunsch } 2203dc5df763SJoerg Wunsch else 2204dc5df763SJoerg Wunsch printf(" (No status)\n"); 22055b81b6b3SRodney W. Grimes } 22062995d110SJoerg Wunsch if ((fd->options & FDOPT_NOERROR) == 0) { 22078177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 22088177437dSPoul-Henning Kamp bp->bio_error = EIO; 2209fb35bd37SJoerg Wunsch bp->bio_resid = bp->bio_bcount - fdc->fd->skip; 2210fb35bd37SJoerg Wunsch } else 2211fb35bd37SJoerg Wunsch bp->bio_resid = 0; 2212e93e63cbSBruce Evans fdc->bp = NULL; 22135b81b6b3SRodney W. Grimes fdc->fd->skip = 0; 22145f830ea2SJoerg Wunsch device_unbusy(fd->dev); 221580980460SPoul-Henning Kamp biofinish(bp, fdc->fd->device_stats, 0); 221692ed385aSRodney W. Grimes fdc->state = FINDWORK; 22175c1a1eaeSBruce Evans fdc->flags |= FDC_NEEDS_RESET; 22185b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 22195b81b6b3SRodney W. Grimes fdc->fdu = -1; 222092ed385aSRodney W. Grimes return (1); 22215b81b6b3SRodney W. Grimes } 22225b81b6b3SRodney W. Grimes fdc->retry++; 22235b81b6b3SRodney W. Grimes return (1); 22245b81b6b3SRodney W. Grimes } 22255b81b6b3SRodney W. Grimes 22261fdb6e6cSPoul-Henning Kamp static void 22271fdb6e6cSPoul-Henning Kamp fdbiodone(struct bio *bp) 22281fdb6e6cSPoul-Henning Kamp { 22291fdb6e6cSPoul-Henning Kamp wakeup(bp); 22301fdb6e6cSPoul-Henning Kamp } 22311fdb6e6cSPoul-Henning Kamp 2232b39c878eSAndrey A. Chernov static int 223389c9c53dSPoul-Henning Kamp fdmisccmd(struct cdev *dev, u_int cmd, void *data) 2234b39c878eSAndrey A. Chernov { 2235b39c878eSAndrey A. Chernov fdu_t fdu; 2236b39c878eSAndrey A. Chernov fd_p fd; 22371fdb6e6cSPoul-Henning Kamp struct bio *bp; 2238f664aeeeSJoerg Wunsch struct fd_formb *finfo; 2239f664aeeeSJoerg Wunsch struct fdc_readid *idfield; 22403a2f7427SDavid Greenman size_t fdblk; 224102aad38cSPoul-Henning Kamp int error; 2242b39c878eSAndrey A. Chernov 2243a2f19df9SPoul-Henning Kamp fd = dev->si_drv1; 2244503799eaSPoul-Henning Kamp fdu = fd->fdu; 22453a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2246f664aeeeSJoerg Wunsch finfo = (struct fd_formb *)data; 2247f664aeeeSJoerg Wunsch idfield = (struct fdc_readid *)data; 2248b39c878eSAndrey A. Chernov 22498595de4aSPoul-Henning Kamp bp = malloc(sizeof(struct bio), M_TEMP, M_WAITOK | M_ZERO); 2250b39c878eSAndrey A. Chernov 2251b39c878eSAndrey A. Chernov /* 2252b52b7f46SPoul-Henning Kamp * Set up a bio request for fdstrategy(). bio_offset is faked 2253f664aeeeSJoerg Wunsch * so that fdstrategy() will seek to the the requested 2254817988beSPoul-Henning Kamp * cylinder, and use the desired head. 2255b39c878eSAndrey A. Chernov */ 2256f664aeeeSJoerg Wunsch bp->bio_cmd = cmd; 2257419f39ceSPoul-Henning Kamp if (cmd == FDBIO_FORMAT) { 2258b52b7f46SPoul-Henning Kamp bp->bio_offset = 2259f664aeeeSJoerg Wunsch (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + 2260b52b7f46SPoul-Henning Kamp finfo->head * fd->ft->sectrac) * fdblk; 2261f664aeeeSJoerg Wunsch bp->bio_bcount = sizeof(struct fd_idfield_data) * 2262f664aeeeSJoerg Wunsch finfo->fd_formb_nsecs; 2263419f39ceSPoul-Henning Kamp } else if (cmd == FDBIO_RDSECTID) { 2264b52b7f46SPoul-Henning Kamp bp->bio_offset = 2265f664aeeeSJoerg Wunsch (idfield->cyl * (fd->ft->sectrac * fd->ft->heads) + 2266b52b7f46SPoul-Henning Kamp idfield->head * fd->ft->sectrac) * fdblk; 2267250300ebSJoerg Wunsch bp->bio_bcount = sizeof(struct fdc_readid); 2268f664aeeeSJoerg Wunsch } else 2269f664aeeeSJoerg Wunsch panic("wrong cmd in fdmisccmd()"); 2270f664aeeeSJoerg Wunsch bp->bio_data = data; 2271250300ebSJoerg Wunsch bp->bio_dev = dev; 2272250300ebSJoerg Wunsch bp->bio_done = fdbiodone; 2273817988beSPoul-Henning Kamp bp->bio_flags = 0; 2274250300ebSJoerg Wunsch 2275c3bdb2f7SPoul-Henning Kamp /* Now run the command. */ 2276f664aeeeSJoerg Wunsch fdstrategy(bp); 227702aad38cSPoul-Henning Kamp error = biowait(bp, "fdcmd"); 2278c3bdb2f7SPoul-Henning Kamp 2279f664aeeeSJoerg Wunsch free(bp, M_TEMP); 228002aad38cSPoul-Henning Kamp return (error); 2281f664aeeeSJoerg Wunsch } 22825b81b6b3SRodney W. Grimes 22833e425b96SJulian Elischer static int 228489c9c53dSPoul-Henning Kamp fdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 2285f5f7ba03SJordan K. Hubbard { 2286f664aeeeSJoerg Wunsch fdu_t fdu; 2287f664aeeeSJoerg Wunsch fd_p fd; 22882995d110SJoerg Wunsch struct fdc_status *fsp; 2289250300ebSJoerg Wunsch struct fdc_readid *rid; 2290503799eaSPoul-Henning Kamp int error; 2291f5f7ba03SJordan K. Hubbard 2292a2f19df9SPoul-Henning Kamp fd = dev->si_drv1; 2293503799eaSPoul-Henning Kamp fdu = fd->fdu; 2294fb35bd37SJoerg Wunsch 22951a6bed68SJoerg Wunsch /* 22961a6bed68SJoerg Wunsch * First, handle everything that could be done with 22971a6bed68SJoerg Wunsch * FD_NONBLOCK still being set. 22981a6bed68SJoerg Wunsch */ 22991a6bed68SJoerg Wunsch switch (cmd) { 2300c91a63aaSPoul-Henning Kamp 2301c91a63aaSPoul-Henning Kamp case DIOCGMEDIASIZE: 230229ec21d1SYaroslav Tykhiy if (fd->ft == 0) 230329ec21d1SYaroslav Tykhiy return ((fd->flags & FD_NONBLOCK) ? EAGAIN : ENXIO); 2304c91a63aaSPoul-Henning Kamp *(off_t *)addr = (128 << (fd->ft->secsize)) * fd->ft->size; 2305c91a63aaSPoul-Henning Kamp return (0); 2306c91a63aaSPoul-Henning Kamp 2307c91a63aaSPoul-Henning Kamp case DIOCGSECTORSIZE: 230829ec21d1SYaroslav Tykhiy if (fd->ft == 0) 230929ec21d1SYaroslav Tykhiy return ((fd->flags & FD_NONBLOCK) ? EAGAIN : ENXIO); 2310c91a63aaSPoul-Henning Kamp *(u_int *)addr = 128 << (fd->ft->secsize); 2311c91a63aaSPoul-Henning Kamp return (0); 2312c91a63aaSPoul-Henning Kamp 23131a6bed68SJoerg Wunsch case FIONBIO: 23141a6bed68SJoerg Wunsch if (*(int *)addr != 0) 23151a6bed68SJoerg Wunsch fd->flags |= FD_NONBLOCK; 23161a6bed68SJoerg Wunsch else { 23171a6bed68SJoerg Wunsch if (fd->ft == 0) { 23181a6bed68SJoerg Wunsch /* 23191a6bed68SJoerg Wunsch * No drive type has been selected yet, 23201a6bed68SJoerg Wunsch * cannot turn FNONBLOCK off. 23211a6bed68SJoerg Wunsch */ 23221a6bed68SJoerg Wunsch return (EINVAL); 23231a6bed68SJoerg Wunsch } 23241a6bed68SJoerg Wunsch fd->flags &= ~FD_NONBLOCK; 23251a6bed68SJoerg Wunsch } 23261a6bed68SJoerg Wunsch return (0); 2327fb35bd37SJoerg Wunsch 23281a6bed68SJoerg Wunsch case FIOASYNC: 23291a6bed68SJoerg Wunsch /* keep the generic fcntl() code happy */ 23301a6bed68SJoerg Wunsch return (0); 23311a6bed68SJoerg Wunsch 23321a6bed68SJoerg Wunsch case FD_GTYPE: /* get drive type */ 23331a6bed68SJoerg Wunsch if (fd->ft == 0) 23341a6bed68SJoerg Wunsch /* no type known yet, return the native type */ 23351a6bed68SJoerg Wunsch *(struct fd_type *)addr = fd_native_types[fd->type]; 23361a6bed68SJoerg Wunsch else 23371a6bed68SJoerg Wunsch *(struct fd_type *)addr = *fd->ft; 23381a6bed68SJoerg Wunsch return (0); 23391a6bed68SJoerg Wunsch 23401a6bed68SJoerg Wunsch case FD_STYPE: /* set drive type */ 2341a3a10d1cSBruce Evans /* 2342a3a10d1cSBruce Evans * Allow setting drive type temporarily iff 2343a3a10d1cSBruce Evans * currently unset. Used for fdformat so any 2344a3a10d1cSBruce Evans * user can set it, and then start formatting. 2345a3a10d1cSBruce Evans */ 2346a3a10d1cSBruce Evans if (fd->ft) 2347a3a10d1cSBruce Evans return (EINVAL); /* already set */ 2348503799eaSPoul-Henning Kamp fd->fts[0] = *(struct fd_type *)addr; 2349a3a10d1cSBruce Evans fd->ft = &fd->fts[0]; 2350a3a10d1cSBruce Evans fd->flags |= FD_UA; 23511a6bed68SJoerg Wunsch return (0); 23521a6bed68SJoerg Wunsch 23531a6bed68SJoerg Wunsch case FD_GOPTS: /* get drive options */ 2354503799eaSPoul-Henning Kamp *(int *)addr = fd->options + FDOPT_AUTOSEL; 23551a6bed68SJoerg Wunsch return (0); 23561a6bed68SJoerg Wunsch 23571a6bed68SJoerg Wunsch case FD_SOPTS: /* set drive options */ 23581a6bed68SJoerg Wunsch fd->options = *(int *)addr & ~FDOPT_AUTOSEL; 23591a6bed68SJoerg Wunsch return (0); 23601a6bed68SJoerg Wunsch 23611a6bed68SJoerg Wunsch #ifdef FDC_DEBUG 23621a6bed68SJoerg Wunsch case FD_DEBUG: 23631a6bed68SJoerg Wunsch if ((fd_debug != 0) != (*(int *)addr != 0)) { 23641a6bed68SJoerg Wunsch fd_debug = (*(int *)addr != 0); 23651a6bed68SJoerg Wunsch printf("fd%d: debugging turned %s\n", 23661a6bed68SJoerg Wunsch fd->fdu, fd_debug ? "on" : "off"); 23671a6bed68SJoerg Wunsch } 23681a6bed68SJoerg Wunsch return (0); 23691a6bed68SJoerg Wunsch #endif 23701a6bed68SJoerg Wunsch 23711a6bed68SJoerg Wunsch case FD_CLRERR: 237244731cabSJohn Baldwin if (suser(td) != 0) 23731a6bed68SJoerg Wunsch return (EPERM); 23741a6bed68SJoerg Wunsch fd->fdc->fdc_errs = 0; 23751a6bed68SJoerg Wunsch return (0); 23761a6bed68SJoerg Wunsch 23771a6bed68SJoerg Wunsch case FD_GSTAT: 23781a6bed68SJoerg Wunsch fsp = (struct fdc_status *)addr; 23791a6bed68SJoerg Wunsch if ((fd->fdc->flags & FDC_STAT_VALID) == 0) 23801a6bed68SJoerg Wunsch return (EINVAL); 23811a6bed68SJoerg Wunsch memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); 23821a6bed68SJoerg Wunsch return (0); 23831a6bed68SJoerg Wunsch 23841a6bed68SJoerg Wunsch case FD_GDTYPE: 23851a6bed68SJoerg Wunsch *(enum fd_drivetype *)addr = fd->type; 23861a6bed68SJoerg Wunsch return (0); 23871a6bed68SJoerg Wunsch } 23881a6bed68SJoerg Wunsch 23891a6bed68SJoerg Wunsch /* 23901a6bed68SJoerg Wunsch * Now handle everything else. Make sure we have a valid 23911a6bed68SJoerg Wunsch * drive type. 23921a6bed68SJoerg Wunsch */ 23931a6bed68SJoerg Wunsch if (fd->flags & FD_NONBLOCK) 23941a6bed68SJoerg Wunsch return (EAGAIN); 23951a6bed68SJoerg Wunsch if (fd->ft == 0) 23961a6bed68SJoerg Wunsch return (ENXIO); 2397fb35bd37SJoerg Wunsch error = 0; 2398f5f7ba03SJordan K. Hubbard 23996182fdbdSPeter Wemm switch (cmd) { 2400f664aeeeSJoerg Wunsch 2401b39c878eSAndrey A. Chernov case FD_FORM: 2402b39c878eSAndrey A. Chernov if ((flag & FWRITE) == 0) 2403fb35bd37SJoerg Wunsch return (EBADF); /* must be opened for writing */ 2404fb35bd37SJoerg Wunsch if (((struct fd_formb *)addr)->format_version != 2405b39c878eSAndrey A. Chernov FD_FORMAT_VERSION) 2406fb35bd37SJoerg Wunsch return (EINVAL); /* wrong version of formatting prog */ 2407419f39ceSPoul-Henning Kamp error = fdmisccmd(dev, FDBIO_FORMAT, addr); 2408b39c878eSAndrey A. Chernov break; 2409b39c878eSAndrey A. Chernov 2410b39c878eSAndrey A. Chernov case FD_GTYPE: /* get drive type */ 24113e425b96SJulian Elischer *(struct fd_type *)addr = *fd->ft; 2412f5f7ba03SJordan K. Hubbard break; 2413f5f7ba03SJordan K. Hubbard 24143a2f7427SDavid Greenman case FD_STYPE: /* set drive type */ 24153a2f7427SDavid Greenman /* this is considered harmful; only allow for superuser */ 241644731cabSJohn Baldwin if (suser(td) != 0) 2417fb35bd37SJoerg Wunsch return (EPERM); 24183e425b96SJulian Elischer *fd->ft = *(struct fd_type *)addr; 24193a2f7427SDavid Greenman break; 24203a2f7427SDavid Greenman 24213a2f7427SDavid Greenman case FD_GOPTS: /* get drive options */ 24223e425b96SJulian Elischer *(int *)addr = fd->options; 24233a2f7427SDavid Greenman break; 24243a2f7427SDavid Greenman 24253a2f7427SDavid Greenman case FD_SOPTS: /* set drive options */ 24263e425b96SJulian Elischer fd->options = *(int *)addr; 24273a2f7427SDavid Greenman break; 24283a2f7427SDavid Greenman 2429f664aeeeSJoerg Wunsch #ifdef FDC_DEBUG 2430f664aeeeSJoerg Wunsch case FD_DEBUG: 24310e17a5bcSJoerg Wunsch if ((fd_debug != 0) != (*(int *)addr != 0)) { 24320e17a5bcSJoerg Wunsch fd_debug = (*(int *)addr != 0); 24330e17a5bcSJoerg Wunsch printf("fd%d: debugging turned %s\n", 24340e17a5bcSJoerg Wunsch fd->fdu, fd_debug ? "on" : "off"); 24350e17a5bcSJoerg Wunsch } 2436f664aeeeSJoerg Wunsch break; 2437f664aeeeSJoerg Wunsch #endif 2438f664aeeeSJoerg Wunsch 24392995d110SJoerg Wunsch case FD_CLRERR: 244044731cabSJohn Baldwin if (suser(td) != 0) 2441fb35bd37SJoerg Wunsch return (EPERM); 24422995d110SJoerg Wunsch fd->fdc->fdc_errs = 0; 24432995d110SJoerg Wunsch break; 24442995d110SJoerg Wunsch 24452995d110SJoerg Wunsch case FD_GSTAT: 24462995d110SJoerg Wunsch fsp = (struct fdc_status *)addr; 24472995d110SJoerg Wunsch if ((fd->fdc->flags & FDC_STAT_VALID) == 0) 2448fb35bd37SJoerg Wunsch return (EINVAL); 24492995d110SJoerg Wunsch memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); 24502995d110SJoerg Wunsch break; 24512995d110SJoerg Wunsch 2452250300ebSJoerg Wunsch case FD_READID: 2453250300ebSJoerg Wunsch rid = (struct fdc_readid *)addr; 2454250300ebSJoerg Wunsch if (rid->cyl > MAX_CYLINDER || rid->head > MAX_HEAD) 2455fb35bd37SJoerg Wunsch return (EINVAL); 2456419f39ceSPoul-Henning Kamp error = fdmisccmd(dev, FDBIO_RDSECTID, addr); 2457250300ebSJoerg Wunsch break; 2458250300ebSJoerg Wunsch 2459f5f7ba03SJordan K. Hubbard default: 24603a2f7427SDavid Greenman error = ENOTTY; 2461f5f7ba03SJordan K. Hubbard break; 2462f5f7ba03SJordan K. Hubbard } 2463f5f7ba03SJordan K. Hubbard return (error); 2464f5f7ba03SJordan K. Hubbard } 2465