187f6c662SJulian Elischer /* 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 * 3. All advertising materials mentioning features or use of this software 3364860614SJoerg Wunsch * must display the following acknowledgement: 3464860614SJoerg Wunsch * This product includes software developed by the University of 3564860614SJoerg Wunsch * California, Berkeley and its contributors. 3664860614SJoerg Wunsch * 4. Neither the name of the University nor the names of its contributors 3764860614SJoerg Wunsch * may be used to endorse or promote products derived from this software 3864860614SJoerg Wunsch * without specific prior written permission. 395b81b6b3SRodney W. Grimes * 405b81b6b3SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 415b81b6b3SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 425b81b6b3SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 435b81b6b3SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 445b81b6b3SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 455b81b6b3SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 465b81b6b3SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 475b81b6b3SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 485b81b6b3SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 495b81b6b3SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 505b81b6b3SRodney W. Grimes * SUCH DAMAGE. 515b81b6b3SRodney W. Grimes * 52dc4ff321SRodney W. Grimes * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 53c3aac50fSPeter Wemm * $FreeBSD$ 545b81b6b3SRodney W. Grimes */ 555b81b6b3SRodney W. Grimes 56d2fb4892SJoerg Wunsch #include "opt_fdc.h" 575f830ea2SJoerg Wunsch #include "card.h" 585b81b6b3SRodney W. Grimes 59b99f0a4aSAndrew Moore #include <sys/param.h> 60b99f0a4aSAndrew Moore #include <sys/systm.h> 619626b608SPoul-Henning Kamp #include <sys/bio.h> 626182fdbdSPeter Wemm #include <sys/bus.h> 636182fdbdSPeter Wemm #include <sys/conf.h> 64b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h> 65fb919e4dSMark Murray #include <sys/disklabel.h> 666182fdbdSPeter Wemm #include <sys/fcntl.h> 67e774b251SJoerg Wunsch #include <sys/fdcio.h> 68fb919e4dSMark Murray #include <sys/kernel.h> 69fb919e4dSMark Murray #include <sys/lock.h> 70b99f0a4aSAndrew Moore #include <sys/malloc.h> 716182fdbdSPeter Wemm #include <sys/module.h> 72fb919e4dSMark Murray #include <sys/mutex.h> 733a2f7427SDavid Greenman #include <sys/proc.h> 74b99f0a4aSAndrew Moore #include <sys/syslog.h> 756182fdbdSPeter Wemm 766182fdbdSPeter Wemm #include <machine/bus.h> 776182fdbdSPeter Wemm #include <sys/rman.h> 786182fdbdSPeter Wemm 796182fdbdSPeter Wemm #include <machine/clock.h> 806182fdbdSPeter Wemm #include <machine/resource.h> 81dc5df763SJoerg Wunsch #include <machine/stdarg.h> 826182fdbdSPeter Wemm 836182fdbdSPeter Wemm #include <isa/isavar.h> 84a97c75b7SDoug Rabson #include <isa/isareg.h> 85a97c75b7SDoug Rabson #include <isa/fdreg.h> 86a97c75b7SDoug Rabson #include <isa/rtc.h> 876182fdbdSPeter Wemm 88246ed35dSJoerg Wunsch enum fdc_type 89246ed35dSJoerg Wunsch { 90246ed35dSJoerg Wunsch FDC_NE765, FDC_I82077, FDC_NE72065, FDC_UNKNOWN = -1 91246ed35dSJoerg Wunsch }; 92246ed35dSJoerg Wunsch 93246ed35dSJoerg Wunsch enum fdc_states { 94246ed35dSJoerg Wunsch DEVIDLE, 95246ed35dSJoerg Wunsch FINDWORK, 96246ed35dSJoerg Wunsch DOSEEK, 97246ed35dSJoerg Wunsch SEEKCOMPLETE , 98246ed35dSJoerg Wunsch IOCOMPLETE, 99246ed35dSJoerg Wunsch RECALCOMPLETE, 100246ed35dSJoerg Wunsch STARTRECAL, 101246ed35dSJoerg Wunsch RESETCTLR, 102246ed35dSJoerg Wunsch SEEKWAIT, 103246ed35dSJoerg Wunsch RECALWAIT, 104246ed35dSJoerg Wunsch MOTORWAIT, 105246ed35dSJoerg Wunsch IOTIMEDOUT, 106246ed35dSJoerg Wunsch RESETCOMPLETE, 107246ed35dSJoerg Wunsch PIOREAD 108246ed35dSJoerg Wunsch }; 109246ed35dSJoerg Wunsch 110246ed35dSJoerg Wunsch #ifdef FDC_DEBUG 111246ed35dSJoerg Wunsch static char const * const fdstates[] = { 112246ed35dSJoerg Wunsch "DEVIDLE", 113246ed35dSJoerg Wunsch "FINDWORK", 114246ed35dSJoerg Wunsch "DOSEEK", 115246ed35dSJoerg Wunsch "SEEKCOMPLETE", 116246ed35dSJoerg Wunsch "IOCOMPLETE", 117246ed35dSJoerg Wunsch "RECALCOMPLETE", 118246ed35dSJoerg Wunsch "STARTRECAL", 119246ed35dSJoerg Wunsch "RESETCTLR", 120246ed35dSJoerg Wunsch "SEEKWAIT", 121246ed35dSJoerg Wunsch "RECALWAIT", 122246ed35dSJoerg Wunsch "MOTORWAIT", 123246ed35dSJoerg Wunsch "IOTIMEDOUT", 124246ed35dSJoerg Wunsch "RESETCOMPLETE", 125246ed35dSJoerg Wunsch "PIOREAD" 126246ed35dSJoerg Wunsch }; 127246ed35dSJoerg Wunsch #endif 128246ed35dSJoerg Wunsch 129246ed35dSJoerg Wunsch /* 130246ed35dSJoerg Wunsch * Per controller structure (softc). 131246ed35dSJoerg Wunsch */ 132246ed35dSJoerg Wunsch struct fdc_data 133246ed35dSJoerg Wunsch { 134246ed35dSJoerg Wunsch int fdcu; /* our unit number */ 135246ed35dSJoerg Wunsch int dmachan; 136246ed35dSJoerg Wunsch int flags; 137246ed35dSJoerg Wunsch #define FDC_ATTACHED 0x01 138246ed35dSJoerg Wunsch #define FDC_STAT_VALID 0x08 139246ed35dSJoerg Wunsch #define FDC_HAS_FIFO 0x10 140246ed35dSJoerg Wunsch #define FDC_NEEDS_RESET 0x20 141246ed35dSJoerg Wunsch #define FDC_NODMA 0x40 142246ed35dSJoerg Wunsch #define FDC_ISPNP 0x80 143246ed35dSJoerg Wunsch #define FDC_ISPCMCIA 0x100 144246ed35dSJoerg Wunsch struct fd_data *fd; 145246ed35dSJoerg Wunsch int fdu; /* the active drive */ 146246ed35dSJoerg Wunsch enum fdc_states state; 147246ed35dSJoerg Wunsch int retry; 1483a5c2c86SJoerg Wunsch #ifndef PC98 149246ed35dSJoerg Wunsch int fdout; /* mirror of the w/o digital output reg */ 1503a5c2c86SJoerg Wunsch #endif 151246ed35dSJoerg Wunsch u_int status[7]; /* copy of the registers */ 152246ed35dSJoerg Wunsch enum fdc_type fdct; /* chip version of FDC */ 153246ed35dSJoerg Wunsch int fdc_errs; /* number of logged errors */ 154246ed35dSJoerg Wunsch int dma_overruns; /* number of DMA overruns */ 155246ed35dSJoerg Wunsch struct bio_queue_head head; 156246ed35dSJoerg Wunsch struct bio *bp; /* active buffer */ 1573a5c2c86SJoerg Wunsch #ifdef PC98 1583a5c2c86SJoerg Wunsch struct resource *res_ioport, *res_fdsio, *res_fdemsio; 1593a5c2c86SJoerg Wunsch struct resource *res_irq, *res_drq; 1603a5c2c86SJoerg Wunsch int rid_ioport, rid_irq, rid_drq; 1613a5c2c86SJoerg Wunsch #else 162246ed35dSJoerg Wunsch struct resource *res_ioport, *res_ctl, *res_irq, *res_drq; 163246ed35dSJoerg Wunsch int rid_ioport, rid_ctl, rid_irq, rid_drq; 1643a5c2c86SJoerg Wunsch #endif 165246ed35dSJoerg Wunsch int port_off; 166246ed35dSJoerg Wunsch bus_space_tag_t portt; 167246ed35dSJoerg Wunsch bus_space_handle_t porth; 1683a5c2c86SJoerg Wunsch #ifdef PC98 1693a5c2c86SJoerg Wunsch bus_space_tag_t sc_fdsiot; 1703a5c2c86SJoerg Wunsch bus_space_handle_t sc_fdsioh; 1713a5c2c86SJoerg Wunsch bus_space_tag_t sc_fdemsiot; 1723a5c2c86SJoerg Wunsch bus_space_handle_t sc_fdemsioh; 1733a5c2c86SJoerg Wunsch #else 174246ed35dSJoerg Wunsch bus_space_tag_t ctlt; 175246ed35dSJoerg Wunsch bus_space_handle_t ctlh; 1763a5c2c86SJoerg Wunsch #endif 177246ed35dSJoerg Wunsch void *fdc_intr; 178246ed35dSJoerg Wunsch struct device *fdc_dev; 1793a5c2c86SJoerg Wunsch #ifndef PC98 180246ed35dSJoerg Wunsch void (*fdctl_wr)(struct fdc_data *fdc, u_int8_t v); 1813a5c2c86SJoerg Wunsch #endif 182246ed35dSJoerg Wunsch }; 183246ed35dSJoerg Wunsch 184246ed35dSJoerg Wunsch typedef int fdu_t; 185246ed35dSJoerg Wunsch typedef int fdcu_t; 186246ed35dSJoerg Wunsch typedef int fdsu_t; 187246ed35dSJoerg Wunsch typedef struct fd_data *fd_p; 188246ed35dSJoerg Wunsch typedef struct fdc_data *fdc_p; 189246ed35dSJoerg Wunsch typedef enum fdc_type fdc_t; 190246ed35dSJoerg Wunsch 191246ed35dSJoerg Wunsch #define FDUNIT(s) (((s) >> 6) & 3) 192246ed35dSJoerg Wunsch #define FDTYPE(s) ((s) & 0x3f) 193246ed35dSJoerg Wunsch 194246ed35dSJoerg Wunsch /* 195246ed35dSJoerg Wunsch * fdc maintains a set (1!) of ivars per child of each controller. 196246ed35dSJoerg Wunsch */ 197246ed35dSJoerg Wunsch enum fdc_device_ivars { 198246ed35dSJoerg Wunsch FDC_IVAR_FDUNIT, 199246ed35dSJoerg Wunsch }; 200246ed35dSJoerg Wunsch 201246ed35dSJoerg Wunsch /* 202246ed35dSJoerg Wunsch * Simple access macros for the ivars. 203246ed35dSJoerg Wunsch */ 204246ed35dSJoerg Wunsch #define FDC_ACCESSOR(A, B, T) \ 205246ed35dSJoerg Wunsch static __inline T fdc_get_ ## A(device_t dev) \ 206246ed35dSJoerg Wunsch { \ 207246ed35dSJoerg Wunsch uintptr_t v; \ 208246ed35dSJoerg Wunsch BUS_READ_IVAR(device_get_parent(dev), dev, FDC_IVAR_ ## B, &v); \ 209246ed35dSJoerg Wunsch return (T) v; \ 210246ed35dSJoerg Wunsch } 211246ed35dSJoerg Wunsch FDC_ACCESSOR(fdunit, FDUNIT, int) 212246ed35dSJoerg Wunsch 2130722d6abSJoerg Wunsch /* configuration flags */ 2140722d6abSJoerg Wunsch #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ 215e34c71eaSJoerg Wunsch #define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */ 2160722d6abSJoerg Wunsch 2170722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */ 2180722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED 0x1000 2190722d6abSJoerg Wunsch 220dc5df763SJoerg Wunsch /* error returns for fd_cmd() */ 221dc5df763SJoerg Wunsch #define FD_FAILED -1 222dc5df763SJoerg Wunsch #define FD_NOT_VALID -2 223dc5df763SJoerg Wunsch #define FDC_ERRMAX 100 /* do not log more */ 2243fef646eSJoerg Wunsch /* 2253fef646eSJoerg Wunsch * Stop retrying after this many DMA overruns. Since each retry takes 22683edbfa5SJoerg Wunsch * one revolution, with 300 rpm., 25 retries take approximately 5 2273fef646eSJoerg Wunsch * seconds which the read attempt will block in case the DMA overrun 2283fef646eSJoerg Wunsch * is persistent. 2293fef646eSJoerg Wunsch */ 2303fef646eSJoerg Wunsch #define FDC_DMAOV_MAX 25 231dc5df763SJoerg Wunsch 2326fb89845SKATO Takenori #define NUMTYPES 17 2336fb89845SKATO Takenori #define NUMDENS (NUMTYPES - 7) 2347ca0641bSAndrey A. Chernov 235246ed35dSJoerg Wunsch #define NO_TYPE 0 236b99f0a4aSAndrew Moore #define FD_1720 1 237b99f0a4aSAndrew Moore #define FD_1480 2 238b99f0a4aSAndrew Moore #define FD_1440 3 239b99f0a4aSAndrew Moore #define FD_1200 4 240b99f0a4aSAndrew Moore #define FD_820 5 241b99f0a4aSAndrew Moore #define FD_800 6 242b99f0a4aSAndrew Moore #define FD_720 7 243b99f0a4aSAndrew Moore #define FD_360 8 2446fb89845SKATO Takenori #define FD_640 9 2456fb89845SKATO Takenori #define FD_1232 10 246ed2fa05eSAndrey A. Chernov 2476fb89845SKATO Takenori #define FD_1480in5_25 11 2486fb89845SKATO Takenori #define FD_1440in5_25 12 2496fb89845SKATO Takenori #define FD_820in5_25 13 2506fb89845SKATO Takenori #define FD_800in5_25 14 2516fb89845SKATO Takenori #define FD_720in5_25 15 2526fb89845SKATO Takenori #define FD_360in5_25 16 2536fb89845SKATO Takenori #define FD_640in5_25 17 254b99f0a4aSAndrew Moore 255f664aeeeSJoerg Wunsch #define BIO_RDSECTID BIO_CMD1 2567ca0641bSAndrey A. Chernov 2576f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] = 2585b81b6b3SRodney W. Grimes { 259126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ 260126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ 261126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ 262126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ 263126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ 264126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ 265126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ 266b0568305SAndrey A. Chernov { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ 2676fb89845SKATO Takenori { 8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /* 640K in DD 5.25in */ 2686fb89845SKATO Takenori { 8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */ 269ed2fa05eSAndrey A. Chernov 270126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ 271126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ 272126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ 273126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ 274126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ 275126518a1SAndrey A. Chernov { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ 2766fb89845SKATO Takenori { 8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /* 640K in HD 5.25in */ 2775b81b6b3SRodney W. Grimes }; 2785b81b6b3SRodney W. Grimes 279b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2 /* 2 floppies */ 280dc16046fSJoerg Wunsch 281f8ce7dd5SJoerg Wunsch #define MAX_SEC_SIZE (128 << 3) 28264860614SJoerg Wunsch #define MAX_CYLINDER 85 /* some people really stress their drives 28364860614SJoerg Wunsch * up to cyl 82 */ 284250300ebSJoerg Wunsch #define MAX_HEAD 1 285f8ce7dd5SJoerg Wunsch 2866182fdbdSPeter Wemm static devclass_t fdc_devclass; 2875b81b6b3SRodney W. Grimes 288246ed35dSJoerg Wunsch /* 289246ed35dSJoerg Wunsch * Per drive structure (softc). 290246ed35dSJoerg Wunsch */ 2916182fdbdSPeter Wemm struct fd_data { 292b99f0a4aSAndrew Moore struct fdc_data *fdc; /* pointer to controller structure */ 2935b81b6b3SRodney W. Grimes int fdsu; /* this units number on this controller */ 2943a2f7427SDavid Greenman int type; /* Drive type (FD_1440...) */ 2955b81b6b3SRodney W. Grimes struct fd_type *ft; /* pointer to the type descriptor */ 2965b81b6b3SRodney W. Grimes int flags; 2975b81b6b3SRodney W. Grimes #define FD_OPEN 0x01 /* it's open */ 2985b81b6b3SRodney W. Grimes #define FD_ACTIVE 0x02 /* it's active */ 2995b81b6b3SRodney W. Grimes #define FD_MOTOR 0x04 /* motor should be on */ 3005b81b6b3SRodney W. Grimes #define FD_MOTOR_WAIT 0x08 /* motor coming up */ 3015b81b6b3SRodney W. Grimes int skip; 3025b81b6b3SRodney W. Grimes int hddrv; 303dc5df763SJoerg Wunsch #define FD_NO_TRACK -2 3045b81b6b3SRodney W. Grimes int track; /* where we think the head is */ 3053a2f7427SDavid Greenman int options; /* user configurable options, see ioctl_fd.h */ 30602a19910SJustin T. Gibbs struct callout_handle toffhandle; 30702a19910SJustin T. Gibbs struct callout_handle tohandle; 308b2dfb1f9SJustin T. Gibbs struct devstat device_stats; 309e219897aSJoerg Wunsch eventhandler_tag clonetag; 310e219897aSJoerg Wunsch dev_t masterdev; 31160444853SJoerg Wunsch #define NCLONEDEVS 10 /* must match the table below */ 31260444853SJoerg Wunsch dev_t clonedevs[NCLONEDEVS]; 3136182fdbdSPeter Wemm device_t dev; 3146182fdbdSPeter Wemm fdu_t fdu; 3156182fdbdSPeter Wemm }; 31637286586SPeter Wemm 31737286586SPeter Wemm struct fdc_ivars { 31837286586SPeter Wemm int fdunit; 31937286586SPeter Wemm }; 3206182fdbdSPeter Wemm static devclass_t fd_devclass; 3215b81b6b3SRodney W. Grimes 322246ed35dSJoerg Wunsch /* 323246ed35dSJoerg Wunsch * Throughout this file the following conventions will be used: 324246ed35dSJoerg Wunsch * 325246ed35dSJoerg Wunsch * fd is a pointer to the fd_data struct for the drive in question 326246ed35dSJoerg Wunsch * fdc is a pointer to the fdc_data struct for the controller 327246ed35dSJoerg Wunsch * fdu is the floppy drive unit number 328246ed35dSJoerg Wunsch * fdcu is the floppy controller unit number 329246ed35dSJoerg Wunsch * fdsu is the floppy drive unit number on that controller. (sub-unit) 330246ed35dSJoerg Wunsch */ 331b99f0a4aSAndrew Moore 332246ed35dSJoerg Wunsch /* 333246ed35dSJoerg Wunsch * Function declarations, same (chaotic) order as they appear in the 334246ed35dSJoerg Wunsch * file. Re-ordering is too late now, it would only obfuscate the 335246ed35dSJoerg Wunsch * diffs against old and offspring versions (like the PC98 one). 336246ed35dSJoerg Wunsch * 337246ed35dSJoerg Wunsch * Anyone adding functions here, please keep this sequence the same 338246ed35dSJoerg Wunsch * as below -- makes locating a particular function in the body much 339246ed35dSJoerg Wunsch * easier. 340246ed35dSJoerg Wunsch */ 341246ed35dSJoerg Wunsch static void fdout_wr(fdc_p, u_int8_t); 342246ed35dSJoerg Wunsch static u_int8_t fdsts_rd(fdc_p); 343246ed35dSJoerg Wunsch static void fddata_wr(fdc_p, u_int8_t); 344246ed35dSJoerg Wunsch static u_int8_t fddata_rd(fdc_p); 345246ed35dSJoerg Wunsch static void fdctl_wr_isa(fdc_p, u_int8_t); 346246ed35dSJoerg Wunsch #if NCARD > 0 347246ed35dSJoerg Wunsch static void fdctl_wr_pcmcia(fdc_p, u_int8_t); 348246ed35dSJoerg Wunsch #endif 349246ed35dSJoerg Wunsch #if 0 350246ed35dSJoerg Wunsch static u_int8_t fdin_rd(fdc_p); 351246ed35dSJoerg Wunsch #endif 352246ed35dSJoerg Wunsch static int fdc_err(struct fdc_data *, const char *); 353246ed35dSJoerg Wunsch static int fd_cmd(struct fdc_data *, int, ...); 354246ed35dSJoerg Wunsch static int enable_fifo(fdc_p fdc); 355246ed35dSJoerg Wunsch static int fd_sense_drive_status(fdc_p, int *); 356246ed35dSJoerg Wunsch static int fd_sense_int(fdc_p, int *, int *); 357246ed35dSJoerg Wunsch static int fd_read_status(fdc_p); 358246ed35dSJoerg Wunsch static int fdc_alloc_resources(struct fdc_data *); 359246ed35dSJoerg Wunsch static void fdc_release_resources(struct fdc_data *); 360246ed35dSJoerg Wunsch static int fdc_read_ivar(device_t, device_t, int, uintptr_t *); 361246ed35dSJoerg Wunsch static int fdc_probe(device_t); 362246ed35dSJoerg Wunsch #if NCARD > 0 363246ed35dSJoerg Wunsch static int fdc_pccard_probe(device_t); 364246ed35dSJoerg Wunsch #endif 365246ed35dSJoerg Wunsch static int fdc_detach(device_t dev); 366246ed35dSJoerg Wunsch static void fdc_add_child(device_t, const char *, int); 367246ed35dSJoerg Wunsch static int fdc_attach(device_t); 368246ed35dSJoerg Wunsch static int fdc_print_child(device_t, device_t); 369246ed35dSJoerg Wunsch static void fd_clone (void *, char *, int, dev_t *); 370246ed35dSJoerg Wunsch static int fd_probe(device_t); 371246ed35dSJoerg Wunsch static int fd_attach(device_t); 372246ed35dSJoerg Wunsch static int fd_detach(device_t); 3736182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int); 3743a2f7427SDavid Greenman # define TURNON 1 3753a2f7427SDavid Greenman # define TURNOFF 0 3763a2f7427SDavid Greenman static timeout_t fd_turnoff; 3773a2f7427SDavid Greenman static timeout_t fd_motor_on; 3786182fdbdSPeter Wemm static void fd_turnon(struct fd_data *); 3793a2f7427SDavid Greenman static void fdc_reset(fdc_p); 3806182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *); 38180909a7dSJoerg Wunsch static int out_fdc(struct fdc_data *, int); 382246ed35dSJoerg Wunsch /* 383246ed35dSJoerg Wunsch * The open function is named Fdopen() to avoid confusion with fdopen() 384246ed35dSJoerg Wunsch * in fd(4). The difference is now only meaningful for debuggers. 385246ed35dSJoerg Wunsch */ 386246ed35dSJoerg Wunsch static d_open_t Fdopen; 387246ed35dSJoerg Wunsch static d_close_t fdclose; 388246ed35dSJoerg Wunsch static d_strategy_t fdstrategy; 3896182fdbdSPeter Wemm static void fdstart(struct fdc_data *); 3905c1a1eaeSBruce Evans static timeout_t fd_iotimeout; 3913a2f7427SDavid Greenman static timeout_t fd_pseudointr; 392246ed35dSJoerg Wunsch static driver_intr_t fdc_intr; 393246ed35dSJoerg Wunsch static int fdcpio(fdc_p, long, caddr_t, u_int); 3946182fdbdSPeter Wemm static int fdstate(struct fdc_data *); 3956182fdbdSPeter Wemm static int retrier(struct fdc_data *); 396246ed35dSJoerg Wunsch static void fdbiodone(struct bio *); 397f664aeeeSJoerg Wunsch static int fdmisccmd(dev_t, u_int, void *); 398246ed35dSJoerg Wunsch static d_ioctl_t fdioctl; 399d66c374fSTor Egge 400d66c374fSTor Egge static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ 401d66c374fSTor Egge 402d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 4033a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */ 404cba2a7c6SBruce Evans static int volatile fd_debug = 0; 4050e17a5bcSJoerg Wunsch #define TRACE0(arg) do { if (fd_debug) printf(arg); } while (0) 4060e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { if (fd_debug) printf(arg1, arg2); } while (0) 407d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */ 4080e17a5bcSJoerg Wunsch #define TRACE0(arg) do { } while (0) 4090e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { } while (0) 410d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 4115b81b6b3SRodney W. Grimes 412246ed35dSJoerg Wunsch /* 413246ed35dSJoerg Wunsch * Bus space handling (access to low-level IO). 414246ed35dSJoerg Wunsch */ 415427ccf00SDoug Rabson static void 416427ccf00SDoug Rabson fdout_wr(fdc_p fdc, u_int8_t v) 417427ccf00SDoug Rabson { 418427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); 419427ccf00SDoug Rabson } 420427ccf00SDoug Rabson 421427ccf00SDoug Rabson static u_int8_t 422427ccf00SDoug Rabson fdsts_rd(fdc_p fdc) 423427ccf00SDoug Rabson { 424427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); 425427ccf00SDoug Rabson } 426427ccf00SDoug Rabson 427427ccf00SDoug Rabson static void 428427ccf00SDoug Rabson fddata_wr(fdc_p fdc, u_int8_t v) 429427ccf00SDoug Rabson { 430427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); 431427ccf00SDoug Rabson } 432427ccf00SDoug Rabson 433427ccf00SDoug Rabson static u_int8_t 434427ccf00SDoug Rabson fddata_rd(fdc_p fdc) 435427ccf00SDoug Rabson { 436427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); 437427ccf00SDoug Rabson } 438427ccf00SDoug Rabson 439427ccf00SDoug Rabson static void 4405f830ea2SJoerg Wunsch fdctl_wr_isa(fdc_p fdc, u_int8_t v) 441427ccf00SDoug Rabson { 442427ccf00SDoug Rabson bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); 443427ccf00SDoug Rabson } 444427ccf00SDoug Rabson 44558c9d623SPeter Wemm #if NCARD > 0 4465f830ea2SJoerg Wunsch static void 4475f830ea2SJoerg Wunsch fdctl_wr_pcmcia(fdc_p fdc, u_int8_t v) 4485f830ea2SJoerg Wunsch { 4495f830ea2SJoerg Wunsch bus_space_write_1(fdc->portt, fdc->porth, FDCTL+fdc->port_off, v); 4505f830ea2SJoerg Wunsch } 45158c9d623SPeter Wemm #endif 4525f830ea2SJoerg Wunsch 453427ccf00SDoug Rabson #if 0 454427ccf00SDoug Rabson 455427ccf00SDoug Rabson static u_int8_t 456427ccf00SDoug Rabson fdin_rd(fdc_p fdc) 457427ccf00SDoug Rabson { 458427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDIN); 459427ccf00SDoug Rabson } 460427ccf00SDoug Rabson 461427ccf00SDoug Rabson #endif 462427ccf00SDoug Rabson 46387f6c662SJulian Elischer #define CDEV_MAJOR 9 4644e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = { 4654e2f199eSPoul-Henning Kamp /* open */ Fdopen, 4664e2f199eSPoul-Henning Kamp /* close */ fdclose, 4674e2f199eSPoul-Henning Kamp /* read */ physread, 4684e2f199eSPoul-Henning Kamp /* write */ physwrite, 4694e2f199eSPoul-Henning Kamp /* ioctl */ fdioctl, 4704e2f199eSPoul-Henning Kamp /* poll */ nopoll, 4714e2f199eSPoul-Henning Kamp /* mmap */ nommap, 4724e2f199eSPoul-Henning Kamp /* strategy */ fdstrategy, 4734e2f199eSPoul-Henning Kamp /* name */ "fd", 4744e2f199eSPoul-Henning Kamp /* maj */ CDEV_MAJOR, 4754e2f199eSPoul-Henning Kamp /* dump */ nodump, 4764e2f199eSPoul-Henning Kamp /* psize */ nopsize, 4774e2f199eSPoul-Henning Kamp /* flags */ D_DISK, 4784e2f199eSPoul-Henning Kamp }; 4794e2f199eSPoul-Henning Kamp 480246ed35dSJoerg Wunsch /* 481246ed35dSJoerg Wunsch * Auxiliary functions. Well, some only. Others are scattered 482246ed35dSJoerg Wunsch * throughout the entire file. 483246ed35dSJoerg Wunsch */ 484dc5df763SJoerg Wunsch static int 4856182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s) 486dc5df763SJoerg Wunsch { 4876182fdbdSPeter Wemm fdc->fdc_errs++; 48816b04b6aSJoerg Wunsch if (s) { 489b6e5f28eSPeter Wemm if (fdc->fdc_errs < FDC_ERRMAX) 490b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, "%s", s); 491b6e5f28eSPeter Wemm else if (fdc->fdc_errs == FDC_ERRMAX) 492b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, "too many errors, not " 493b6e5f28eSPeter Wemm "logging any more\n"); 49416b04b6aSJoerg Wunsch } 495dc5df763SJoerg Wunsch 496dc5df763SJoerg Wunsch return FD_FAILED; 497dc5df763SJoerg Wunsch } 498dc5df763SJoerg Wunsch 499dc5df763SJoerg Wunsch /* 500dc5df763SJoerg Wunsch * fd_cmd: Send a command to the chip. Takes a varargs with this structure: 501dc5df763SJoerg Wunsch * Unit number, 502dc5df763SJoerg Wunsch * # of output bytes, output bytes as ints ..., 503dc5df763SJoerg Wunsch * # of input bytes, input bytes as ints ... 504dc5df763SJoerg Wunsch */ 5056f4e0bebSPoul-Henning Kamp static int 5066182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...) 507dc5df763SJoerg Wunsch { 508dc5df763SJoerg Wunsch u_char cmd; 509dc5df763SJoerg Wunsch int n_in; 510dc5df763SJoerg Wunsch int n; 511dc5df763SJoerg Wunsch va_list ap; 512dc5df763SJoerg Wunsch 513dc5df763SJoerg Wunsch va_start(ap, n_out); 514dc5df763SJoerg Wunsch cmd = (u_char)(va_arg(ap, int)); 515dc5df763SJoerg Wunsch va_end(ap); 516dc5df763SJoerg Wunsch va_start(ap, n_out); 517dc5df763SJoerg Wunsch for (n = 0; n < n_out; n++) 518dc5df763SJoerg Wunsch { 5196182fdbdSPeter Wemm if (out_fdc(fdc, va_arg(ap, int)) < 0) 520dc5df763SJoerg Wunsch { 521dc5df763SJoerg Wunsch char msg[50]; 5222127f260SArchie Cobbs snprintf(msg, sizeof(msg), 523dc5df763SJoerg Wunsch "cmd %x failed at out byte %d of %d\n", 524dc5df763SJoerg Wunsch cmd, n + 1, n_out); 5256182fdbdSPeter Wemm return fdc_err(fdc, msg); 526dc5df763SJoerg Wunsch } 527dc5df763SJoerg Wunsch } 528dc5df763SJoerg Wunsch n_in = va_arg(ap, int); 529dc5df763SJoerg Wunsch for (n = 0; n < n_in; n++) 530dc5df763SJoerg Wunsch { 531dc5df763SJoerg Wunsch int *ptr = va_arg(ap, int *); 5326182fdbdSPeter Wemm if (fd_in(fdc, ptr) < 0) 533dc5df763SJoerg Wunsch { 534dc5df763SJoerg Wunsch char msg[50]; 5352127f260SArchie Cobbs snprintf(msg, sizeof(msg), 536dc5df763SJoerg Wunsch "cmd %02x failed at in byte %d of %d\n", 537dc5df763SJoerg Wunsch cmd, n + 1, n_in); 5386182fdbdSPeter Wemm return fdc_err(fdc, msg); 539dc5df763SJoerg Wunsch } 540dc5df763SJoerg Wunsch } 541dc5df763SJoerg Wunsch 542dc5df763SJoerg Wunsch return 0; 543dc5df763SJoerg Wunsch } 544dc5df763SJoerg Wunsch 5456f4e0bebSPoul-Henning Kamp static int 546d66c374fSTor Egge enable_fifo(fdc_p fdc) 547d66c374fSTor Egge { 548d66c374fSTor Egge int i, j; 549d66c374fSTor Egge 550d66c374fSTor Egge if ((fdc->flags & FDC_HAS_FIFO) == 0) { 551d66c374fSTor Egge 552d66c374fSTor Egge /* 553d66c374fSTor Egge * Cannot use fd_cmd the normal way here, since 554d66c374fSTor Egge * this might be an invalid command. Thus we send the 555d66c374fSTor Egge * first byte, and check for an early turn of data directon. 556d66c374fSTor Egge */ 557d66c374fSTor Egge 5586182fdbdSPeter Wemm if (out_fdc(fdc, I8207X_CONFIGURE) < 0) 5596182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 560d66c374fSTor Egge 561d66c374fSTor Egge /* If command is invalid, return */ 562d66c374fSTor Egge j = 100000; 563427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) 564d66c374fSTor Egge != NE7_RQM && j-- > 0) 565d66c374fSTor Egge if (i == (NE7_DIO | NE7_RQM)) { 566d66c374fSTor Egge fdc_reset(fdc); 567d66c374fSTor Egge return FD_FAILED; 568d66c374fSTor Egge } 569d66c374fSTor Egge if (j<0 || 5706182fdbdSPeter Wemm fd_cmd(fdc, 3, 571d66c374fSTor Egge 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { 572d66c374fSTor Egge fdc_reset(fdc); 5736182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 574d66c374fSTor Egge } 575d66c374fSTor Egge fdc->flags |= FDC_HAS_FIFO; 576d66c374fSTor Egge return 0; 577d66c374fSTor Egge } 5786182fdbdSPeter Wemm if (fd_cmd(fdc, 4, 579d66c374fSTor Egge I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) 5806182fdbdSPeter Wemm return fdc_err(fdc, "Re-enable FIFO failed\n"); 581d66c374fSTor Egge return 0; 582d66c374fSTor Egge } 583d66c374fSTor Egge 584d66c374fSTor Egge static int 585dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p) 586dc5df763SJoerg Wunsch { 587dc5df763SJoerg Wunsch int st3; 588dc5df763SJoerg Wunsch 5896182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) 590dc5df763SJoerg Wunsch { 5916182fdbdSPeter Wemm return fdc_err(fdc, "Sense Drive Status failed\n"); 592dc5df763SJoerg Wunsch } 593dc5df763SJoerg Wunsch if (st3p) 594dc5df763SJoerg Wunsch *st3p = st3; 595dc5df763SJoerg Wunsch 596dc5df763SJoerg Wunsch return 0; 597dc5df763SJoerg Wunsch } 598dc5df763SJoerg Wunsch 5996f4e0bebSPoul-Henning Kamp static int 600dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp) 601dc5df763SJoerg Wunsch { 6026182fdbdSPeter Wemm int cyl, st0, ret; 603dc5df763SJoerg Wunsch 6046182fdbdSPeter Wemm ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); 6056182fdbdSPeter Wemm if (ret) { 6066182fdbdSPeter Wemm (void)fdc_err(fdc, 607dc5df763SJoerg Wunsch "sense intr err reading stat reg 0\n"); 608dc5df763SJoerg Wunsch return ret; 609dc5df763SJoerg Wunsch } 610dc5df763SJoerg Wunsch 611dc5df763SJoerg Wunsch if (st0p) 612dc5df763SJoerg Wunsch *st0p = st0; 613dc5df763SJoerg Wunsch 6146182fdbdSPeter Wemm if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { 615dc5df763SJoerg Wunsch /* 616dc5df763SJoerg Wunsch * There doesn't seem to have been an interrupt. 617dc5df763SJoerg Wunsch */ 618dc5df763SJoerg Wunsch return FD_NOT_VALID; 619dc5df763SJoerg Wunsch } 620dc5df763SJoerg Wunsch 6216182fdbdSPeter Wemm if (fd_in(fdc, &cyl) < 0) { 6226182fdbdSPeter Wemm return fdc_err(fdc, "can't get cyl num\n"); 623dc5df763SJoerg Wunsch } 624dc5df763SJoerg Wunsch 625dc5df763SJoerg Wunsch if (cylp) 626dc5df763SJoerg Wunsch *cylp = cyl; 627dc5df763SJoerg Wunsch 628dc5df763SJoerg Wunsch return 0; 629dc5df763SJoerg Wunsch } 630dc5df763SJoerg Wunsch 631dc5df763SJoerg Wunsch 6326f4e0bebSPoul-Henning Kamp static int 63364860614SJoerg Wunsch fd_read_status(fdc_p fdc) 634dc5df763SJoerg Wunsch { 635dc5df763SJoerg Wunsch int i, ret; 636b5e8ce9fSBruce Evans 637246ed35dSJoerg Wunsch for (i = ret = 0; i < 7; i++) { 638b5e8ce9fSBruce Evans /* 63964860614SJoerg Wunsch * XXX types are poorly chosen. Only bytes can be read 640a838d83dSBruce Evans * from the hardware, but fdc->status[] wants u_ints and 641b5e8ce9fSBruce Evans * fd_in() gives ints. 642b5e8ce9fSBruce Evans */ 643b5e8ce9fSBruce Evans int status; 644b5e8ce9fSBruce Evans 6456182fdbdSPeter Wemm ret = fd_in(fdc, &status); 646b5e8ce9fSBruce Evans fdc->status[i] = status; 647b5e8ce9fSBruce Evans if (ret != 0) 648dc5df763SJoerg Wunsch break; 649dc5df763SJoerg Wunsch } 650dc5df763SJoerg Wunsch 651dc5df763SJoerg Wunsch if (ret == 0) 652dc5df763SJoerg Wunsch fdc->flags |= FDC_STAT_VALID; 653dc5df763SJoerg Wunsch else 654dc5df763SJoerg Wunsch fdc->flags &= ~FDC_STAT_VALID; 655dc5df763SJoerg Wunsch 656dc5df763SJoerg Wunsch return ret; 657dc5df763SJoerg Wunsch } 658dc5df763SJoerg Wunsch 6593a2f7427SDavid Greenman static int 66037286586SPeter Wemm fdc_alloc_resources(struct fdc_data *fdc) 6615b81b6b3SRodney W. Grimes { 66237286586SPeter Wemm device_t dev; 663e219897aSJoerg Wunsch int ispnp, ispcmcia, nports; 6645b81b6b3SRodney W. Grimes 66537286586SPeter Wemm dev = fdc->fdc_dev; 6665f830ea2SJoerg Wunsch ispnp = (fdc->flags & FDC_ISPNP) != 0; 6675f830ea2SJoerg Wunsch ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0; 6686182fdbdSPeter Wemm fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; 6696182fdbdSPeter Wemm fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; 6706182fdbdSPeter Wemm 671b6e5f28eSPeter Wemm /* 6725f830ea2SJoerg Wunsch * On standard ISA, we don't just use an 8 port range 6735f830ea2SJoerg Wunsch * (e.g. 0x3f0-0x3f7) since that covers an IDE control 6745f830ea2SJoerg Wunsch * register at 0x3f6. 6755f830ea2SJoerg Wunsch * 676b6e5f28eSPeter Wemm * Isn't PC hardware wonderful. 6775f830ea2SJoerg Wunsch * 6785f830ea2SJoerg Wunsch * The Y-E Data PCMCIA FDC doesn't have this problem, it 6795f830ea2SJoerg Wunsch * uses the register with offset 6 for pseudo-DMA, and the 6805f830ea2SJoerg Wunsch * one with offset 7 as control register. 681b6e5f28eSPeter Wemm */ 682e219897aSJoerg Wunsch nports = ispcmcia ? 8 : (ispnp ? 1 : 6); 6836182fdbdSPeter Wemm fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, 6846182fdbdSPeter Wemm &fdc->rid_ioport, 0ul, ~0ul, 685e219897aSJoerg Wunsch nports, RF_ACTIVE); 6866182fdbdSPeter Wemm if (fdc->res_ioport == 0) { 687e219897aSJoerg Wunsch device_printf(dev, "cannot reserve I/O port range (%d ports)\n", 688e219897aSJoerg Wunsch nports); 68937286586SPeter Wemm return ENXIO; 6906182fdbdSPeter Wemm } 691427ccf00SDoug Rabson fdc->portt = rman_get_bustag(fdc->res_ioport); 692427ccf00SDoug Rabson fdc->porth = rman_get_bushandle(fdc->res_ioport); 693427ccf00SDoug Rabson 6945f830ea2SJoerg Wunsch if (!ispcmcia) { 695427ccf00SDoug Rabson /* 6965f830ea2SJoerg Wunsch * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7 6975f830ea2SJoerg Wunsch * and some at 0x3f0-0x3f5,0x3f7. We detect the former 6985f830ea2SJoerg Wunsch * by checking the size and adjust the port address 6995f830ea2SJoerg Wunsch * accordingly. 700b6e5f28eSPeter Wemm */ 701b6e5f28eSPeter Wemm if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) 702b6e5f28eSPeter Wemm fdc->port_off = -2; 703b6e5f28eSPeter Wemm 704b6e5f28eSPeter Wemm /* 7055f830ea2SJoerg Wunsch * Register the control port range as rid 1 if it 7065f830ea2SJoerg Wunsch * isn't there already. Most PnP BIOSen will have 7075f830ea2SJoerg Wunsch * already done this but non-PnP configurations don't. 708a2639a18SPeter Wemm * 7095f830ea2SJoerg Wunsch * And some (!!) report 0x3f2-0x3f5 and completely 7105f830ea2SJoerg Wunsch * leave out the control register! It seems that some 7115f830ea2SJoerg Wunsch * non-antique controller chips have a different 7125f830ea2SJoerg Wunsch * method of programming the transfer speed which 7135f830ea2SJoerg Wunsch * doesn't require the control register, but it's 7145f830ea2SJoerg Wunsch * mighty bogus as the chip still responds to the 7155f830ea2SJoerg Wunsch * address for the control register. 716427ccf00SDoug Rabson */ 717b6e5f28eSPeter Wemm if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { 718b9da888fSPeter Wemm u_long ctlstart; 719b9da888fSPeter Wemm 720b6e5f28eSPeter Wemm /* Find the control port, usually 0x3f7 */ 7215f830ea2SJoerg Wunsch ctlstart = rman_get_start(fdc->res_ioport) + 7225f830ea2SJoerg Wunsch fdc->port_off + 7; 723b6e5f28eSPeter Wemm 724b6e5f28eSPeter Wemm bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1); 725b9da888fSPeter Wemm } 726b6e5f28eSPeter Wemm 727b6e5f28eSPeter Wemm /* 728b6e5f28eSPeter Wemm * Now (finally!) allocate the control port. 729b6e5f28eSPeter Wemm */ 730427ccf00SDoug Rabson fdc->rid_ctl = 1; 7315f830ea2SJoerg Wunsch fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, 7325f830ea2SJoerg Wunsch &fdc->rid_ctl, 733b6e5f28eSPeter Wemm 0ul, ~0ul, 1, RF_ACTIVE); 734427ccf00SDoug Rabson if (fdc->res_ctl == 0) { 7355f830ea2SJoerg Wunsch device_printf(dev, 736e219897aSJoerg Wunsch "cannot reserve control I/O port range (control port)\n"); 73737286586SPeter Wemm return ENXIO; 738427ccf00SDoug Rabson } 739427ccf00SDoug Rabson fdc->ctlt = rman_get_bustag(fdc->res_ctl); 740427ccf00SDoug Rabson fdc->ctlh = rman_get_bushandle(fdc->res_ctl); 7415f830ea2SJoerg Wunsch } 7426182fdbdSPeter Wemm 7436182fdbdSPeter Wemm fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, 7446182fdbdSPeter Wemm &fdc->rid_irq, 0ul, ~0ul, 1, 7456182fdbdSPeter Wemm RF_ACTIVE); 7466182fdbdSPeter Wemm if (fdc->res_irq == 0) { 747427ccf00SDoug Rabson device_printf(dev, "cannot reserve interrupt line\n"); 74837286586SPeter Wemm return ENXIO; 7496182fdbdSPeter Wemm } 7505f830ea2SJoerg Wunsch 7515f830ea2SJoerg Wunsch if ((fdc->flags & FDC_NODMA) == 0) { 7526182fdbdSPeter Wemm fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, 7536182fdbdSPeter Wemm &fdc->rid_drq, 0ul, ~0ul, 1, 7546182fdbdSPeter Wemm RF_ACTIVE); 7556182fdbdSPeter Wemm if (fdc->res_drq == 0) { 756427ccf00SDoug Rabson device_printf(dev, "cannot reserve DMA request line\n"); 75737286586SPeter Wemm return ENXIO; 7586182fdbdSPeter Wemm } 7596182fdbdSPeter Wemm fdc->dmachan = fdc->res_drq->r_start; 7605f830ea2SJoerg Wunsch } 76137286586SPeter Wemm 76237286586SPeter Wemm return 0; 76337286586SPeter Wemm } 76437286586SPeter Wemm 76537286586SPeter Wemm static void 76637286586SPeter Wemm fdc_release_resources(struct fdc_data *fdc) 76737286586SPeter Wemm { 76837286586SPeter Wemm device_t dev; 76937286586SPeter Wemm 77037286586SPeter Wemm dev = fdc->fdc_dev; 77137286586SPeter Wemm if (fdc->res_irq != 0) { 77237286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 77337286586SPeter Wemm fdc->res_irq); 77437286586SPeter Wemm bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 77537286586SPeter Wemm fdc->res_irq); 77637286586SPeter Wemm } 77737286586SPeter Wemm if (fdc->res_ctl != 0) { 77837286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, 77937286586SPeter Wemm fdc->res_ctl); 78037286586SPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, 78137286586SPeter Wemm fdc->res_ctl); 78237286586SPeter Wemm } 78337286586SPeter Wemm if (fdc->res_ioport != 0) { 78437286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 78537286586SPeter Wemm fdc->res_ioport); 78637286586SPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 78737286586SPeter Wemm fdc->res_ioport); 78837286586SPeter Wemm } 78937286586SPeter Wemm if (fdc->res_drq != 0) { 79037286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 79137286586SPeter Wemm fdc->res_drq); 79237286586SPeter Wemm bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 79337286586SPeter Wemm fdc->res_drq); 79437286586SPeter Wemm } 79537286586SPeter Wemm } 79637286586SPeter Wemm 797246ed35dSJoerg Wunsch /* 798246ed35dSJoerg Wunsch * Configuration/initialization stuff, per controller. 799246ed35dSJoerg Wunsch */ 80037286586SPeter Wemm 80137286586SPeter Wemm static struct isa_pnp_id fdc_ids[] = { 80237286586SPeter Wemm {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ 80337286586SPeter Wemm {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ 80437286586SPeter Wemm {0} 80537286586SPeter Wemm }; 80637286586SPeter Wemm 80737286586SPeter Wemm static int 8083aae7b16SBruce Evans fdc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 80937286586SPeter Wemm { 81037286586SPeter Wemm struct fdc_ivars *ivars = device_get_ivars(child); 81137286586SPeter Wemm 81237286586SPeter Wemm switch (which) { 81337286586SPeter Wemm case FDC_IVAR_FDUNIT: 81437286586SPeter Wemm *result = ivars->fdunit; 81537286586SPeter Wemm break; 81637286586SPeter Wemm default: 81737286586SPeter Wemm return ENOENT; 81837286586SPeter Wemm } 81937286586SPeter Wemm return 0; 82037286586SPeter Wemm } 82137286586SPeter Wemm 82237286586SPeter Wemm static int 82337286586SPeter Wemm fdc_probe(device_t dev) 82437286586SPeter Wemm { 82537286586SPeter Wemm int error, ic_type; 82637286586SPeter Wemm struct fdc_data *fdc; 82737286586SPeter Wemm 82837286586SPeter Wemm fdc = device_get_softc(dev); 82937286586SPeter Wemm bzero(fdc, sizeof *fdc); 83037286586SPeter Wemm fdc->fdc_dev = dev; 8315f830ea2SJoerg Wunsch fdc->fdctl_wr = fdctl_wr_isa; 83237286586SPeter Wemm 83337286586SPeter Wemm /* Check pnp ids */ 83437286586SPeter Wemm error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); 83537286586SPeter Wemm if (error == ENXIO) 83637286586SPeter Wemm return ENXIO; 8375f830ea2SJoerg Wunsch if (error == 0) 8385f830ea2SJoerg Wunsch fdc->flags |= FDC_ISPNP; 83937286586SPeter Wemm 84037286586SPeter Wemm /* Attempt to allocate our resources for the duration of the probe */ 84137286586SPeter Wemm error = fdc_alloc_resources(fdc); 84237286586SPeter Wemm if (error) 84337286586SPeter Wemm goto out; 8445b81b6b3SRodney W. Grimes 84516111cedSAndrew Moore /* First - lets reset the floppy controller */ 846427ccf00SDoug Rabson fdout_wr(fdc, 0); 84716111cedSAndrew Moore DELAY(100); 848427ccf00SDoug Rabson fdout_wr(fdc, FDO_FRST); 84916111cedSAndrew Moore 8505b81b6b3SRodney W. Grimes /* see if it can handle a command */ 8516182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), 8526182fdbdSPeter Wemm NE7_SPEC_2(2, 0), 0)) { 8536182fdbdSPeter Wemm error = ENXIO; 8546182fdbdSPeter Wemm goto out; 8555b81b6b3SRodney W. Grimes } 8566182fdbdSPeter Wemm 8576182fdbdSPeter Wemm if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { 8586182fdbdSPeter Wemm ic_type = (u_char)ic_type; 8596182fdbdSPeter Wemm switch (ic_type) { 8606182fdbdSPeter Wemm case 0x80: 8616182fdbdSPeter Wemm device_set_desc(dev, "NEC 765 or clone"); 8626182fdbdSPeter Wemm fdc->fdct = FDC_NE765; 8636182fdbdSPeter Wemm break; 8646182fdbdSPeter Wemm case 0x81: 8656182fdbdSPeter Wemm device_set_desc(dev, "Intel 82077 or clone"); 8666182fdbdSPeter Wemm fdc->fdct = FDC_I82077; 8676182fdbdSPeter Wemm break; 8686182fdbdSPeter Wemm case 0x90: 8696182fdbdSPeter Wemm device_set_desc(dev, "NEC 72065B or clone"); 8706182fdbdSPeter Wemm fdc->fdct = FDC_NE72065; 8716182fdbdSPeter Wemm break; 8726182fdbdSPeter Wemm default: 8736182fdbdSPeter Wemm device_set_desc(dev, "generic floppy controller"); 8746182fdbdSPeter Wemm fdc->fdct = FDC_UNKNOWN; 8756182fdbdSPeter Wemm break; 8766182fdbdSPeter Wemm } 8776182fdbdSPeter Wemm } 8786182fdbdSPeter Wemm 8796182fdbdSPeter Wemm out: 88037286586SPeter Wemm fdc_release_resources(fdc); 8816182fdbdSPeter Wemm return (error); 8825b81b6b3SRodney W. Grimes } 8835b81b6b3SRodney W. Grimes 8845f830ea2SJoerg Wunsch #if NCARD > 0 8855f830ea2SJoerg Wunsch 8865f830ea2SJoerg Wunsch static int 8875f830ea2SJoerg Wunsch fdc_pccard_probe(device_t dev) 8885f830ea2SJoerg Wunsch { 8895f830ea2SJoerg Wunsch int error; 8905f830ea2SJoerg Wunsch struct fdc_data *fdc; 8915f830ea2SJoerg Wunsch 8925f830ea2SJoerg Wunsch fdc = device_get_softc(dev); 8935f830ea2SJoerg Wunsch bzero(fdc, sizeof *fdc); 8945f830ea2SJoerg Wunsch fdc->fdc_dev = dev; 8955f830ea2SJoerg Wunsch fdc->fdctl_wr = fdctl_wr_pcmcia; 8965f830ea2SJoerg Wunsch 8975f830ea2SJoerg Wunsch fdc->flags |= FDC_ISPCMCIA | FDC_NODMA; 8985f830ea2SJoerg Wunsch 8995f830ea2SJoerg Wunsch /* Attempt to allocate our resources for the duration of the probe */ 9005f830ea2SJoerg Wunsch error = fdc_alloc_resources(fdc); 9015f830ea2SJoerg Wunsch if (error) 9025f830ea2SJoerg Wunsch goto out; 9035f830ea2SJoerg Wunsch 9045f830ea2SJoerg Wunsch /* First - lets reset the floppy controller */ 9055f830ea2SJoerg Wunsch fdout_wr(fdc, 0); 9065f830ea2SJoerg Wunsch DELAY(100); 9075f830ea2SJoerg Wunsch fdout_wr(fdc, FDO_FRST); 9085f830ea2SJoerg Wunsch 9095f830ea2SJoerg Wunsch /* see if it can handle a command */ 9105f830ea2SJoerg Wunsch if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), 9115f830ea2SJoerg Wunsch NE7_SPEC_2(2, 0), 0)) { 9125f830ea2SJoerg Wunsch error = ENXIO; 9135f830ea2SJoerg Wunsch goto out; 9145f830ea2SJoerg Wunsch } 9155f830ea2SJoerg Wunsch 9165f830ea2SJoerg Wunsch device_set_desc(dev, "Y-E Data PCMCIA floppy"); 9175f830ea2SJoerg Wunsch fdc->fdct = FDC_NE765; 9185f830ea2SJoerg Wunsch 9195f830ea2SJoerg Wunsch out: 9205f830ea2SJoerg Wunsch fdc_release_resources(fdc); 9215f830ea2SJoerg Wunsch return (error); 9225f830ea2SJoerg Wunsch } 9235f830ea2SJoerg Wunsch 924e219897aSJoerg Wunsch #endif /* NCARD > 0 */ 925e219897aSJoerg Wunsch 9265f830ea2SJoerg Wunsch static int 927e219897aSJoerg Wunsch fdc_detach(device_t dev) 9285f830ea2SJoerg Wunsch { 9295f830ea2SJoerg Wunsch struct fdc_data *fdc; 9305f830ea2SJoerg Wunsch int error; 9315f830ea2SJoerg Wunsch 9325f830ea2SJoerg Wunsch fdc = device_get_softc(dev); 9335f830ea2SJoerg Wunsch 9345f830ea2SJoerg Wunsch /* have our children detached first */ 9355f830ea2SJoerg Wunsch if ((error = bus_generic_detach(dev))) 9365f830ea2SJoerg Wunsch return (error); 9375f830ea2SJoerg Wunsch 938e219897aSJoerg Wunsch /* reset controller, turn motor off */ 939e219897aSJoerg Wunsch fdout_wr(fdc, 0); 940e219897aSJoerg Wunsch 941e219897aSJoerg Wunsch if ((fdc->flags & FDC_NODMA) == 0) 942e219897aSJoerg Wunsch isa_dma_release(fdc->dmachan); 943e219897aSJoerg Wunsch 9445f830ea2SJoerg Wunsch if ((fdc->flags & FDC_ATTACHED) == 0) { 9455f830ea2SJoerg Wunsch device_printf(dev, "already unloaded\n"); 9465f830ea2SJoerg Wunsch return (0); 9475f830ea2SJoerg Wunsch } 9485f830ea2SJoerg Wunsch fdc->flags &= ~FDC_ATTACHED; 9495f830ea2SJoerg Wunsch 9505f830ea2SJoerg Wunsch BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, 9515f830ea2SJoerg Wunsch fdc->fdc_intr); 9525f830ea2SJoerg Wunsch fdc_release_resources(fdc); 9535f830ea2SJoerg Wunsch device_printf(dev, "unload\n"); 9545f830ea2SJoerg Wunsch return (0); 9555f830ea2SJoerg Wunsch } 9565f830ea2SJoerg Wunsch 9575b81b6b3SRodney W. Grimes /* 95837286586SPeter Wemm * Add a child device to the fdc controller. It will then be probed etc. 9595b81b6b3SRodney W. Grimes */ 9606182fdbdSPeter Wemm static void 96137286586SPeter Wemm fdc_add_child(device_t dev, const char *name, int unit) 9625b81b6b3SRodney W. Grimes { 96337286586SPeter Wemm int disabled; 96437286586SPeter Wemm struct fdc_ivars *ivar; 9656182fdbdSPeter Wemm device_t child; 96692200632SGarrett Wollman 9677cc0979fSDavid Malone ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT | M_ZERO); 96837286586SPeter Wemm if (ivar == NULL) 9696182fdbdSPeter Wemm return; 97037286586SPeter Wemm if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0) 97137286586SPeter Wemm ivar->fdunit = 0; 972fe0d4089SMatthew N. Dodd child = device_add_child(dev, name, unit); 97337286586SPeter Wemm if (child == NULL) 9746182fdbdSPeter Wemm return; 97537286586SPeter Wemm device_set_ivars(child, ivar); 976a97c75b7SDoug Rabson if (resource_int_value(name, unit, "disabled", &disabled) == 0 977a97c75b7SDoug Rabson && disabled != 0) 9786182fdbdSPeter Wemm device_disable(child); 9796182fdbdSPeter Wemm } 9806182fdbdSPeter Wemm 9816182fdbdSPeter Wemm static int 9826182fdbdSPeter Wemm fdc_attach(device_t dev) 9836182fdbdSPeter Wemm { 98437286586SPeter Wemm struct fdc_data *fdc; 9852398f0cdSPeter Wemm const char *name, *dname; 9865d54fe91SJoerg Wunsch int i, error, dunit; 987427ccf00SDoug Rabson 98837286586SPeter Wemm fdc = device_get_softc(dev); 98937286586SPeter Wemm error = fdc_alloc_resources(fdc); 99037286586SPeter Wemm if (error) { 991f8ce7dd5SJoerg Wunsch device_printf(dev, "cannot re-acquire resources\n"); 99237286586SPeter Wemm return error; 99337286586SPeter Wemm } 99437286586SPeter Wemm error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, 9953c36743eSMark Murray INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc, 9963c36743eSMark Murray &fdc->fdc_intr); 99737286586SPeter Wemm if (error) { 99837286586SPeter Wemm device_printf(dev, "cannot setup interrupt\n"); 99937286586SPeter Wemm return error; 100037286586SPeter Wemm } 100137286586SPeter Wemm fdc->fdcu = device_get_unit(dev); 1002fb35bd37SJoerg Wunsch fdc->flags |= FDC_ATTACHED | FDC_NEEDS_RESET; 10036182fdbdSPeter Wemm 10045f830ea2SJoerg Wunsch if ((fdc->flags & FDC_NODMA) == 0) { 1005f8ce7dd5SJoerg Wunsch /* 1006f8ce7dd5SJoerg Wunsch * Acquire the DMA channel forever, the driver will do 1007f8ce7dd5SJoerg Wunsch * the rest 1008f8ce7dd5SJoerg Wunsch * XXX should integrate with rman 1009f8ce7dd5SJoerg Wunsch */ 1010100f78bbSSujal Patel isa_dma_acquire(fdc->dmachan); 1011f8ce7dd5SJoerg Wunsch isa_dmainit(fdc->dmachan, MAX_SEC_SIZE); 10125f830ea2SJoerg Wunsch } 10135b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 10146182fdbdSPeter Wemm 10153a2f7427SDavid Greenman /* reset controller, turn motor off, clear fdout mirror reg */ 101664860614SJoerg Wunsch fdout_wr(fdc, fdc->fdout = 0); 10178177437dSPoul-Henning Kamp bioq_init(&fdc->head); 10185b81b6b3SRodney W. Grimes 10196182fdbdSPeter Wemm /* 102037286586SPeter Wemm * Probe and attach any children. We should probably detect 102137286586SPeter Wemm * devices from the BIOS unless overridden. 10226182fdbdSPeter Wemm */ 1023ada54f9eSPeter Wemm name = device_get_nameunit(dev); 10242398f0cdSPeter Wemm i = 0; 10252398f0cdSPeter Wemm while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) 10262398f0cdSPeter Wemm fdc_add_child(dev, dname, dunit); 102737286586SPeter Wemm 102860444853SJoerg Wunsch if ((error = bus_generic_attach(dev)) != 0) 102960444853SJoerg Wunsch return (error); 103060444853SJoerg Wunsch 103160444853SJoerg Wunsch return (0); 10326182fdbdSPeter Wemm } 10336182fdbdSPeter Wemm 103415317dd8SMatthew N. Dodd static int 10356182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child) 10366182fdbdSPeter Wemm { 103715317dd8SMatthew N. Dodd int retval = 0; 103815317dd8SMatthew N. Dodd 103915317dd8SMatthew N. Dodd retval += bus_print_child_header(me, child); 104015317dd8SMatthew N. Dodd retval += printf(" on %s drive %d\n", device_get_nameunit(me), 104137286586SPeter Wemm fdc_get_fdunit(child)); 104215317dd8SMatthew N. Dodd 104315317dd8SMatthew N. Dodd return (retval); 10446182fdbdSPeter Wemm } 10456182fdbdSPeter Wemm 104616e68fc6SPeter Wemm static device_method_t fdc_methods[] = { 104716e68fc6SPeter Wemm /* Device interface */ 104816e68fc6SPeter Wemm DEVMETHOD(device_probe, fdc_probe), 104916e68fc6SPeter Wemm DEVMETHOD(device_attach, fdc_attach), 1050e219897aSJoerg Wunsch DEVMETHOD(device_detach, fdc_detach), 105116e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 105216e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), 105316e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), 105416e68fc6SPeter Wemm 105516e68fc6SPeter Wemm /* Bus interface */ 105616e68fc6SPeter Wemm DEVMETHOD(bus_print_child, fdc_print_child), 105737286586SPeter Wemm DEVMETHOD(bus_read_ivar, fdc_read_ivar), 105816e68fc6SPeter Wemm /* Our children never use any other bus interface methods. */ 105916e68fc6SPeter Wemm 106016e68fc6SPeter Wemm { 0, 0 } 106116e68fc6SPeter Wemm }; 106216e68fc6SPeter Wemm 106316e68fc6SPeter Wemm static driver_t fdc_driver = { 106416e68fc6SPeter Wemm "fdc", 106516e68fc6SPeter Wemm fdc_methods, 106616e68fc6SPeter Wemm sizeof(struct fdc_data) 106716e68fc6SPeter Wemm }; 106816e68fc6SPeter Wemm 106916e68fc6SPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); 10705f063c7bSMike Smith DRIVER_MODULE(fdc, acpi, fdc_driver, fdc_devclass, 0, 0); 107116e68fc6SPeter Wemm 10725f830ea2SJoerg Wunsch #if NCARD > 0 10735f830ea2SJoerg Wunsch 10745f830ea2SJoerg Wunsch static device_method_t fdc_pccard_methods[] = { 10755f830ea2SJoerg Wunsch /* Device interface */ 10765f830ea2SJoerg Wunsch DEVMETHOD(device_probe, fdc_pccard_probe), 10775f830ea2SJoerg Wunsch DEVMETHOD(device_attach, fdc_attach), 1078e219897aSJoerg Wunsch DEVMETHOD(device_detach, fdc_detach), 10795f830ea2SJoerg Wunsch DEVMETHOD(device_shutdown, bus_generic_shutdown), 10805f830ea2SJoerg Wunsch DEVMETHOD(device_suspend, bus_generic_suspend), 10815f830ea2SJoerg Wunsch DEVMETHOD(device_resume, bus_generic_resume), 10825f830ea2SJoerg Wunsch 10835f830ea2SJoerg Wunsch /* Bus interface */ 10845f830ea2SJoerg Wunsch DEVMETHOD(bus_print_child, fdc_print_child), 10855f830ea2SJoerg Wunsch DEVMETHOD(bus_read_ivar, fdc_read_ivar), 10865f830ea2SJoerg Wunsch /* Our children never use any other bus interface methods. */ 10875f830ea2SJoerg Wunsch 10885f830ea2SJoerg Wunsch { 0, 0 } 10895f830ea2SJoerg Wunsch }; 10905f830ea2SJoerg Wunsch 10915f830ea2SJoerg Wunsch static driver_t fdc_pccard_driver = { 10925f830ea2SJoerg Wunsch "fdc", 10935f830ea2SJoerg Wunsch fdc_pccard_methods, 10945f830ea2SJoerg Wunsch sizeof(struct fdc_data) 10955f830ea2SJoerg Wunsch }; 10965f830ea2SJoerg Wunsch 10975f830ea2SJoerg Wunsch DRIVER_MODULE(fdc, pccard, fdc_pccard_driver, fdc_devclass, 0, 0); 10985f830ea2SJoerg Wunsch 10995f830ea2SJoerg Wunsch #endif /* NCARD > 0 */ 11005f830ea2SJoerg Wunsch 11013f54a085SPoul-Henning Kamp static struct { 11023f54a085SPoul-Henning Kamp char *match; 11033f54a085SPoul-Henning Kamp int minor; 11043f54a085SPoul-Henning Kamp int link; 11053f54a085SPoul-Henning Kamp } fd_suffix[] = { 110660444853SJoerg Wunsch /* 110760444853SJoerg Wunsch * Genuine clone devices must come first, and their number must 110860444853SJoerg Wunsch * match NCLONEDEVS above. 110960444853SJoerg Wunsch */ 11103f54a085SPoul-Henning Kamp { ".1720", 1, 0 }, 11113f54a085SPoul-Henning Kamp { ".1480", 2, 0 }, 11123f54a085SPoul-Henning Kamp { ".1440", 3, 0 }, 11133f54a085SPoul-Henning Kamp { ".1200", 4, 0 }, 11143f54a085SPoul-Henning Kamp { ".820", 5, 0 }, 11153f54a085SPoul-Henning Kamp { ".800", 6, 0 }, 11163f54a085SPoul-Henning Kamp { ".720", 7, 0 }, 11173f54a085SPoul-Henning Kamp { ".360", 8, 0 }, 11183f54a085SPoul-Henning Kamp { ".640", 9, 0 }, 11193f54a085SPoul-Henning Kamp { ".1232", 10, 0 }, 112060444853SJoerg Wunsch { "a", 0, 1 }, 112160444853SJoerg Wunsch { "b", 0, 1 }, 112260444853SJoerg Wunsch { "c", 0, 1 }, 112360444853SJoerg Wunsch { "d", 0, 1 }, 112460444853SJoerg Wunsch { "e", 0, 1 }, 112560444853SJoerg Wunsch { "f", 0, 1 }, 112660444853SJoerg Wunsch { "g", 0, 1 }, 112760444853SJoerg Wunsch { "h", 0, 1 }, 11283f54a085SPoul-Henning Kamp { 0, 0 } 11293f54a085SPoul-Henning Kamp }; 1130246ed35dSJoerg Wunsch 11313f54a085SPoul-Henning Kamp static void 113280909a7dSJoerg Wunsch fd_clone(void *arg, char *name, int namelen, dev_t *dev) 11333f54a085SPoul-Henning Kamp { 113460444853SJoerg Wunsch struct fd_data *fd; 11353f54a085SPoul-Henning Kamp int u, d, i; 11363f54a085SPoul-Henning Kamp char *n; 11373f54a085SPoul-Henning Kamp 113860444853SJoerg Wunsch fd = (struct fd_data *)arg; 11393f54a085SPoul-Henning Kamp if (*dev != NODEV) 11403f54a085SPoul-Henning Kamp return; 1141db901281SPoul-Henning Kamp if (dev_stdclone(name, &n, "fd", &u) != 2) 11423f54a085SPoul-Henning Kamp return; 11433f54a085SPoul-Henning Kamp for (i = 0; ; i++) { 11443f54a085SPoul-Henning Kamp if (fd_suffix[i].match == NULL) 11453f54a085SPoul-Henning Kamp return; 11463f54a085SPoul-Henning Kamp if (strcmp(n, fd_suffix[i].match)) 11473f54a085SPoul-Henning Kamp continue; 11483f54a085SPoul-Henning Kamp d = fd_suffix[i].minor; 11493f54a085SPoul-Henning Kamp break; 11503f54a085SPoul-Henning Kamp } 11513f54a085SPoul-Henning Kamp if (fd_suffix[i].link == 0) { 11523f54a085SPoul-Henning Kamp *dev = make_dev(&fd_cdevsw, (u << 6) + d, 11533f54a085SPoul-Henning Kamp UID_ROOT, GID_OPERATOR, 0640, name); 115460444853SJoerg Wunsch fd->clonedevs[i] = *dev; 11553f54a085SPoul-Henning Kamp } else { 115660444853SJoerg Wunsch *dev = make_dev_alias(fd->masterdev, name); 11573f54a085SPoul-Henning Kamp } 11583f54a085SPoul-Henning Kamp } 11593f54a085SPoul-Henning Kamp 116016e68fc6SPeter Wemm /* 1161246ed35dSJoerg Wunsch * Configuration/initialization, per drive. 116216e68fc6SPeter Wemm */ 11636182fdbdSPeter Wemm static int 11646182fdbdSPeter Wemm fd_probe(device_t dev) 11656182fdbdSPeter Wemm { 11666182fdbdSPeter Wemm int i; 11676182fdbdSPeter Wemm u_int fdt, st0, st3; 11686182fdbdSPeter Wemm struct fd_data *fd; 11696182fdbdSPeter Wemm struct fdc_data *fdc; 11706182fdbdSPeter Wemm fdsu_t fdsu; 11716182fdbdSPeter Wemm static int fd_fifo = 0; 11726182fdbdSPeter Wemm 11736182fdbdSPeter Wemm fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ 11746182fdbdSPeter Wemm fd = device_get_softc(dev); 11756182fdbdSPeter Wemm fdc = device_get_softc(device_get_parent(dev)); 11766182fdbdSPeter Wemm 11776182fdbdSPeter Wemm bzero(fd, sizeof *fd); 11786182fdbdSPeter Wemm fd->dev = dev; 11796182fdbdSPeter Wemm fd->fdc = fdc; 11806182fdbdSPeter Wemm fd->fdsu = fdsu; 11816182fdbdSPeter Wemm fd->fdu = device_get_unit(dev); 11826182fdbdSPeter Wemm 1183a97c75b7SDoug Rabson #ifdef __i386__ 1184b99f0a4aSAndrew Moore /* look up what bios thinks we have */ 11856182fdbdSPeter Wemm switch (fd->fdu) { 11866182fdbdSPeter Wemm case 0: 11875f830ea2SJoerg Wunsch if ((fdc->flags & FDC_ISPCMCIA)) 11885f830ea2SJoerg Wunsch fdt = RTCFDT_144M; 11895f830ea2SJoerg Wunsch else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) 11900722d6abSJoerg Wunsch fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; 11910722d6abSJoerg Wunsch else 11920722d6abSJoerg Wunsch fdt = (rtcin(RTC_FDISKETTE) & 0xf0); 1193b99f0a4aSAndrew Moore break; 11946182fdbdSPeter Wemm case 1: 11956182fdbdSPeter Wemm fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); 1196dc5df763SJoerg Wunsch break; 1197dc5df763SJoerg Wunsch default: 11986182fdbdSPeter Wemm fdt = RTCFDT_NONE; 1199dc5df763SJoerg Wunsch break; 12006b7bd95bSJoerg Wunsch } 1201a97c75b7SDoug Rabson #else 1202a97c75b7SDoug Rabson fdt = RTCFDT_144M; /* XXX probably */ 1203a97c75b7SDoug Rabson #endif 12046182fdbdSPeter Wemm 12056182fdbdSPeter Wemm /* is there a unit? */ 12066182fdbdSPeter Wemm if (fdt == RTCFDT_NONE) 12076182fdbdSPeter Wemm return (ENXIO); 12086182fdbdSPeter Wemm 12096182fdbdSPeter Wemm /* select it */ 12106182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNON); 1211fb35bd37SJoerg Wunsch fdc_reset(fdc); /* XXX reset, then unreset, etc. */ 12126182fdbdSPeter Wemm DELAY(1000000); /* 1 sec */ 12136182fdbdSPeter Wemm 12148de0675cSPeter Wemm /* XXX This doesn't work before the first set_motor() */ 12156182fdbdSPeter Wemm if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN 1216e34c71eaSJoerg Wunsch && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 12176182fdbdSPeter Wemm && enable_fifo(fdc) == 0) { 1218b6e5f28eSPeter Wemm device_printf(device_get_parent(dev), 1219b6e5f28eSPeter Wemm "FIFO enabled, %d bytes threshold\n", fifo_threshold); 1220d66c374fSTor Egge } 12216182fdbdSPeter Wemm fd_fifo = 1; 12226182fdbdSPeter Wemm 12236182fdbdSPeter Wemm if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) 12246182fdbdSPeter Wemm && (st3 & NE7_ST3_T0)) { 1225dc5df763SJoerg Wunsch /* if at track 0, first seek inwards */ 1226dc5df763SJoerg Wunsch /* seek some steps: */ 12276182fdbdSPeter Wemm fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); 1228dc5df763SJoerg Wunsch DELAY(300000); /* ...wait a moment... */ 12296182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ 1230dc5df763SJoerg Wunsch } 1231dc5df763SJoerg Wunsch 1232dc5df763SJoerg Wunsch /* If we're at track 0 first seek inwards. */ 12336182fdbdSPeter Wemm if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { 1234dc5df763SJoerg Wunsch /* Seek some steps... */ 12356182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 1236dc5df763SJoerg Wunsch /* ...wait a moment... */ 1237dc5df763SJoerg Wunsch DELAY(300000); 1238dc5df763SJoerg Wunsch /* make ctrlr happy: */ 12396182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); 1240dc5df763SJoerg Wunsch } 1241dc5df763SJoerg Wunsch } 1242dc5df763SJoerg Wunsch 12436b7bd95bSJoerg Wunsch for (i = 0; i < 2; i++) { 12446b7bd95bSJoerg Wunsch /* 12456b7bd95bSJoerg Wunsch * we must recalibrate twice, just in case the 12466b7bd95bSJoerg Wunsch * heads have been beyond cylinder 76, since most 12476b7bd95bSJoerg Wunsch * FDCs still barf when attempting to recalibrate 12486b7bd95bSJoerg Wunsch * more than 77 steps 12496b7bd95bSJoerg Wunsch */ 1250dc5df763SJoerg Wunsch /* go back to 0: */ 12516182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 12526b7bd95bSJoerg Wunsch /* a second being enough for full stroke seek*/ 12536b7bd95bSJoerg Wunsch DELAY(i == 0 ? 1000000 : 300000); 12545b81b6b3SRodney W. Grimes 12556b7bd95bSJoerg Wunsch /* anything responding? */ 1256dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, 0) == 0 && 1257dc5df763SJoerg Wunsch (st0 & NE7_ST0_EC) == 0) 12586b7bd95bSJoerg Wunsch break; /* already probed succesfully */ 12596b7bd95bSJoerg Wunsch } 1260dc5df763SJoerg Wunsch } 12616b7bd95bSJoerg Wunsch 12626182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNOFF); 12633a2f7427SDavid Greenman 12643a2f7427SDavid Greenman if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ 12656182fdbdSPeter Wemm return (ENXIO); 12665b81b6b3SRodney W. Grimes 1267dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 1268b99f0a4aSAndrew Moore fd->fdc = fdc; 1269b99f0a4aSAndrew Moore fd->fdsu = fdsu; 12703a2f7427SDavid Greenman fd->options = 0; 127102a19910SJustin T. Gibbs callout_handle_init(&fd->toffhandle); 127202a19910SJustin T. Gibbs callout_handle_init(&fd->tohandle); 12735b81b6b3SRodney W. Grimes 1274b99f0a4aSAndrew Moore switch (fdt) { 12757ca0641bSAndrey A. Chernov case RTCFDT_12M: 12766182fdbdSPeter Wemm device_set_desc(dev, "1200-KB 5.25\" drive"); 1277b99f0a4aSAndrew Moore fd->type = FD_1200; 12787ca0641bSAndrey A. Chernov break; 12790722d6abSJoerg Wunsch case RTCFDT_144M | RTCFDT_144M_PRETENDED: 12806182fdbdSPeter Wemm device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); 12810722d6abSJoerg Wunsch fdt = RTCFDT_144M; 12826182fdbdSPeter Wemm fd->type = FD_1440; 12837ca0641bSAndrey A. Chernov case RTCFDT_144M: 12846182fdbdSPeter Wemm device_set_desc(dev, "1440-KB 3.5\" drive"); 1285b99f0a4aSAndrew Moore fd->type = FD_1440; 12867ca0641bSAndrey A. Chernov break; 1287290dd077SJoerg Wunsch case RTCFDT_288M: 128886a727d9SJoerg Wunsch case RTCFDT_288M_1: 12896182fdbdSPeter Wemm device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); 1290290dd077SJoerg Wunsch fd->type = FD_1440; 1291290dd077SJoerg Wunsch break; 12927ca0641bSAndrey A. Chernov case RTCFDT_360K: 12936182fdbdSPeter Wemm device_set_desc(dev, "360-KB 5.25\" drive"); 1294b99f0a4aSAndrew Moore fd->type = FD_360; 12957ca0641bSAndrey A. Chernov break; 1296ed2fa05eSAndrey A. Chernov case RTCFDT_720K: 12976182fdbdSPeter Wemm printf("720-KB 3.5\" drive"); 1298b99f0a4aSAndrew Moore fd->type = FD_720; 1299ed2fa05eSAndrey A. Chernov break; 13007ca0641bSAndrey A. Chernov default: 13016182fdbdSPeter Wemm return (ENXIO); 13025b81b6b3SRodney W. Grimes } 13036182fdbdSPeter Wemm return (0); 13046182fdbdSPeter Wemm } 13056182fdbdSPeter Wemm 13066182fdbdSPeter Wemm static int 13076182fdbdSPeter Wemm fd_attach(device_t dev) 13086182fdbdSPeter Wemm { 13096182fdbdSPeter Wemm struct fd_data *fd; 131064860614SJoerg Wunsch static int cdevsw_add_done; 131160444853SJoerg Wunsch int i; 13126182fdbdSPeter Wemm 131302211baeSNick Hibma if (!cdevsw_add_done) { 1314475ad603SPeter Wemm cdevsw_add(&fd_cdevsw); /* XXX */ 131564860614SJoerg Wunsch cdevsw_add_done = 1; 131602211baeSNick Hibma } 131764860614SJoerg Wunsch fd = device_get_softc(dev); 131860444853SJoerg Wunsch fd->clonetag = EVENTHANDLER_REGISTER(dev_clone, fd_clone, fd, 1000); 13195f431174SJoerg Wunsch fd->masterdev = make_dev(&fd_cdevsw, fd->fdu << 6, 13203f54a085SPoul-Henning Kamp UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu); 132160444853SJoerg Wunsch for (i = 0; i < NCLONEDEVS; i++) 132260444853SJoerg Wunsch fd->clonedevs[i] = NODEV; 13236182fdbdSPeter Wemm devstat_add_entry(&fd->device_stats, device_get_name(dev), 1324f8ce7dd5SJoerg Wunsch device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS, 13252a888f93SKenneth D. Merry DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, 13262a888f93SKenneth D. Merry DEVSTAT_PRIORITY_FD); 13276182fdbdSPeter Wemm return (0); 13285b81b6b3SRodney W. Grimes } 13295b81b6b3SRodney W. Grimes 13305f830ea2SJoerg Wunsch static int 13315f830ea2SJoerg Wunsch fd_detach(device_t dev) 13325f830ea2SJoerg Wunsch { 13335f830ea2SJoerg Wunsch struct fd_data *fd; 133460444853SJoerg Wunsch int i; 13355f830ea2SJoerg Wunsch 13365f830ea2SJoerg Wunsch fd = device_get_softc(dev); 133760444853SJoerg Wunsch untimeout(fd_turnoff, fd, fd->toffhandle); 1338e219897aSJoerg Wunsch devstat_remove_entry(&fd->device_stats); 1339e219897aSJoerg Wunsch destroy_dev(fd->masterdev); 134060444853SJoerg Wunsch for (i = 0; i < NCLONEDEVS; i++) 134160444853SJoerg Wunsch if (fd->clonedevs[i] != NODEV) 134260444853SJoerg Wunsch destroy_dev(fd->clonedevs[i]); 1343e219897aSJoerg Wunsch cdevsw_remove(&fd_cdevsw); 1344e219897aSJoerg Wunsch EVENTHANDLER_DEREGISTER(dev_clone, fd->clonetag); 13455f830ea2SJoerg Wunsch 13465f830ea2SJoerg Wunsch return (0); 13475f830ea2SJoerg Wunsch } 13485f830ea2SJoerg Wunsch 134916e68fc6SPeter Wemm static device_method_t fd_methods[] = { 135016e68fc6SPeter Wemm /* Device interface */ 135116e68fc6SPeter Wemm DEVMETHOD(device_probe, fd_probe), 135216e68fc6SPeter Wemm DEVMETHOD(device_attach, fd_attach), 13535f830ea2SJoerg Wunsch DEVMETHOD(device_detach, fd_detach), 135416e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 135516e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ 135616e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ 135716e68fc6SPeter Wemm 135816e68fc6SPeter Wemm { 0, 0 } 135916e68fc6SPeter Wemm }; 136016e68fc6SPeter Wemm 136116e68fc6SPeter Wemm static driver_t fd_driver = { 136216e68fc6SPeter Wemm "fd", 136316e68fc6SPeter Wemm fd_methods, 136416e68fc6SPeter Wemm sizeof(struct fd_data) 136516e68fc6SPeter Wemm }; 136616e68fc6SPeter Wemm 1367475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); 136816e68fc6SPeter Wemm 1369246ed35dSJoerg Wunsch /* 1370246ed35dSJoerg Wunsch * More auxiliary functions. 1371246ed35dSJoerg Wunsch */ 1372246ed35dSJoerg Wunsch /* 1373246ed35dSJoerg Wunsch * Motor control stuff. 1374246ed35dSJoerg Wunsch * Remember to not deselect the drive we're working on. 1375246ed35dSJoerg Wunsch */ 13763a2f7427SDavid Greenman static void 13776182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon) 13785b81b6b3SRodney W. Grimes { 1379fb35bd37SJoerg Wunsch int fdout; 13803a2f7427SDavid Greenman 1381fb35bd37SJoerg Wunsch fdout = fdc->fdout; 13823a2f7427SDavid Greenman if (turnon) { 13833a2f7427SDavid Greenman fdout &= ~FDO_FDSEL; 1384fb35bd37SJoerg Wunsch fdout |= (FDO_MOEN0 << fdsu) | FDO_FDMAEN | FDO_FRST | fdsu; 13853a2f7427SDavid Greenman } else 13863a2f7427SDavid Greenman fdout &= ~(FDO_MOEN0 << fdsu); 13876182fdbdSPeter Wemm fdc->fdout = fdout; 1388fb35bd37SJoerg Wunsch fdout_wr(fdc, fdout); 13893a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdout); 13903a2f7427SDavid Greenman } 13913a2f7427SDavid Greenman 1392381fe1aaSGarrett Wollman static void 13936182fdbdSPeter Wemm fd_turnoff(void *xfd) 13945b81b6b3SRodney W. Grimes { 1395f5f7ba03SJordan K. Hubbard int s; 13966182fdbdSPeter Wemm fd_p fd = xfd; 1397dc16046fSJoerg Wunsch 13986182fdbdSPeter Wemm TRACE1("[fd%d: turnoff]", fd->fdu); 13998335c1b8SBruce Evans 14005f830ea2SJoerg Wunsch s = splbio(); 14018335c1b8SBruce Evans /* 14028335c1b8SBruce Evans * Don't turn off the motor yet if the drive is active. 14035f830ea2SJoerg Wunsch * 14045f830ea2SJoerg Wunsch * If we got here, this could only mean we missed an interrupt. 14055f830ea2SJoerg Wunsch * This can e. g. happen on the Y-E Date PCMCIA floppy controller 14065f830ea2SJoerg Wunsch * after a controller reset. Just schedule a pseudo-interrupt 14075f830ea2SJoerg Wunsch * so the state machine gets re-entered. 14088335c1b8SBruce Evans */ 14096182fdbdSPeter Wemm if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { 14105f830ea2SJoerg Wunsch fdc_intr(fd->fdc); 14115f830ea2SJoerg Wunsch splx(s); 14128335c1b8SBruce Evans return; 14138335c1b8SBruce Evans } 14148335c1b8SBruce Evans 14155b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR; 14166182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNOFF); 1417f5f7ba03SJordan K. Hubbard splx(s); 14185b81b6b3SRodney W. Grimes } 14195b81b6b3SRodney W. Grimes 14203a2f7427SDavid Greenman static void 14216182fdbdSPeter Wemm fd_motor_on(void *xfd) 14225b81b6b3SRodney W. Grimes { 1423f5f7ba03SJordan K. Hubbard int s; 14246182fdbdSPeter Wemm fd_p fd = xfd; 1425f5f7ba03SJordan K. Hubbard 1426f5f7ba03SJordan K. Hubbard s = splbio(); 14275b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR_WAIT; 14285b81b6b3SRodney W. Grimes if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) 14295b81b6b3SRodney W. Grimes { 14306182fdbdSPeter Wemm fdc_intr(fd->fdc); 14315b81b6b3SRodney W. Grimes } 1432f5f7ba03SJordan K. Hubbard splx(s); 14335b81b6b3SRodney W. Grimes } 14345b81b6b3SRodney W. Grimes 14353a2f7427SDavid Greenman static void 14366182fdbdSPeter Wemm fd_turnon(fd_p fd) 14375b81b6b3SRodney W. Grimes { 14385b81b6b3SRodney W. Grimes if(!(fd->flags & FD_MOTOR)) 14395b81b6b3SRodney W. Grimes { 14403a2f7427SDavid Greenman fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); 14416182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNON); 14426182fdbdSPeter Wemm timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ 14435b81b6b3SRodney W. Grimes } 14445b81b6b3SRodney W. Grimes } 14455b81b6b3SRodney W. Grimes 1446381fe1aaSGarrett Wollman static void 1447dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc) 14485b81b6b3SRodney W. Grimes { 14493a2f7427SDavid Greenman /* Try a reset, keep motor on */ 1450427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 14513a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 14523a2f7427SDavid Greenman DELAY(100); 14533a2f7427SDavid Greenman /* enable FDC, but defer interrupts a moment */ 1454427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); 14553a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); 14563a2f7427SDavid Greenman DELAY(100); 1457427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout); 14583a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout); 14593a2f7427SDavid Greenman 146064860614SJoerg Wunsch /* XXX after a reset, silently believe the FDC will accept commands */ 14616182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1462dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1463dc5df763SJoerg Wunsch 0); 1464d66c374fSTor Egge if (fdc->flags & FDC_HAS_FIFO) 1465d66c374fSTor Egge (void) enable_fifo(fdc); 14665b81b6b3SRodney W. Grimes } 14675b81b6b3SRodney W. Grimes 1468246ed35dSJoerg Wunsch /* 1469246ed35dSJoerg Wunsch * FDC IO functions, take care of the main status register, timeout 1470246ed35dSJoerg Wunsch * in case the desired status bits are never set. 1471246ed35dSJoerg Wunsch */ 1472b5e8ce9fSBruce Evans static int 14736182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr) 1474dc5df763SJoerg Wunsch { 1475dc5df763SJoerg Wunsch int i, j = 100000; 1476427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) 1477dc5df763SJoerg Wunsch != (NE7_DIO|NE7_RQM) && j-- > 0) 1478dc5df763SJoerg Wunsch if (i == NE7_RQM) 14796182fdbdSPeter Wemm return fdc_err(fdc, "ready for output in input\n"); 1480dc5df763SJoerg Wunsch if (j <= 0) 14816182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); 1482d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 1483427ccf00SDoug Rabson i = fddata_rd(fdc); 1484dc5df763SJoerg Wunsch TRACE1("[FDDATA->0x%x]", (unsigned char)i); 1485dc5df763SJoerg Wunsch *ptr = i; 1486dc5df763SJoerg Wunsch return 0; 1487d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 1488427ccf00SDoug Rabson i = fddata_rd(fdc); 1489dc5df763SJoerg Wunsch if (ptr) 1490dc5df763SJoerg Wunsch *ptr = i; 1491dc5df763SJoerg Wunsch return 0; 1492d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 1493dc5df763SJoerg Wunsch } 1494dc5df763SJoerg Wunsch 1495dc5df763SJoerg Wunsch int 14966182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x) 14975b81b6b3SRodney W. Grimes { 14983b3837dbSRodney W. Grimes int i; 14995b81b6b3SRodney W. Grimes 15003b3837dbSRodney W. Grimes /* Check that the direction bit is set */ 15013b3837dbSRodney W. Grimes i = 100000; 1502427ccf00SDoug Rabson while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); 15036182fdbdSPeter Wemm if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); 15043b3837dbSRodney W. Grimes 15053b3837dbSRodney W. Grimes /* Check that the floppy controller is ready for a command */ 15063b3837dbSRodney W. Grimes i = 100000; 1507427ccf00SDoug Rabson while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); 150816b04b6aSJoerg Wunsch if (i <= 0) 15096182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); 15103b3837dbSRodney W. Grimes 15113b3837dbSRodney W. Grimes /* Send the command and return */ 1512427ccf00SDoug Rabson fddata_wr(fdc, x); 15133a2f7427SDavid Greenman TRACE1("[0x%x->FDDATA]", x); 15145b81b6b3SRodney W. Grimes return (0); 15155b81b6b3SRodney W. Grimes } 15165b81b6b3SRodney W. Grimes 1517246ed35dSJoerg Wunsch /* 1518246ed35dSJoerg Wunsch * Block device driver interface functions (interspersed with even more 1519246ed35dSJoerg Wunsch * auxiliary functions). 1520246ed35dSJoerg Wunsch */ 1521381fe1aaSGarrett Wollman int 1522b40ce416SJulian Elischer Fdopen(dev_t dev, int flags, int mode, struct thread *td) 15235b81b6b3SRodney W. Grimes { 15245b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 152520a29168SAndrey A. Chernov int type = FDTYPE(minor(dev)); 15266182fdbdSPeter Wemm fd_p fd; 1527b99f0a4aSAndrew Moore fdc_p fdc; 15285b81b6b3SRodney W. Grimes 15295b81b6b3SRodney W. Grimes /* check bounds */ 15306182fdbdSPeter Wemm if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) 1531b99f0a4aSAndrew Moore return (ENXIO); 15326182fdbdSPeter Wemm fdc = fd->fdc; 15336182fdbdSPeter Wemm if ((fdc == NULL) || (fd->type == NO_TYPE)) 1534b99f0a4aSAndrew Moore return (ENXIO); 1535b99f0a4aSAndrew Moore if (type > NUMDENS) 1536b99f0a4aSAndrew Moore return (ENXIO); 15377ca0641bSAndrey A. Chernov if (type == 0) 15386182fdbdSPeter Wemm type = fd->type; 15397ca0641bSAndrey A. Chernov else { 15403e425b96SJulian Elischer /* 15413e425b96SJulian Elischer * For each type of basic drive, make sure we are trying 15423e425b96SJulian Elischer * to open a type it can do, 15433e425b96SJulian Elischer */ 15446182fdbdSPeter Wemm if (type != fd->type) { 15456182fdbdSPeter Wemm switch (fd->type) { 15467ca0641bSAndrey A. Chernov case FD_360: 15477ca0641bSAndrey A. Chernov return (ENXIO); 1548ed2fa05eSAndrey A. Chernov case FD_720: 1549b39c878eSAndrey A. Chernov if ( type != FD_820 1550b39c878eSAndrey A. Chernov && type != FD_800 15516fb89845SKATO Takenori && type != FD_640 1552ed2fa05eSAndrey A. Chernov ) 1553ed2fa05eSAndrey A. Chernov return (ENXIO); 1554ed2fa05eSAndrey A. Chernov break; 15557ca0641bSAndrey A. Chernov case FD_1200: 1556b39c878eSAndrey A. Chernov switch (type) { 1557b39c878eSAndrey A. Chernov case FD_1480: 1558b39c878eSAndrey A. Chernov type = FD_1480in5_25; 1559fa4700b4SAndrey A. Chernov break; 15607ca0641bSAndrey A. Chernov case FD_1440: 1561b39c878eSAndrey A. Chernov type = FD_1440in5_25; 1562b39c878eSAndrey A. Chernov break; 15636fb89845SKATO Takenori case FD_1232: 15646fb89845SKATO Takenori break; 1565b39c878eSAndrey A. Chernov case FD_820: 1566b39c878eSAndrey A. Chernov type = FD_820in5_25; 1567b39c878eSAndrey A. Chernov break; 1568b39c878eSAndrey A. Chernov case FD_800: 1569b39c878eSAndrey A. Chernov type = FD_800in5_25; 1570b39c878eSAndrey A. Chernov break; 1571b39c878eSAndrey A. Chernov case FD_720: 1572b39c878eSAndrey A. Chernov type = FD_720in5_25; 1573b39c878eSAndrey A. Chernov break; 15746fb89845SKATO Takenori case FD_640: 15756fb89845SKATO Takenori type = FD_640in5_25; 15766fb89845SKATO Takenori break; 1577b39c878eSAndrey A. Chernov case FD_360: 1578b39c878eSAndrey A. Chernov type = FD_360in5_25; 1579b39c878eSAndrey A. Chernov break; 1580b39c878eSAndrey A. Chernov default: 1581b39c878eSAndrey A. Chernov return(ENXIO); 1582b39c878eSAndrey A. Chernov } 1583b39c878eSAndrey A. Chernov break; 1584b39c878eSAndrey A. Chernov case FD_1440: 1585b39c878eSAndrey A. Chernov if ( type != FD_1720 1586b39c878eSAndrey A. Chernov && type != FD_1480 1587ed2fa05eSAndrey A. Chernov && type != FD_1200 1588b39c878eSAndrey A. Chernov && type != FD_820 1589b39c878eSAndrey A. Chernov && type != FD_800 1590b39c878eSAndrey A. Chernov && type != FD_720 15916fb89845SKATO Takenori && type != FD_640 15927ca0641bSAndrey A. Chernov ) 1593dffff499SAndrey A. Chernov return(ENXIO); 1594fa4700b4SAndrey A. Chernov break; 15957ca0641bSAndrey A. Chernov } 15967ca0641bSAndrey A. Chernov } 1597fa4700b4SAndrey A. Chernov } 15986182fdbdSPeter Wemm fd->ft = fd_types + type - 1; 15996182fdbdSPeter Wemm fd->flags |= FD_OPEN; 16003fef646eSJoerg Wunsch /* 16013fef646eSJoerg Wunsch * Clearing the DMA overrun counter at open time is a bit messy. 16023fef646eSJoerg Wunsch * Since we're only managing one counter per controller, opening 16033fef646eSJoerg Wunsch * the second drive could mess it up. Anyway, if the DMA overrun 16043fef646eSJoerg Wunsch * condition is really persistent, it will eventually time out 16053fef646eSJoerg Wunsch * still. OTOH, clearing it here will ensure we'll at least start 16063fef646eSJoerg Wunsch * trying again after a previous (maybe even long ago) failure. 16073fef646eSJoerg Wunsch * Also, this is merely a stop-gap measure only that should not 16083fef646eSJoerg Wunsch * happen during normal operation, so we can tolerate it to be a 16093fef646eSJoerg Wunsch * bit sloppy about this. 16103fef646eSJoerg Wunsch */ 16113fef646eSJoerg Wunsch fdc->dma_overruns = 0; 16125f830ea2SJoerg Wunsch 16135b81b6b3SRodney W. Grimes return 0; 16145b81b6b3SRodney W. Grimes } 16155b81b6b3SRodney W. Grimes 1616381fe1aaSGarrett Wollman int 1617b40ce416SJulian Elischer fdclose(dev_t dev, int flags, int mode, struct thread *td) 16185b81b6b3SRodney W. Grimes { 16195b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 16206182fdbdSPeter Wemm struct fd_data *fd; 1621b99f0a4aSAndrew Moore 16226182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 16236182fdbdSPeter Wemm fd->flags &= ~FD_OPEN; 16242995d110SJoerg Wunsch fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR); 1625dc16046fSJoerg Wunsch 16265b81b6b3SRodney W. Grimes return (0); 16275b81b6b3SRodney W. Grimes } 16285b81b6b3SRodney W. Grimes 16293a2f7427SDavid Greenman void 16308177437dSPoul-Henning Kamp fdstrategy(struct bio *bp) 16313a2f7427SDavid Greenman { 1632fb35bd37SJoerg Wunsch long blknum, nblocks; 16333a2f7427SDavid Greenman int s; 16343a2f7427SDavid Greenman fdu_t fdu; 16353a2f7427SDavid Greenman fdc_p fdc; 16363a2f7427SDavid Greenman fd_p fd; 16373a2f7427SDavid Greenman size_t fdblk; 16383a2f7427SDavid Greenman 16398177437dSPoul-Henning Kamp fdu = FDUNIT(minor(bp->bio_dev)); 16406182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 16416182fdbdSPeter Wemm if (fd == 0) 16426182fdbdSPeter Wemm panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", 16438177437dSPoul-Henning Kamp (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev)); 16443a2f7427SDavid Greenman fdc = fd->fdc; 164569acd21dSWarner Losh if (fd->type == NO_TYPE) { 16468177437dSPoul-Henning Kamp bp->bio_error = ENXIO; 16478177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 16483b178206SWarner Losh goto bad; 164964860614SJoerg Wunsch } 1650d3628763SRodney W. Grimes fdblk = 128 << (fd->ft->secsize); 1651f664aeeeSJoerg Wunsch if (bp->bio_cmd != BIO_FORMAT && bp->bio_cmd != BIO_RDSECTID) { 16528177437dSPoul-Henning Kamp if (bp->bio_blkno < 0) { 1653dc5df763SJoerg Wunsch printf( 16546a0e6f42SRodney W. Grimes "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", 16558177437dSPoul-Henning Kamp fdu, (u_long)bp->bio_blkno, bp->bio_bcount); 16568177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 16578177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 16583a2f7427SDavid Greenman goto bad; 16593a2f7427SDavid Greenman } 16608177437dSPoul-Henning Kamp if ((bp->bio_bcount % fdblk) != 0) { 16618177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 16628177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 16633a2f7427SDavid Greenman goto bad; 16643a2f7427SDavid Greenman } 16653a2f7427SDavid Greenman } 16663a2f7427SDavid Greenman 16673a2f7427SDavid Greenman /* 16683a2f7427SDavid Greenman * Set up block calculations. 16693a2f7427SDavid Greenman */ 16708177437dSPoul-Henning Kamp if (bp->bio_blkno > 20000000) { 1671bb6382faSJoerg Wunsch /* 1672bb6382faSJoerg Wunsch * Reject unreasonably high block number, prevent the 1673bb6382faSJoerg Wunsch * multiplication below from overflowing. 1674bb6382faSJoerg Wunsch */ 16758177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 16768177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 16773a2f7427SDavid Greenman goto bad; 16783a2f7427SDavid Greenman } 1679fb35bd37SJoerg Wunsch blknum = bp->bio_blkno * DEV_BSIZE / fdblk; 1680bb6382faSJoerg Wunsch nblocks = fd->ft->size; 1681fb35bd37SJoerg Wunsch if (blknum + bp->bio_bcount / fdblk > nblocks) { 1682fb35bd37SJoerg Wunsch if (blknum >= nblocks) { 1683fb35bd37SJoerg Wunsch if (bp->bio_cmd == BIO_READ) 1684fb35bd37SJoerg Wunsch bp->bio_resid = bp->bio_bcount; 1685fb35bd37SJoerg Wunsch else { 1686fb35bd37SJoerg Wunsch bp->bio_error = ENOSPC; 16878177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 1688bb6382faSJoerg Wunsch } 1689fb35bd37SJoerg Wunsch goto bad; /* not always bad, but EOF */ 1690fb35bd37SJoerg Wunsch } 1691fb35bd37SJoerg Wunsch bp->bio_bcount = (nblocks - blknum) * fdblk; 1692bb6382faSJoerg Wunsch } 16938177437dSPoul-Henning Kamp bp->bio_pblkno = bp->bio_blkno; 16943a2f7427SDavid Greenman s = splbio(); 16958177437dSPoul-Henning Kamp bioqdisksort(&fdc->head, bp); 16966182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ 1697b2dfb1f9SJustin T. Gibbs devstat_start_transaction(&fd->device_stats); 16985f830ea2SJoerg Wunsch device_busy(fd->dev); 16996182fdbdSPeter Wemm fdstart(fdc); 17003a2f7427SDavid Greenman splx(s); 17013a2f7427SDavid Greenman return; 17023a2f7427SDavid Greenman 17033a2f7427SDavid Greenman bad: 17043a2f7427SDavid Greenman biodone(bp); 17053a2f7427SDavid Greenman } 17063a2f7427SDavid Greenman 1707246ed35dSJoerg Wunsch /* 1708246ed35dSJoerg Wunsch * fdstart 1709246ed35dSJoerg Wunsch * 1710246ed35dSJoerg Wunsch * We have just queued something. If the controller is not busy 1711246ed35dSJoerg Wunsch * then simulate the case where it has just finished a command 1712246ed35dSJoerg Wunsch * So that it (the interrupt routine) looks on the queue for more 1713246ed35dSJoerg Wunsch * work to do and picks up what we just added. 1714246ed35dSJoerg Wunsch * 1715246ed35dSJoerg Wunsch * If the controller is already busy, we need do nothing, as it 1716246ed35dSJoerg Wunsch * will pick up our work when the present work completes. 1717246ed35dSJoerg Wunsch */ 1718381fe1aaSGarrett Wollman static void 17196182fdbdSPeter Wemm fdstart(struct fdc_data *fdc) 17205b81b6b3SRodney W. Grimes { 17215b81b6b3SRodney W. Grimes int s; 17225b81b6b3SRodney W. Grimes 17235b81b6b3SRodney W. Grimes s = splbio(); 17246182fdbdSPeter Wemm if(fdc->state == DEVIDLE) 17255b81b6b3SRodney W. Grimes { 17266182fdbdSPeter Wemm fdc_intr(fdc); 17275b81b6b3SRodney W. Grimes } 17285b81b6b3SRodney W. Grimes splx(s); 17295b81b6b3SRodney W. Grimes } 17305b81b6b3SRodney W. Grimes 1731381fe1aaSGarrett Wollman static void 17326182fdbdSPeter Wemm fd_iotimeout(void *xfdc) 17335b81b6b3SRodney W. Grimes { 17345c1a1eaeSBruce Evans fdc_p fdc; 1735f5f7ba03SJordan K. Hubbard int s; 17365b81b6b3SRodney W. Grimes 17376182fdbdSPeter Wemm fdc = xfdc; 17385c1a1eaeSBruce Evans TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); 17395b81b6b3SRodney W. Grimes 17403a2f7427SDavid Greenman /* 17413a2f7427SDavid Greenman * Due to IBM's brain-dead design, the FDC has a faked ready 17423a2f7427SDavid Greenman * signal, hardwired to ready == true. Thus, any command 17433a2f7427SDavid Greenman * issued if there's no diskette in the drive will _never_ 17443a2f7427SDavid Greenman * complete, and must be aborted by resetting the FDC. 17453a2f7427SDavid Greenman * Many thanks, Big Blue! 17465c1a1eaeSBruce Evans * The FDC must not be reset directly, since that would 17475c1a1eaeSBruce Evans * interfere with the state machine. Instead, pretend that 17485c1a1eaeSBruce Evans * the command completed but was invalid. The state machine 17495c1a1eaeSBruce Evans * will reset the FDC and retry once. 17503a2f7427SDavid Greenman */ 17513a2f7427SDavid Greenman s = splbio(); 17525c1a1eaeSBruce Evans fdc->status[0] = NE7_ST0_IC_IV; 17535c1a1eaeSBruce Evans fdc->flags &= ~FDC_STAT_VALID; 17545c1a1eaeSBruce Evans fdc->state = IOTIMEDOUT; 17556182fdbdSPeter Wemm fdc_intr(fdc); 1756f5f7ba03SJordan K. Hubbard splx(s); 17575b81b6b3SRodney W. Grimes } 17585b81b6b3SRodney W. Grimes 1759246ed35dSJoerg Wunsch /* Just ensure it has the right spl. */ 1760381fe1aaSGarrett Wollman static void 17616182fdbdSPeter Wemm fd_pseudointr(void *xfdc) 17625b81b6b3SRodney W. Grimes { 17635b81b6b3SRodney W. Grimes int s; 17643a2f7427SDavid Greenman 17655b81b6b3SRodney W. Grimes s = splbio(); 17666182fdbdSPeter Wemm fdc_intr(xfdc); 17675b81b6b3SRodney W. Grimes splx(s); 17685b81b6b3SRodney W. Grimes } 17695b81b6b3SRodney W. Grimes 1770246ed35dSJoerg Wunsch /* 1771246ed35dSJoerg Wunsch * fdc_intr 1772246ed35dSJoerg Wunsch * 1773246ed35dSJoerg Wunsch * Keep calling the state machine until it returns a 0. 1774246ed35dSJoerg Wunsch * Always called at splbio. 1775246ed35dSJoerg Wunsch */ 1776fe310de8SBruce Evans static void 17776182fdbdSPeter Wemm fdc_intr(void *xfdc) 17785b81b6b3SRodney W. Grimes { 17796182fdbdSPeter Wemm fdc_p fdc = xfdc; 17806182fdbdSPeter Wemm while(fdstate(fdc)) 1781381fe1aaSGarrett Wollman ; 17825b81b6b3SRodney W. Grimes } 17835b81b6b3SRodney W. Grimes 178469acd21dSWarner Losh /* 1785246ed35dSJoerg Wunsch * Magic pseudo-DMA initialization for YE FDC. Sets count and 1786246ed35dSJoerg Wunsch * direction. 178769acd21dSWarner Losh */ 17883b178206SWarner Losh #define SET_BCDR(fdc,wr,cnt,port) \ 17893b178206SWarner Losh bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port, \ 17903b178206SWarner Losh ((cnt)-1) & 0xff); \ 17913b178206SWarner Losh bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \ 17923b178206SWarner Losh ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))); 179369acd21dSWarner Losh 179469acd21dSWarner Losh /* 1795246ed35dSJoerg Wunsch * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy. 179669acd21dSWarner Losh */ 17974c34deeeSJoerg Wunsch static int 17984c34deeeSJoerg Wunsch fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) 179969acd21dSWarner Losh { 180069acd21dSWarner Losh u_char *cptr = (u_char *)addr; 180169acd21dSWarner Losh 180221144e3bSPoul-Henning Kamp if (flags == BIO_READ) { 180369acd21dSWarner Losh if (fdc->state != PIOREAD) { 180469acd21dSWarner Losh fdc->state = PIOREAD; 180569acd21dSWarner Losh return(0); 180664860614SJoerg Wunsch } 18073b178206SWarner Losh SET_BCDR(fdc, 0, count, 0); 18083b178206SWarner Losh bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off + 18093b178206SWarner Losh FDC_YE_DATAPORT, cptr, count); 181069acd21dSWarner Losh } else { 18113b178206SWarner Losh bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off + 18123b178206SWarner Losh FDC_YE_DATAPORT, cptr, count); 18133b178206SWarner Losh SET_BCDR(fdc, 0, count, 0); 181464860614SJoerg Wunsch } 181569acd21dSWarner Losh return(1); 181669acd21dSWarner Losh } 181769acd21dSWarner Losh 1818246ed35dSJoerg Wunsch /* 1819246ed35dSJoerg Wunsch * The controller state machine. 1820246ed35dSJoerg Wunsch * 1821246ed35dSJoerg Wunsch * If it returns a non zero value, it should be called again immediately. 1822246ed35dSJoerg Wunsch */ 18233a2f7427SDavid Greenman static int 18246182fdbdSPeter Wemm fdstate(fdc_p fdc) 18255b81b6b3SRodney W. Grimes { 182664860614SJoerg Wunsch struct fdc_readid *idp; 1827fb35bd37SJoerg Wunsch int read, format, rdsectid, cylinder, head, i, sec = 0, sectrac; 1828fb35bd37SJoerg Wunsch int st0, cyl, st3, idf; 1829fb35bd37SJoerg Wunsch unsigned long blknum; 18305b81b6b3SRodney W. Grimes fdu_t fdu = fdc->fdu; 18315b81b6b3SRodney W. Grimes fd_p fd; 18328177437dSPoul-Henning Kamp register struct bio *bp; 1833b39c878eSAndrey A. Chernov struct fd_formb *finfo = NULL; 18343a2f7427SDavid Greenman size_t fdblk; 18355b81b6b3SRodney W. Grimes 1836e93e63cbSBruce Evans bp = fdc->bp; 1837e93e63cbSBruce Evans if (bp == NULL) { 18388177437dSPoul-Henning Kamp bp = bioq_first(&fdc->head); 1839e93e63cbSBruce Evans if (bp != NULL) { 18408177437dSPoul-Henning Kamp bioq_remove(&fdc->head, bp); 1841e93e63cbSBruce Evans fdc->bp = bp; 1842e93e63cbSBruce Evans } 1843e93e63cbSBruce Evans } 1844e93e63cbSBruce Evans if (bp == NULL) { 1845246ed35dSJoerg Wunsch /* 1846246ed35dSJoerg Wunsch * Nothing left for this controller to do, 1847246ed35dSJoerg Wunsch * force into the IDLE state. 1848246ed35dSJoerg Wunsch */ 18495b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 18506182fdbdSPeter Wemm if (fdc->fd) { 1851b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, 1852b6e5f28eSPeter Wemm "unexpected valid fd pointer\n"); 18535b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 18545b81b6b3SRodney W. Grimes fdc->fdu = -1; 18555b81b6b3SRodney W. Grimes } 18566182fdbdSPeter Wemm TRACE1("[fdc%d IDLE]", fdc->fdcu); 18575b81b6b3SRodney W. Grimes return (0); 18585b81b6b3SRodney W. Grimes } 18598177437dSPoul-Henning Kamp fdu = FDUNIT(minor(bp->bio_dev)); 18606182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 18613a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 1862b6e5f28eSPeter Wemm if (fdc->fd && (fd != fdc->fd)) 1863b6e5f28eSPeter Wemm device_printf(fd->dev, "confused fd pointers\n"); 18648177437dSPoul-Henning Kamp read = bp->bio_cmd == BIO_READ; 186556a23089SPoul-Henning Kamp if (read) 186656a23089SPoul-Henning Kamp idf = ISADMA_READ; 186756a23089SPoul-Henning Kamp else 186856a23089SPoul-Henning Kamp idf = ISADMA_WRITE; 186964860614SJoerg Wunsch format = bp->bio_cmd == BIO_FORMAT; 1870f664aeeeSJoerg Wunsch rdsectid = bp->bio_cmd == BIO_RDSECTID; 1871fb35bd37SJoerg Wunsch if (format) 18728177437dSPoul-Henning Kamp finfo = (struct fd_formb *)bp->bio_data; 18735b81b6b3SRodney W. Grimes TRACE1("fd%d", fdu); 18745b81b6b3SRodney W. Grimes TRACE1("[%s]", fdstates[fdc->state]); 18755b81b6b3SRodney W. Grimes TRACE1("(0x%x)", fd->flags); 18766182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); 18776182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 18785b81b6b3SRodney W. Grimes switch (fdc->state) 18795b81b6b3SRodney W. Grimes { 18805b81b6b3SRodney W. Grimes case DEVIDLE: 18815b81b6b3SRodney W. Grimes case FINDWORK: /* we have found new work */ 18825b81b6b3SRodney W. Grimes fdc->retry = 0; 18835b81b6b3SRodney W. Grimes fd->skip = 0; 18845b81b6b3SRodney W. Grimes fdc->fd = fd; 18855b81b6b3SRodney W. Grimes fdc->fdu = fdu; 18865f830ea2SJoerg Wunsch fdc->fdctl_wr(fdc, fd->ft->trans); 18873a2f7427SDavid Greenman TRACE1("[0x%x->FDCTL]", fd->ft->trans); 1888246ed35dSJoerg Wunsch /* 1889246ed35dSJoerg Wunsch * If the next drive has a motor startup pending, then 1890246ed35dSJoerg Wunsch * it will start up in its own good time. 1891246ed35dSJoerg Wunsch */ 18926182fdbdSPeter Wemm if(fd->flags & FD_MOTOR_WAIT) { 18935b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 1894246ed35dSJoerg Wunsch return (0); /* will return later */ 18955b81b6b3SRodney W. Grimes } 1896246ed35dSJoerg Wunsch /* 1897246ed35dSJoerg Wunsch * Maybe if it's not starting, it SHOULD be starting. 1898246ed35dSJoerg Wunsch */ 18995b81b6b3SRodney W. Grimes if (!(fd->flags & FD_MOTOR)) 19005b81b6b3SRodney W. Grimes { 19015b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 19026182fdbdSPeter Wemm fd_turnon(fd); 1903246ed35dSJoerg Wunsch return (0); /* will return later */ 19045b81b6b3SRodney W. Grimes } 19055b81b6b3SRodney W. Grimes else /* at least make sure we are selected */ 19065b81b6b3SRodney W. Grimes { 19076182fdbdSPeter Wemm set_motor(fdc, fd->fdsu, TURNON); 19085b81b6b3SRodney W. Grimes } 19095c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 19105c1a1eaeSBruce Evans fdc->state = RESETCTLR; 19115c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 19125c1a1eaeSBruce Evans } else 19135e235068SJordan K. Hubbard fdc->state = DOSEEK; 1914246ed35dSJoerg Wunsch return (1); /* will return immediately */ 1915fb35bd37SJoerg Wunsch 19165b81b6b3SRodney W. Grimes case DOSEEK: 1917fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 1918fb35bd37SJoerg Wunsch cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1919fb35bd37SJoerg Wunsch if (cylinder == fd->track) 19205b81b6b3SRodney W. Grimes { 19215b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 1922246ed35dSJoerg Wunsch return (1); /* will return immediately */ 19235b81b6b3SRodney W. Grimes } 19246182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, 1925fb35bd37SJoerg Wunsch fd->fdsu, cylinder * fd->ft->steptrac, 1926dc5df763SJoerg Wunsch 0)) 1927dc8603e3SJoerg Wunsch { 1928dc8603e3SJoerg Wunsch /* 1929246ed35dSJoerg Wunsch * Seek command not accepted, looks like 1930dc8603e3SJoerg Wunsch * the FDC went off to the Saints... 1931dc8603e3SJoerg Wunsch */ 1932dc8603e3SJoerg Wunsch fdc->retry = 6; /* try a reset */ 19336182fdbdSPeter Wemm return(retrier(fdc)); 1934dc8603e3SJoerg Wunsch } 1935dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 19365b81b6b3SRodney W. Grimes fdc->state = SEEKWAIT; 19375b81b6b3SRodney W. Grimes return(0); /* will return later */ 1938fb35bd37SJoerg Wunsch 19395b81b6b3SRodney W. Grimes case SEEKWAIT: 19405b81b6b3SRodney W. Grimes /* allow heads to settle */ 19416182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 16); 19425b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 19435b81b6b3SRodney W. Grimes return(0); /* will return later */ 1944fb35bd37SJoerg Wunsch 1945246ed35dSJoerg Wunsch case SEEKCOMPLETE : /* seek done, start DMA */ 1946fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 1947fb35bd37SJoerg Wunsch cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1948fb35bd37SJoerg Wunsch 1949246ed35dSJoerg Wunsch /* Make sure seek really happened. */ 19506182fdbdSPeter Wemm if(fd->track == FD_NO_TRACK) { 1951fb35bd37SJoerg Wunsch int descyl = cylinder * fd->ft->steptrac; 19523a2f7427SDavid Greenman do { 19533a2f7427SDavid Greenman /* 1954dc5df763SJoerg Wunsch * This might be a "ready changed" interrupt, 1955dc5df763SJoerg Wunsch * which cannot really happen since the 1956dc5df763SJoerg Wunsch * RDY pin is hardwired to + 5 volts. This 1957dc5df763SJoerg Wunsch * generally indicates a "bouncing" intr 1958dc5df763SJoerg Wunsch * line, so do one of the following: 1959dc5df763SJoerg Wunsch * 1960dc5df763SJoerg Wunsch * When running on an enhanced FDC that is 1961dc5df763SJoerg Wunsch * known to not go stuck after responding 1962dc5df763SJoerg Wunsch * with INVALID, fetch all interrupt states 1963dc5df763SJoerg Wunsch * until seeing either an INVALID or a 1964dc5df763SJoerg Wunsch * real interrupt condition. 1965dc5df763SJoerg Wunsch * 1966dc5df763SJoerg Wunsch * When running on a dumb old NE765, give 1967dc5df763SJoerg Wunsch * up immediately. The controller will 1968dc5df763SJoerg Wunsch * provide up to four dummy RC interrupt 1969dc5df763SJoerg Wunsch * conditions right after reset (for the 1970dc5df763SJoerg Wunsch * corresponding four drives), so this is 1971dc5df763SJoerg Wunsch * our only chance to get notice that it 1972dc5df763SJoerg Wunsch * was not the FDC that caused the interrupt. 19733a2f7427SDavid Greenman */ 1974dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) 1975dc5df763SJoerg Wunsch == FD_NOT_VALID) 1976246ed35dSJoerg Wunsch return (0); /* will return later */ 1977dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 1978dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 1979246ed35dSJoerg Wunsch return (0); /* hope for a real intr */ 19803a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 1981dc5df763SJoerg Wunsch 19826182fdbdSPeter Wemm if (0 == descyl) { 1983dc5df763SJoerg Wunsch int failed = 0; 19843a2f7427SDavid Greenman /* 19853a2f7427SDavid Greenman * seek to cyl 0 requested; make sure we are 19863a2f7427SDavid Greenman * really there 19873a2f7427SDavid Greenman */ 1988dc5df763SJoerg Wunsch if (fd_sense_drive_status(fdc, &st3)) 1989dc5df763SJoerg Wunsch failed = 1; 19903a2f7427SDavid Greenman if ((st3 & NE7_ST3_T0) == 0) { 19913a2f7427SDavid Greenman printf( 19923a2f7427SDavid Greenman "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", 19933a2f7427SDavid Greenman fdu, st3, NE7_ST3BITS); 1994dc5df763SJoerg Wunsch failed = 1; 1995dc5df763SJoerg Wunsch } 1996dc5df763SJoerg Wunsch 19976182fdbdSPeter Wemm if (failed) { 19983a2f7427SDavid Greenman if(fdc->retry < 3) 19993a2f7427SDavid Greenman fdc->retry = 3; 20006182fdbdSPeter Wemm return (retrier(fdc)); 20013a2f7427SDavid Greenman } 20023a2f7427SDavid Greenman } 2003dc5df763SJoerg Wunsch 20046182fdbdSPeter Wemm if (cyl != descyl) { 20053a2f7427SDavid Greenman printf( 20063a2f7427SDavid Greenman "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", 20072d9d0204SRodney W. Grimes fdu, descyl, cyl, st0); 2008e5d7d243SBruce Evans if (fdc->retry < 3) 2009e5d7d243SBruce Evans fdc->retry = 3; 20106182fdbdSPeter Wemm return (retrier(fdc)); 20115b81b6b3SRodney W. Grimes } 20125b81b6b3SRodney W. Grimes } 20135b81b6b3SRodney W. Grimes 2014fb35bd37SJoerg Wunsch fd->track = cylinder; 2015fb35bd37SJoerg Wunsch if (format) 2016fb35bd37SJoerg Wunsch fd->skip = (char *)&(finfo->fd_formb_cylno(0)) 2017fb35bd37SJoerg Wunsch - (char *)finfo; 2018250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 20198177437dSPoul-Henning Kamp isa_dmastart(idf, bp->bio_data+fd->skip, 20208177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, fdc->dmachan); 2021fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 20225b81b6b3SRodney W. Grimes sectrac = fd->ft->sectrac; 20235b81b6b3SRodney W. Grimes sec = blknum % (sectrac * fd->ft->heads); 20245b81b6b3SRodney W. Grimes head = sec / sectrac; 20255b81b6b3SRodney W. Grimes sec = sec % sectrac + 1; 20263a2f7427SDavid Greenman fd->hddrv = ((head&1)<<2)+fdu; 20273a2f7427SDavid Greenman 2028250300ebSJoerg Wunsch if(format || !(read || rdsectid)) 20293a2f7427SDavid Greenman { 20303a2f7427SDavid Greenman /* make sure the drive is writable */ 2031dc5df763SJoerg Wunsch if(fd_sense_drive_status(fdc, &st3) != 0) 2032dc8603e3SJoerg Wunsch { 2033dc8603e3SJoerg Wunsch /* stuck controller? */ 20345f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 203556a23089SPoul-Henning Kamp isa_dmadone(idf, 20368177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 20378177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 20385c1a1eaeSBruce Evans fdc->dmachan); 2039dc8603e3SJoerg Wunsch fdc->retry = 6; /* reset the beast */ 20406182fdbdSPeter Wemm return (retrier(fdc)); 2041dc8603e3SJoerg Wunsch } 20423a2f7427SDavid Greenman if(st3 & NE7_ST3_WP) 20433a2f7427SDavid Greenman { 20443a2f7427SDavid Greenman /* 20453a2f7427SDavid Greenman * XXX YES! this is ugly. 20463a2f7427SDavid Greenman * in order to force the current operation 20473a2f7427SDavid Greenman * to fail, we will have to fake an FDC 20483a2f7427SDavid Greenman * error - all error handling is done 20493a2f7427SDavid Greenman * by the retrier() 20503a2f7427SDavid Greenman */ 20513a2f7427SDavid Greenman fdc->status[0] = NE7_ST0_IC_AT; 20523a2f7427SDavid Greenman fdc->status[1] = NE7_ST1_NW; 20533a2f7427SDavid Greenman fdc->status[2] = 0; 20543a2f7427SDavid Greenman fdc->status[3] = fd->track; 20553a2f7427SDavid Greenman fdc->status[4] = head; 20563a2f7427SDavid Greenman fdc->status[5] = sec; 20573a2f7427SDavid Greenman fdc->retry = 8; /* break out immediately */ 20583a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; /* not really... */ 2059246ed35dSJoerg Wunsch return (1); /* will return immediately */ 20603a2f7427SDavid Greenman } 20613a2f7427SDavid Greenman } 20625b81b6b3SRodney W. Grimes 20636182fdbdSPeter Wemm if (format) { 20645f830ea2SJoerg Wunsch if (fdc->flags & FDC_NODMA) { 20655f830ea2SJoerg Wunsch /* 20665f830ea2SJoerg Wunsch * This seems to be necessary for 20675f830ea2SJoerg Wunsch * whatever obscure reason; if we omit 20685f830ea2SJoerg Wunsch * it, we end up filling the sector ID 20695f830ea2SJoerg Wunsch * fields of the newly formatted track 20705f830ea2SJoerg Wunsch * entirely with garbage, causing 20715f830ea2SJoerg Wunsch * `wrong cylinder' errors all over 20725f830ea2SJoerg Wunsch * the place when trying to read them 20735f830ea2SJoerg Wunsch * back. 20745f830ea2SJoerg Wunsch * 20755f830ea2SJoerg Wunsch * Umpf. 20765f830ea2SJoerg Wunsch */ 20778177437dSPoul-Henning Kamp SET_BCDR(fdc, 1, bp->bio_bcount, 0); 20785f830ea2SJoerg Wunsch 20798177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd, 20808177437dSPoul-Henning Kamp bp->bio_data+fd->skip, 20818177437dSPoul-Henning Kamp bp->bio_bcount); 20825f830ea2SJoerg Wunsch 20835f830ea2SJoerg Wunsch } 2084b39c878eSAndrey A. Chernov /* formatting */ 20856182fdbdSPeter Wemm if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, 2086dc5df763SJoerg Wunsch finfo->fd_formb_secshift, 2087dc5df763SJoerg Wunsch finfo->fd_formb_nsecs, 2088dc5df763SJoerg Wunsch finfo->fd_formb_gaplen, 20896182fdbdSPeter Wemm finfo->fd_formb_fillbyte, 0)) { 2090dc8603e3SJoerg Wunsch /* controller fell over */ 20915f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 209256a23089SPoul-Henning Kamp isa_dmadone(idf, 20938177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 20948177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 20955c1a1eaeSBruce Evans fdc->dmachan); 2096dc8603e3SJoerg Wunsch fdc->retry = 6; 20976182fdbdSPeter Wemm return (retrier(fdc)); 2098dc8603e3SJoerg Wunsch } 2099250300ebSJoerg Wunsch } else if (rdsectid) { 2100250300ebSJoerg Wunsch if (fd_cmd(fdc, 2, NE7CMD_READID, head << 2 | fdu, 0)) { 2101250300ebSJoerg Wunsch /* controller jamming */ 2102250300ebSJoerg Wunsch fdc->retry = 6; 2103250300ebSJoerg Wunsch return (retrier(fdc)); 2104250300ebSJoerg Wunsch } 21056182fdbdSPeter Wemm } else { 2106250300ebSJoerg Wunsch /* read or write operation */ 21073b178206SWarner Losh if (fdc->flags & FDC_NODMA) { 210869acd21dSWarner Losh /* 2109246ed35dSJoerg Wunsch * This seems to be necessary even when 2110246ed35dSJoerg Wunsch * reading data. 211169acd21dSWarner Losh */ 21123b178206SWarner Losh SET_BCDR(fdc, 1, fdblk, 0); 211369acd21dSWarner Losh 211469acd21dSWarner Losh /* 2115246ed35dSJoerg Wunsch * Perform the write pseudo-DMA before 2116246ed35dSJoerg Wunsch * the WRITE command is sent. 211769acd21dSWarner Losh */ 211869acd21dSWarner Losh if (!read) 21198177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd, 21208177437dSPoul-Henning Kamp bp->bio_data+fd->skip, 212169acd21dSWarner Losh fdblk); 212269acd21dSWarner Losh } 21236182fdbdSPeter Wemm if (fd_cmd(fdc, 9, 2124dc5df763SJoerg Wunsch (read ? NE7CMD_READ : NE7CMD_WRITE), 2125dc5df763SJoerg Wunsch head << 2 | fdu, /* head & unit */ 2126dc5df763SJoerg Wunsch fd->track, /* track */ 2127dc5df763SJoerg Wunsch head, 2128dc5df763SJoerg Wunsch sec, /* sector + 1 */ 2129dc5df763SJoerg Wunsch fd->ft->secsize, /* sector size */ 2130dc5df763SJoerg Wunsch sectrac, /* sectors/track */ 2131dc5df763SJoerg Wunsch fd->ft->gap, /* gap size */ 2132dc5df763SJoerg Wunsch fd->ft->datalen, /* data length */ 21336182fdbdSPeter Wemm 0)) { 2134dc8603e3SJoerg Wunsch /* the beast is sleeping again */ 21355f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 213656a23089SPoul-Henning Kamp isa_dmadone(idf, 21378177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 21388177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 21395c1a1eaeSBruce Evans fdc->dmachan); 2140dc8603e3SJoerg Wunsch fdc->retry = 6; 21416182fdbdSPeter Wemm return (retrier(fdc)); 21425b81b6b3SRodney W. Grimes } 2143b39c878eSAndrey A. Chernov } 2144250300ebSJoerg Wunsch if (!rdsectid && (fdc->flags & FDC_NODMA)) 214569acd21dSWarner Losh /* 2146246ed35dSJoerg Wunsch * If this is a read, then simply await interrupt 2147246ed35dSJoerg Wunsch * before performing PIO. 214869acd21dSWarner Losh */ 21498177437dSPoul-Henning Kamp if (read && !fdcpio(fdc,bp->bio_cmd, 21508177437dSPoul-Henning Kamp bp->bio_data+fd->skip,fdblk)) { 21513b178206SWarner Losh fd->tohandle = timeout(fd_iotimeout, fdc, hz); 215269acd21dSWarner Losh return(0); /* will return later */ 215364860614SJoerg Wunsch } 215469acd21dSWarner Losh 215569acd21dSWarner Losh /* 2156246ed35dSJoerg Wunsch * Write (or format) operation will fall through and 2157246ed35dSJoerg Wunsch * await completion interrupt. 215869acd21dSWarner Losh */ 21595b81b6b3SRodney W. Grimes fdc->state = IOCOMPLETE; 21606182fdbdSPeter Wemm fd->tohandle = timeout(fd_iotimeout, fdc, hz); 21615b81b6b3SRodney W. Grimes return (0); /* will return later */ 2162fb35bd37SJoerg Wunsch 216369acd21dSWarner Losh case PIOREAD: 216469acd21dSWarner Losh /* 2165246ed35dSJoerg Wunsch * Actually perform the PIO read. The IOCOMPLETE case 216669acd21dSWarner Losh * removes the timeout for us. 216769acd21dSWarner Losh */ 21688177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk); 216969acd21dSWarner Losh fdc->state = IOCOMPLETE; 217069acd21dSWarner Losh /* FALLTHROUGH */ 2171246ed35dSJoerg Wunsch case IOCOMPLETE: /* IO done, post-analyze */ 21726182fdbdSPeter Wemm untimeout(fd_iotimeout, fdc, fd->tohandle); 2173dc5df763SJoerg Wunsch 217464860614SJoerg Wunsch if (fd_read_status(fdc)) { 2175250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 21768177437dSPoul-Henning Kamp isa_dmadone(idf, bp->bio_data + fd->skip, 21778177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 21785c1a1eaeSBruce Evans fdc->dmachan); 2179dc5df763SJoerg Wunsch if (fdc->retry < 6) 2180dc5df763SJoerg Wunsch fdc->retry = 6; /* force a reset */ 21816182fdbdSPeter Wemm return (retrier(fdc)); 21825b81b6b3SRodney W. Grimes } 2183dc5df763SJoerg Wunsch 21843a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; 2185dc5df763SJoerg Wunsch 21863a2f7427SDavid Greenman /* FALLTHROUGH */ 21873a2f7427SDavid Greenman case IOTIMEDOUT: 2188250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 21898177437dSPoul-Henning Kamp isa_dmadone(idf, bp->bio_data + fd->skip, 21908177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, fdc->dmachan); 21916182fdbdSPeter Wemm if (fdc->status[0] & NE7_ST0_IC) { 21923a2f7427SDavid Greenman if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 21933a2f7427SDavid Greenman && fdc->status[1] & NE7_ST1_OR) { 2194b39c878eSAndrey A. Chernov /* 21953fef646eSJoerg Wunsch * DMA overrun. Someone hogged the bus and 21963fef646eSJoerg Wunsch * didn't release it in time for the next 21973fef646eSJoerg Wunsch * FDC transfer. 21983fef646eSJoerg Wunsch * 21993fef646eSJoerg Wunsch * We normally restart this without bumping 22003fef646eSJoerg Wunsch * the retry counter. However, in case 22013fef646eSJoerg Wunsch * something is seriously messed up (like 22023fef646eSJoerg Wunsch * broken hardware), we rather limit the 22033fef646eSJoerg Wunsch * number of retries so the IO operation 22043fef646eSJoerg Wunsch * doesn't block indefinately. 2205b39c878eSAndrey A. Chernov */ 22063fef646eSJoerg Wunsch if (fdc->dma_overruns++ < FDC_DMAOV_MAX) { 2207b39c878eSAndrey A. Chernov fdc->state = SEEKCOMPLETE; 2208246ed35dSJoerg Wunsch return (1);/* will return immediately */ 22093fef646eSJoerg Wunsch } /* else fall through */ 2210b39c878eSAndrey A. Chernov } 22113fef646eSJoerg Wunsch if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV 22123a2f7427SDavid Greenman && fdc->retry < 6) 22133a2f7427SDavid Greenman fdc->retry = 6; /* force a reset */ 22143a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 22153a2f7427SDavid Greenman && fdc->status[2] & NE7_ST2_WC 22163a2f7427SDavid Greenman && fdc->retry < 3) 22173a2f7427SDavid Greenman fdc->retry = 3; /* force recalibrate */ 22186182fdbdSPeter Wemm return (retrier(fdc)); 22195b81b6b3SRodney W. Grimes } 22205b81b6b3SRodney W. Grimes /* All OK */ 2221250300ebSJoerg Wunsch if (rdsectid) { 2222250300ebSJoerg Wunsch /* copy out ID field contents */ 222364860614SJoerg Wunsch idp = (struct fdc_readid *)bp->bio_data; 2224250300ebSJoerg Wunsch idp->cyl = fdc->status[3]; 2225250300ebSJoerg Wunsch idp->head = fdc->status[4]; 2226250300ebSJoerg Wunsch idp->sec = fdc->status[5]; 2227250300ebSJoerg Wunsch idp->secshift = fdc->status[6]; 2228250300ebSJoerg Wunsch } 22293fef646eSJoerg Wunsch /* Operation successful, retry DMA overruns again next time. */ 22303fef646eSJoerg Wunsch fdc->dma_overruns = 0; 22313a2f7427SDavid Greenman fd->skip += fdblk; 2232fb35bd37SJoerg Wunsch if (!rdsectid && !format && fd->skip < bp->bio_bcount) { 22335b81b6b3SRodney W. Grimes /* set up next transfer */ 22345b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 22356182fdbdSPeter Wemm } else { 22365b81b6b3SRodney W. Grimes /* ALL DONE */ 22375b81b6b3SRodney W. Grimes fd->skip = 0; 2238fb35bd37SJoerg Wunsch bp->bio_resid = 0; 2239e93e63cbSBruce Evans fdc->bp = NULL; 22405f830ea2SJoerg Wunsch device_unbusy(fd->dev); 2241a468031cSPoul-Henning Kamp biofinish(bp, &fd->device_stats, 0); 22425b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 22435b81b6b3SRodney W. Grimes fdc->fdu = -1; 22445b81b6b3SRodney W. Grimes fdc->state = FINDWORK; 22455b81b6b3SRodney W. Grimes } 2246246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2247fb35bd37SJoerg Wunsch 22485b81b6b3SRodney W. Grimes case RESETCTLR: 22493a2f7427SDavid Greenman fdc_reset(fdc); 22505b81b6b3SRodney W. Grimes fdc->retry++; 22515c1a1eaeSBruce Evans fdc->state = RESETCOMPLETE; 2252246ed35dSJoerg Wunsch return (0); /* will return later */ 2253fb35bd37SJoerg Wunsch 22545c1a1eaeSBruce Evans case RESETCOMPLETE: 22555c1a1eaeSBruce Evans /* 22565c1a1eaeSBruce Evans * Discard all the results from the reset so that they 22575c1a1eaeSBruce Evans * can't cause an unexpected interrupt later. 22585c1a1eaeSBruce Evans */ 22590e317d05SJoerg Wunsch for (i = 0; i < 4; i++) 22600e317d05SJoerg Wunsch (void)fd_sense_int(fdc, &st0, &cyl); 22615c1a1eaeSBruce Evans fdc->state = STARTRECAL; 2262fb35bd37SJoerg Wunsch /* FALLTHROUGH */ 22635c1a1eaeSBruce Evans case STARTRECAL: 22646182fdbdSPeter Wemm if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { 2265dc8603e3SJoerg Wunsch /* arrgl */ 2266dc8603e3SJoerg Wunsch fdc->retry = 6; 22676182fdbdSPeter Wemm return (retrier(fdc)); 2268dc8603e3SJoerg Wunsch } 22695b81b6b3SRodney W. Grimes fdc->state = RECALWAIT; 22705b81b6b3SRodney W. Grimes return (0); /* will return later */ 2271fb35bd37SJoerg Wunsch 22725b81b6b3SRodney W. Grimes case RECALWAIT: 22735b81b6b3SRodney W. Grimes /* allow heads to settle */ 22746182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 8); 22755b81b6b3SRodney W. Grimes fdc->state = RECALCOMPLETE; 22765b81b6b3SRodney W. Grimes return (0); /* will return later */ 2277fb35bd37SJoerg Wunsch 22785b81b6b3SRodney W. Grimes case RECALCOMPLETE: 22793a2f7427SDavid Greenman do { 22803a2f7427SDavid Greenman /* 2281dc5df763SJoerg Wunsch * See SEEKCOMPLETE for a comment on this: 22823a2f7427SDavid Greenman */ 2283dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) 2284246ed35dSJoerg Wunsch return (0); /* will return later */ 2285dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 2286dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 2287246ed35dSJoerg Wunsch return (0); /* hope for a real intr */ 22883a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 22893a2f7427SDavid Greenman if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) 22905b81b6b3SRodney W. Grimes { 2291dc8603e3SJoerg Wunsch if(fdc->retry > 3) 2292dc8603e3SJoerg Wunsch /* 2293246ed35dSJoerg Wunsch * A recalibrate from beyond cylinder 77 2294dc8603e3SJoerg Wunsch * will "fail" due to the FDC limitations; 2295dc8603e3SJoerg Wunsch * since people used to complain much about 2296dc8603e3SJoerg Wunsch * the failure message, try not logging 2297dc8603e3SJoerg Wunsch * this one if it seems to be the first 2298246ed35dSJoerg Wunsch * time in a line. 2299dc8603e3SJoerg Wunsch */ 2300dc8603e3SJoerg Wunsch printf("fd%d: recal failed ST0 %b cyl %d\n", 2301dc8603e3SJoerg Wunsch fdu, st0, NE7_ST0BITS, cyl); 23023a2f7427SDavid Greenman if(fdc->retry < 3) fdc->retry = 3; 23036182fdbdSPeter Wemm return (retrier(fdc)); 23045b81b6b3SRodney W. Grimes } 23055b81b6b3SRodney W. Grimes fd->track = 0; 23065b81b6b3SRodney W. Grimes /* Seek (probably) necessary */ 23075b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 2308246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2309fb35bd37SJoerg Wunsch 23105b81b6b3SRodney W. Grimes case MOTORWAIT: 23115b81b6b3SRodney W. Grimes if(fd->flags & FD_MOTOR_WAIT) 23125b81b6b3SRodney W. Grimes { 23135b81b6b3SRodney W. Grimes return (0); /* time's not up yet */ 23145b81b6b3SRodney W. Grimes } 23155c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 23165c1a1eaeSBruce Evans fdc->state = RESETCTLR; 23175c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 2318fb35bd37SJoerg Wunsch } else 2319fb35bd37SJoerg Wunsch fdc->state = DOSEEK; 2320246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2321fb35bd37SJoerg Wunsch 23225b81b6b3SRodney W. Grimes default: 2323b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, "unexpected FD int->"); 232464860614SJoerg Wunsch if (fd_read_status(fdc) == 0) 2325a838d83dSBruce Evans printf("FDC status :%x %x %x %x %x %x %x ", 23265b81b6b3SRodney W. Grimes fdc->status[0], 23275b81b6b3SRodney W. Grimes fdc->status[1], 23285b81b6b3SRodney W. Grimes fdc->status[2], 23295b81b6b3SRodney W. Grimes fdc->status[3], 23305b81b6b3SRodney W. Grimes fdc->status[4], 23315b81b6b3SRodney W. Grimes fdc->status[5], 23325b81b6b3SRodney W. Grimes fdc->status[6] ); 23333a2f7427SDavid Greenman else 2334dac0f2dbSJoerg Wunsch printf("No status available "); 2335dac0f2dbSJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) != 0) 2336dac0f2dbSJoerg Wunsch { 2337dac0f2dbSJoerg Wunsch printf("[controller is dead now]\n"); 2338246ed35dSJoerg Wunsch return (0); /* will return later */ 23395b81b6b3SRodney W. Grimes } 2340dac0f2dbSJoerg Wunsch printf("ST0 = %x, PCN = %x\n", st0, cyl); 2341246ed35dSJoerg Wunsch return (0); /* will return later */ 2342dac0f2dbSJoerg Wunsch } 2343246ed35dSJoerg Wunsch /* noone should ever get here */ 23445b81b6b3SRodney W. Grimes } 23455b81b6b3SRodney W. Grimes 2346aaf08d94SGarrett Wollman static int 23476182fdbdSPeter Wemm retrier(struct fdc_data *fdc) 23485b81b6b3SRodney W. Grimes { 23498177437dSPoul-Henning Kamp struct bio *bp; 23506182fdbdSPeter Wemm struct fd_data *fd; 23516182fdbdSPeter Wemm int fdu; 23525b81b6b3SRodney W. Grimes 2353e93e63cbSBruce Evans bp = fdc->bp; 23545b81b6b3SRodney W. Grimes 23556182fdbdSPeter Wemm /* XXX shouldn't this be cached somewhere? */ 23568177437dSPoul-Henning Kamp fdu = FDUNIT(minor(bp->bio_dev)); 23576182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 23586182fdbdSPeter Wemm if (fd->options & FDOPT_NORETRY) 23593a2f7427SDavid Greenman goto fail; 23606182fdbdSPeter Wemm 23616182fdbdSPeter Wemm switch (fdc->retry) { 23625b81b6b3SRodney W. Grimes case 0: case 1: case 2: 23635b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 23645b81b6b3SRodney W. Grimes break; 23655b81b6b3SRodney W. Grimes case 3: case 4: case 5: 23665b81b6b3SRodney W. Grimes fdc->state = STARTRECAL; 23675b81b6b3SRodney W. Grimes break; 23685b81b6b3SRodney W. Grimes case 6: 23695b81b6b3SRodney W. Grimes fdc->state = RESETCTLR; 23705b81b6b3SRodney W. Grimes break; 23715b81b6b3SRodney W. Grimes case 7: 23725b81b6b3SRodney W. Grimes break; 23735b81b6b3SRodney W. Grimes default: 23743a2f7427SDavid Greenman fail: 2375fb35bd37SJoerg Wunsch if ((fd->options & FDOPT_NOERRLOG) == 0) { 2376fb35bd37SJoerg Wunsch diskerr(bp, "hard error", fdc->fd->skip / DEV_BSIZE, 23773a2f7427SDavid Greenman (struct disklabel *)NULL); 2378fb35bd37SJoerg Wunsch if (fdc->flags & FDC_STAT_VALID) { 2379dc5df763SJoerg Wunsch printf( 2380a838d83dSBruce Evans " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", 2381dc5df763SJoerg Wunsch fdc->status[0], NE7_ST0BITS, 2382dc5df763SJoerg Wunsch fdc->status[1], NE7_ST1BITS, 2383dc5df763SJoerg Wunsch fdc->status[2], NE7_ST2BITS, 2384dc5df763SJoerg Wunsch fdc->status[3], fdc->status[4], 2385dc5df763SJoerg Wunsch fdc->status[5]); 2386dc5df763SJoerg Wunsch } 2387dc5df763SJoerg Wunsch else 2388dc5df763SJoerg Wunsch printf(" (No status)\n"); 23895b81b6b3SRodney W. Grimes } 23902995d110SJoerg Wunsch if ((fd->options & FDOPT_NOERROR) == 0) { 23918177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 23928177437dSPoul-Henning Kamp bp->bio_error = EIO; 2393fb35bd37SJoerg Wunsch bp->bio_resid = bp->bio_bcount - fdc->fd->skip; 2394fb35bd37SJoerg Wunsch } else 2395fb35bd37SJoerg Wunsch bp->bio_resid = 0; 2396e93e63cbSBruce Evans fdc->bp = NULL; 23975b81b6b3SRodney W. Grimes fdc->fd->skip = 0; 23985f830ea2SJoerg Wunsch device_unbusy(fd->dev); 2399a468031cSPoul-Henning Kamp biofinish(bp, &fdc->fd->device_stats, 0); 240092ed385aSRodney W. Grimes fdc->state = FINDWORK; 24015c1a1eaeSBruce Evans fdc->flags |= FDC_NEEDS_RESET; 24025b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 24035b81b6b3SRodney W. Grimes fdc->fdu = -1; 240492ed385aSRodney W. Grimes return (1); 24055b81b6b3SRodney W. Grimes } 24065b81b6b3SRodney W. Grimes fdc->retry++; 24075b81b6b3SRodney W. Grimes return (1); 24085b81b6b3SRodney W. Grimes } 24095b81b6b3SRodney W. Grimes 24101fdb6e6cSPoul-Henning Kamp static void 24111fdb6e6cSPoul-Henning Kamp fdbiodone(struct bio *bp) 24121fdb6e6cSPoul-Henning Kamp { 24131fdb6e6cSPoul-Henning Kamp wakeup(bp); 24141fdb6e6cSPoul-Henning Kamp } 24151fdb6e6cSPoul-Henning Kamp 2416b39c878eSAndrey A. Chernov static int 2417f664aeeeSJoerg Wunsch fdmisccmd(dev_t dev, u_int cmd, void *data) 2418b39c878eSAndrey A. Chernov { 2419b39c878eSAndrey A. Chernov fdu_t fdu; 2420b39c878eSAndrey A. Chernov fd_p fd; 24211fdb6e6cSPoul-Henning Kamp struct bio *bp; 2422f664aeeeSJoerg Wunsch struct fd_formb *finfo; 2423f664aeeeSJoerg Wunsch struct fdc_readid *idfield; 24243a2f7427SDavid Greenman size_t fdblk; 2425b39c878eSAndrey A. Chernov 2426b39c878eSAndrey A. Chernov fdu = FDUNIT(minor(dev)); 24276182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 24283a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2429f664aeeeSJoerg Wunsch finfo = (struct fd_formb *)data; 2430f664aeeeSJoerg Wunsch idfield = (struct fdc_readid *)data; 2431b39c878eSAndrey A. Chernov 2432fb35bd37SJoerg Wunsch bp = malloc(sizeof(struct bio), M_TEMP, M_ZERO); 2433b39c878eSAndrey A. Chernov 2434b39c878eSAndrey A. Chernov /* 2435f664aeeeSJoerg Wunsch * Set up a bio request for fdstrategy(). bio_blkno is faked 2436f664aeeeSJoerg Wunsch * so that fdstrategy() will seek to the the requested 2437f664aeeeSJoerg Wunsch * cylinder, and use the desired head. Since we are not 2438f664aeeeSJoerg Wunsch * interested in bioqdisksort() munging with our faked bio 2439f664aeeeSJoerg Wunsch * request, we mark it as being an ordered request. 2440b39c878eSAndrey A. Chernov */ 2441f664aeeeSJoerg Wunsch bp->bio_cmd = cmd; 2442f664aeeeSJoerg Wunsch if (cmd == BIO_FORMAT) { 2443f664aeeeSJoerg Wunsch bp->bio_blkno = 2444f664aeeeSJoerg Wunsch (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + 2445f664aeeeSJoerg Wunsch finfo->head * fd->ft->sectrac) * 2446f664aeeeSJoerg Wunsch fdblk / DEV_BSIZE; 2447f664aeeeSJoerg Wunsch bp->bio_bcount = sizeof(struct fd_idfield_data) * 2448f664aeeeSJoerg Wunsch finfo->fd_formb_nsecs; 2449f664aeeeSJoerg Wunsch } else if (cmd == BIO_RDSECTID) { 2450f664aeeeSJoerg Wunsch bp->bio_blkno = 2451f664aeeeSJoerg Wunsch (idfield->cyl * (fd->ft->sectrac * fd->ft->heads) + 2452f664aeeeSJoerg Wunsch idfield->head * fd->ft->sectrac) * 2453f664aeeeSJoerg Wunsch fdblk / DEV_BSIZE; 2454250300ebSJoerg Wunsch bp->bio_bcount = sizeof(struct fdc_readid); 2455f664aeeeSJoerg Wunsch } else 2456f664aeeeSJoerg Wunsch panic("wrong cmd in fdmisccmd()"); 2457f664aeeeSJoerg Wunsch bp->bio_data = data; 2458250300ebSJoerg Wunsch bp->bio_dev = dev; 2459250300ebSJoerg Wunsch bp->bio_done = fdbiodone; 2460f664aeeeSJoerg Wunsch bp->bio_flags = BIO_ORDERED; 2461250300ebSJoerg Wunsch 2462f5f7ba03SJordan K. Hubbard /* 2463fb35bd37SJoerg Wunsch * Now run the command. The wait loop is a version of bufwait() 2464fb35bd37SJoerg Wunsch * adapted for struct bio instead of struct buf and specialized 2465fb35bd37SJoerg Wunsch * for the current context. 2466f5f7ba03SJordan K. Hubbard */ 2467f664aeeeSJoerg Wunsch fdstrategy(bp); 2468f664aeeeSJoerg Wunsch while ((bp->bio_flags & BIO_DONE) == 0) 2469f664aeeeSJoerg Wunsch tsleep(bp, PRIBIO, "fdcmd", 0); 2470f664aeeeSJoerg Wunsch 2471f664aeeeSJoerg Wunsch free(bp, M_TEMP); 2472f664aeeeSJoerg Wunsch return (bp->bio_flags & BIO_ERROR ? bp->bio_error : 0); 2473f664aeeeSJoerg Wunsch } 24745b81b6b3SRodney W. Grimes 24753e425b96SJulian Elischer static int 2476b40ce416SJulian Elischer fdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 2477f5f7ba03SJordan K. Hubbard { 2478f664aeeeSJoerg Wunsch fdu_t fdu; 2479f664aeeeSJoerg Wunsch fd_p fd; 2480f5f7ba03SJordan K. Hubbard struct fd_type *fdt; 2481fb35bd37SJoerg Wunsch struct disklabel *lp; 24822995d110SJoerg Wunsch struct fdc_status *fsp; 2483250300ebSJoerg Wunsch struct fdc_readid *rid; 2484f664aeeeSJoerg Wunsch size_t fdblk; 2485fb35bd37SJoerg Wunsch int error; 2486f5f7ba03SJordan K. Hubbard 2487f664aeeeSJoerg Wunsch fdu = FDUNIT(minor(dev)); 2488f664aeeeSJoerg Wunsch fd = devclass_get_softc(fd_devclass, fdu); 2489fb35bd37SJoerg Wunsch 2490fb35bd37SJoerg Wunsch 24913a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2492fb35bd37SJoerg Wunsch error = 0; 2493f5f7ba03SJordan K. Hubbard 24946182fdbdSPeter Wemm switch (cmd) { 2495f5f7ba03SJordan K. Hubbard case DIOCGDINFO: 2496fb35bd37SJoerg Wunsch lp = malloc(sizeof(*lp), M_TEMP, M_ZERO); 2497fb35bd37SJoerg Wunsch lp->d_secsize = fdblk; 24986182fdbdSPeter Wemm fdt = fd->ft; 2499fb35bd37SJoerg Wunsch lp->d_secpercyl = fdt->size / fdt->tracks; 2500fb35bd37SJoerg Wunsch lp->d_type = DTYPE_FLOPPY; 2501fb35bd37SJoerg Wunsch if (readdisklabel(dkmodpart(dev, RAW_PART), lp) != NULL) 2502f5f7ba03SJordan K. Hubbard error = EINVAL; 2503fb35bd37SJoerg Wunsch else 2504fb35bd37SJoerg Wunsch *(struct disklabel *)addr = *lp; 2505fb35bd37SJoerg Wunsch free(lp, M_TEMP); 2506f5f7ba03SJordan K. Hubbard break; 2507f5f7ba03SJordan K. Hubbard 2508f5f7ba03SJordan K. Hubbard case DIOCSDINFO: 2509f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2510fb35bd37SJoerg Wunsch return (EBADF); 2511fb35bd37SJoerg Wunsch /* 2512fb35bd37SJoerg Wunsch * XXX perhaps should call setdisklabel() to do error checking 2513fb35bd37SJoerg Wunsch * although there is nowhere to "set" the result. Perhaps 2514fb35bd37SJoerg Wunsch * should always just fail. 2515fb35bd37SJoerg Wunsch */ 2516f5f7ba03SJordan K. Hubbard break; 2517f5f7ba03SJordan K. Hubbard 2518f5f7ba03SJordan K. Hubbard case DIOCWLABEL: 2519f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2520fb35bd37SJoerg Wunsch return (EBADF); 2521f5f7ba03SJordan K. Hubbard break; 2522f5f7ba03SJordan K. Hubbard 2523f5f7ba03SJordan K. Hubbard case DIOCWDINFO: 2524fb35bd37SJoerg Wunsch if ((flag & FWRITE) == 0) 2525fb35bd37SJoerg Wunsch return (EBADF); 2526fb35bd37SJoerg Wunsch lp = malloc(DEV_BSIZE, M_TEMP, M_ZERO); 2527fb35bd37SJoerg Wunsch error = setdisklabel(lp, (struct disklabel *)addr, (u_long)0); 2528fb35bd37SJoerg Wunsch if (error != 0) 2529fb35bd37SJoerg Wunsch error = writedisklabel(dev, lp); 2530fb35bd37SJoerg Wunsch free(lp, M_TEMP); 2531b39c878eSAndrey A. Chernov break; 2532f664aeeeSJoerg Wunsch 2533b39c878eSAndrey A. Chernov case FD_FORM: 2534b39c878eSAndrey A. Chernov if ((flag & FWRITE) == 0) 2535fb35bd37SJoerg Wunsch return (EBADF); /* must be opened for writing */ 2536fb35bd37SJoerg Wunsch if (((struct fd_formb *)addr)->format_version != 2537b39c878eSAndrey A. Chernov FD_FORMAT_VERSION) 2538fb35bd37SJoerg Wunsch return (EINVAL); /* wrong version of formatting prog */ 2539f664aeeeSJoerg Wunsch error = fdmisccmd(dev, BIO_FORMAT, addr); 2540b39c878eSAndrey A. Chernov break; 2541b39c878eSAndrey A. Chernov 2542b39c878eSAndrey A. Chernov case FD_GTYPE: /* get drive type */ 25433e425b96SJulian Elischer *(struct fd_type *)addr = *fd->ft; 2544f5f7ba03SJordan K. Hubbard break; 2545f5f7ba03SJordan K. Hubbard 25463a2f7427SDavid Greenman case FD_STYPE: /* set drive type */ 25473a2f7427SDavid Greenman /* this is considered harmful; only allow for superuser */ 2548b40ce416SJulian Elischer if (suser_td(td) != 0) 2549fb35bd37SJoerg Wunsch return (EPERM); 25503e425b96SJulian Elischer *fd->ft = *(struct fd_type *)addr; 25513a2f7427SDavid Greenman break; 25523a2f7427SDavid Greenman 25533a2f7427SDavid Greenman case FD_GOPTS: /* get drive options */ 25543e425b96SJulian Elischer *(int *)addr = fd->options; 25553a2f7427SDavid Greenman break; 25563a2f7427SDavid Greenman 25573a2f7427SDavid Greenman case FD_SOPTS: /* set drive options */ 25583e425b96SJulian Elischer fd->options = *(int *)addr; 25593a2f7427SDavid Greenman break; 25603a2f7427SDavid Greenman 2561f664aeeeSJoerg Wunsch #ifdef FDC_DEBUG 2562f664aeeeSJoerg Wunsch case FD_DEBUG: 25630e17a5bcSJoerg Wunsch if ((fd_debug != 0) != (*(int *)addr != 0)) { 25640e17a5bcSJoerg Wunsch fd_debug = (*(int *)addr != 0); 25650e17a5bcSJoerg Wunsch printf("fd%d: debugging turned %s\n", 25660e17a5bcSJoerg Wunsch fd->fdu, fd_debug ? "on" : "off"); 25670e17a5bcSJoerg Wunsch } 2568f664aeeeSJoerg Wunsch break; 2569f664aeeeSJoerg Wunsch #endif 2570f664aeeeSJoerg Wunsch 25712995d110SJoerg Wunsch case FD_CLRERR: 2572b40ce416SJulian Elischer if (suser_td(td) != 0) 2573fb35bd37SJoerg Wunsch return (EPERM); 25742995d110SJoerg Wunsch fd->fdc->fdc_errs = 0; 25752995d110SJoerg Wunsch break; 25762995d110SJoerg Wunsch 25772995d110SJoerg Wunsch case FD_GSTAT: 25782995d110SJoerg Wunsch fsp = (struct fdc_status *)addr; 25792995d110SJoerg Wunsch if ((fd->fdc->flags & FDC_STAT_VALID) == 0) 2580fb35bd37SJoerg Wunsch return (EINVAL); 25812995d110SJoerg Wunsch memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); 25822995d110SJoerg Wunsch break; 25832995d110SJoerg Wunsch 2584250300ebSJoerg Wunsch case FD_READID: 2585250300ebSJoerg Wunsch rid = (struct fdc_readid *)addr; 2586250300ebSJoerg Wunsch if (rid->cyl > MAX_CYLINDER || rid->head > MAX_HEAD) 2587fb35bd37SJoerg Wunsch return (EINVAL); 2588f664aeeeSJoerg Wunsch error = fdmisccmd(dev, BIO_RDSECTID, addr); 2589250300ebSJoerg Wunsch break; 2590250300ebSJoerg Wunsch 2591f5f7ba03SJordan K. Hubbard default: 25923a2f7427SDavid Greenman error = ENOTTY; 2593f5f7ba03SJordan K. Hubbard break; 2594f5f7ba03SJordan K. Hubbard } 2595f5f7ba03SJordan K. Hubbard return (error); 2596f5f7ba03SJordan K. Hubbard } 2597