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; 148246ed35dSJoerg Wunsch int fdout; /* mirror of the w/o digital output reg */ 149246ed35dSJoerg Wunsch u_int status[7]; /* copy of the registers */ 150246ed35dSJoerg Wunsch enum fdc_type fdct; /* chip version of FDC */ 151246ed35dSJoerg Wunsch int fdc_errs; /* number of logged errors */ 152246ed35dSJoerg Wunsch int dma_overruns; /* number of DMA overruns */ 153246ed35dSJoerg Wunsch struct bio_queue_head head; 154246ed35dSJoerg Wunsch struct bio *bp; /* active buffer */ 155246ed35dSJoerg Wunsch struct resource *res_ioport, *res_ctl, *res_irq, *res_drq; 156246ed35dSJoerg Wunsch int rid_ioport, rid_ctl, rid_irq, rid_drq; 157246ed35dSJoerg Wunsch int port_off; 158246ed35dSJoerg Wunsch bus_space_tag_t portt; 159246ed35dSJoerg Wunsch bus_space_handle_t porth; 160246ed35dSJoerg Wunsch bus_space_tag_t ctlt; 161246ed35dSJoerg Wunsch bus_space_handle_t ctlh; 162246ed35dSJoerg Wunsch void *fdc_intr; 163246ed35dSJoerg Wunsch struct device *fdc_dev; 164246ed35dSJoerg Wunsch void (*fdctl_wr)(struct fdc_data *fdc, u_int8_t v); 165246ed35dSJoerg Wunsch }; 166246ed35dSJoerg Wunsch 167246ed35dSJoerg Wunsch typedef int fdu_t; 168246ed35dSJoerg Wunsch typedef int fdcu_t; 169246ed35dSJoerg Wunsch typedef int fdsu_t; 170246ed35dSJoerg Wunsch typedef struct fd_data *fd_p; 171246ed35dSJoerg Wunsch typedef struct fdc_data *fdc_p; 172246ed35dSJoerg Wunsch typedef enum fdc_type fdc_t; 173246ed35dSJoerg Wunsch 174246ed35dSJoerg Wunsch #define FDUNIT(s) (((s) >> 6) & 3) 175246ed35dSJoerg Wunsch #define FDTYPE(s) ((s) & 0x3f) 176246ed35dSJoerg Wunsch 177246ed35dSJoerg Wunsch /* 178246ed35dSJoerg Wunsch * fdc maintains a set (1!) of ivars per child of each controller. 179246ed35dSJoerg Wunsch */ 180246ed35dSJoerg Wunsch enum fdc_device_ivars { 181246ed35dSJoerg Wunsch FDC_IVAR_FDUNIT, 182246ed35dSJoerg Wunsch }; 183246ed35dSJoerg Wunsch 184246ed35dSJoerg Wunsch /* 185246ed35dSJoerg Wunsch * Simple access macros for the ivars. 186246ed35dSJoerg Wunsch */ 187246ed35dSJoerg Wunsch #define FDC_ACCESSOR(A, B, T) \ 188246ed35dSJoerg Wunsch static __inline T fdc_get_ ## A(device_t dev) \ 189246ed35dSJoerg Wunsch { \ 190246ed35dSJoerg Wunsch uintptr_t v; \ 191246ed35dSJoerg Wunsch BUS_READ_IVAR(device_get_parent(dev), dev, FDC_IVAR_ ## B, &v); \ 192246ed35dSJoerg Wunsch return (T) v; \ 193246ed35dSJoerg Wunsch } 194246ed35dSJoerg Wunsch FDC_ACCESSOR(fdunit, FDUNIT, int) 195246ed35dSJoerg Wunsch 1960722d6abSJoerg Wunsch /* configuration flags */ 1970722d6abSJoerg Wunsch #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ 198e34c71eaSJoerg Wunsch #define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */ 1990722d6abSJoerg Wunsch 2000722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */ 2010722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED 0x1000 2020722d6abSJoerg Wunsch 203dc5df763SJoerg Wunsch /* error returns for fd_cmd() */ 204dc5df763SJoerg Wunsch #define FD_FAILED -1 205dc5df763SJoerg Wunsch #define FD_NOT_VALID -2 206dc5df763SJoerg Wunsch #define FDC_ERRMAX 100 /* do not log more */ 2073fef646eSJoerg Wunsch /* 2083fef646eSJoerg Wunsch * Stop retrying after this many DMA overruns. Since each retry takes 20983edbfa5SJoerg Wunsch * one revolution, with 300 rpm., 25 retries take approximately 5 2103fef646eSJoerg Wunsch * seconds which the read attempt will block in case the DMA overrun 2113fef646eSJoerg Wunsch * is persistent. 2123fef646eSJoerg Wunsch */ 2133fef646eSJoerg Wunsch #define FDC_DMAOV_MAX 25 214dc5df763SJoerg Wunsch 2156fb89845SKATO Takenori #define NUMTYPES 17 2166fb89845SKATO Takenori #define NUMDENS (NUMTYPES - 7) 2177ca0641bSAndrey A. Chernov 218246ed35dSJoerg Wunsch #define NO_TYPE 0 219b99f0a4aSAndrew Moore #define FD_1720 1 220b99f0a4aSAndrew Moore #define FD_1480 2 221b99f0a4aSAndrew Moore #define FD_1440 3 222b99f0a4aSAndrew Moore #define FD_1200 4 223b99f0a4aSAndrew Moore #define FD_820 5 224b99f0a4aSAndrew Moore #define FD_800 6 225b99f0a4aSAndrew Moore #define FD_720 7 226b99f0a4aSAndrew Moore #define FD_360 8 2276fb89845SKATO Takenori #define FD_640 9 2286fb89845SKATO Takenori #define FD_1232 10 229ed2fa05eSAndrey A. Chernov 2306fb89845SKATO Takenori #define FD_1480in5_25 11 2316fb89845SKATO Takenori #define FD_1440in5_25 12 2326fb89845SKATO Takenori #define FD_820in5_25 13 2336fb89845SKATO Takenori #define FD_800in5_25 14 2346fb89845SKATO Takenori #define FD_720in5_25 15 2356fb89845SKATO Takenori #define FD_360in5_25 16 2366fb89845SKATO Takenori #define FD_640in5_25 17 237b99f0a4aSAndrew Moore 238f664aeeeSJoerg Wunsch #define BIO_RDSECTID BIO_CMD1 2397ca0641bSAndrey A. Chernov 2406f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] = 2415b81b6b3SRodney W. Grimes { 242126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ 243126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ 244126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ 245126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ 246126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ 247126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ 248126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ 249b0568305SAndrey A. Chernov { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ 2506fb89845SKATO Takenori { 8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /* 640K in DD 5.25in */ 2516fb89845SKATO Takenori { 8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */ 252ed2fa05eSAndrey A. Chernov 253126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ 254126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ 255126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ 256126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ 257126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ 258126518a1SAndrey A. Chernov { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ 2596fb89845SKATO Takenori { 8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /* 640K in HD 5.25in */ 2605b81b6b3SRodney W. Grimes }; 2615b81b6b3SRodney W. Grimes 262b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2 /* 2 floppies */ 263dc16046fSJoerg Wunsch 264f8ce7dd5SJoerg Wunsch #define MAX_SEC_SIZE (128 << 3) 26564860614SJoerg Wunsch #define MAX_CYLINDER 85 /* some people really stress their drives 26664860614SJoerg Wunsch * up to cyl 82 */ 267250300ebSJoerg Wunsch #define MAX_HEAD 1 268f8ce7dd5SJoerg Wunsch 2696182fdbdSPeter Wemm static devclass_t fdc_devclass; 2705b81b6b3SRodney W. Grimes 271246ed35dSJoerg Wunsch /* 272246ed35dSJoerg Wunsch * Per drive structure (softc). 273246ed35dSJoerg Wunsch */ 2746182fdbdSPeter Wemm struct fd_data { 275b99f0a4aSAndrew Moore struct fdc_data *fdc; /* pointer to controller structure */ 2765b81b6b3SRodney W. Grimes int fdsu; /* this units number on this controller */ 2773a2f7427SDavid Greenman int type; /* Drive type (FD_1440...) */ 2785b81b6b3SRodney W. Grimes struct fd_type *ft; /* pointer to the type descriptor */ 2795b81b6b3SRodney W. Grimes int flags; 2805b81b6b3SRodney W. Grimes #define FD_OPEN 0x01 /* it's open */ 2815b81b6b3SRodney W. Grimes #define FD_ACTIVE 0x02 /* it's active */ 2825b81b6b3SRodney W. Grimes #define FD_MOTOR 0x04 /* motor should be on */ 2835b81b6b3SRodney W. Grimes #define FD_MOTOR_WAIT 0x08 /* motor coming up */ 2845b81b6b3SRodney W. Grimes int skip; 2855b81b6b3SRodney W. Grimes int hddrv; 286dc5df763SJoerg Wunsch #define FD_NO_TRACK -2 2875b81b6b3SRodney W. Grimes int track; /* where we think the head is */ 2883a2f7427SDavid Greenman int options; /* user configurable options, see ioctl_fd.h */ 28902a19910SJustin T. Gibbs struct callout_handle toffhandle; 29002a19910SJustin T. Gibbs struct callout_handle tohandle; 291b2dfb1f9SJustin T. Gibbs struct devstat device_stats; 292e219897aSJoerg Wunsch eventhandler_tag clonetag; 293e219897aSJoerg Wunsch dev_t masterdev; 29460444853SJoerg Wunsch #define NCLONEDEVS 10 /* must match the table below */ 29560444853SJoerg Wunsch dev_t clonedevs[NCLONEDEVS]; 2966182fdbdSPeter Wemm device_t dev; 2976182fdbdSPeter Wemm fdu_t fdu; 2986182fdbdSPeter Wemm }; 29937286586SPeter Wemm 30037286586SPeter Wemm struct fdc_ivars { 30137286586SPeter Wemm int fdunit; 30237286586SPeter Wemm }; 3036182fdbdSPeter Wemm static devclass_t fd_devclass; 3045b81b6b3SRodney W. Grimes 305246ed35dSJoerg Wunsch /* 306246ed35dSJoerg Wunsch * Throughout this file the following conventions will be used: 307246ed35dSJoerg Wunsch * 308246ed35dSJoerg Wunsch * fd is a pointer to the fd_data struct for the drive in question 309246ed35dSJoerg Wunsch * fdc is a pointer to the fdc_data struct for the controller 310246ed35dSJoerg Wunsch * fdu is the floppy drive unit number 311246ed35dSJoerg Wunsch * fdcu is the floppy controller unit number 312246ed35dSJoerg Wunsch * fdsu is the floppy drive unit number on that controller. (sub-unit) 313246ed35dSJoerg Wunsch */ 314b99f0a4aSAndrew Moore 315246ed35dSJoerg Wunsch /* 316246ed35dSJoerg Wunsch * Function declarations, same (chaotic) order as they appear in the 317246ed35dSJoerg Wunsch * file. Re-ordering is too late now, it would only obfuscate the 318246ed35dSJoerg Wunsch * diffs against old and offspring versions (like the PC98 one). 319246ed35dSJoerg Wunsch * 320246ed35dSJoerg Wunsch * Anyone adding functions here, please keep this sequence the same 321246ed35dSJoerg Wunsch * as below -- makes locating a particular function in the body much 322246ed35dSJoerg Wunsch * easier. 323246ed35dSJoerg Wunsch */ 324246ed35dSJoerg Wunsch static void fdout_wr(fdc_p, u_int8_t); 325246ed35dSJoerg Wunsch static u_int8_t fdsts_rd(fdc_p); 326246ed35dSJoerg Wunsch static void fddata_wr(fdc_p, u_int8_t); 327246ed35dSJoerg Wunsch static u_int8_t fddata_rd(fdc_p); 328246ed35dSJoerg Wunsch static void fdctl_wr_isa(fdc_p, u_int8_t); 329246ed35dSJoerg Wunsch #if NCARD > 0 330246ed35dSJoerg Wunsch static void fdctl_wr_pcmcia(fdc_p, u_int8_t); 331246ed35dSJoerg Wunsch #endif 332246ed35dSJoerg Wunsch #if 0 333246ed35dSJoerg Wunsch static u_int8_t fdin_rd(fdc_p); 334246ed35dSJoerg Wunsch #endif 335246ed35dSJoerg Wunsch static int fdc_err(struct fdc_data *, const char *); 336246ed35dSJoerg Wunsch static int fd_cmd(struct fdc_data *, int, ...); 337246ed35dSJoerg Wunsch static int enable_fifo(fdc_p fdc); 338246ed35dSJoerg Wunsch static int fd_sense_drive_status(fdc_p, int *); 339246ed35dSJoerg Wunsch static int fd_sense_int(fdc_p, int *, int *); 340246ed35dSJoerg Wunsch static int fd_read_status(fdc_p); 341246ed35dSJoerg Wunsch static int fdc_alloc_resources(struct fdc_data *); 342246ed35dSJoerg Wunsch static void fdc_release_resources(struct fdc_data *); 343246ed35dSJoerg Wunsch static int fdc_read_ivar(device_t, device_t, int, uintptr_t *); 344246ed35dSJoerg Wunsch static int fdc_probe(device_t); 345246ed35dSJoerg Wunsch #if NCARD > 0 346246ed35dSJoerg Wunsch static int fdc_pccard_probe(device_t); 347246ed35dSJoerg Wunsch #endif 348246ed35dSJoerg Wunsch static int fdc_detach(device_t dev); 349246ed35dSJoerg Wunsch static void fdc_add_child(device_t, const char *, int); 350246ed35dSJoerg Wunsch static int fdc_attach(device_t); 351246ed35dSJoerg Wunsch static int fdc_print_child(device_t, device_t); 352246ed35dSJoerg Wunsch static void fd_clone (void *, char *, int, dev_t *); 353246ed35dSJoerg Wunsch static int fd_probe(device_t); 354246ed35dSJoerg Wunsch static int fd_attach(device_t); 355246ed35dSJoerg Wunsch static int fd_detach(device_t); 3566182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int); 3573a2f7427SDavid Greenman # define TURNON 1 3583a2f7427SDavid Greenman # define TURNOFF 0 3593a2f7427SDavid Greenman static timeout_t fd_turnoff; 3603a2f7427SDavid Greenman static timeout_t fd_motor_on; 3616182fdbdSPeter Wemm static void fd_turnon(struct fd_data *); 3623a2f7427SDavid Greenman static void fdc_reset(fdc_p); 3636182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *); 36480909a7dSJoerg Wunsch static int out_fdc(struct fdc_data *, int); 365246ed35dSJoerg Wunsch /* 366246ed35dSJoerg Wunsch * The open function is named Fdopen() to avoid confusion with fdopen() 367246ed35dSJoerg Wunsch * in fd(4). The difference is now only meaningful for debuggers. 368246ed35dSJoerg Wunsch */ 369246ed35dSJoerg Wunsch static d_open_t Fdopen; 370246ed35dSJoerg Wunsch static d_close_t fdclose; 371246ed35dSJoerg Wunsch static d_strategy_t fdstrategy; 3726182fdbdSPeter Wemm static void fdstart(struct fdc_data *); 3735c1a1eaeSBruce Evans static timeout_t fd_iotimeout; 3743a2f7427SDavid Greenman static timeout_t fd_pseudointr; 375246ed35dSJoerg Wunsch static driver_intr_t fdc_intr; 376246ed35dSJoerg Wunsch static int fdcpio(fdc_p, long, caddr_t, u_int); 3776182fdbdSPeter Wemm static int fdstate(struct fdc_data *); 3786182fdbdSPeter Wemm static int retrier(struct fdc_data *); 379246ed35dSJoerg Wunsch static void fdbiodone(struct bio *); 380f664aeeeSJoerg Wunsch static int fdmisccmd(dev_t, u_int, void *); 381246ed35dSJoerg Wunsch static d_ioctl_t fdioctl; 382d66c374fSTor Egge 383d66c374fSTor Egge static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ 384d66c374fSTor Egge 385d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 3863a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */ 387cba2a7c6SBruce Evans static int volatile fd_debug = 0; 3880e17a5bcSJoerg Wunsch #define TRACE0(arg) do { if (fd_debug) printf(arg); } while (0) 3890e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { if (fd_debug) printf(arg1, arg2); } while (0) 390d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */ 3910e17a5bcSJoerg Wunsch #define TRACE0(arg) do { } while (0) 3920e17a5bcSJoerg Wunsch #define TRACE1(arg1, arg2) do { } while (0) 393d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 3945b81b6b3SRodney W. Grimes 395246ed35dSJoerg Wunsch /* 396246ed35dSJoerg Wunsch * Bus space handling (access to low-level IO). 397246ed35dSJoerg Wunsch */ 398427ccf00SDoug Rabson static void 399427ccf00SDoug Rabson fdout_wr(fdc_p fdc, u_int8_t v) 400427ccf00SDoug Rabson { 401427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); 402427ccf00SDoug Rabson } 403427ccf00SDoug Rabson 404427ccf00SDoug Rabson static u_int8_t 405427ccf00SDoug Rabson fdsts_rd(fdc_p fdc) 406427ccf00SDoug Rabson { 407427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); 408427ccf00SDoug Rabson } 409427ccf00SDoug Rabson 410427ccf00SDoug Rabson static void 411427ccf00SDoug Rabson fddata_wr(fdc_p fdc, u_int8_t v) 412427ccf00SDoug Rabson { 413427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); 414427ccf00SDoug Rabson } 415427ccf00SDoug Rabson 416427ccf00SDoug Rabson static u_int8_t 417427ccf00SDoug Rabson fddata_rd(fdc_p fdc) 418427ccf00SDoug Rabson { 419427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); 420427ccf00SDoug Rabson } 421427ccf00SDoug Rabson 422427ccf00SDoug Rabson static void 4235f830ea2SJoerg Wunsch fdctl_wr_isa(fdc_p fdc, u_int8_t v) 424427ccf00SDoug Rabson { 425427ccf00SDoug Rabson bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); 426427ccf00SDoug Rabson } 427427ccf00SDoug Rabson 42858c9d623SPeter Wemm #if NCARD > 0 4295f830ea2SJoerg Wunsch static void 4305f830ea2SJoerg Wunsch fdctl_wr_pcmcia(fdc_p fdc, u_int8_t v) 4315f830ea2SJoerg Wunsch { 4325f830ea2SJoerg Wunsch bus_space_write_1(fdc->portt, fdc->porth, FDCTL+fdc->port_off, v); 4335f830ea2SJoerg Wunsch } 43458c9d623SPeter Wemm #endif 4355f830ea2SJoerg Wunsch 436427ccf00SDoug Rabson #if 0 437427ccf00SDoug Rabson 438427ccf00SDoug Rabson static u_int8_t 439427ccf00SDoug Rabson fdin_rd(fdc_p fdc) 440427ccf00SDoug Rabson { 441427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDIN); 442427ccf00SDoug Rabson } 443427ccf00SDoug Rabson 444427ccf00SDoug Rabson #endif 445427ccf00SDoug Rabson 44687f6c662SJulian Elischer #define CDEV_MAJOR 9 4474e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = { 4484e2f199eSPoul-Henning Kamp /* open */ Fdopen, 4494e2f199eSPoul-Henning Kamp /* close */ fdclose, 4504e2f199eSPoul-Henning Kamp /* read */ physread, 4514e2f199eSPoul-Henning Kamp /* write */ physwrite, 4524e2f199eSPoul-Henning Kamp /* ioctl */ fdioctl, 4534e2f199eSPoul-Henning Kamp /* poll */ nopoll, 4544e2f199eSPoul-Henning Kamp /* mmap */ nommap, 4554e2f199eSPoul-Henning Kamp /* strategy */ fdstrategy, 4564e2f199eSPoul-Henning Kamp /* name */ "fd", 4574e2f199eSPoul-Henning Kamp /* maj */ CDEV_MAJOR, 4584e2f199eSPoul-Henning Kamp /* dump */ nodump, 4594e2f199eSPoul-Henning Kamp /* psize */ nopsize, 4604e2f199eSPoul-Henning Kamp /* flags */ D_DISK, 4614e2f199eSPoul-Henning Kamp }; 4624e2f199eSPoul-Henning Kamp 463246ed35dSJoerg Wunsch /* 464246ed35dSJoerg Wunsch * Auxiliary functions. Well, some only. Others are scattered 465246ed35dSJoerg Wunsch * throughout the entire file. 466246ed35dSJoerg Wunsch */ 467dc5df763SJoerg Wunsch static int 4686182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s) 469dc5df763SJoerg Wunsch { 4706182fdbdSPeter Wemm fdc->fdc_errs++; 47116b04b6aSJoerg Wunsch if (s) { 472b6e5f28eSPeter Wemm if (fdc->fdc_errs < FDC_ERRMAX) 473b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, "%s", s); 474b6e5f28eSPeter Wemm else if (fdc->fdc_errs == FDC_ERRMAX) 475b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, "too many errors, not " 476b6e5f28eSPeter Wemm "logging any more\n"); 47716b04b6aSJoerg Wunsch } 478dc5df763SJoerg Wunsch 479dc5df763SJoerg Wunsch return FD_FAILED; 480dc5df763SJoerg Wunsch } 481dc5df763SJoerg Wunsch 482dc5df763SJoerg Wunsch /* 483dc5df763SJoerg Wunsch * fd_cmd: Send a command to the chip. Takes a varargs with this structure: 484dc5df763SJoerg Wunsch * Unit number, 485dc5df763SJoerg Wunsch * # of output bytes, output bytes as ints ..., 486dc5df763SJoerg Wunsch * # of input bytes, input bytes as ints ... 487dc5df763SJoerg Wunsch */ 4886f4e0bebSPoul-Henning Kamp static int 4896182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...) 490dc5df763SJoerg Wunsch { 491dc5df763SJoerg Wunsch u_char cmd; 492dc5df763SJoerg Wunsch int n_in; 493dc5df763SJoerg Wunsch int n; 494dc5df763SJoerg Wunsch va_list ap; 495dc5df763SJoerg Wunsch 496dc5df763SJoerg Wunsch va_start(ap, n_out); 497dc5df763SJoerg Wunsch cmd = (u_char)(va_arg(ap, int)); 498dc5df763SJoerg Wunsch va_end(ap); 499dc5df763SJoerg Wunsch va_start(ap, n_out); 500dc5df763SJoerg Wunsch for (n = 0; n < n_out; n++) 501dc5df763SJoerg Wunsch { 5026182fdbdSPeter Wemm if (out_fdc(fdc, va_arg(ap, int)) < 0) 503dc5df763SJoerg Wunsch { 504dc5df763SJoerg Wunsch char msg[50]; 5052127f260SArchie Cobbs snprintf(msg, sizeof(msg), 506dc5df763SJoerg Wunsch "cmd %x failed at out byte %d of %d\n", 507dc5df763SJoerg Wunsch cmd, n + 1, n_out); 5086182fdbdSPeter Wemm return fdc_err(fdc, msg); 509dc5df763SJoerg Wunsch } 510dc5df763SJoerg Wunsch } 511dc5df763SJoerg Wunsch n_in = va_arg(ap, int); 512dc5df763SJoerg Wunsch for (n = 0; n < n_in; n++) 513dc5df763SJoerg Wunsch { 514dc5df763SJoerg Wunsch int *ptr = va_arg(ap, int *); 5156182fdbdSPeter Wemm if (fd_in(fdc, ptr) < 0) 516dc5df763SJoerg Wunsch { 517dc5df763SJoerg Wunsch char msg[50]; 5182127f260SArchie Cobbs snprintf(msg, sizeof(msg), 519dc5df763SJoerg Wunsch "cmd %02x failed at in byte %d of %d\n", 520dc5df763SJoerg Wunsch cmd, n + 1, n_in); 5216182fdbdSPeter Wemm return fdc_err(fdc, msg); 522dc5df763SJoerg Wunsch } 523dc5df763SJoerg Wunsch } 524dc5df763SJoerg Wunsch 525dc5df763SJoerg Wunsch return 0; 526dc5df763SJoerg Wunsch } 527dc5df763SJoerg Wunsch 5286f4e0bebSPoul-Henning Kamp static int 529d66c374fSTor Egge enable_fifo(fdc_p fdc) 530d66c374fSTor Egge { 531d66c374fSTor Egge int i, j; 532d66c374fSTor Egge 533d66c374fSTor Egge if ((fdc->flags & FDC_HAS_FIFO) == 0) { 534d66c374fSTor Egge 535d66c374fSTor Egge /* 536d66c374fSTor Egge * Cannot use fd_cmd the normal way here, since 537d66c374fSTor Egge * this might be an invalid command. Thus we send the 538d66c374fSTor Egge * first byte, and check for an early turn of data directon. 539d66c374fSTor Egge */ 540d66c374fSTor Egge 5416182fdbdSPeter Wemm if (out_fdc(fdc, I8207X_CONFIGURE) < 0) 5426182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 543d66c374fSTor Egge 544d66c374fSTor Egge /* If command is invalid, return */ 545d66c374fSTor Egge j = 100000; 546427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) 547d66c374fSTor Egge != NE7_RQM && j-- > 0) 548d66c374fSTor Egge if (i == (NE7_DIO | NE7_RQM)) { 549d66c374fSTor Egge fdc_reset(fdc); 550d66c374fSTor Egge return FD_FAILED; 551d66c374fSTor Egge } 552d66c374fSTor Egge if (j<0 || 5536182fdbdSPeter Wemm fd_cmd(fdc, 3, 554d66c374fSTor Egge 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { 555d66c374fSTor Egge fdc_reset(fdc); 5566182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 557d66c374fSTor Egge } 558d66c374fSTor Egge fdc->flags |= FDC_HAS_FIFO; 559d66c374fSTor Egge return 0; 560d66c374fSTor Egge } 5616182fdbdSPeter Wemm if (fd_cmd(fdc, 4, 562d66c374fSTor Egge I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) 5636182fdbdSPeter Wemm return fdc_err(fdc, "Re-enable FIFO failed\n"); 564d66c374fSTor Egge return 0; 565d66c374fSTor Egge } 566d66c374fSTor Egge 567d66c374fSTor Egge static int 568dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p) 569dc5df763SJoerg Wunsch { 570dc5df763SJoerg Wunsch int st3; 571dc5df763SJoerg Wunsch 5726182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) 573dc5df763SJoerg Wunsch { 5746182fdbdSPeter Wemm return fdc_err(fdc, "Sense Drive Status failed\n"); 575dc5df763SJoerg Wunsch } 576dc5df763SJoerg Wunsch if (st3p) 577dc5df763SJoerg Wunsch *st3p = st3; 578dc5df763SJoerg Wunsch 579dc5df763SJoerg Wunsch return 0; 580dc5df763SJoerg Wunsch } 581dc5df763SJoerg Wunsch 5826f4e0bebSPoul-Henning Kamp static int 583dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp) 584dc5df763SJoerg Wunsch { 5856182fdbdSPeter Wemm int cyl, st0, ret; 586dc5df763SJoerg Wunsch 5876182fdbdSPeter Wemm ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); 5886182fdbdSPeter Wemm if (ret) { 5896182fdbdSPeter Wemm (void)fdc_err(fdc, 590dc5df763SJoerg Wunsch "sense intr err reading stat reg 0\n"); 591dc5df763SJoerg Wunsch return ret; 592dc5df763SJoerg Wunsch } 593dc5df763SJoerg Wunsch 594dc5df763SJoerg Wunsch if (st0p) 595dc5df763SJoerg Wunsch *st0p = st0; 596dc5df763SJoerg Wunsch 5976182fdbdSPeter Wemm if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { 598dc5df763SJoerg Wunsch /* 599dc5df763SJoerg Wunsch * There doesn't seem to have been an interrupt. 600dc5df763SJoerg Wunsch */ 601dc5df763SJoerg Wunsch return FD_NOT_VALID; 602dc5df763SJoerg Wunsch } 603dc5df763SJoerg Wunsch 6046182fdbdSPeter Wemm if (fd_in(fdc, &cyl) < 0) { 6056182fdbdSPeter Wemm return fdc_err(fdc, "can't get cyl num\n"); 606dc5df763SJoerg Wunsch } 607dc5df763SJoerg Wunsch 608dc5df763SJoerg Wunsch if (cylp) 609dc5df763SJoerg Wunsch *cylp = cyl; 610dc5df763SJoerg Wunsch 611dc5df763SJoerg Wunsch return 0; 612dc5df763SJoerg Wunsch } 613dc5df763SJoerg Wunsch 614dc5df763SJoerg Wunsch 6156f4e0bebSPoul-Henning Kamp static int 61664860614SJoerg Wunsch fd_read_status(fdc_p fdc) 617dc5df763SJoerg Wunsch { 618dc5df763SJoerg Wunsch int i, ret; 619b5e8ce9fSBruce Evans 620246ed35dSJoerg Wunsch for (i = ret = 0; i < 7; i++) { 621b5e8ce9fSBruce Evans /* 62264860614SJoerg Wunsch * XXX types are poorly chosen. Only bytes can be read 623a838d83dSBruce Evans * from the hardware, but fdc->status[] wants u_ints and 624b5e8ce9fSBruce Evans * fd_in() gives ints. 625b5e8ce9fSBruce Evans */ 626b5e8ce9fSBruce Evans int status; 627b5e8ce9fSBruce Evans 6286182fdbdSPeter Wemm ret = fd_in(fdc, &status); 629b5e8ce9fSBruce Evans fdc->status[i] = status; 630b5e8ce9fSBruce Evans if (ret != 0) 631dc5df763SJoerg Wunsch break; 632dc5df763SJoerg Wunsch } 633dc5df763SJoerg Wunsch 634dc5df763SJoerg Wunsch if (ret == 0) 635dc5df763SJoerg Wunsch fdc->flags |= FDC_STAT_VALID; 636dc5df763SJoerg Wunsch else 637dc5df763SJoerg Wunsch fdc->flags &= ~FDC_STAT_VALID; 638dc5df763SJoerg Wunsch 639dc5df763SJoerg Wunsch return ret; 640dc5df763SJoerg Wunsch } 641dc5df763SJoerg Wunsch 6423a2f7427SDavid Greenman static int 64337286586SPeter Wemm fdc_alloc_resources(struct fdc_data *fdc) 6445b81b6b3SRodney W. Grimes { 64537286586SPeter Wemm device_t dev; 646e219897aSJoerg Wunsch int ispnp, ispcmcia, nports; 6475b81b6b3SRodney W. Grimes 64837286586SPeter Wemm dev = fdc->fdc_dev; 6495f830ea2SJoerg Wunsch ispnp = (fdc->flags & FDC_ISPNP) != 0; 6505f830ea2SJoerg Wunsch ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0; 6516182fdbdSPeter Wemm fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; 6526182fdbdSPeter Wemm fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; 6536182fdbdSPeter Wemm 654b6e5f28eSPeter Wemm /* 6555f830ea2SJoerg Wunsch * On standard ISA, we don't just use an 8 port range 6565f830ea2SJoerg Wunsch * (e.g. 0x3f0-0x3f7) since that covers an IDE control 6575f830ea2SJoerg Wunsch * register at 0x3f6. 6585f830ea2SJoerg Wunsch * 659b6e5f28eSPeter Wemm * Isn't PC hardware wonderful. 6605f830ea2SJoerg Wunsch * 6615f830ea2SJoerg Wunsch * The Y-E Data PCMCIA FDC doesn't have this problem, it 6625f830ea2SJoerg Wunsch * uses the register with offset 6 for pseudo-DMA, and the 6635f830ea2SJoerg Wunsch * one with offset 7 as control register. 664b6e5f28eSPeter Wemm */ 665e219897aSJoerg Wunsch nports = ispcmcia ? 8 : (ispnp ? 1 : 6); 6666182fdbdSPeter Wemm fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, 6676182fdbdSPeter Wemm &fdc->rid_ioport, 0ul, ~0ul, 668e219897aSJoerg Wunsch nports, RF_ACTIVE); 6696182fdbdSPeter Wemm if (fdc->res_ioport == 0) { 670e219897aSJoerg Wunsch device_printf(dev, "cannot reserve I/O port range (%d ports)\n", 671e219897aSJoerg Wunsch nports); 67237286586SPeter Wemm return ENXIO; 6736182fdbdSPeter Wemm } 674427ccf00SDoug Rabson fdc->portt = rman_get_bustag(fdc->res_ioport); 675427ccf00SDoug Rabson fdc->porth = rman_get_bushandle(fdc->res_ioport); 676427ccf00SDoug Rabson 6775f830ea2SJoerg Wunsch if (!ispcmcia) { 678427ccf00SDoug Rabson /* 6795f830ea2SJoerg Wunsch * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7 6805f830ea2SJoerg Wunsch * and some at 0x3f0-0x3f5,0x3f7. We detect the former 6815f830ea2SJoerg Wunsch * by checking the size and adjust the port address 6825f830ea2SJoerg Wunsch * accordingly. 683b6e5f28eSPeter Wemm */ 684b6e5f28eSPeter Wemm if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) 685b6e5f28eSPeter Wemm fdc->port_off = -2; 686b6e5f28eSPeter Wemm 687b6e5f28eSPeter Wemm /* 6885f830ea2SJoerg Wunsch * Register the control port range as rid 1 if it 6895f830ea2SJoerg Wunsch * isn't there already. Most PnP BIOSen will have 6905f830ea2SJoerg Wunsch * already done this but non-PnP configurations don't. 691a2639a18SPeter Wemm * 6925f830ea2SJoerg Wunsch * And some (!!) report 0x3f2-0x3f5 and completely 6935f830ea2SJoerg Wunsch * leave out the control register! It seems that some 6945f830ea2SJoerg Wunsch * non-antique controller chips have a different 6955f830ea2SJoerg Wunsch * method of programming the transfer speed which 6965f830ea2SJoerg Wunsch * doesn't require the control register, but it's 6975f830ea2SJoerg Wunsch * mighty bogus as the chip still responds to the 6985f830ea2SJoerg Wunsch * address for the control register. 699427ccf00SDoug Rabson */ 700b6e5f28eSPeter Wemm if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { 701b9da888fSPeter Wemm u_long ctlstart; 702b9da888fSPeter Wemm 703b6e5f28eSPeter Wemm /* Find the control port, usually 0x3f7 */ 7045f830ea2SJoerg Wunsch ctlstart = rman_get_start(fdc->res_ioport) + 7055f830ea2SJoerg Wunsch fdc->port_off + 7; 706b6e5f28eSPeter Wemm 707b6e5f28eSPeter Wemm bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1); 708b9da888fSPeter Wemm } 709b6e5f28eSPeter Wemm 710b6e5f28eSPeter Wemm /* 711b6e5f28eSPeter Wemm * Now (finally!) allocate the control port. 712b6e5f28eSPeter Wemm */ 713427ccf00SDoug Rabson fdc->rid_ctl = 1; 7145f830ea2SJoerg Wunsch fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, 7155f830ea2SJoerg Wunsch &fdc->rid_ctl, 716b6e5f28eSPeter Wemm 0ul, ~0ul, 1, RF_ACTIVE); 717427ccf00SDoug Rabson if (fdc->res_ctl == 0) { 7185f830ea2SJoerg Wunsch device_printf(dev, 719e219897aSJoerg Wunsch "cannot reserve control I/O port range (control port)\n"); 72037286586SPeter Wemm return ENXIO; 721427ccf00SDoug Rabson } 722427ccf00SDoug Rabson fdc->ctlt = rman_get_bustag(fdc->res_ctl); 723427ccf00SDoug Rabson fdc->ctlh = rman_get_bushandle(fdc->res_ctl); 7245f830ea2SJoerg Wunsch } 7256182fdbdSPeter Wemm 7266182fdbdSPeter Wemm fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, 7276182fdbdSPeter Wemm &fdc->rid_irq, 0ul, ~0ul, 1, 7286182fdbdSPeter Wemm RF_ACTIVE); 7296182fdbdSPeter Wemm if (fdc->res_irq == 0) { 730427ccf00SDoug Rabson device_printf(dev, "cannot reserve interrupt line\n"); 73137286586SPeter Wemm return ENXIO; 7326182fdbdSPeter Wemm } 7335f830ea2SJoerg Wunsch 7345f830ea2SJoerg Wunsch if ((fdc->flags & FDC_NODMA) == 0) { 7356182fdbdSPeter Wemm fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, 7366182fdbdSPeter Wemm &fdc->rid_drq, 0ul, ~0ul, 1, 7376182fdbdSPeter Wemm RF_ACTIVE); 7386182fdbdSPeter Wemm if (fdc->res_drq == 0) { 739427ccf00SDoug Rabson device_printf(dev, "cannot reserve DMA request line\n"); 74037286586SPeter Wemm return ENXIO; 7416182fdbdSPeter Wemm } 7426182fdbdSPeter Wemm fdc->dmachan = fdc->res_drq->r_start; 7435f830ea2SJoerg Wunsch } 74437286586SPeter Wemm 74537286586SPeter Wemm return 0; 74637286586SPeter Wemm } 74737286586SPeter Wemm 74837286586SPeter Wemm static void 74937286586SPeter Wemm fdc_release_resources(struct fdc_data *fdc) 75037286586SPeter Wemm { 75137286586SPeter Wemm device_t dev; 75237286586SPeter Wemm 75337286586SPeter Wemm dev = fdc->fdc_dev; 75437286586SPeter Wemm if (fdc->res_irq != 0) { 75537286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 75637286586SPeter Wemm fdc->res_irq); 75737286586SPeter Wemm bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 75837286586SPeter Wemm fdc->res_irq); 75937286586SPeter Wemm } 76037286586SPeter Wemm if (fdc->res_ctl != 0) { 76137286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, 76237286586SPeter Wemm fdc->res_ctl); 76337286586SPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, 76437286586SPeter Wemm fdc->res_ctl); 76537286586SPeter Wemm } 76637286586SPeter Wemm if (fdc->res_ioport != 0) { 76737286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 76837286586SPeter Wemm fdc->res_ioport); 76937286586SPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 77037286586SPeter Wemm fdc->res_ioport); 77137286586SPeter Wemm } 77237286586SPeter Wemm if (fdc->res_drq != 0) { 77337286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 77437286586SPeter Wemm fdc->res_drq); 77537286586SPeter Wemm bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 77637286586SPeter Wemm fdc->res_drq); 77737286586SPeter Wemm } 77837286586SPeter Wemm } 77937286586SPeter Wemm 780246ed35dSJoerg Wunsch /* 781246ed35dSJoerg Wunsch * Configuration/initialization stuff, per controller. 782246ed35dSJoerg Wunsch */ 78337286586SPeter Wemm 78437286586SPeter Wemm static struct isa_pnp_id fdc_ids[] = { 78537286586SPeter Wemm {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ 78637286586SPeter Wemm {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ 78737286586SPeter Wemm {0} 78837286586SPeter Wemm }; 78937286586SPeter Wemm 79037286586SPeter Wemm static int 7913aae7b16SBruce Evans fdc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 79237286586SPeter Wemm { 79337286586SPeter Wemm struct fdc_ivars *ivars = device_get_ivars(child); 79437286586SPeter Wemm 79537286586SPeter Wemm switch (which) { 79637286586SPeter Wemm case FDC_IVAR_FDUNIT: 79737286586SPeter Wemm *result = ivars->fdunit; 79837286586SPeter Wemm break; 79937286586SPeter Wemm default: 80037286586SPeter Wemm return ENOENT; 80137286586SPeter Wemm } 80237286586SPeter Wemm return 0; 80337286586SPeter Wemm } 80437286586SPeter Wemm 80537286586SPeter Wemm static int 80637286586SPeter Wemm fdc_probe(device_t dev) 80737286586SPeter Wemm { 80837286586SPeter Wemm int error, ic_type; 80937286586SPeter Wemm struct fdc_data *fdc; 81037286586SPeter Wemm 81137286586SPeter Wemm fdc = device_get_softc(dev); 81237286586SPeter Wemm bzero(fdc, sizeof *fdc); 81337286586SPeter Wemm fdc->fdc_dev = dev; 8145f830ea2SJoerg Wunsch fdc->fdctl_wr = fdctl_wr_isa; 81537286586SPeter Wemm 81637286586SPeter Wemm /* Check pnp ids */ 81737286586SPeter Wemm error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); 81837286586SPeter Wemm if (error == ENXIO) 81937286586SPeter Wemm return ENXIO; 8205f830ea2SJoerg Wunsch if (error == 0) 8215f830ea2SJoerg Wunsch fdc->flags |= FDC_ISPNP; 82237286586SPeter Wemm 82337286586SPeter Wemm /* Attempt to allocate our resources for the duration of the probe */ 82437286586SPeter Wemm error = fdc_alloc_resources(fdc); 82537286586SPeter Wemm if (error) 82637286586SPeter Wemm goto out; 8275b81b6b3SRodney W. Grimes 82816111cedSAndrew Moore /* First - lets reset the floppy controller */ 829427ccf00SDoug Rabson fdout_wr(fdc, 0); 83016111cedSAndrew Moore DELAY(100); 831427ccf00SDoug Rabson fdout_wr(fdc, FDO_FRST); 83216111cedSAndrew Moore 8335b81b6b3SRodney W. Grimes /* see if it can handle a command */ 8346182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), 8356182fdbdSPeter Wemm NE7_SPEC_2(2, 0), 0)) { 8366182fdbdSPeter Wemm error = ENXIO; 8376182fdbdSPeter Wemm goto out; 8385b81b6b3SRodney W. Grimes } 8396182fdbdSPeter Wemm 8406182fdbdSPeter Wemm if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { 8416182fdbdSPeter Wemm ic_type = (u_char)ic_type; 8426182fdbdSPeter Wemm switch (ic_type) { 8436182fdbdSPeter Wemm case 0x80: 8446182fdbdSPeter Wemm device_set_desc(dev, "NEC 765 or clone"); 8456182fdbdSPeter Wemm fdc->fdct = FDC_NE765; 8466182fdbdSPeter Wemm break; 8476182fdbdSPeter Wemm case 0x81: 8486182fdbdSPeter Wemm device_set_desc(dev, "Intel 82077 or clone"); 8496182fdbdSPeter Wemm fdc->fdct = FDC_I82077; 8506182fdbdSPeter Wemm break; 8516182fdbdSPeter Wemm case 0x90: 8526182fdbdSPeter Wemm device_set_desc(dev, "NEC 72065B or clone"); 8536182fdbdSPeter Wemm fdc->fdct = FDC_NE72065; 8546182fdbdSPeter Wemm break; 8556182fdbdSPeter Wemm default: 8566182fdbdSPeter Wemm device_set_desc(dev, "generic floppy controller"); 8576182fdbdSPeter Wemm fdc->fdct = FDC_UNKNOWN; 8586182fdbdSPeter Wemm break; 8596182fdbdSPeter Wemm } 8606182fdbdSPeter Wemm } 8616182fdbdSPeter Wemm 8626182fdbdSPeter Wemm out: 86337286586SPeter Wemm fdc_release_resources(fdc); 8646182fdbdSPeter Wemm return (error); 8655b81b6b3SRodney W. Grimes } 8665b81b6b3SRodney W. Grimes 8675f830ea2SJoerg Wunsch #if NCARD > 0 8685f830ea2SJoerg Wunsch 8695f830ea2SJoerg Wunsch static int 8705f830ea2SJoerg Wunsch fdc_pccard_probe(device_t dev) 8715f830ea2SJoerg Wunsch { 8725f830ea2SJoerg Wunsch int error; 8735f830ea2SJoerg Wunsch struct fdc_data *fdc; 8745f830ea2SJoerg Wunsch 8755f830ea2SJoerg Wunsch fdc = device_get_softc(dev); 8765f830ea2SJoerg Wunsch bzero(fdc, sizeof *fdc); 8775f830ea2SJoerg Wunsch fdc->fdc_dev = dev; 8785f830ea2SJoerg Wunsch fdc->fdctl_wr = fdctl_wr_pcmcia; 8795f830ea2SJoerg Wunsch 8805f830ea2SJoerg Wunsch fdc->flags |= FDC_ISPCMCIA | FDC_NODMA; 8815f830ea2SJoerg Wunsch 8825f830ea2SJoerg Wunsch /* Attempt to allocate our resources for the duration of the probe */ 8835f830ea2SJoerg Wunsch error = fdc_alloc_resources(fdc); 8845f830ea2SJoerg Wunsch if (error) 8855f830ea2SJoerg Wunsch goto out; 8865f830ea2SJoerg Wunsch 8875f830ea2SJoerg Wunsch /* First - lets reset the floppy controller */ 8885f830ea2SJoerg Wunsch fdout_wr(fdc, 0); 8895f830ea2SJoerg Wunsch DELAY(100); 8905f830ea2SJoerg Wunsch fdout_wr(fdc, FDO_FRST); 8915f830ea2SJoerg Wunsch 8925f830ea2SJoerg Wunsch /* see if it can handle a command */ 8935f830ea2SJoerg Wunsch if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), 8945f830ea2SJoerg Wunsch NE7_SPEC_2(2, 0), 0)) { 8955f830ea2SJoerg Wunsch error = ENXIO; 8965f830ea2SJoerg Wunsch goto out; 8975f830ea2SJoerg Wunsch } 8985f830ea2SJoerg Wunsch 8995f830ea2SJoerg Wunsch device_set_desc(dev, "Y-E Data PCMCIA floppy"); 9005f830ea2SJoerg Wunsch fdc->fdct = FDC_NE765; 9015f830ea2SJoerg Wunsch 9025f830ea2SJoerg Wunsch out: 9035f830ea2SJoerg Wunsch fdc_release_resources(fdc); 9045f830ea2SJoerg Wunsch return (error); 9055f830ea2SJoerg Wunsch } 9065f830ea2SJoerg Wunsch 907e219897aSJoerg Wunsch #endif /* NCARD > 0 */ 908e219897aSJoerg Wunsch 9095f830ea2SJoerg Wunsch static int 910e219897aSJoerg Wunsch fdc_detach(device_t dev) 9115f830ea2SJoerg Wunsch { 9125f830ea2SJoerg Wunsch struct fdc_data *fdc; 9135f830ea2SJoerg Wunsch int error; 9145f830ea2SJoerg Wunsch 9155f830ea2SJoerg Wunsch fdc = device_get_softc(dev); 9165f830ea2SJoerg Wunsch 9175f830ea2SJoerg Wunsch /* have our children detached first */ 9185f830ea2SJoerg Wunsch if ((error = bus_generic_detach(dev))) 9195f830ea2SJoerg Wunsch return (error); 9205f830ea2SJoerg Wunsch 921e219897aSJoerg Wunsch /* reset controller, turn motor off */ 922e219897aSJoerg Wunsch fdout_wr(fdc, 0); 923e219897aSJoerg Wunsch 924e219897aSJoerg Wunsch if ((fdc->flags & FDC_NODMA) == 0) 925e219897aSJoerg Wunsch isa_dma_release(fdc->dmachan); 926e219897aSJoerg Wunsch 9275f830ea2SJoerg Wunsch if ((fdc->flags & FDC_ATTACHED) == 0) { 9285f830ea2SJoerg Wunsch device_printf(dev, "already unloaded\n"); 9295f830ea2SJoerg Wunsch return (0); 9305f830ea2SJoerg Wunsch } 9315f830ea2SJoerg Wunsch fdc->flags &= ~FDC_ATTACHED; 9325f830ea2SJoerg Wunsch 9335f830ea2SJoerg Wunsch BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, 9345f830ea2SJoerg Wunsch fdc->fdc_intr); 9355f830ea2SJoerg Wunsch fdc_release_resources(fdc); 9365f830ea2SJoerg Wunsch device_printf(dev, "unload\n"); 9375f830ea2SJoerg Wunsch return (0); 9385f830ea2SJoerg Wunsch } 9395f830ea2SJoerg Wunsch 9405b81b6b3SRodney W. Grimes /* 94137286586SPeter Wemm * Add a child device to the fdc controller. It will then be probed etc. 9425b81b6b3SRodney W. Grimes */ 9436182fdbdSPeter Wemm static void 94437286586SPeter Wemm fdc_add_child(device_t dev, const char *name, int unit) 9455b81b6b3SRodney W. Grimes { 94637286586SPeter Wemm int disabled; 94737286586SPeter Wemm struct fdc_ivars *ivar; 9486182fdbdSPeter Wemm device_t child; 94992200632SGarrett Wollman 9507cc0979fSDavid Malone ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT | M_ZERO); 95137286586SPeter Wemm if (ivar == NULL) 9526182fdbdSPeter Wemm return; 95337286586SPeter Wemm if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0) 95437286586SPeter Wemm ivar->fdunit = 0; 955fe0d4089SMatthew N. Dodd child = device_add_child(dev, name, unit); 95637286586SPeter Wemm if (child == NULL) 9576182fdbdSPeter Wemm return; 95837286586SPeter Wemm device_set_ivars(child, ivar); 959a97c75b7SDoug Rabson if (resource_int_value(name, unit, "disabled", &disabled) == 0 960a97c75b7SDoug Rabson && disabled != 0) 9616182fdbdSPeter Wemm device_disable(child); 9626182fdbdSPeter Wemm } 9636182fdbdSPeter Wemm 9646182fdbdSPeter Wemm static int 9656182fdbdSPeter Wemm fdc_attach(device_t dev) 9666182fdbdSPeter Wemm { 96737286586SPeter Wemm struct fdc_data *fdc; 9682398f0cdSPeter Wemm const char *name, *dname; 9695d54fe91SJoerg Wunsch int i, error, dunit; 970427ccf00SDoug Rabson 97137286586SPeter Wemm fdc = device_get_softc(dev); 97237286586SPeter Wemm error = fdc_alloc_resources(fdc); 97337286586SPeter Wemm if (error) { 974f8ce7dd5SJoerg Wunsch device_printf(dev, "cannot re-acquire resources\n"); 97537286586SPeter Wemm return error; 97637286586SPeter Wemm } 97737286586SPeter Wemm error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, 9783c36743eSMark Murray INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc, 9793c36743eSMark Murray &fdc->fdc_intr); 98037286586SPeter Wemm if (error) { 98137286586SPeter Wemm device_printf(dev, "cannot setup interrupt\n"); 98237286586SPeter Wemm return error; 98337286586SPeter Wemm } 98437286586SPeter Wemm fdc->fdcu = device_get_unit(dev); 985fb35bd37SJoerg Wunsch fdc->flags |= FDC_ATTACHED | FDC_NEEDS_RESET; 9866182fdbdSPeter Wemm 9875f830ea2SJoerg Wunsch if ((fdc->flags & FDC_NODMA) == 0) { 988f8ce7dd5SJoerg Wunsch /* 989f8ce7dd5SJoerg Wunsch * Acquire the DMA channel forever, the driver will do 990f8ce7dd5SJoerg Wunsch * the rest 991f8ce7dd5SJoerg Wunsch * XXX should integrate with rman 992f8ce7dd5SJoerg Wunsch */ 993100f78bbSSujal Patel isa_dma_acquire(fdc->dmachan); 994f8ce7dd5SJoerg Wunsch isa_dmainit(fdc->dmachan, MAX_SEC_SIZE); 9955f830ea2SJoerg Wunsch } 9965b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 9976182fdbdSPeter Wemm 9983a2f7427SDavid Greenman /* reset controller, turn motor off, clear fdout mirror reg */ 99964860614SJoerg Wunsch fdout_wr(fdc, fdc->fdout = 0); 10008177437dSPoul-Henning Kamp bioq_init(&fdc->head); 10015b81b6b3SRodney W. Grimes 10026182fdbdSPeter Wemm /* 100337286586SPeter Wemm * Probe and attach any children. We should probably detect 100437286586SPeter Wemm * devices from the BIOS unless overridden. 10056182fdbdSPeter Wemm */ 1006ada54f9eSPeter Wemm name = device_get_nameunit(dev); 10072398f0cdSPeter Wemm i = 0; 10082398f0cdSPeter Wemm while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) 10092398f0cdSPeter Wemm fdc_add_child(dev, dname, dunit); 101037286586SPeter Wemm 101160444853SJoerg Wunsch if ((error = bus_generic_attach(dev)) != 0) 101260444853SJoerg Wunsch return (error); 101360444853SJoerg Wunsch 101460444853SJoerg Wunsch return (0); 10156182fdbdSPeter Wemm } 10166182fdbdSPeter Wemm 101715317dd8SMatthew N. Dodd static int 10186182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child) 10196182fdbdSPeter Wemm { 102015317dd8SMatthew N. Dodd int retval = 0; 102115317dd8SMatthew N. Dodd 102215317dd8SMatthew N. Dodd retval += bus_print_child_header(me, child); 102315317dd8SMatthew N. Dodd retval += printf(" on %s drive %d\n", device_get_nameunit(me), 102437286586SPeter Wemm fdc_get_fdunit(child)); 102515317dd8SMatthew N. Dodd 102615317dd8SMatthew N. Dodd return (retval); 10276182fdbdSPeter Wemm } 10286182fdbdSPeter Wemm 102916e68fc6SPeter Wemm static device_method_t fdc_methods[] = { 103016e68fc6SPeter Wemm /* Device interface */ 103116e68fc6SPeter Wemm DEVMETHOD(device_probe, fdc_probe), 103216e68fc6SPeter Wemm DEVMETHOD(device_attach, fdc_attach), 1033e219897aSJoerg Wunsch DEVMETHOD(device_detach, fdc_detach), 103416e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 103516e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), 103616e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), 103716e68fc6SPeter Wemm 103816e68fc6SPeter Wemm /* Bus interface */ 103916e68fc6SPeter Wemm DEVMETHOD(bus_print_child, fdc_print_child), 104037286586SPeter Wemm DEVMETHOD(bus_read_ivar, fdc_read_ivar), 104116e68fc6SPeter Wemm /* Our children never use any other bus interface methods. */ 104216e68fc6SPeter Wemm 104316e68fc6SPeter Wemm { 0, 0 } 104416e68fc6SPeter Wemm }; 104516e68fc6SPeter Wemm 104616e68fc6SPeter Wemm static driver_t fdc_driver = { 104716e68fc6SPeter Wemm "fdc", 104816e68fc6SPeter Wemm fdc_methods, 104916e68fc6SPeter Wemm sizeof(struct fdc_data) 105016e68fc6SPeter Wemm }; 105116e68fc6SPeter Wemm 105216e68fc6SPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); 105316e68fc6SPeter Wemm 10545f830ea2SJoerg Wunsch #if NCARD > 0 10555f830ea2SJoerg Wunsch 10565f830ea2SJoerg Wunsch static device_method_t fdc_pccard_methods[] = { 10575f830ea2SJoerg Wunsch /* Device interface */ 10585f830ea2SJoerg Wunsch DEVMETHOD(device_probe, fdc_pccard_probe), 10595f830ea2SJoerg Wunsch DEVMETHOD(device_attach, fdc_attach), 1060e219897aSJoerg Wunsch DEVMETHOD(device_detach, fdc_detach), 10615f830ea2SJoerg Wunsch DEVMETHOD(device_shutdown, bus_generic_shutdown), 10625f830ea2SJoerg Wunsch DEVMETHOD(device_suspend, bus_generic_suspend), 10635f830ea2SJoerg Wunsch DEVMETHOD(device_resume, bus_generic_resume), 10645f830ea2SJoerg Wunsch 10655f830ea2SJoerg Wunsch /* Bus interface */ 10665f830ea2SJoerg Wunsch DEVMETHOD(bus_print_child, fdc_print_child), 10675f830ea2SJoerg Wunsch DEVMETHOD(bus_read_ivar, fdc_read_ivar), 10685f830ea2SJoerg Wunsch /* Our children never use any other bus interface methods. */ 10695f830ea2SJoerg Wunsch 10705f830ea2SJoerg Wunsch { 0, 0 } 10715f830ea2SJoerg Wunsch }; 10725f830ea2SJoerg Wunsch 10735f830ea2SJoerg Wunsch static driver_t fdc_pccard_driver = { 10745f830ea2SJoerg Wunsch "fdc", 10755f830ea2SJoerg Wunsch fdc_pccard_methods, 10765f830ea2SJoerg Wunsch sizeof(struct fdc_data) 10775f830ea2SJoerg Wunsch }; 10785f830ea2SJoerg Wunsch 10795f830ea2SJoerg Wunsch DRIVER_MODULE(fdc, pccard, fdc_pccard_driver, fdc_devclass, 0, 0); 10805f830ea2SJoerg Wunsch 10815f830ea2SJoerg Wunsch #endif /* NCARD > 0 */ 10825f830ea2SJoerg Wunsch 10833f54a085SPoul-Henning Kamp static struct { 10843f54a085SPoul-Henning Kamp char *match; 10853f54a085SPoul-Henning Kamp int minor; 10863f54a085SPoul-Henning Kamp int link; 10873f54a085SPoul-Henning Kamp } fd_suffix[] = { 108860444853SJoerg Wunsch /* 108960444853SJoerg Wunsch * Genuine clone devices must come first, and their number must 109060444853SJoerg Wunsch * match NCLONEDEVS above. 109160444853SJoerg Wunsch */ 10923f54a085SPoul-Henning Kamp { ".1720", 1, 0 }, 10933f54a085SPoul-Henning Kamp { ".1480", 2, 0 }, 10943f54a085SPoul-Henning Kamp { ".1440", 3, 0 }, 10953f54a085SPoul-Henning Kamp { ".1200", 4, 0 }, 10963f54a085SPoul-Henning Kamp { ".820", 5, 0 }, 10973f54a085SPoul-Henning Kamp { ".800", 6, 0 }, 10983f54a085SPoul-Henning Kamp { ".720", 7, 0 }, 10993f54a085SPoul-Henning Kamp { ".360", 8, 0 }, 11003f54a085SPoul-Henning Kamp { ".640", 9, 0 }, 11013f54a085SPoul-Henning Kamp { ".1232", 10, 0 }, 110260444853SJoerg Wunsch { "a", 0, 1 }, 110360444853SJoerg Wunsch { "b", 0, 1 }, 110460444853SJoerg Wunsch { "c", 0, 1 }, 110560444853SJoerg Wunsch { "d", 0, 1 }, 110660444853SJoerg Wunsch { "e", 0, 1 }, 110760444853SJoerg Wunsch { "f", 0, 1 }, 110860444853SJoerg Wunsch { "g", 0, 1 }, 110960444853SJoerg Wunsch { "h", 0, 1 }, 11103f54a085SPoul-Henning Kamp { 0, 0 } 11113f54a085SPoul-Henning Kamp }; 1112246ed35dSJoerg Wunsch 11133f54a085SPoul-Henning Kamp static void 111480909a7dSJoerg Wunsch fd_clone(void *arg, char *name, int namelen, dev_t *dev) 11153f54a085SPoul-Henning Kamp { 111660444853SJoerg Wunsch struct fd_data *fd; 11173f54a085SPoul-Henning Kamp int u, d, i; 11183f54a085SPoul-Henning Kamp char *n; 11193f54a085SPoul-Henning Kamp 112060444853SJoerg Wunsch fd = (struct fd_data *)arg; 11213f54a085SPoul-Henning Kamp if (*dev != NODEV) 11223f54a085SPoul-Henning Kamp return; 1123db901281SPoul-Henning Kamp if (dev_stdclone(name, &n, "fd", &u) != 2) 11243f54a085SPoul-Henning Kamp return; 11253f54a085SPoul-Henning Kamp for (i = 0; ; i++) { 11263f54a085SPoul-Henning Kamp if (fd_suffix[i].match == NULL) 11273f54a085SPoul-Henning Kamp return; 11283f54a085SPoul-Henning Kamp if (strcmp(n, fd_suffix[i].match)) 11293f54a085SPoul-Henning Kamp continue; 11303f54a085SPoul-Henning Kamp d = fd_suffix[i].minor; 11313f54a085SPoul-Henning Kamp break; 11323f54a085SPoul-Henning Kamp } 11333f54a085SPoul-Henning Kamp if (fd_suffix[i].link == 0) { 11343f54a085SPoul-Henning Kamp *dev = make_dev(&fd_cdevsw, (u << 6) + d, 11353f54a085SPoul-Henning Kamp UID_ROOT, GID_OPERATOR, 0640, name); 113660444853SJoerg Wunsch fd->clonedevs[i] = *dev; 11373f54a085SPoul-Henning Kamp } else { 113860444853SJoerg Wunsch *dev = make_dev_alias(fd->masterdev, name); 11393f54a085SPoul-Henning Kamp } 11403f54a085SPoul-Henning Kamp } 11413f54a085SPoul-Henning Kamp 114216e68fc6SPeter Wemm /* 1143246ed35dSJoerg Wunsch * Configuration/initialization, per drive. 114416e68fc6SPeter Wemm */ 11456182fdbdSPeter Wemm static int 11466182fdbdSPeter Wemm fd_probe(device_t dev) 11476182fdbdSPeter Wemm { 11486182fdbdSPeter Wemm int i; 11496182fdbdSPeter Wemm u_int fdt, st0, st3; 11506182fdbdSPeter Wemm struct fd_data *fd; 11516182fdbdSPeter Wemm struct fdc_data *fdc; 11526182fdbdSPeter Wemm fdsu_t fdsu; 11536182fdbdSPeter Wemm static int fd_fifo = 0; 11546182fdbdSPeter Wemm 11556182fdbdSPeter Wemm fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ 11566182fdbdSPeter Wemm fd = device_get_softc(dev); 11576182fdbdSPeter Wemm fdc = device_get_softc(device_get_parent(dev)); 11586182fdbdSPeter Wemm 11596182fdbdSPeter Wemm bzero(fd, sizeof *fd); 11606182fdbdSPeter Wemm fd->dev = dev; 11616182fdbdSPeter Wemm fd->fdc = fdc; 11626182fdbdSPeter Wemm fd->fdsu = fdsu; 11636182fdbdSPeter Wemm fd->fdu = device_get_unit(dev); 11646182fdbdSPeter Wemm 1165a97c75b7SDoug Rabson #ifdef __i386__ 1166b99f0a4aSAndrew Moore /* look up what bios thinks we have */ 11676182fdbdSPeter Wemm switch (fd->fdu) { 11686182fdbdSPeter Wemm case 0: 11695f830ea2SJoerg Wunsch if ((fdc->flags & FDC_ISPCMCIA)) 11705f830ea2SJoerg Wunsch fdt = RTCFDT_144M; 11715f830ea2SJoerg Wunsch else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) 11720722d6abSJoerg Wunsch fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; 11730722d6abSJoerg Wunsch else 11740722d6abSJoerg Wunsch fdt = (rtcin(RTC_FDISKETTE) & 0xf0); 1175b99f0a4aSAndrew Moore break; 11766182fdbdSPeter Wemm case 1: 11776182fdbdSPeter Wemm fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); 1178dc5df763SJoerg Wunsch break; 1179dc5df763SJoerg Wunsch default: 11806182fdbdSPeter Wemm fdt = RTCFDT_NONE; 1181dc5df763SJoerg Wunsch break; 11826b7bd95bSJoerg Wunsch } 1183a97c75b7SDoug Rabson #else 1184a97c75b7SDoug Rabson fdt = RTCFDT_144M; /* XXX probably */ 1185a97c75b7SDoug Rabson #endif 11866182fdbdSPeter Wemm 11876182fdbdSPeter Wemm /* is there a unit? */ 11886182fdbdSPeter Wemm if (fdt == RTCFDT_NONE) 11896182fdbdSPeter Wemm return (ENXIO); 11906182fdbdSPeter Wemm 11916182fdbdSPeter Wemm /* select it */ 11926182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNON); 1193fb35bd37SJoerg Wunsch fdc_reset(fdc); /* XXX reset, then unreset, etc. */ 11946182fdbdSPeter Wemm DELAY(1000000); /* 1 sec */ 11956182fdbdSPeter Wemm 11968de0675cSPeter Wemm /* XXX This doesn't work before the first set_motor() */ 11976182fdbdSPeter Wemm if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN 1198e34c71eaSJoerg Wunsch && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 11996182fdbdSPeter Wemm && enable_fifo(fdc) == 0) { 1200b6e5f28eSPeter Wemm device_printf(device_get_parent(dev), 1201b6e5f28eSPeter Wemm "FIFO enabled, %d bytes threshold\n", fifo_threshold); 1202d66c374fSTor Egge } 12036182fdbdSPeter Wemm fd_fifo = 1; 12046182fdbdSPeter Wemm 12056182fdbdSPeter Wemm if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) 12066182fdbdSPeter Wemm && (st3 & NE7_ST3_T0)) { 1207dc5df763SJoerg Wunsch /* if at track 0, first seek inwards */ 1208dc5df763SJoerg Wunsch /* seek some steps: */ 12096182fdbdSPeter Wemm fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); 1210dc5df763SJoerg Wunsch DELAY(300000); /* ...wait a moment... */ 12116182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ 1212dc5df763SJoerg Wunsch } 1213dc5df763SJoerg Wunsch 1214dc5df763SJoerg Wunsch /* If we're at track 0 first seek inwards. */ 12156182fdbdSPeter Wemm if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { 1216dc5df763SJoerg Wunsch /* Seek some steps... */ 12176182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 1218dc5df763SJoerg Wunsch /* ...wait a moment... */ 1219dc5df763SJoerg Wunsch DELAY(300000); 1220dc5df763SJoerg Wunsch /* make ctrlr happy: */ 12216182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); 1222dc5df763SJoerg Wunsch } 1223dc5df763SJoerg Wunsch } 1224dc5df763SJoerg Wunsch 12256b7bd95bSJoerg Wunsch for (i = 0; i < 2; i++) { 12266b7bd95bSJoerg Wunsch /* 12276b7bd95bSJoerg Wunsch * we must recalibrate twice, just in case the 12286b7bd95bSJoerg Wunsch * heads have been beyond cylinder 76, since most 12296b7bd95bSJoerg Wunsch * FDCs still barf when attempting to recalibrate 12306b7bd95bSJoerg Wunsch * more than 77 steps 12316b7bd95bSJoerg Wunsch */ 1232dc5df763SJoerg Wunsch /* go back to 0: */ 12336182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 12346b7bd95bSJoerg Wunsch /* a second being enough for full stroke seek*/ 12356b7bd95bSJoerg Wunsch DELAY(i == 0 ? 1000000 : 300000); 12365b81b6b3SRodney W. Grimes 12376b7bd95bSJoerg Wunsch /* anything responding? */ 1238dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, 0) == 0 && 1239dc5df763SJoerg Wunsch (st0 & NE7_ST0_EC) == 0) 12406b7bd95bSJoerg Wunsch break; /* already probed succesfully */ 12416b7bd95bSJoerg Wunsch } 1242dc5df763SJoerg Wunsch } 12436b7bd95bSJoerg Wunsch 12446182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNOFF); 12453a2f7427SDavid Greenman 12463a2f7427SDavid Greenman if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ 12476182fdbdSPeter Wemm return (ENXIO); 12485b81b6b3SRodney W. Grimes 1249dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 1250b99f0a4aSAndrew Moore fd->fdc = fdc; 1251b99f0a4aSAndrew Moore fd->fdsu = fdsu; 12523a2f7427SDavid Greenman fd->options = 0; 125302a19910SJustin T. Gibbs callout_handle_init(&fd->toffhandle); 125402a19910SJustin T. Gibbs callout_handle_init(&fd->tohandle); 12555b81b6b3SRodney W. Grimes 1256b99f0a4aSAndrew Moore switch (fdt) { 12577ca0641bSAndrey A. Chernov case RTCFDT_12M: 12586182fdbdSPeter Wemm device_set_desc(dev, "1200-KB 5.25\" drive"); 1259b99f0a4aSAndrew Moore fd->type = FD_1200; 12607ca0641bSAndrey A. Chernov break; 12610722d6abSJoerg Wunsch case RTCFDT_144M | RTCFDT_144M_PRETENDED: 12626182fdbdSPeter Wemm device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); 12630722d6abSJoerg Wunsch fdt = RTCFDT_144M; 12646182fdbdSPeter Wemm fd->type = FD_1440; 12657ca0641bSAndrey A. Chernov case RTCFDT_144M: 12666182fdbdSPeter Wemm device_set_desc(dev, "1440-KB 3.5\" drive"); 1267b99f0a4aSAndrew Moore fd->type = FD_1440; 12687ca0641bSAndrey A. Chernov break; 1269290dd077SJoerg Wunsch case RTCFDT_288M: 127086a727d9SJoerg Wunsch case RTCFDT_288M_1: 12716182fdbdSPeter Wemm device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); 1272290dd077SJoerg Wunsch fd->type = FD_1440; 1273290dd077SJoerg Wunsch break; 12747ca0641bSAndrey A. Chernov case RTCFDT_360K: 12756182fdbdSPeter Wemm device_set_desc(dev, "360-KB 5.25\" drive"); 1276b99f0a4aSAndrew Moore fd->type = FD_360; 12777ca0641bSAndrey A. Chernov break; 1278ed2fa05eSAndrey A. Chernov case RTCFDT_720K: 12796182fdbdSPeter Wemm printf("720-KB 3.5\" drive"); 1280b99f0a4aSAndrew Moore fd->type = FD_720; 1281ed2fa05eSAndrey A. Chernov break; 12827ca0641bSAndrey A. Chernov default: 12836182fdbdSPeter Wemm return (ENXIO); 12845b81b6b3SRodney W. Grimes } 12856182fdbdSPeter Wemm return (0); 12866182fdbdSPeter Wemm } 12876182fdbdSPeter Wemm 12886182fdbdSPeter Wemm static int 12896182fdbdSPeter Wemm fd_attach(device_t dev) 12906182fdbdSPeter Wemm { 12916182fdbdSPeter Wemm struct fd_data *fd; 129264860614SJoerg Wunsch static int cdevsw_add_done; 129360444853SJoerg Wunsch int i; 12946182fdbdSPeter Wemm 129502211baeSNick Hibma if (!cdevsw_add_done) { 1296475ad603SPeter Wemm cdevsw_add(&fd_cdevsw); /* XXX */ 129764860614SJoerg Wunsch cdevsw_add_done = 1; 129802211baeSNick Hibma } 129964860614SJoerg Wunsch fd = device_get_softc(dev); 130060444853SJoerg Wunsch fd->clonetag = EVENTHANDLER_REGISTER(dev_clone, fd_clone, fd, 1000); 13015f431174SJoerg Wunsch fd->masterdev = make_dev(&fd_cdevsw, fd->fdu << 6, 13023f54a085SPoul-Henning Kamp UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu); 130360444853SJoerg Wunsch for (i = 0; i < NCLONEDEVS; i++) 130460444853SJoerg Wunsch fd->clonedevs[i] = NODEV; 13056182fdbdSPeter Wemm devstat_add_entry(&fd->device_stats, device_get_name(dev), 1306f8ce7dd5SJoerg Wunsch device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS, 13072a888f93SKenneth D. Merry DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, 13082a888f93SKenneth D. Merry DEVSTAT_PRIORITY_FD); 13096182fdbdSPeter Wemm return (0); 13105b81b6b3SRodney W. Grimes } 13115b81b6b3SRodney W. Grimes 13125f830ea2SJoerg Wunsch static int 13135f830ea2SJoerg Wunsch fd_detach(device_t dev) 13145f830ea2SJoerg Wunsch { 13155f830ea2SJoerg Wunsch struct fd_data *fd; 131660444853SJoerg Wunsch int i; 13175f830ea2SJoerg Wunsch 13185f830ea2SJoerg Wunsch fd = device_get_softc(dev); 131960444853SJoerg Wunsch untimeout(fd_turnoff, fd, fd->toffhandle); 1320e219897aSJoerg Wunsch devstat_remove_entry(&fd->device_stats); 1321e219897aSJoerg Wunsch destroy_dev(fd->masterdev); 132260444853SJoerg Wunsch for (i = 0; i < NCLONEDEVS; i++) 132360444853SJoerg Wunsch if (fd->clonedevs[i] != NODEV) 132460444853SJoerg Wunsch destroy_dev(fd->clonedevs[i]); 1325e219897aSJoerg Wunsch cdevsw_remove(&fd_cdevsw); 1326e219897aSJoerg Wunsch EVENTHANDLER_DEREGISTER(dev_clone, fd->clonetag); 13275f830ea2SJoerg Wunsch 13285f830ea2SJoerg Wunsch return (0); 13295f830ea2SJoerg Wunsch } 13305f830ea2SJoerg Wunsch 133116e68fc6SPeter Wemm static device_method_t fd_methods[] = { 133216e68fc6SPeter Wemm /* Device interface */ 133316e68fc6SPeter Wemm DEVMETHOD(device_probe, fd_probe), 133416e68fc6SPeter Wemm DEVMETHOD(device_attach, fd_attach), 13355f830ea2SJoerg Wunsch DEVMETHOD(device_detach, fd_detach), 133616e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 133716e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ 133816e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ 133916e68fc6SPeter Wemm 134016e68fc6SPeter Wemm { 0, 0 } 134116e68fc6SPeter Wemm }; 134216e68fc6SPeter Wemm 134316e68fc6SPeter Wemm static driver_t fd_driver = { 134416e68fc6SPeter Wemm "fd", 134516e68fc6SPeter Wemm fd_methods, 134616e68fc6SPeter Wemm sizeof(struct fd_data) 134716e68fc6SPeter Wemm }; 134816e68fc6SPeter Wemm 1349475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); 135016e68fc6SPeter Wemm 1351246ed35dSJoerg Wunsch /* 1352246ed35dSJoerg Wunsch * More auxiliary functions. 1353246ed35dSJoerg Wunsch */ 1354246ed35dSJoerg Wunsch /* 1355246ed35dSJoerg Wunsch * Motor control stuff. 1356246ed35dSJoerg Wunsch * Remember to not deselect the drive we're working on. 1357246ed35dSJoerg Wunsch */ 13583a2f7427SDavid Greenman static void 13596182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon) 13605b81b6b3SRodney W. Grimes { 1361fb35bd37SJoerg Wunsch int fdout; 13623a2f7427SDavid Greenman 1363fb35bd37SJoerg Wunsch fdout = fdc->fdout; 13643a2f7427SDavid Greenman if (turnon) { 13653a2f7427SDavid Greenman fdout &= ~FDO_FDSEL; 1366fb35bd37SJoerg Wunsch fdout |= (FDO_MOEN0 << fdsu) | FDO_FDMAEN | FDO_FRST | fdsu; 13673a2f7427SDavid Greenman } else 13683a2f7427SDavid Greenman fdout &= ~(FDO_MOEN0 << fdsu); 13696182fdbdSPeter Wemm fdc->fdout = fdout; 1370fb35bd37SJoerg Wunsch fdout_wr(fdc, fdout); 13713a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdout); 13723a2f7427SDavid Greenman } 13733a2f7427SDavid Greenman 1374381fe1aaSGarrett Wollman static void 13756182fdbdSPeter Wemm fd_turnoff(void *xfd) 13765b81b6b3SRodney W. Grimes { 1377f5f7ba03SJordan K. Hubbard int s; 13786182fdbdSPeter Wemm fd_p fd = xfd; 1379dc16046fSJoerg Wunsch 13806182fdbdSPeter Wemm TRACE1("[fd%d: turnoff]", fd->fdu); 13818335c1b8SBruce Evans 13825f830ea2SJoerg Wunsch s = splbio(); 13838335c1b8SBruce Evans /* 13848335c1b8SBruce Evans * Don't turn off the motor yet if the drive is active. 13855f830ea2SJoerg Wunsch * 13865f830ea2SJoerg Wunsch * If we got here, this could only mean we missed an interrupt. 13875f830ea2SJoerg Wunsch * This can e. g. happen on the Y-E Date PCMCIA floppy controller 13885f830ea2SJoerg Wunsch * after a controller reset. Just schedule a pseudo-interrupt 13895f830ea2SJoerg Wunsch * so the state machine gets re-entered. 13908335c1b8SBruce Evans */ 13916182fdbdSPeter Wemm if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { 13925f830ea2SJoerg Wunsch fdc_intr(fd->fdc); 13935f830ea2SJoerg Wunsch splx(s); 13948335c1b8SBruce Evans return; 13958335c1b8SBruce Evans } 13968335c1b8SBruce Evans 13975b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR; 13986182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNOFF); 1399f5f7ba03SJordan K. Hubbard splx(s); 14005b81b6b3SRodney W. Grimes } 14015b81b6b3SRodney W. Grimes 14023a2f7427SDavid Greenman static void 14036182fdbdSPeter Wemm fd_motor_on(void *xfd) 14045b81b6b3SRodney W. Grimes { 1405f5f7ba03SJordan K. Hubbard int s; 14066182fdbdSPeter Wemm fd_p fd = xfd; 1407f5f7ba03SJordan K. Hubbard 1408f5f7ba03SJordan K. Hubbard s = splbio(); 14095b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR_WAIT; 14105b81b6b3SRodney W. Grimes if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) 14115b81b6b3SRodney W. Grimes { 14126182fdbdSPeter Wemm fdc_intr(fd->fdc); 14135b81b6b3SRodney W. Grimes } 1414f5f7ba03SJordan K. Hubbard splx(s); 14155b81b6b3SRodney W. Grimes } 14165b81b6b3SRodney W. Grimes 14173a2f7427SDavid Greenman static void 14186182fdbdSPeter Wemm fd_turnon(fd_p fd) 14195b81b6b3SRodney W. Grimes { 14205b81b6b3SRodney W. Grimes if(!(fd->flags & FD_MOTOR)) 14215b81b6b3SRodney W. Grimes { 14223a2f7427SDavid Greenman fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); 14236182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNON); 14246182fdbdSPeter Wemm timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ 14255b81b6b3SRodney W. Grimes } 14265b81b6b3SRodney W. Grimes } 14275b81b6b3SRodney W. Grimes 1428381fe1aaSGarrett Wollman static void 1429dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc) 14305b81b6b3SRodney W. Grimes { 14313a2f7427SDavid Greenman /* Try a reset, keep motor on */ 1432427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 14333a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 14343a2f7427SDavid Greenman DELAY(100); 14353a2f7427SDavid Greenman /* enable FDC, but defer interrupts a moment */ 1436427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); 14373a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); 14383a2f7427SDavid Greenman DELAY(100); 1439427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout); 14403a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout); 14413a2f7427SDavid Greenman 144264860614SJoerg Wunsch /* XXX after a reset, silently believe the FDC will accept commands */ 14436182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1444dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1445dc5df763SJoerg Wunsch 0); 1446d66c374fSTor Egge if (fdc->flags & FDC_HAS_FIFO) 1447d66c374fSTor Egge (void) enable_fifo(fdc); 14485b81b6b3SRodney W. Grimes } 14495b81b6b3SRodney W. Grimes 1450246ed35dSJoerg Wunsch /* 1451246ed35dSJoerg Wunsch * FDC IO functions, take care of the main status register, timeout 1452246ed35dSJoerg Wunsch * in case the desired status bits are never set. 1453246ed35dSJoerg Wunsch */ 1454b5e8ce9fSBruce Evans static int 14556182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr) 1456dc5df763SJoerg Wunsch { 1457dc5df763SJoerg Wunsch int i, j = 100000; 1458427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) 1459dc5df763SJoerg Wunsch != (NE7_DIO|NE7_RQM) && j-- > 0) 1460dc5df763SJoerg Wunsch if (i == NE7_RQM) 14616182fdbdSPeter Wemm return fdc_err(fdc, "ready for output in input\n"); 1462dc5df763SJoerg Wunsch if (j <= 0) 14636182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); 1464d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 1465427ccf00SDoug Rabson i = fddata_rd(fdc); 1466dc5df763SJoerg Wunsch TRACE1("[FDDATA->0x%x]", (unsigned char)i); 1467dc5df763SJoerg Wunsch *ptr = i; 1468dc5df763SJoerg Wunsch return 0; 1469d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 1470427ccf00SDoug Rabson i = fddata_rd(fdc); 1471dc5df763SJoerg Wunsch if (ptr) 1472dc5df763SJoerg Wunsch *ptr = i; 1473dc5df763SJoerg Wunsch return 0; 1474d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 1475dc5df763SJoerg Wunsch } 1476dc5df763SJoerg Wunsch 1477dc5df763SJoerg Wunsch int 14786182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x) 14795b81b6b3SRodney W. Grimes { 14803b3837dbSRodney W. Grimes int i; 14815b81b6b3SRodney W. Grimes 14823b3837dbSRodney W. Grimes /* Check that the direction bit is set */ 14833b3837dbSRodney W. Grimes i = 100000; 1484427ccf00SDoug Rabson while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); 14856182fdbdSPeter Wemm if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); 14863b3837dbSRodney W. Grimes 14873b3837dbSRodney W. Grimes /* Check that the floppy controller is ready for a command */ 14883b3837dbSRodney W. Grimes i = 100000; 1489427ccf00SDoug Rabson while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); 149016b04b6aSJoerg Wunsch if (i <= 0) 14916182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); 14923b3837dbSRodney W. Grimes 14933b3837dbSRodney W. Grimes /* Send the command and return */ 1494427ccf00SDoug Rabson fddata_wr(fdc, x); 14953a2f7427SDavid Greenman TRACE1("[0x%x->FDDATA]", x); 14965b81b6b3SRodney W. Grimes return (0); 14975b81b6b3SRodney W. Grimes } 14985b81b6b3SRodney W. Grimes 1499246ed35dSJoerg Wunsch /* 1500246ed35dSJoerg Wunsch * Block device driver interface functions (interspersed with even more 1501246ed35dSJoerg Wunsch * auxiliary functions). 1502246ed35dSJoerg Wunsch */ 1503381fe1aaSGarrett Wollman int 1504671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p) 15055b81b6b3SRodney W. Grimes { 15065b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 150720a29168SAndrey A. Chernov int type = FDTYPE(minor(dev)); 15086182fdbdSPeter Wemm fd_p fd; 1509b99f0a4aSAndrew Moore fdc_p fdc; 15105b81b6b3SRodney W. Grimes 15115b81b6b3SRodney W. Grimes /* check bounds */ 15126182fdbdSPeter Wemm if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) 1513b99f0a4aSAndrew Moore return (ENXIO); 15146182fdbdSPeter Wemm fdc = fd->fdc; 15156182fdbdSPeter Wemm if ((fdc == NULL) || (fd->type == NO_TYPE)) 1516b99f0a4aSAndrew Moore return (ENXIO); 1517b99f0a4aSAndrew Moore if (type > NUMDENS) 1518b99f0a4aSAndrew Moore return (ENXIO); 15197ca0641bSAndrey A. Chernov if (type == 0) 15206182fdbdSPeter Wemm type = fd->type; 15217ca0641bSAndrey A. Chernov else { 15223e425b96SJulian Elischer /* 15233e425b96SJulian Elischer * For each type of basic drive, make sure we are trying 15243e425b96SJulian Elischer * to open a type it can do, 15253e425b96SJulian Elischer */ 15266182fdbdSPeter Wemm if (type != fd->type) { 15276182fdbdSPeter Wemm switch (fd->type) { 15287ca0641bSAndrey A. Chernov case FD_360: 15297ca0641bSAndrey A. Chernov return (ENXIO); 1530ed2fa05eSAndrey A. Chernov case FD_720: 1531b39c878eSAndrey A. Chernov if ( type != FD_820 1532b39c878eSAndrey A. Chernov && type != FD_800 15336fb89845SKATO Takenori && type != FD_640 1534ed2fa05eSAndrey A. Chernov ) 1535ed2fa05eSAndrey A. Chernov return (ENXIO); 1536ed2fa05eSAndrey A. Chernov break; 15377ca0641bSAndrey A. Chernov case FD_1200: 1538b39c878eSAndrey A. Chernov switch (type) { 1539b39c878eSAndrey A. Chernov case FD_1480: 1540b39c878eSAndrey A. Chernov type = FD_1480in5_25; 1541fa4700b4SAndrey A. Chernov break; 15427ca0641bSAndrey A. Chernov case FD_1440: 1543b39c878eSAndrey A. Chernov type = FD_1440in5_25; 1544b39c878eSAndrey A. Chernov break; 15456fb89845SKATO Takenori case FD_1232: 15466fb89845SKATO Takenori break; 1547b39c878eSAndrey A. Chernov case FD_820: 1548b39c878eSAndrey A. Chernov type = FD_820in5_25; 1549b39c878eSAndrey A. Chernov break; 1550b39c878eSAndrey A. Chernov case FD_800: 1551b39c878eSAndrey A. Chernov type = FD_800in5_25; 1552b39c878eSAndrey A. Chernov break; 1553b39c878eSAndrey A. Chernov case FD_720: 1554b39c878eSAndrey A. Chernov type = FD_720in5_25; 1555b39c878eSAndrey A. Chernov break; 15566fb89845SKATO Takenori case FD_640: 15576fb89845SKATO Takenori type = FD_640in5_25; 15586fb89845SKATO Takenori break; 1559b39c878eSAndrey A. Chernov case FD_360: 1560b39c878eSAndrey A. Chernov type = FD_360in5_25; 1561b39c878eSAndrey A. Chernov break; 1562b39c878eSAndrey A. Chernov default: 1563b39c878eSAndrey A. Chernov return(ENXIO); 1564b39c878eSAndrey A. Chernov } 1565b39c878eSAndrey A. Chernov break; 1566b39c878eSAndrey A. Chernov case FD_1440: 1567b39c878eSAndrey A. Chernov if ( type != FD_1720 1568b39c878eSAndrey A. Chernov && type != FD_1480 1569ed2fa05eSAndrey A. Chernov && type != FD_1200 1570b39c878eSAndrey A. Chernov && type != FD_820 1571b39c878eSAndrey A. Chernov && type != FD_800 1572b39c878eSAndrey A. Chernov && type != FD_720 15736fb89845SKATO Takenori && type != FD_640 15747ca0641bSAndrey A. Chernov ) 1575dffff499SAndrey A. Chernov return(ENXIO); 1576fa4700b4SAndrey A. Chernov break; 15777ca0641bSAndrey A. Chernov } 15787ca0641bSAndrey A. Chernov } 1579fa4700b4SAndrey A. Chernov } 15806182fdbdSPeter Wemm fd->ft = fd_types + type - 1; 15816182fdbdSPeter Wemm fd->flags |= FD_OPEN; 15823fef646eSJoerg Wunsch /* 15833fef646eSJoerg Wunsch * Clearing the DMA overrun counter at open time is a bit messy. 15843fef646eSJoerg Wunsch * Since we're only managing one counter per controller, opening 15853fef646eSJoerg Wunsch * the second drive could mess it up. Anyway, if the DMA overrun 15863fef646eSJoerg Wunsch * condition is really persistent, it will eventually time out 15873fef646eSJoerg Wunsch * still. OTOH, clearing it here will ensure we'll at least start 15883fef646eSJoerg Wunsch * trying again after a previous (maybe even long ago) failure. 15893fef646eSJoerg Wunsch * Also, this is merely a stop-gap measure only that should not 15903fef646eSJoerg Wunsch * happen during normal operation, so we can tolerate it to be a 15913fef646eSJoerg Wunsch * bit sloppy about this. 15923fef646eSJoerg Wunsch */ 15933fef646eSJoerg Wunsch fdc->dma_overruns = 0; 15945f830ea2SJoerg Wunsch 15955b81b6b3SRodney W. Grimes return 0; 15965b81b6b3SRodney W. Grimes } 15975b81b6b3SRodney W. Grimes 1598381fe1aaSGarrett Wollman int 1599671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p) 16005b81b6b3SRodney W. Grimes { 16015b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 16026182fdbdSPeter Wemm struct fd_data *fd; 1603b99f0a4aSAndrew Moore 16046182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 16056182fdbdSPeter Wemm fd->flags &= ~FD_OPEN; 16062995d110SJoerg Wunsch fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR); 1607dc16046fSJoerg Wunsch 16085b81b6b3SRodney W. Grimes return (0); 16095b81b6b3SRodney W. Grimes } 16105b81b6b3SRodney W. Grimes 16113a2f7427SDavid Greenman void 16128177437dSPoul-Henning Kamp fdstrategy(struct bio *bp) 16133a2f7427SDavid Greenman { 1614fb35bd37SJoerg Wunsch long blknum, nblocks; 16153a2f7427SDavid Greenman int s; 16163a2f7427SDavid Greenman fdu_t fdu; 16173a2f7427SDavid Greenman fdc_p fdc; 16183a2f7427SDavid Greenman fd_p fd; 16193a2f7427SDavid Greenman size_t fdblk; 16203a2f7427SDavid Greenman 16218177437dSPoul-Henning Kamp fdu = FDUNIT(minor(bp->bio_dev)); 16226182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 16236182fdbdSPeter Wemm if (fd == 0) 16246182fdbdSPeter Wemm panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", 16258177437dSPoul-Henning Kamp (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev)); 16263a2f7427SDavid Greenman fdc = fd->fdc; 162769acd21dSWarner Losh if (fd->type == NO_TYPE) { 16288177437dSPoul-Henning Kamp bp->bio_error = ENXIO; 16298177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 16303b178206SWarner Losh goto bad; 163164860614SJoerg Wunsch } 1632d3628763SRodney W. Grimes fdblk = 128 << (fd->ft->secsize); 1633f664aeeeSJoerg Wunsch if (bp->bio_cmd != BIO_FORMAT && bp->bio_cmd != BIO_RDSECTID) { 16348177437dSPoul-Henning Kamp if (bp->bio_blkno < 0) { 1635dc5df763SJoerg Wunsch printf( 16366a0e6f42SRodney W. Grimes "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", 16378177437dSPoul-Henning Kamp fdu, (u_long)bp->bio_blkno, bp->bio_bcount); 16388177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 16398177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 16403a2f7427SDavid Greenman goto bad; 16413a2f7427SDavid Greenman } 16428177437dSPoul-Henning Kamp if ((bp->bio_bcount % fdblk) != 0) { 16438177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 16448177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 16453a2f7427SDavid Greenman goto bad; 16463a2f7427SDavid Greenman } 16473a2f7427SDavid Greenman } 16483a2f7427SDavid Greenman 16493a2f7427SDavid Greenman /* 16503a2f7427SDavid Greenman * Set up block calculations. 16513a2f7427SDavid Greenman */ 16528177437dSPoul-Henning Kamp if (bp->bio_blkno > 20000000) { 1653bb6382faSJoerg Wunsch /* 1654bb6382faSJoerg Wunsch * Reject unreasonably high block number, prevent the 1655bb6382faSJoerg Wunsch * multiplication below from overflowing. 1656bb6382faSJoerg Wunsch */ 16578177437dSPoul-Henning Kamp bp->bio_error = EINVAL; 16588177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 16593a2f7427SDavid Greenman goto bad; 16603a2f7427SDavid Greenman } 1661fb35bd37SJoerg Wunsch blknum = bp->bio_blkno * DEV_BSIZE / fdblk; 1662bb6382faSJoerg Wunsch nblocks = fd->ft->size; 1663fb35bd37SJoerg Wunsch if (blknum + bp->bio_bcount / fdblk > nblocks) { 1664fb35bd37SJoerg Wunsch if (blknum >= nblocks) { 1665fb35bd37SJoerg Wunsch if (bp->bio_cmd == BIO_READ) 1666fb35bd37SJoerg Wunsch bp->bio_resid = bp->bio_bcount; 1667fb35bd37SJoerg Wunsch else { 1668fb35bd37SJoerg Wunsch bp->bio_error = ENOSPC; 16698177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 1670bb6382faSJoerg Wunsch } 1671fb35bd37SJoerg Wunsch goto bad; /* not always bad, but EOF */ 1672fb35bd37SJoerg Wunsch } 1673fb35bd37SJoerg Wunsch bp->bio_bcount = (nblocks - blknum) * fdblk; 1674bb6382faSJoerg Wunsch } 16758177437dSPoul-Henning Kamp bp->bio_pblkno = bp->bio_blkno; 16763a2f7427SDavid Greenman s = splbio(); 16778177437dSPoul-Henning Kamp bioqdisksort(&fdc->head, bp); 16786182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ 1679b2dfb1f9SJustin T. Gibbs devstat_start_transaction(&fd->device_stats); 16805f830ea2SJoerg Wunsch device_busy(fd->dev); 16816182fdbdSPeter Wemm fdstart(fdc); 16823a2f7427SDavid Greenman splx(s); 16833a2f7427SDavid Greenman return; 16843a2f7427SDavid Greenman 16853a2f7427SDavid Greenman bad: 16863a2f7427SDavid Greenman biodone(bp); 16873a2f7427SDavid Greenman } 16883a2f7427SDavid Greenman 1689246ed35dSJoerg Wunsch /* 1690246ed35dSJoerg Wunsch * fdstart 1691246ed35dSJoerg Wunsch * 1692246ed35dSJoerg Wunsch * We have just queued something. If the controller is not busy 1693246ed35dSJoerg Wunsch * then simulate the case where it has just finished a command 1694246ed35dSJoerg Wunsch * So that it (the interrupt routine) looks on the queue for more 1695246ed35dSJoerg Wunsch * work to do and picks up what we just added. 1696246ed35dSJoerg Wunsch * 1697246ed35dSJoerg Wunsch * If the controller is already busy, we need do nothing, as it 1698246ed35dSJoerg Wunsch * will pick up our work when the present work completes. 1699246ed35dSJoerg Wunsch */ 1700381fe1aaSGarrett Wollman static void 17016182fdbdSPeter Wemm fdstart(struct fdc_data *fdc) 17025b81b6b3SRodney W. Grimes { 17035b81b6b3SRodney W. Grimes int s; 17045b81b6b3SRodney W. Grimes 17055b81b6b3SRodney W. Grimes s = splbio(); 17066182fdbdSPeter Wemm if(fdc->state == DEVIDLE) 17075b81b6b3SRodney W. Grimes { 17086182fdbdSPeter Wemm fdc_intr(fdc); 17095b81b6b3SRodney W. Grimes } 17105b81b6b3SRodney W. Grimes splx(s); 17115b81b6b3SRodney W. Grimes } 17125b81b6b3SRodney W. Grimes 1713381fe1aaSGarrett Wollman static void 17146182fdbdSPeter Wemm fd_iotimeout(void *xfdc) 17155b81b6b3SRodney W. Grimes { 17165c1a1eaeSBruce Evans fdc_p fdc; 1717f5f7ba03SJordan K. Hubbard int s; 17185b81b6b3SRodney W. Grimes 17196182fdbdSPeter Wemm fdc = xfdc; 17205c1a1eaeSBruce Evans TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); 17215b81b6b3SRodney W. Grimes 17223a2f7427SDavid Greenman /* 17233a2f7427SDavid Greenman * Due to IBM's brain-dead design, the FDC has a faked ready 17243a2f7427SDavid Greenman * signal, hardwired to ready == true. Thus, any command 17253a2f7427SDavid Greenman * issued if there's no diskette in the drive will _never_ 17263a2f7427SDavid Greenman * complete, and must be aborted by resetting the FDC. 17273a2f7427SDavid Greenman * Many thanks, Big Blue! 17285c1a1eaeSBruce Evans * The FDC must not be reset directly, since that would 17295c1a1eaeSBruce Evans * interfere with the state machine. Instead, pretend that 17305c1a1eaeSBruce Evans * the command completed but was invalid. The state machine 17315c1a1eaeSBruce Evans * will reset the FDC and retry once. 17323a2f7427SDavid Greenman */ 17333a2f7427SDavid Greenman s = splbio(); 17345c1a1eaeSBruce Evans fdc->status[0] = NE7_ST0_IC_IV; 17355c1a1eaeSBruce Evans fdc->flags &= ~FDC_STAT_VALID; 17365c1a1eaeSBruce Evans fdc->state = IOTIMEDOUT; 17376182fdbdSPeter Wemm fdc_intr(fdc); 1738f5f7ba03SJordan K. Hubbard splx(s); 17395b81b6b3SRodney W. Grimes } 17405b81b6b3SRodney W. Grimes 1741246ed35dSJoerg Wunsch /* Just ensure it has the right spl. */ 1742381fe1aaSGarrett Wollman static void 17436182fdbdSPeter Wemm fd_pseudointr(void *xfdc) 17445b81b6b3SRodney W. Grimes { 17455b81b6b3SRodney W. Grimes int s; 17463a2f7427SDavid Greenman 17475b81b6b3SRodney W. Grimes s = splbio(); 17486182fdbdSPeter Wemm fdc_intr(xfdc); 17495b81b6b3SRodney W. Grimes splx(s); 17505b81b6b3SRodney W. Grimes } 17515b81b6b3SRodney W. Grimes 1752246ed35dSJoerg Wunsch /* 1753246ed35dSJoerg Wunsch * fdc_intr 1754246ed35dSJoerg Wunsch * 1755246ed35dSJoerg Wunsch * Keep calling the state machine until it returns a 0. 1756246ed35dSJoerg Wunsch * Always called at splbio. 1757246ed35dSJoerg Wunsch */ 1758fe310de8SBruce Evans static void 17596182fdbdSPeter Wemm fdc_intr(void *xfdc) 17605b81b6b3SRodney W. Grimes { 17616182fdbdSPeter Wemm fdc_p fdc = xfdc; 17626182fdbdSPeter Wemm while(fdstate(fdc)) 1763381fe1aaSGarrett Wollman ; 17645b81b6b3SRodney W. Grimes } 17655b81b6b3SRodney W. Grimes 176669acd21dSWarner Losh /* 1767246ed35dSJoerg Wunsch * Magic pseudo-DMA initialization for YE FDC. Sets count and 1768246ed35dSJoerg Wunsch * direction. 176969acd21dSWarner Losh */ 17703b178206SWarner Losh #define SET_BCDR(fdc,wr,cnt,port) \ 17713b178206SWarner Losh bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port, \ 17723b178206SWarner Losh ((cnt)-1) & 0xff); \ 17733b178206SWarner Losh bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \ 17743b178206SWarner Losh ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))); 177569acd21dSWarner Losh 177669acd21dSWarner Losh /* 1777246ed35dSJoerg Wunsch * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy. 177869acd21dSWarner Losh */ 17794c34deeeSJoerg Wunsch static int 17804c34deeeSJoerg Wunsch fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) 178169acd21dSWarner Losh { 178269acd21dSWarner Losh u_char *cptr = (u_char *)addr; 178369acd21dSWarner Losh 178421144e3bSPoul-Henning Kamp if (flags == BIO_READ) { 178569acd21dSWarner Losh if (fdc->state != PIOREAD) { 178669acd21dSWarner Losh fdc->state = PIOREAD; 178769acd21dSWarner Losh return(0); 178864860614SJoerg Wunsch } 17893b178206SWarner Losh SET_BCDR(fdc, 0, count, 0); 17903b178206SWarner Losh bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off + 17913b178206SWarner Losh FDC_YE_DATAPORT, cptr, count); 179269acd21dSWarner Losh } else { 17933b178206SWarner Losh bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off + 17943b178206SWarner Losh FDC_YE_DATAPORT, cptr, count); 17953b178206SWarner Losh SET_BCDR(fdc, 0, count, 0); 179664860614SJoerg Wunsch } 179769acd21dSWarner Losh return(1); 179869acd21dSWarner Losh } 179969acd21dSWarner Losh 1800246ed35dSJoerg Wunsch /* 1801246ed35dSJoerg Wunsch * The controller state machine. 1802246ed35dSJoerg Wunsch * 1803246ed35dSJoerg Wunsch * If it returns a non zero value, it should be called again immediately. 1804246ed35dSJoerg Wunsch */ 18053a2f7427SDavid Greenman static int 18066182fdbdSPeter Wemm fdstate(fdc_p fdc) 18075b81b6b3SRodney W. Grimes { 180864860614SJoerg Wunsch struct fdc_readid *idp; 1809fb35bd37SJoerg Wunsch int read, format, rdsectid, cylinder, head, i, sec = 0, sectrac; 1810fb35bd37SJoerg Wunsch int st0, cyl, st3, idf; 1811fb35bd37SJoerg Wunsch unsigned long blknum; 18125b81b6b3SRodney W. Grimes fdu_t fdu = fdc->fdu; 18135b81b6b3SRodney W. Grimes fd_p fd; 18148177437dSPoul-Henning Kamp register struct bio *bp; 1815b39c878eSAndrey A. Chernov struct fd_formb *finfo = NULL; 18163a2f7427SDavid Greenman size_t fdblk; 18175b81b6b3SRodney W. Grimes 1818e93e63cbSBruce Evans bp = fdc->bp; 1819e93e63cbSBruce Evans if (bp == NULL) { 18208177437dSPoul-Henning Kamp bp = bioq_first(&fdc->head); 1821e93e63cbSBruce Evans if (bp != NULL) { 18228177437dSPoul-Henning Kamp bioq_remove(&fdc->head, bp); 1823e93e63cbSBruce Evans fdc->bp = bp; 1824e93e63cbSBruce Evans } 1825e93e63cbSBruce Evans } 1826e93e63cbSBruce Evans if (bp == NULL) { 1827246ed35dSJoerg Wunsch /* 1828246ed35dSJoerg Wunsch * Nothing left for this controller to do, 1829246ed35dSJoerg Wunsch * force into the IDLE state. 1830246ed35dSJoerg Wunsch */ 18315b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 18326182fdbdSPeter Wemm if (fdc->fd) { 1833b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, 1834b6e5f28eSPeter Wemm "unexpected valid fd pointer\n"); 18355b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 18365b81b6b3SRodney W. Grimes fdc->fdu = -1; 18375b81b6b3SRodney W. Grimes } 18386182fdbdSPeter Wemm TRACE1("[fdc%d IDLE]", fdc->fdcu); 18395b81b6b3SRodney W. Grimes return (0); 18405b81b6b3SRodney W. Grimes } 18418177437dSPoul-Henning Kamp fdu = FDUNIT(minor(bp->bio_dev)); 18426182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 18433a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 1844b6e5f28eSPeter Wemm if (fdc->fd && (fd != fdc->fd)) 1845b6e5f28eSPeter Wemm device_printf(fd->dev, "confused fd pointers\n"); 18468177437dSPoul-Henning Kamp read = bp->bio_cmd == BIO_READ; 184756a23089SPoul-Henning Kamp if (read) 184856a23089SPoul-Henning Kamp idf = ISADMA_READ; 184956a23089SPoul-Henning Kamp else 185056a23089SPoul-Henning Kamp idf = ISADMA_WRITE; 185164860614SJoerg Wunsch format = bp->bio_cmd == BIO_FORMAT; 1852f664aeeeSJoerg Wunsch rdsectid = bp->bio_cmd == BIO_RDSECTID; 1853fb35bd37SJoerg Wunsch if (format) 18548177437dSPoul-Henning Kamp finfo = (struct fd_formb *)bp->bio_data; 18555b81b6b3SRodney W. Grimes TRACE1("fd%d", fdu); 18565b81b6b3SRodney W. Grimes TRACE1("[%s]", fdstates[fdc->state]); 18575b81b6b3SRodney W. Grimes TRACE1("(0x%x)", fd->flags); 18586182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); 18596182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 18605b81b6b3SRodney W. Grimes switch (fdc->state) 18615b81b6b3SRodney W. Grimes { 18625b81b6b3SRodney W. Grimes case DEVIDLE: 18635b81b6b3SRodney W. Grimes case FINDWORK: /* we have found new work */ 18645b81b6b3SRodney W. Grimes fdc->retry = 0; 18655b81b6b3SRodney W. Grimes fd->skip = 0; 18665b81b6b3SRodney W. Grimes fdc->fd = fd; 18675b81b6b3SRodney W. Grimes fdc->fdu = fdu; 18685f830ea2SJoerg Wunsch fdc->fdctl_wr(fdc, fd->ft->trans); 18693a2f7427SDavid Greenman TRACE1("[0x%x->FDCTL]", fd->ft->trans); 1870246ed35dSJoerg Wunsch /* 1871246ed35dSJoerg Wunsch * If the next drive has a motor startup pending, then 1872246ed35dSJoerg Wunsch * it will start up in its own good time. 1873246ed35dSJoerg Wunsch */ 18746182fdbdSPeter Wemm if(fd->flags & FD_MOTOR_WAIT) { 18755b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 1876246ed35dSJoerg Wunsch return (0); /* will return later */ 18775b81b6b3SRodney W. Grimes } 1878246ed35dSJoerg Wunsch /* 1879246ed35dSJoerg Wunsch * Maybe if it's not starting, it SHOULD be starting. 1880246ed35dSJoerg Wunsch */ 18815b81b6b3SRodney W. Grimes if (!(fd->flags & FD_MOTOR)) 18825b81b6b3SRodney W. Grimes { 18835b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 18846182fdbdSPeter Wemm fd_turnon(fd); 1885246ed35dSJoerg Wunsch return (0); /* will return later */ 18865b81b6b3SRodney W. Grimes } 18875b81b6b3SRodney W. Grimes else /* at least make sure we are selected */ 18885b81b6b3SRodney W. Grimes { 18896182fdbdSPeter Wemm set_motor(fdc, fd->fdsu, TURNON); 18905b81b6b3SRodney W. Grimes } 18915c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 18925c1a1eaeSBruce Evans fdc->state = RESETCTLR; 18935c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 18945c1a1eaeSBruce Evans } else 18955e235068SJordan K. Hubbard fdc->state = DOSEEK; 1896246ed35dSJoerg Wunsch return (1); /* will return immediately */ 1897fb35bd37SJoerg Wunsch 18985b81b6b3SRodney W. Grimes case DOSEEK: 1899fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 1900fb35bd37SJoerg Wunsch cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1901fb35bd37SJoerg Wunsch if (cylinder == fd->track) 19025b81b6b3SRodney W. Grimes { 19035b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 1904246ed35dSJoerg Wunsch return (1); /* will return immediately */ 19055b81b6b3SRodney W. Grimes } 19066182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, 1907fb35bd37SJoerg Wunsch fd->fdsu, cylinder * fd->ft->steptrac, 1908dc5df763SJoerg Wunsch 0)) 1909dc8603e3SJoerg Wunsch { 1910dc8603e3SJoerg Wunsch /* 1911246ed35dSJoerg Wunsch * Seek command not accepted, looks like 1912dc8603e3SJoerg Wunsch * the FDC went off to the Saints... 1913dc8603e3SJoerg Wunsch */ 1914dc8603e3SJoerg Wunsch fdc->retry = 6; /* try a reset */ 19156182fdbdSPeter Wemm return(retrier(fdc)); 1916dc8603e3SJoerg Wunsch } 1917dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 19185b81b6b3SRodney W. Grimes fdc->state = SEEKWAIT; 19195b81b6b3SRodney W. Grimes return(0); /* will return later */ 1920fb35bd37SJoerg Wunsch 19215b81b6b3SRodney W. Grimes case SEEKWAIT: 19225b81b6b3SRodney W. Grimes /* allow heads to settle */ 19236182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 16); 19245b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 19255b81b6b3SRodney W. Grimes return(0); /* will return later */ 1926fb35bd37SJoerg Wunsch 1927246ed35dSJoerg Wunsch case SEEKCOMPLETE : /* seek done, start DMA */ 1928fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 1929fb35bd37SJoerg Wunsch cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1930fb35bd37SJoerg Wunsch 1931246ed35dSJoerg Wunsch /* Make sure seek really happened. */ 19326182fdbdSPeter Wemm if(fd->track == FD_NO_TRACK) { 1933fb35bd37SJoerg Wunsch int descyl = cylinder * fd->ft->steptrac; 19343a2f7427SDavid Greenman do { 19353a2f7427SDavid Greenman /* 1936dc5df763SJoerg Wunsch * This might be a "ready changed" interrupt, 1937dc5df763SJoerg Wunsch * which cannot really happen since the 1938dc5df763SJoerg Wunsch * RDY pin is hardwired to + 5 volts. This 1939dc5df763SJoerg Wunsch * generally indicates a "bouncing" intr 1940dc5df763SJoerg Wunsch * line, so do one of the following: 1941dc5df763SJoerg Wunsch * 1942dc5df763SJoerg Wunsch * When running on an enhanced FDC that is 1943dc5df763SJoerg Wunsch * known to not go stuck after responding 1944dc5df763SJoerg Wunsch * with INVALID, fetch all interrupt states 1945dc5df763SJoerg Wunsch * until seeing either an INVALID or a 1946dc5df763SJoerg Wunsch * real interrupt condition. 1947dc5df763SJoerg Wunsch * 1948dc5df763SJoerg Wunsch * When running on a dumb old NE765, give 1949dc5df763SJoerg Wunsch * up immediately. The controller will 1950dc5df763SJoerg Wunsch * provide up to four dummy RC interrupt 1951dc5df763SJoerg Wunsch * conditions right after reset (for the 1952dc5df763SJoerg Wunsch * corresponding four drives), so this is 1953dc5df763SJoerg Wunsch * our only chance to get notice that it 1954dc5df763SJoerg Wunsch * was not the FDC that caused the interrupt. 19553a2f7427SDavid Greenman */ 1956dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) 1957dc5df763SJoerg Wunsch == FD_NOT_VALID) 1958246ed35dSJoerg Wunsch return (0); /* will return later */ 1959dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 1960dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 1961246ed35dSJoerg Wunsch return (0); /* hope for a real intr */ 19623a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 1963dc5df763SJoerg Wunsch 19646182fdbdSPeter Wemm if (0 == descyl) { 1965dc5df763SJoerg Wunsch int failed = 0; 19663a2f7427SDavid Greenman /* 19673a2f7427SDavid Greenman * seek to cyl 0 requested; make sure we are 19683a2f7427SDavid Greenman * really there 19693a2f7427SDavid Greenman */ 1970dc5df763SJoerg Wunsch if (fd_sense_drive_status(fdc, &st3)) 1971dc5df763SJoerg Wunsch failed = 1; 19723a2f7427SDavid Greenman if ((st3 & NE7_ST3_T0) == 0) { 19733a2f7427SDavid Greenman printf( 19743a2f7427SDavid Greenman "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", 19753a2f7427SDavid Greenman fdu, st3, NE7_ST3BITS); 1976dc5df763SJoerg Wunsch failed = 1; 1977dc5df763SJoerg Wunsch } 1978dc5df763SJoerg Wunsch 19796182fdbdSPeter Wemm if (failed) { 19803a2f7427SDavid Greenman if(fdc->retry < 3) 19813a2f7427SDavid Greenman fdc->retry = 3; 19826182fdbdSPeter Wemm return (retrier(fdc)); 19833a2f7427SDavid Greenman } 19843a2f7427SDavid Greenman } 1985dc5df763SJoerg Wunsch 19866182fdbdSPeter Wemm if (cyl != descyl) { 19873a2f7427SDavid Greenman printf( 19883a2f7427SDavid Greenman "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", 19892d9d0204SRodney W. Grimes fdu, descyl, cyl, st0); 1990e5d7d243SBruce Evans if (fdc->retry < 3) 1991e5d7d243SBruce Evans fdc->retry = 3; 19926182fdbdSPeter Wemm return (retrier(fdc)); 19935b81b6b3SRodney W. Grimes } 19945b81b6b3SRodney W. Grimes } 19955b81b6b3SRodney W. Grimes 1996fb35bd37SJoerg Wunsch fd->track = cylinder; 1997fb35bd37SJoerg Wunsch if (format) 1998fb35bd37SJoerg Wunsch fd->skip = (char *)&(finfo->fd_formb_cylno(0)) 1999fb35bd37SJoerg Wunsch - (char *)finfo; 2000250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 20018177437dSPoul-Henning Kamp isa_dmastart(idf, bp->bio_data+fd->skip, 20028177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, fdc->dmachan); 2003fb35bd37SJoerg Wunsch blknum = bp->bio_pblkno + fd->skip / fdblk; 20045b81b6b3SRodney W. Grimes sectrac = fd->ft->sectrac; 20055b81b6b3SRodney W. Grimes sec = blknum % (sectrac * fd->ft->heads); 20065b81b6b3SRodney W. Grimes head = sec / sectrac; 20075b81b6b3SRodney W. Grimes sec = sec % sectrac + 1; 20083a2f7427SDavid Greenman fd->hddrv = ((head&1)<<2)+fdu; 20093a2f7427SDavid Greenman 2010250300ebSJoerg Wunsch if(format || !(read || rdsectid)) 20113a2f7427SDavid Greenman { 20123a2f7427SDavid Greenman /* make sure the drive is writable */ 2013dc5df763SJoerg Wunsch if(fd_sense_drive_status(fdc, &st3) != 0) 2014dc8603e3SJoerg Wunsch { 2015dc8603e3SJoerg Wunsch /* stuck controller? */ 20165f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 201756a23089SPoul-Henning Kamp isa_dmadone(idf, 20188177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 20198177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 20205c1a1eaeSBruce Evans fdc->dmachan); 2021dc8603e3SJoerg Wunsch fdc->retry = 6; /* reset the beast */ 20226182fdbdSPeter Wemm return (retrier(fdc)); 2023dc8603e3SJoerg Wunsch } 20243a2f7427SDavid Greenman if(st3 & NE7_ST3_WP) 20253a2f7427SDavid Greenman { 20263a2f7427SDavid Greenman /* 20273a2f7427SDavid Greenman * XXX YES! this is ugly. 20283a2f7427SDavid Greenman * in order to force the current operation 20293a2f7427SDavid Greenman * to fail, we will have to fake an FDC 20303a2f7427SDavid Greenman * error - all error handling is done 20313a2f7427SDavid Greenman * by the retrier() 20323a2f7427SDavid Greenman */ 20333a2f7427SDavid Greenman fdc->status[0] = NE7_ST0_IC_AT; 20343a2f7427SDavid Greenman fdc->status[1] = NE7_ST1_NW; 20353a2f7427SDavid Greenman fdc->status[2] = 0; 20363a2f7427SDavid Greenman fdc->status[3] = fd->track; 20373a2f7427SDavid Greenman fdc->status[4] = head; 20383a2f7427SDavid Greenman fdc->status[5] = sec; 20393a2f7427SDavid Greenman fdc->retry = 8; /* break out immediately */ 20403a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; /* not really... */ 2041246ed35dSJoerg Wunsch return (1); /* will return immediately */ 20423a2f7427SDavid Greenman } 20433a2f7427SDavid Greenman } 20445b81b6b3SRodney W. Grimes 20456182fdbdSPeter Wemm if (format) { 20465f830ea2SJoerg Wunsch if (fdc->flags & FDC_NODMA) { 20475f830ea2SJoerg Wunsch /* 20485f830ea2SJoerg Wunsch * This seems to be necessary for 20495f830ea2SJoerg Wunsch * whatever obscure reason; if we omit 20505f830ea2SJoerg Wunsch * it, we end up filling the sector ID 20515f830ea2SJoerg Wunsch * fields of the newly formatted track 20525f830ea2SJoerg Wunsch * entirely with garbage, causing 20535f830ea2SJoerg Wunsch * `wrong cylinder' errors all over 20545f830ea2SJoerg Wunsch * the place when trying to read them 20555f830ea2SJoerg Wunsch * back. 20565f830ea2SJoerg Wunsch * 20575f830ea2SJoerg Wunsch * Umpf. 20585f830ea2SJoerg Wunsch */ 20598177437dSPoul-Henning Kamp SET_BCDR(fdc, 1, bp->bio_bcount, 0); 20605f830ea2SJoerg Wunsch 20618177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd, 20628177437dSPoul-Henning Kamp bp->bio_data+fd->skip, 20638177437dSPoul-Henning Kamp bp->bio_bcount); 20645f830ea2SJoerg Wunsch 20655f830ea2SJoerg Wunsch } 2066b39c878eSAndrey A. Chernov /* formatting */ 20676182fdbdSPeter Wemm if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, 2068dc5df763SJoerg Wunsch finfo->fd_formb_secshift, 2069dc5df763SJoerg Wunsch finfo->fd_formb_nsecs, 2070dc5df763SJoerg Wunsch finfo->fd_formb_gaplen, 20716182fdbdSPeter Wemm finfo->fd_formb_fillbyte, 0)) { 2072dc8603e3SJoerg Wunsch /* controller fell over */ 20735f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 207456a23089SPoul-Henning Kamp isa_dmadone(idf, 20758177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 20768177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 20775c1a1eaeSBruce Evans fdc->dmachan); 2078dc8603e3SJoerg Wunsch fdc->retry = 6; 20796182fdbdSPeter Wemm return (retrier(fdc)); 2080dc8603e3SJoerg Wunsch } 2081250300ebSJoerg Wunsch } else if (rdsectid) { 2082250300ebSJoerg Wunsch if (fd_cmd(fdc, 2, NE7CMD_READID, head << 2 | fdu, 0)) { 2083250300ebSJoerg Wunsch /* controller jamming */ 2084250300ebSJoerg Wunsch fdc->retry = 6; 2085250300ebSJoerg Wunsch return (retrier(fdc)); 2086250300ebSJoerg Wunsch } 20876182fdbdSPeter Wemm } else { 2088250300ebSJoerg Wunsch /* read or write operation */ 20893b178206SWarner Losh if (fdc->flags & FDC_NODMA) { 209069acd21dSWarner Losh /* 2091246ed35dSJoerg Wunsch * This seems to be necessary even when 2092246ed35dSJoerg Wunsch * reading data. 209369acd21dSWarner Losh */ 20943b178206SWarner Losh SET_BCDR(fdc, 1, fdblk, 0); 209569acd21dSWarner Losh 209669acd21dSWarner Losh /* 2097246ed35dSJoerg Wunsch * Perform the write pseudo-DMA before 2098246ed35dSJoerg Wunsch * the WRITE command is sent. 209969acd21dSWarner Losh */ 210069acd21dSWarner Losh if (!read) 21018177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd, 21028177437dSPoul-Henning Kamp bp->bio_data+fd->skip, 210369acd21dSWarner Losh fdblk); 210469acd21dSWarner Losh } 21056182fdbdSPeter Wemm if (fd_cmd(fdc, 9, 2106dc5df763SJoerg Wunsch (read ? NE7CMD_READ : NE7CMD_WRITE), 2107dc5df763SJoerg Wunsch head << 2 | fdu, /* head & unit */ 2108dc5df763SJoerg Wunsch fd->track, /* track */ 2109dc5df763SJoerg Wunsch head, 2110dc5df763SJoerg Wunsch sec, /* sector + 1 */ 2111dc5df763SJoerg Wunsch fd->ft->secsize, /* sector size */ 2112dc5df763SJoerg Wunsch sectrac, /* sectors/track */ 2113dc5df763SJoerg Wunsch fd->ft->gap, /* gap size */ 2114dc5df763SJoerg Wunsch fd->ft->datalen, /* data length */ 21156182fdbdSPeter Wemm 0)) { 2116dc8603e3SJoerg Wunsch /* the beast is sleeping again */ 21175f830ea2SJoerg Wunsch if (!(fdc->flags & FDC_NODMA)) 211856a23089SPoul-Henning Kamp isa_dmadone(idf, 21198177437dSPoul-Henning Kamp bp->bio_data + fd->skip, 21208177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 21215c1a1eaeSBruce Evans fdc->dmachan); 2122dc8603e3SJoerg Wunsch fdc->retry = 6; 21236182fdbdSPeter Wemm return (retrier(fdc)); 21245b81b6b3SRodney W. Grimes } 2125b39c878eSAndrey A. Chernov } 2126250300ebSJoerg Wunsch if (!rdsectid && (fdc->flags & FDC_NODMA)) 212769acd21dSWarner Losh /* 2128246ed35dSJoerg Wunsch * If this is a read, then simply await interrupt 2129246ed35dSJoerg Wunsch * before performing PIO. 213069acd21dSWarner Losh */ 21318177437dSPoul-Henning Kamp if (read && !fdcpio(fdc,bp->bio_cmd, 21328177437dSPoul-Henning Kamp bp->bio_data+fd->skip,fdblk)) { 21333b178206SWarner Losh fd->tohandle = timeout(fd_iotimeout, fdc, hz); 213469acd21dSWarner Losh return(0); /* will return later */ 213564860614SJoerg Wunsch } 213669acd21dSWarner Losh 213769acd21dSWarner Losh /* 2138246ed35dSJoerg Wunsch * Write (or format) operation will fall through and 2139246ed35dSJoerg Wunsch * await completion interrupt. 214069acd21dSWarner Losh */ 21415b81b6b3SRodney W. Grimes fdc->state = IOCOMPLETE; 21426182fdbdSPeter Wemm fd->tohandle = timeout(fd_iotimeout, fdc, hz); 21435b81b6b3SRodney W. Grimes return (0); /* will return later */ 2144fb35bd37SJoerg Wunsch 214569acd21dSWarner Losh case PIOREAD: 214669acd21dSWarner Losh /* 2147246ed35dSJoerg Wunsch * Actually perform the PIO read. The IOCOMPLETE case 214869acd21dSWarner Losh * removes the timeout for us. 214969acd21dSWarner Losh */ 21508177437dSPoul-Henning Kamp (void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk); 215169acd21dSWarner Losh fdc->state = IOCOMPLETE; 215269acd21dSWarner Losh /* FALLTHROUGH */ 2153246ed35dSJoerg Wunsch case IOCOMPLETE: /* IO done, post-analyze */ 21546182fdbdSPeter Wemm untimeout(fd_iotimeout, fdc, fd->tohandle); 2155dc5df763SJoerg Wunsch 215664860614SJoerg Wunsch if (fd_read_status(fdc)) { 2157250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 21588177437dSPoul-Henning Kamp isa_dmadone(idf, bp->bio_data + fd->skip, 21598177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, 21605c1a1eaeSBruce Evans fdc->dmachan); 2161dc5df763SJoerg Wunsch if (fdc->retry < 6) 2162dc5df763SJoerg Wunsch fdc->retry = 6; /* force a reset */ 21636182fdbdSPeter Wemm return (retrier(fdc)); 21645b81b6b3SRodney W. Grimes } 2165dc5df763SJoerg Wunsch 21663a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; 2167dc5df763SJoerg Wunsch 21683a2f7427SDavid Greenman /* FALLTHROUGH */ 21693a2f7427SDavid Greenman case IOTIMEDOUT: 2170250300ebSJoerg Wunsch if (!rdsectid && !(fdc->flags & FDC_NODMA)) 21718177437dSPoul-Henning Kamp isa_dmadone(idf, bp->bio_data + fd->skip, 21728177437dSPoul-Henning Kamp format ? bp->bio_bcount : fdblk, fdc->dmachan); 21736182fdbdSPeter Wemm if (fdc->status[0] & NE7_ST0_IC) { 21743a2f7427SDavid Greenman if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 21753a2f7427SDavid Greenman && fdc->status[1] & NE7_ST1_OR) { 2176b39c878eSAndrey A. Chernov /* 21773fef646eSJoerg Wunsch * DMA overrun. Someone hogged the bus and 21783fef646eSJoerg Wunsch * didn't release it in time for the next 21793fef646eSJoerg Wunsch * FDC transfer. 21803fef646eSJoerg Wunsch * 21813fef646eSJoerg Wunsch * We normally restart this without bumping 21823fef646eSJoerg Wunsch * the retry counter. However, in case 21833fef646eSJoerg Wunsch * something is seriously messed up (like 21843fef646eSJoerg Wunsch * broken hardware), we rather limit the 21853fef646eSJoerg Wunsch * number of retries so the IO operation 21863fef646eSJoerg Wunsch * doesn't block indefinately. 2187b39c878eSAndrey A. Chernov */ 21883fef646eSJoerg Wunsch if (fdc->dma_overruns++ < FDC_DMAOV_MAX) { 2189b39c878eSAndrey A. Chernov fdc->state = SEEKCOMPLETE; 2190246ed35dSJoerg Wunsch return (1);/* will return immediately */ 21913fef646eSJoerg Wunsch } /* else fall through */ 2192b39c878eSAndrey A. Chernov } 21933fef646eSJoerg Wunsch if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV 21943a2f7427SDavid Greenman && fdc->retry < 6) 21953a2f7427SDavid Greenman fdc->retry = 6; /* force a reset */ 21963a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 21973a2f7427SDavid Greenman && fdc->status[2] & NE7_ST2_WC 21983a2f7427SDavid Greenman && fdc->retry < 3) 21993a2f7427SDavid Greenman fdc->retry = 3; /* force recalibrate */ 22006182fdbdSPeter Wemm return (retrier(fdc)); 22015b81b6b3SRodney W. Grimes } 22025b81b6b3SRodney W. Grimes /* All OK */ 2203250300ebSJoerg Wunsch if (rdsectid) { 2204250300ebSJoerg Wunsch /* copy out ID field contents */ 220564860614SJoerg Wunsch idp = (struct fdc_readid *)bp->bio_data; 2206250300ebSJoerg Wunsch idp->cyl = fdc->status[3]; 2207250300ebSJoerg Wunsch idp->head = fdc->status[4]; 2208250300ebSJoerg Wunsch idp->sec = fdc->status[5]; 2209250300ebSJoerg Wunsch idp->secshift = fdc->status[6]; 2210250300ebSJoerg Wunsch } 22113fef646eSJoerg Wunsch /* Operation successful, retry DMA overruns again next time. */ 22123fef646eSJoerg Wunsch fdc->dma_overruns = 0; 22133a2f7427SDavid Greenman fd->skip += fdblk; 2214fb35bd37SJoerg Wunsch if (!rdsectid && !format && fd->skip < bp->bio_bcount) { 22155b81b6b3SRodney W. Grimes /* set up next transfer */ 22165b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 22176182fdbdSPeter Wemm } else { 22185b81b6b3SRodney W. Grimes /* ALL DONE */ 22195b81b6b3SRodney W. Grimes fd->skip = 0; 2220fb35bd37SJoerg Wunsch bp->bio_resid = 0; 2221e93e63cbSBruce Evans fdc->bp = NULL; 22225f830ea2SJoerg Wunsch device_unbusy(fd->dev); 2223a468031cSPoul-Henning Kamp biofinish(bp, &fd->device_stats, 0); 22245b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 22255b81b6b3SRodney W. Grimes fdc->fdu = -1; 22265b81b6b3SRodney W. Grimes fdc->state = FINDWORK; 22275b81b6b3SRodney W. Grimes } 2228246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2229fb35bd37SJoerg Wunsch 22305b81b6b3SRodney W. Grimes case RESETCTLR: 22313a2f7427SDavid Greenman fdc_reset(fdc); 22325b81b6b3SRodney W. Grimes fdc->retry++; 22335c1a1eaeSBruce Evans fdc->state = RESETCOMPLETE; 2234246ed35dSJoerg Wunsch return (0); /* will return later */ 2235fb35bd37SJoerg Wunsch 22365c1a1eaeSBruce Evans case RESETCOMPLETE: 22375c1a1eaeSBruce Evans /* 22385c1a1eaeSBruce Evans * Discard all the results from the reset so that they 22395c1a1eaeSBruce Evans * can't cause an unexpected interrupt later. 22405c1a1eaeSBruce Evans */ 22410e317d05SJoerg Wunsch for (i = 0; i < 4; i++) 22420e317d05SJoerg Wunsch (void)fd_sense_int(fdc, &st0, &cyl); 22435c1a1eaeSBruce Evans fdc->state = STARTRECAL; 2244fb35bd37SJoerg Wunsch /* FALLTHROUGH */ 22455c1a1eaeSBruce Evans case STARTRECAL: 22466182fdbdSPeter Wemm if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { 2247dc8603e3SJoerg Wunsch /* arrgl */ 2248dc8603e3SJoerg Wunsch fdc->retry = 6; 22496182fdbdSPeter Wemm return (retrier(fdc)); 2250dc8603e3SJoerg Wunsch } 22515b81b6b3SRodney W. Grimes fdc->state = RECALWAIT; 22525b81b6b3SRodney W. Grimes return (0); /* will return later */ 2253fb35bd37SJoerg Wunsch 22545b81b6b3SRodney W. Grimes case RECALWAIT: 22555b81b6b3SRodney W. Grimes /* allow heads to settle */ 22566182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 8); 22575b81b6b3SRodney W. Grimes fdc->state = RECALCOMPLETE; 22585b81b6b3SRodney W. Grimes return (0); /* will return later */ 2259fb35bd37SJoerg Wunsch 22605b81b6b3SRodney W. Grimes case RECALCOMPLETE: 22613a2f7427SDavid Greenman do { 22623a2f7427SDavid Greenman /* 2263dc5df763SJoerg Wunsch * See SEEKCOMPLETE for a comment on this: 22643a2f7427SDavid Greenman */ 2265dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) 2266246ed35dSJoerg Wunsch return (0); /* will return later */ 2267dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 2268dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 2269246ed35dSJoerg Wunsch return (0); /* hope for a real intr */ 22703a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 22713a2f7427SDavid Greenman if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) 22725b81b6b3SRodney W. Grimes { 2273dc8603e3SJoerg Wunsch if(fdc->retry > 3) 2274dc8603e3SJoerg Wunsch /* 2275246ed35dSJoerg Wunsch * A recalibrate from beyond cylinder 77 2276dc8603e3SJoerg Wunsch * will "fail" due to the FDC limitations; 2277dc8603e3SJoerg Wunsch * since people used to complain much about 2278dc8603e3SJoerg Wunsch * the failure message, try not logging 2279dc8603e3SJoerg Wunsch * this one if it seems to be the first 2280246ed35dSJoerg Wunsch * time in a line. 2281dc8603e3SJoerg Wunsch */ 2282dc8603e3SJoerg Wunsch printf("fd%d: recal failed ST0 %b cyl %d\n", 2283dc8603e3SJoerg Wunsch fdu, st0, NE7_ST0BITS, cyl); 22843a2f7427SDavid Greenman if(fdc->retry < 3) fdc->retry = 3; 22856182fdbdSPeter Wemm return (retrier(fdc)); 22865b81b6b3SRodney W. Grimes } 22875b81b6b3SRodney W. Grimes fd->track = 0; 22885b81b6b3SRodney W. Grimes /* Seek (probably) necessary */ 22895b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 2290246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2291fb35bd37SJoerg Wunsch 22925b81b6b3SRodney W. Grimes case MOTORWAIT: 22935b81b6b3SRodney W. Grimes if(fd->flags & FD_MOTOR_WAIT) 22945b81b6b3SRodney W. Grimes { 22955b81b6b3SRodney W. Grimes return (0); /* time's not up yet */ 22965b81b6b3SRodney W. Grimes } 22975c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 22985c1a1eaeSBruce Evans fdc->state = RESETCTLR; 22995c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 2300fb35bd37SJoerg Wunsch } else 2301fb35bd37SJoerg Wunsch fdc->state = DOSEEK; 2302246ed35dSJoerg Wunsch return (1); /* will return immediately */ 2303fb35bd37SJoerg Wunsch 23045b81b6b3SRodney W. Grimes default: 2305b6e5f28eSPeter Wemm device_printf(fdc->fdc_dev, "unexpected FD int->"); 230664860614SJoerg Wunsch if (fd_read_status(fdc) == 0) 2307a838d83dSBruce Evans printf("FDC status :%x %x %x %x %x %x %x ", 23085b81b6b3SRodney W. Grimes fdc->status[0], 23095b81b6b3SRodney W. Grimes fdc->status[1], 23105b81b6b3SRodney W. Grimes fdc->status[2], 23115b81b6b3SRodney W. Grimes fdc->status[3], 23125b81b6b3SRodney W. Grimes fdc->status[4], 23135b81b6b3SRodney W. Grimes fdc->status[5], 23145b81b6b3SRodney W. Grimes fdc->status[6] ); 23153a2f7427SDavid Greenman else 2316dac0f2dbSJoerg Wunsch printf("No status available "); 2317dac0f2dbSJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) != 0) 2318dac0f2dbSJoerg Wunsch { 2319dac0f2dbSJoerg Wunsch printf("[controller is dead now]\n"); 2320246ed35dSJoerg Wunsch return (0); /* will return later */ 23215b81b6b3SRodney W. Grimes } 2322dac0f2dbSJoerg Wunsch printf("ST0 = %x, PCN = %x\n", st0, cyl); 2323246ed35dSJoerg Wunsch return (0); /* will return later */ 2324dac0f2dbSJoerg Wunsch } 2325246ed35dSJoerg Wunsch /* noone should ever get here */ 23265b81b6b3SRodney W. Grimes } 23275b81b6b3SRodney W. Grimes 2328aaf08d94SGarrett Wollman static int 23296182fdbdSPeter Wemm retrier(struct fdc_data *fdc) 23305b81b6b3SRodney W. Grimes { 23318177437dSPoul-Henning Kamp struct bio *bp; 23326182fdbdSPeter Wemm struct fd_data *fd; 23336182fdbdSPeter Wemm int fdu; 23345b81b6b3SRodney W. Grimes 2335e93e63cbSBruce Evans bp = fdc->bp; 23365b81b6b3SRodney W. Grimes 23376182fdbdSPeter Wemm /* XXX shouldn't this be cached somewhere? */ 23388177437dSPoul-Henning Kamp fdu = FDUNIT(minor(bp->bio_dev)); 23396182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 23406182fdbdSPeter Wemm if (fd->options & FDOPT_NORETRY) 23413a2f7427SDavid Greenman goto fail; 23426182fdbdSPeter Wemm 23436182fdbdSPeter Wemm switch (fdc->retry) { 23445b81b6b3SRodney W. Grimes case 0: case 1: case 2: 23455b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 23465b81b6b3SRodney W. Grimes break; 23475b81b6b3SRodney W. Grimes case 3: case 4: case 5: 23485b81b6b3SRodney W. Grimes fdc->state = STARTRECAL; 23495b81b6b3SRodney W. Grimes break; 23505b81b6b3SRodney W. Grimes case 6: 23515b81b6b3SRodney W. Grimes fdc->state = RESETCTLR; 23525b81b6b3SRodney W. Grimes break; 23535b81b6b3SRodney W. Grimes case 7: 23545b81b6b3SRodney W. Grimes break; 23555b81b6b3SRodney W. Grimes default: 23563a2f7427SDavid Greenman fail: 2357fb35bd37SJoerg Wunsch if ((fd->options & FDOPT_NOERRLOG) == 0) { 2358fb35bd37SJoerg Wunsch diskerr(bp, "hard error", fdc->fd->skip / DEV_BSIZE, 23593a2f7427SDavid Greenman (struct disklabel *)NULL); 2360fb35bd37SJoerg Wunsch if (fdc->flags & FDC_STAT_VALID) { 2361dc5df763SJoerg Wunsch printf( 2362a838d83dSBruce Evans " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", 2363dc5df763SJoerg Wunsch fdc->status[0], NE7_ST0BITS, 2364dc5df763SJoerg Wunsch fdc->status[1], NE7_ST1BITS, 2365dc5df763SJoerg Wunsch fdc->status[2], NE7_ST2BITS, 2366dc5df763SJoerg Wunsch fdc->status[3], fdc->status[4], 2367dc5df763SJoerg Wunsch fdc->status[5]); 2368dc5df763SJoerg Wunsch } 2369dc5df763SJoerg Wunsch else 2370dc5df763SJoerg Wunsch printf(" (No status)\n"); 23715b81b6b3SRodney W. Grimes } 23722995d110SJoerg Wunsch if ((fd->options & FDOPT_NOERROR) == 0) { 23738177437dSPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 23748177437dSPoul-Henning Kamp bp->bio_error = EIO; 2375fb35bd37SJoerg Wunsch bp->bio_resid = bp->bio_bcount - fdc->fd->skip; 2376fb35bd37SJoerg Wunsch } else 2377fb35bd37SJoerg Wunsch bp->bio_resid = 0; 2378e93e63cbSBruce Evans fdc->bp = NULL; 23795b81b6b3SRodney W. Grimes fdc->fd->skip = 0; 23805f830ea2SJoerg Wunsch device_unbusy(fd->dev); 2381a468031cSPoul-Henning Kamp biofinish(bp, &fdc->fd->device_stats, 0); 238292ed385aSRodney W. Grimes fdc->state = FINDWORK; 23835c1a1eaeSBruce Evans fdc->flags |= FDC_NEEDS_RESET; 23845b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 23855b81b6b3SRodney W. Grimes fdc->fdu = -1; 238692ed385aSRodney W. Grimes return (1); 23875b81b6b3SRodney W. Grimes } 23885b81b6b3SRodney W. Grimes fdc->retry++; 23895b81b6b3SRodney W. Grimes return (1); 23905b81b6b3SRodney W. Grimes } 23915b81b6b3SRodney W. Grimes 23921fdb6e6cSPoul-Henning Kamp static void 23931fdb6e6cSPoul-Henning Kamp fdbiodone(struct bio *bp) 23941fdb6e6cSPoul-Henning Kamp { 23951fdb6e6cSPoul-Henning Kamp wakeup(bp); 23961fdb6e6cSPoul-Henning Kamp } 23971fdb6e6cSPoul-Henning Kamp 2398b39c878eSAndrey A. Chernov static int 2399f664aeeeSJoerg Wunsch fdmisccmd(dev_t dev, u_int cmd, void *data) 2400b39c878eSAndrey A. Chernov { 2401b39c878eSAndrey A. Chernov fdu_t fdu; 2402b39c878eSAndrey A. Chernov fd_p fd; 24031fdb6e6cSPoul-Henning Kamp struct bio *bp; 2404f664aeeeSJoerg Wunsch struct fd_formb *finfo; 2405f664aeeeSJoerg Wunsch struct fdc_readid *idfield; 24063a2f7427SDavid Greenman size_t fdblk; 2407b39c878eSAndrey A. Chernov 2408b39c878eSAndrey A. Chernov fdu = FDUNIT(minor(dev)); 24096182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 24103a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2411f664aeeeSJoerg Wunsch finfo = (struct fd_formb *)data; 2412f664aeeeSJoerg Wunsch idfield = (struct fdc_readid *)data; 2413b39c878eSAndrey A. Chernov 2414fb35bd37SJoerg Wunsch bp = malloc(sizeof(struct bio), M_TEMP, M_ZERO); 2415b39c878eSAndrey A. Chernov 2416b39c878eSAndrey A. Chernov /* 2417f664aeeeSJoerg Wunsch * Set up a bio request for fdstrategy(). bio_blkno is faked 2418f664aeeeSJoerg Wunsch * so that fdstrategy() will seek to the the requested 2419f664aeeeSJoerg Wunsch * cylinder, and use the desired head. Since we are not 2420f664aeeeSJoerg Wunsch * interested in bioqdisksort() munging with our faked bio 2421f664aeeeSJoerg Wunsch * request, we mark it as being an ordered request. 2422b39c878eSAndrey A. Chernov */ 2423f664aeeeSJoerg Wunsch bp->bio_cmd = cmd; 2424f664aeeeSJoerg Wunsch if (cmd == BIO_FORMAT) { 2425f664aeeeSJoerg Wunsch bp->bio_blkno = 2426f664aeeeSJoerg Wunsch (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + 2427f664aeeeSJoerg Wunsch finfo->head * fd->ft->sectrac) * 2428f664aeeeSJoerg Wunsch fdblk / DEV_BSIZE; 2429f664aeeeSJoerg Wunsch bp->bio_bcount = sizeof(struct fd_idfield_data) * 2430f664aeeeSJoerg Wunsch finfo->fd_formb_nsecs; 2431f664aeeeSJoerg Wunsch } else if (cmd == BIO_RDSECTID) { 2432f664aeeeSJoerg Wunsch bp->bio_blkno = 2433f664aeeeSJoerg Wunsch (idfield->cyl * (fd->ft->sectrac * fd->ft->heads) + 2434f664aeeeSJoerg Wunsch idfield->head * fd->ft->sectrac) * 2435f664aeeeSJoerg Wunsch fdblk / DEV_BSIZE; 2436250300ebSJoerg Wunsch bp->bio_bcount = sizeof(struct fdc_readid); 2437f664aeeeSJoerg Wunsch } else 2438f664aeeeSJoerg Wunsch panic("wrong cmd in fdmisccmd()"); 2439f664aeeeSJoerg Wunsch bp->bio_data = data; 2440250300ebSJoerg Wunsch bp->bio_dev = dev; 2441250300ebSJoerg Wunsch bp->bio_done = fdbiodone; 2442f664aeeeSJoerg Wunsch bp->bio_flags = BIO_ORDERED; 2443250300ebSJoerg Wunsch 2444f5f7ba03SJordan K. Hubbard /* 2445fb35bd37SJoerg Wunsch * Now run the command. The wait loop is a version of bufwait() 2446fb35bd37SJoerg Wunsch * adapted for struct bio instead of struct buf and specialized 2447fb35bd37SJoerg Wunsch * for the current context. 2448f5f7ba03SJordan K. Hubbard */ 2449f664aeeeSJoerg Wunsch fdstrategy(bp); 2450f664aeeeSJoerg Wunsch while ((bp->bio_flags & BIO_DONE) == 0) 2451f664aeeeSJoerg Wunsch tsleep(bp, PRIBIO, "fdcmd", 0); 2452f664aeeeSJoerg Wunsch 2453f664aeeeSJoerg Wunsch free(bp, M_TEMP); 2454f664aeeeSJoerg Wunsch return (bp->bio_flags & BIO_ERROR ? bp->bio_error : 0); 2455f664aeeeSJoerg Wunsch } 24565b81b6b3SRodney W. Grimes 24573e425b96SJulian Elischer static int 24584c34deeeSJoerg Wunsch fdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) 2459f5f7ba03SJordan K. Hubbard { 2460f664aeeeSJoerg Wunsch fdu_t fdu; 2461f664aeeeSJoerg Wunsch fd_p fd; 2462f5f7ba03SJordan K. Hubbard struct fd_type *fdt; 2463fb35bd37SJoerg Wunsch struct disklabel *lp; 24642995d110SJoerg Wunsch struct fdc_status *fsp; 2465250300ebSJoerg Wunsch struct fdc_readid *rid; 2466f664aeeeSJoerg Wunsch size_t fdblk; 2467fb35bd37SJoerg Wunsch int error; 2468f5f7ba03SJordan K. Hubbard 2469f664aeeeSJoerg Wunsch fdu = FDUNIT(minor(dev)); 2470f664aeeeSJoerg Wunsch fd = devclass_get_softc(fd_devclass, fdu); 2471fb35bd37SJoerg Wunsch 2472fb35bd37SJoerg Wunsch 24733a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2474fb35bd37SJoerg Wunsch error = 0; 2475f5f7ba03SJordan K. Hubbard 24766182fdbdSPeter Wemm switch (cmd) { 2477f5f7ba03SJordan K. Hubbard case DIOCGDINFO: 2478fb35bd37SJoerg Wunsch lp = malloc(sizeof(*lp), M_TEMP, M_ZERO); 2479fb35bd37SJoerg Wunsch lp->d_secsize = fdblk; 24806182fdbdSPeter Wemm fdt = fd->ft; 2481fb35bd37SJoerg Wunsch lp->d_secpercyl = fdt->size / fdt->tracks; 2482fb35bd37SJoerg Wunsch lp->d_type = DTYPE_FLOPPY; 2483fb35bd37SJoerg Wunsch if (readdisklabel(dkmodpart(dev, RAW_PART), lp) != NULL) 2484f5f7ba03SJordan K. Hubbard error = EINVAL; 2485fb35bd37SJoerg Wunsch else 2486fb35bd37SJoerg Wunsch *(struct disklabel *)addr = *lp; 2487fb35bd37SJoerg Wunsch free(lp, M_TEMP); 2488f5f7ba03SJordan K. Hubbard break; 2489f5f7ba03SJordan K. Hubbard 2490f5f7ba03SJordan K. Hubbard case DIOCSDINFO: 2491f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2492fb35bd37SJoerg Wunsch return (EBADF); 2493fb35bd37SJoerg Wunsch /* 2494fb35bd37SJoerg Wunsch * XXX perhaps should call setdisklabel() to do error checking 2495fb35bd37SJoerg Wunsch * although there is nowhere to "set" the result. Perhaps 2496fb35bd37SJoerg Wunsch * should always just fail. 2497fb35bd37SJoerg Wunsch */ 2498f5f7ba03SJordan K. Hubbard break; 2499f5f7ba03SJordan K. Hubbard 2500f5f7ba03SJordan K. Hubbard case DIOCWLABEL: 2501f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2502fb35bd37SJoerg Wunsch return (EBADF); 2503f5f7ba03SJordan K. Hubbard break; 2504f5f7ba03SJordan K. Hubbard 2505f5f7ba03SJordan K. Hubbard case DIOCWDINFO: 2506fb35bd37SJoerg Wunsch if ((flag & FWRITE) == 0) 2507fb35bd37SJoerg Wunsch return (EBADF); 2508fb35bd37SJoerg Wunsch lp = malloc(DEV_BSIZE, M_TEMP, M_ZERO); 2509fb35bd37SJoerg Wunsch error = setdisklabel(lp, (struct disklabel *)addr, (u_long)0); 2510fb35bd37SJoerg Wunsch if (error != 0) 2511fb35bd37SJoerg Wunsch error = writedisklabel(dev, lp); 2512fb35bd37SJoerg Wunsch free(lp, M_TEMP); 2513b39c878eSAndrey A. Chernov break; 2514f664aeeeSJoerg Wunsch 2515b39c878eSAndrey A. Chernov case FD_FORM: 2516b39c878eSAndrey A. Chernov if ((flag & FWRITE) == 0) 2517fb35bd37SJoerg Wunsch return (EBADF); /* must be opened for writing */ 2518fb35bd37SJoerg Wunsch if (((struct fd_formb *)addr)->format_version != 2519b39c878eSAndrey A. Chernov FD_FORMAT_VERSION) 2520fb35bd37SJoerg Wunsch return (EINVAL); /* wrong version of formatting prog */ 2521f664aeeeSJoerg Wunsch error = fdmisccmd(dev, BIO_FORMAT, addr); 2522b39c878eSAndrey A. Chernov break; 2523b39c878eSAndrey A. Chernov 2524b39c878eSAndrey A. Chernov case FD_GTYPE: /* get drive type */ 25253e425b96SJulian Elischer *(struct fd_type *)addr = *fd->ft; 2526f5f7ba03SJordan K. Hubbard break; 2527f5f7ba03SJordan K. Hubbard 25283a2f7427SDavid Greenman case FD_STYPE: /* set drive type */ 25293a2f7427SDavid Greenman /* this is considered harmful; only allow for superuser */ 2530f711d546SPoul-Henning Kamp if (suser(p) != 0) 2531fb35bd37SJoerg Wunsch return (EPERM); 25323e425b96SJulian Elischer *fd->ft = *(struct fd_type *)addr; 25333a2f7427SDavid Greenman break; 25343a2f7427SDavid Greenman 25353a2f7427SDavid Greenman case FD_GOPTS: /* get drive options */ 25363e425b96SJulian Elischer *(int *)addr = fd->options; 25373a2f7427SDavid Greenman break; 25383a2f7427SDavid Greenman 25393a2f7427SDavid Greenman case FD_SOPTS: /* set drive options */ 25403e425b96SJulian Elischer fd->options = *(int *)addr; 25413a2f7427SDavid Greenman break; 25423a2f7427SDavid Greenman 2543f664aeeeSJoerg Wunsch #ifdef FDC_DEBUG 2544f664aeeeSJoerg Wunsch case FD_DEBUG: 25450e17a5bcSJoerg Wunsch if ((fd_debug != 0) != (*(int *)addr != 0)) { 25460e17a5bcSJoerg Wunsch fd_debug = (*(int *)addr != 0); 25470e17a5bcSJoerg Wunsch printf("fd%d: debugging turned %s\n", 25480e17a5bcSJoerg Wunsch fd->fdu, fd_debug ? "on" : "off"); 25490e17a5bcSJoerg Wunsch } 2550f664aeeeSJoerg Wunsch break; 2551f664aeeeSJoerg Wunsch #endif 2552f664aeeeSJoerg Wunsch 25532995d110SJoerg Wunsch case FD_CLRERR: 25542995d110SJoerg Wunsch if (suser(p) != 0) 2555fb35bd37SJoerg Wunsch return (EPERM); 25562995d110SJoerg Wunsch fd->fdc->fdc_errs = 0; 25572995d110SJoerg Wunsch break; 25582995d110SJoerg Wunsch 25592995d110SJoerg Wunsch case FD_GSTAT: 25602995d110SJoerg Wunsch fsp = (struct fdc_status *)addr; 25612995d110SJoerg Wunsch if ((fd->fdc->flags & FDC_STAT_VALID) == 0) 2562fb35bd37SJoerg Wunsch return (EINVAL); 25632995d110SJoerg Wunsch memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); 25642995d110SJoerg Wunsch break; 25652995d110SJoerg Wunsch 2566250300ebSJoerg Wunsch case FD_READID: 2567250300ebSJoerg Wunsch rid = (struct fdc_readid *)addr; 2568250300ebSJoerg Wunsch if (rid->cyl > MAX_CYLINDER || rid->head > MAX_HEAD) 2569fb35bd37SJoerg Wunsch return (EINVAL); 2570f664aeeeSJoerg Wunsch error = fdmisccmd(dev, BIO_RDSECTID, addr); 2571250300ebSJoerg Wunsch break; 2572250300ebSJoerg Wunsch 2573f5f7ba03SJordan K. Hubbard default: 25743a2f7427SDavid Greenman error = ENOTTY; 2575f5f7ba03SJordan K. Hubbard break; 2576f5f7ba03SJordan K. Hubbard } 2577f5f7ba03SJordan K. Hubbard return (error); 2578f5f7ba03SJordan K. Hubbard } 2579