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 * 215b81b6b3SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 225b81b6b3SRodney W. Grimes * modification, are permitted provided that the following conditions 235b81b6b3SRodney W. Grimes * are met: 245b81b6b3SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 255b81b6b3SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 265b81b6b3SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 275b81b6b3SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 285b81b6b3SRodney W. Grimes * documentation and/or other materials provided with the distribution. 295b81b6b3SRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 305b81b6b3SRodney W. Grimes * must display the following acknowledgement: 315b81b6b3SRodney W. Grimes * This product includes software developed by the University of 325b81b6b3SRodney W. Grimes * California, Berkeley and its contributors. 335b81b6b3SRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 345b81b6b3SRodney W. Grimes * may be used to endorse or promote products derived from this software 355b81b6b3SRodney W. Grimes * without specific prior written permission. 365b81b6b3SRodney W. Grimes * 375b81b6b3SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 385b81b6b3SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 395b81b6b3SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 405b81b6b3SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 415b81b6b3SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 425b81b6b3SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 435b81b6b3SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 445b81b6b3SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 455b81b6b3SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 465b81b6b3SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 475b81b6b3SRodney W. Grimes * SUCH DAMAGE. 485b81b6b3SRodney W. Grimes * 49dc4ff321SRodney W. Grimes * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 50c3aac50fSPeter Wemm * $FreeBSD$ 515b81b6b3SRodney W. Grimes * 525b81b6b3SRodney W. Grimes */ 535b81b6b3SRodney W. Grimes 54d2fb4892SJoerg Wunsch #include "opt_fdc.h" 555b81b6b3SRodney W. Grimes 56b99f0a4aSAndrew Moore #include <sys/param.h> 57b99f0a4aSAndrew Moore #include <sys/systm.h> 58b99f0a4aSAndrew Moore #include <sys/kernel.h> 59b99f0a4aSAndrew Moore #include <sys/buf.h> 606182fdbdSPeter Wemm #include <sys/bus.h> 616182fdbdSPeter Wemm #include <sys/conf.h> 626182fdbdSPeter Wemm #include <sys/disklabel.h> 63b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h> 646182fdbdSPeter Wemm #include <sys/fcntl.h> 65b99f0a4aSAndrew Moore #include <sys/malloc.h> 666182fdbdSPeter Wemm #include <sys/module.h> 673a2f7427SDavid Greenman #include <sys/proc.h> 68b99f0a4aSAndrew Moore #include <sys/syslog.h> 696182fdbdSPeter Wemm 706182fdbdSPeter Wemm #include <sys/bus.h> 716182fdbdSPeter Wemm #include <machine/bus.h> 726182fdbdSPeter Wemm #include <sys/rman.h> 736182fdbdSPeter Wemm 746182fdbdSPeter Wemm #include <machine/clock.h> 756182fdbdSPeter Wemm #include <machine/ioctl_fd.h> 766182fdbdSPeter Wemm #include <machine/resource.h> 77dc5df763SJoerg Wunsch #include <machine/stdarg.h> 786182fdbdSPeter Wemm 796182fdbdSPeter Wemm #include <isa/isavar.h> 80a97c75b7SDoug Rabson #include <isa/isareg.h> 81a97c75b7SDoug Rabson #include <isa/fdreg.h> 82a97c75b7SDoug Rabson #include <isa/fdc.h> 83a97c75b7SDoug Rabson #include <isa/rtc.h> 846182fdbdSPeter Wemm 85b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */ 86b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX 875b81b6b3SRodney W. Grimes 880722d6abSJoerg Wunsch /* configuration flags */ 890722d6abSJoerg Wunsch #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ 90e34c71eaSJoerg Wunsch #define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */ 910722d6abSJoerg Wunsch 920722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */ 930722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED 0x1000 940722d6abSJoerg Wunsch 95dc5df763SJoerg Wunsch /* error returns for fd_cmd() */ 96dc5df763SJoerg Wunsch #define FD_FAILED -1 97dc5df763SJoerg Wunsch #define FD_NOT_VALID -2 98dc5df763SJoerg Wunsch #define FDC_ERRMAX 100 /* do not log more */ 99dc5df763SJoerg Wunsch 1006fb89845SKATO Takenori #define NUMTYPES 17 1016fb89845SKATO Takenori #define NUMDENS (NUMTYPES - 7) 1027ca0641bSAndrey A. Chernov 1033a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */ 104b99f0a4aSAndrew Moore #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ 105b99f0a4aSAndrew Moore #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ 106b99f0a4aSAndrew Moore #define FD_1720 1 107b99f0a4aSAndrew Moore #define FD_1480 2 108b99f0a4aSAndrew Moore #define FD_1440 3 109b99f0a4aSAndrew Moore #define FD_1200 4 110b99f0a4aSAndrew Moore #define FD_820 5 111b99f0a4aSAndrew Moore #define FD_800 6 112b99f0a4aSAndrew Moore #define FD_720 7 113b99f0a4aSAndrew Moore #define FD_360 8 1146fb89845SKATO Takenori #define FD_640 9 1156fb89845SKATO Takenori #define FD_1232 10 116ed2fa05eSAndrey A. Chernov 1176fb89845SKATO Takenori #define FD_1480in5_25 11 1186fb89845SKATO Takenori #define FD_1440in5_25 12 1196fb89845SKATO Takenori #define FD_820in5_25 13 1206fb89845SKATO Takenori #define FD_800in5_25 14 1216fb89845SKATO Takenori #define FD_720in5_25 15 1226fb89845SKATO Takenori #define FD_360in5_25 16 1236fb89845SKATO Takenori #define FD_640in5_25 17 124b99f0a4aSAndrew Moore 1257ca0641bSAndrey A. Chernov 1266f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] = 1275b81b6b3SRodney W. Grimes { 128126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ 129126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ 130126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ 131126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ 132126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ 133126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ 134126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ 135b0568305SAndrey A. Chernov { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ 1366fb89845SKATO Takenori { 8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /* 640K in DD 5.25in */ 1376fb89845SKATO Takenori { 8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */ 138ed2fa05eSAndrey A. Chernov 139126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ 140126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ 141126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ 142126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ 143126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ 144126518a1SAndrey A. Chernov { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ 1456fb89845SKATO Takenori { 8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /* 640K in HD 5.25in */ 1465b81b6b3SRodney W. Grimes }; 1475b81b6b3SRodney W. Grimes 148b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2 /* 2 floppies */ 149dc16046fSJoerg Wunsch 1505b81b6b3SRodney W. Grimes /***********************************************************************\ 1515b81b6b3SRodney W. Grimes * Per controller structure. * 1525b81b6b3SRodney W. Grimes \***********************************************************************/ 1536182fdbdSPeter Wemm static devclass_t fdc_devclass; 1545b81b6b3SRodney W. Grimes 1555b81b6b3SRodney W. Grimes /***********************************************************************\ 1565b81b6b3SRodney W. Grimes * Per drive structure. * 157b99f0a4aSAndrew Moore * N per controller (DRVS_PER_CTLR) * 1585b81b6b3SRodney W. Grimes \***********************************************************************/ 1596182fdbdSPeter Wemm struct fd_data { 160b99f0a4aSAndrew Moore struct fdc_data *fdc; /* pointer to controller structure */ 1615b81b6b3SRodney W. Grimes int fdsu; /* this units number on this controller */ 1623a2f7427SDavid Greenman int type; /* Drive type (FD_1440...) */ 1635b81b6b3SRodney W. Grimes struct fd_type *ft; /* pointer to the type descriptor */ 1645b81b6b3SRodney W. Grimes int flags; 1655b81b6b3SRodney W. Grimes #define FD_OPEN 0x01 /* it's open */ 1665b81b6b3SRodney W. Grimes #define FD_ACTIVE 0x02 /* it's active */ 1675b81b6b3SRodney W. Grimes #define FD_MOTOR 0x04 /* motor should be on */ 1685b81b6b3SRodney W. Grimes #define FD_MOTOR_WAIT 0x08 /* motor coming up */ 1695b81b6b3SRodney W. Grimes int skip; 1705b81b6b3SRodney W. Grimes int hddrv; 171dc5df763SJoerg Wunsch #define FD_NO_TRACK -2 1725b81b6b3SRodney W. Grimes int track; /* where we think the head is */ 1733a2f7427SDavid Greenman int options; /* user configurable options, see ioctl_fd.h */ 17402a19910SJustin T. Gibbs struct callout_handle toffhandle; 17502a19910SJustin T. Gibbs struct callout_handle tohandle; 176b2dfb1f9SJustin T. Gibbs struct devstat device_stats; 1776182fdbdSPeter Wemm device_t dev; 1786182fdbdSPeter Wemm fdu_t fdu; 1796182fdbdSPeter Wemm }; 18037286586SPeter Wemm 18137286586SPeter Wemm struct fdc_ivars { 18237286586SPeter Wemm int fdunit; 18337286586SPeter Wemm }; 1846182fdbdSPeter Wemm static devclass_t fd_devclass; 1855b81b6b3SRodney W. Grimes 1865b81b6b3SRodney W. Grimes /***********************************************************************\ 1875b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used: * 1885b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question * 1895b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller * 1905b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number * 1915b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number * 1925b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit) * 1935b81b6b3SRodney W. Grimes \***********************************************************************/ 194b99f0a4aSAndrew Moore 1953a2f7427SDavid Greenman /* needed for ft driver, thus exported */ 1966182fdbdSPeter Wemm int in_fdc(struct fdc_data *); 1976182fdbdSPeter Wemm int out_fdc(struct fdc_data *, int); 1983a2f7427SDavid Greenman 1993a2f7427SDavid Greenman /* internal functions */ 2006182fdbdSPeter Wemm static void fdc_intr(void *); 2016182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int); 2023a2f7427SDavid Greenman # define TURNON 1 2033a2f7427SDavid Greenman # define TURNOFF 0 2043a2f7427SDavid Greenman static timeout_t fd_turnoff; 2053a2f7427SDavid Greenman static timeout_t fd_motor_on; 2066182fdbdSPeter Wemm static void fd_turnon(struct fd_data *); 2073a2f7427SDavid Greenman static void fdc_reset(fdc_p); 2086182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *); 2096182fdbdSPeter Wemm static void fdstart(struct fdc_data *); 2105c1a1eaeSBruce Evans static timeout_t fd_iotimeout; 2113a2f7427SDavid Greenman static timeout_t fd_pseudointr; 2126182fdbdSPeter Wemm static int fdstate(struct fdc_data *); 2136182fdbdSPeter Wemm static int retrier(struct fdc_data *); 2143a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *); 2153a2f7427SDavid Greenman 216d66c374fSTor Egge static int enable_fifo(fdc_p fdc); 217d66c374fSTor Egge 218d66c374fSTor Egge static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ 219d66c374fSTor Egge 220aaf08d94SGarrett Wollman 2215b81b6b3SRodney W. Grimes #define DEVIDLE 0 2225b81b6b3SRodney W. Grimes #define FINDWORK 1 2235b81b6b3SRodney W. Grimes #define DOSEEK 2 2245b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 3 2255b81b6b3SRodney W. Grimes #define IOCOMPLETE 4 2265b81b6b3SRodney W. Grimes #define RECALCOMPLETE 5 2275b81b6b3SRodney W. Grimes #define STARTRECAL 6 2285b81b6b3SRodney W. Grimes #define RESETCTLR 7 2295b81b6b3SRodney W. Grimes #define SEEKWAIT 8 2305b81b6b3SRodney W. Grimes #define RECALWAIT 9 2315b81b6b3SRodney W. Grimes #define MOTORWAIT 10 2325b81b6b3SRodney W. Grimes #define IOTIMEDOUT 11 2335c1a1eaeSBruce Evans #define RESETCOMPLETE 12 23469acd21dSWarner Losh #define PIOREAD 13 2355b81b6b3SRodney W. Grimes 236d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 237cba2a7c6SBruce Evans static char const * const fdstates[] = 2385b81b6b3SRodney W. Grimes { 2395b81b6b3SRodney W. Grimes "DEVIDLE", 2405b81b6b3SRodney W. Grimes "FINDWORK", 2415b81b6b3SRodney W. Grimes "DOSEEK", 2425b81b6b3SRodney W. Grimes "SEEKCOMPLETE", 2435b81b6b3SRodney W. Grimes "IOCOMPLETE", 2445b81b6b3SRodney W. Grimes "RECALCOMPLETE", 2455b81b6b3SRodney W. Grimes "STARTRECAL", 2465b81b6b3SRodney W. Grimes "RESETCTLR", 2475b81b6b3SRodney W. Grimes "SEEKWAIT", 2485b81b6b3SRodney W. Grimes "RECALWAIT", 2495b81b6b3SRodney W. Grimes "MOTORWAIT", 2505c1a1eaeSBruce Evans "IOTIMEDOUT", 2515c1a1eaeSBruce Evans "RESETCOMPLETE", 252ff9607b0SBruce Evans "PIOREAD", 2535b81b6b3SRodney W. Grimes }; 2545b81b6b3SRodney W. Grimes 2553a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */ 256cba2a7c6SBruce Evans static int volatile fd_debug = 0; 2575b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg) 2585b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) 259d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */ 2605b81b6b3SRodney W. Grimes #define TRACE0(arg) 2615b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) 262d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 2635b81b6b3SRodney W. Grimes 264427ccf00SDoug Rabson static void 265427ccf00SDoug Rabson fdout_wr(fdc_p fdc, u_int8_t v) 266427ccf00SDoug Rabson { 267427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); 268427ccf00SDoug Rabson } 269427ccf00SDoug Rabson 270427ccf00SDoug Rabson static u_int8_t 271427ccf00SDoug Rabson fdsts_rd(fdc_p fdc) 272427ccf00SDoug Rabson { 273427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); 274427ccf00SDoug Rabson } 275427ccf00SDoug Rabson 276427ccf00SDoug Rabson static void 277427ccf00SDoug Rabson fddata_wr(fdc_p fdc, u_int8_t v) 278427ccf00SDoug Rabson { 279427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); 280427ccf00SDoug Rabson } 281427ccf00SDoug Rabson 282427ccf00SDoug Rabson static u_int8_t 283427ccf00SDoug Rabson fddata_rd(fdc_p fdc) 284427ccf00SDoug Rabson { 285427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); 286427ccf00SDoug Rabson } 287427ccf00SDoug Rabson 288427ccf00SDoug Rabson static void 289427ccf00SDoug Rabson fdctl_wr(fdc_p fdc, u_int8_t v) 290427ccf00SDoug Rabson { 291427ccf00SDoug Rabson if (fdc->flags & FDC_ISPNP) 292427ccf00SDoug Rabson bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); 293427ccf00SDoug Rabson else 294427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDCTL, v); 295427ccf00SDoug Rabson } 296427ccf00SDoug Rabson 297427ccf00SDoug Rabson #if 0 298427ccf00SDoug Rabson 299427ccf00SDoug Rabson static u_int8_t 300427ccf00SDoug Rabson fdin_rd(fdc_p fdc) 301427ccf00SDoug Rabson { 302427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDIN); 303427ccf00SDoug Rabson } 304427ccf00SDoug Rabson 305427ccf00SDoug Rabson #endif 306427ccf00SDoug Rabson 30769acd21dSWarner Losh #ifdef FDC_YE 30869acd21dSWarner Losh #if NCARD > 0 30969acd21dSWarner Losh #include <sys/select.h> 310d17e4ee6SPeter Wemm #include <sys/module.h> 31169acd21dSWarner Losh #include <pccard/cardinfo.h> 31269acd21dSWarner Losh #include <pccard/driver.h> 31369acd21dSWarner Losh #include <pccard/slot.h> 31469acd21dSWarner Losh 31569acd21dSWarner Losh /* 31669acd21dSWarner Losh * PC-Card (PCMCIA) specific code. 31769acd21dSWarner Losh */ 31869acd21dSWarner Losh static int yeinit(struct pccard_devinfo *); /* init device */ 31969acd21dSWarner Losh static void yeunload(struct pccard_devinfo *); /* Disable driver */ 32069acd21dSWarner Losh static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ 32169acd21dSWarner Losh 322d17e4ee6SPeter Wemm PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); 32369acd21dSWarner Losh 32469acd21dSWarner Losh /* 32569acd21dSWarner Losh * Initialize the device - called from Slot manager. 32669acd21dSWarner Losh */ 32769acd21dSWarner Losh static int yeinit(struct pccard_devinfo *devi) 32869acd21dSWarner Losh { 32969acd21dSWarner Losh fdc_p fdc = &fdc_data[devi->isahd.id_unit]; 33069acd21dSWarner Losh 33169acd21dSWarner Losh fdc->baseport = devi->isahd.id_iobase; 33269acd21dSWarner Losh /* 33369acd21dSWarner Losh * reset controller 33469acd21dSWarner Losh */ 335427ccf00SDoug Rabson fdout_wr(fdc, 0); 33669acd21dSWarner Losh DELAY(100); 337427ccf00SDoug Rabson fdout_wr(fdc, FDO_FRST); 33869acd21dSWarner Losh 33969acd21dSWarner Losh /* 34069acd21dSWarner Losh * wire into system 34169acd21dSWarner Losh */ 34269acd21dSWarner Losh if (yeattach(&devi->isahd) == 0) 34369acd21dSWarner Losh return(ENXIO); 34469acd21dSWarner Losh 34569acd21dSWarner Losh return(0); 34669acd21dSWarner Losh } 34769acd21dSWarner Losh 34869acd21dSWarner Losh /* 34969acd21dSWarner Losh * yeunload - unload the driver and clear the table. 35069acd21dSWarner Losh * XXX TODO: 35169acd21dSWarner Losh * This is usually called when the card is ejected, but 35269acd21dSWarner Losh * can be caused by a modunload of a controller driver. 35369acd21dSWarner Losh * The idea is to reset the driver's view of the device 35469acd21dSWarner Losh * and ensure that any driver entry points such as 35569acd21dSWarner Losh * read and write do not hang. 35669acd21dSWarner Losh */ 35769acd21dSWarner Losh static void yeunload(struct pccard_devinfo *devi) 35869acd21dSWarner Losh { 35969acd21dSWarner Losh if (fd_data[devi->isahd.id_unit].type == NO_TYPE) 36069acd21dSWarner Losh return; 36169acd21dSWarner Losh 36269acd21dSWarner Losh /* 36369acd21dSWarner Losh * this prevents Fdopen() and fdstrategy() from attempting 36469acd21dSWarner Losh * to access unloaded controller 36569acd21dSWarner Losh */ 36669acd21dSWarner Losh fd_data[devi->isahd.id_unit].type = NO_TYPE; 36769acd21dSWarner Losh 36869acd21dSWarner Losh printf("fdc%d: unload\n", devi->isahd.id_unit); 36969acd21dSWarner Losh } 37069acd21dSWarner Losh 37169acd21dSWarner Losh /* 37269acd21dSWarner Losh * yeintr - Shared interrupt called from 37369acd21dSWarner Losh * front end of PC-Card handler. 37469acd21dSWarner Losh */ 37569acd21dSWarner Losh static int yeintr(struct pccard_devinfo *devi) 37669acd21dSWarner Losh { 37769acd21dSWarner Losh fdintr((fdcu_t)devi->isahd.id_unit); 37869acd21dSWarner Losh return(1); 37969acd21dSWarner Losh } 38069acd21dSWarner Losh #endif /* NCARD > 0 */ 38169acd21dSWarner Losh #endif /* FDC_YE */ 38269acd21dSWarner Losh 38387f6c662SJulian Elischer static d_open_t Fdopen; /* NOTE, not fdopen */ 38487f6c662SJulian Elischer static d_close_t fdclose; 38587f6c662SJulian Elischer static d_ioctl_t fdioctl; 38687f6c662SJulian Elischer static d_strategy_t fdstrategy; 38787f6c662SJulian Elischer 38887f6c662SJulian Elischer #define CDEV_MAJOR 9 38987f6c662SJulian Elischer #define BDEV_MAJOR 2 390f7ea2f55SJulian Elischer 3914e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = { 3924e2f199eSPoul-Henning Kamp /* open */ Fdopen, 3934e2f199eSPoul-Henning Kamp /* close */ fdclose, 3944e2f199eSPoul-Henning Kamp /* read */ physread, 3954e2f199eSPoul-Henning Kamp /* write */ physwrite, 3964e2f199eSPoul-Henning Kamp /* ioctl */ fdioctl, 3974e2f199eSPoul-Henning Kamp /* poll */ nopoll, 3984e2f199eSPoul-Henning Kamp /* mmap */ nommap, 3994e2f199eSPoul-Henning Kamp /* strategy */ fdstrategy, 4004e2f199eSPoul-Henning Kamp /* name */ "fd", 4014e2f199eSPoul-Henning Kamp /* maj */ CDEV_MAJOR, 4024e2f199eSPoul-Henning Kamp /* dump */ nodump, 4034e2f199eSPoul-Henning Kamp /* psize */ nopsize, 4044e2f199eSPoul-Henning Kamp /* flags */ D_DISK, 4054e2f199eSPoul-Henning Kamp /* bmaj */ BDEV_MAJOR 4064e2f199eSPoul-Henning Kamp }; 4074e2f199eSPoul-Henning Kamp 408dc5df763SJoerg Wunsch static int 4096182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s) 410dc5df763SJoerg Wunsch { 4116182fdbdSPeter Wemm fdc->fdc_errs++; 41216b04b6aSJoerg Wunsch if (s) { 4136182fdbdSPeter Wemm if (fdc->fdc_errs < FDC_ERRMAX) { 4146182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 4156182fdbdSPeter Wemm printf("%s", s); 4166182fdbdSPeter Wemm } else if (fdc->fdc_errs == FDC_ERRMAX) { 4176182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 4186182fdbdSPeter Wemm printf("too many errors, not logging any more\n"); 4196182fdbdSPeter Wemm } 42016b04b6aSJoerg Wunsch } 421dc5df763SJoerg Wunsch 422dc5df763SJoerg Wunsch return FD_FAILED; 423dc5df763SJoerg Wunsch } 424dc5df763SJoerg Wunsch 425dc5df763SJoerg Wunsch /* 426dc5df763SJoerg Wunsch * fd_cmd: Send a command to the chip. Takes a varargs with this structure: 427dc5df763SJoerg Wunsch * Unit number, 428dc5df763SJoerg Wunsch * # of output bytes, output bytes as ints ..., 429dc5df763SJoerg Wunsch * # of input bytes, input bytes as ints ... 430dc5df763SJoerg Wunsch */ 4316f4e0bebSPoul-Henning Kamp static int 4326182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...) 433dc5df763SJoerg Wunsch { 434dc5df763SJoerg Wunsch u_char cmd; 435dc5df763SJoerg Wunsch int n_in; 436dc5df763SJoerg Wunsch int n; 437dc5df763SJoerg Wunsch va_list ap; 438dc5df763SJoerg Wunsch 439dc5df763SJoerg Wunsch va_start(ap, n_out); 440dc5df763SJoerg Wunsch cmd = (u_char)(va_arg(ap, int)); 441dc5df763SJoerg Wunsch va_end(ap); 442dc5df763SJoerg Wunsch va_start(ap, n_out); 443dc5df763SJoerg Wunsch for (n = 0; n < n_out; n++) 444dc5df763SJoerg Wunsch { 4456182fdbdSPeter Wemm if (out_fdc(fdc, va_arg(ap, int)) < 0) 446dc5df763SJoerg Wunsch { 447dc5df763SJoerg Wunsch char msg[50]; 4482127f260SArchie Cobbs snprintf(msg, sizeof(msg), 449dc5df763SJoerg Wunsch "cmd %x failed at out byte %d of %d\n", 450dc5df763SJoerg Wunsch cmd, n + 1, n_out); 4516182fdbdSPeter Wemm return fdc_err(fdc, msg); 452dc5df763SJoerg Wunsch } 453dc5df763SJoerg Wunsch } 454dc5df763SJoerg Wunsch n_in = va_arg(ap, int); 455dc5df763SJoerg Wunsch for (n = 0; n < n_in; n++) 456dc5df763SJoerg Wunsch { 457dc5df763SJoerg Wunsch int *ptr = va_arg(ap, int *); 4586182fdbdSPeter Wemm if (fd_in(fdc, ptr) < 0) 459dc5df763SJoerg Wunsch { 460dc5df763SJoerg Wunsch char msg[50]; 4612127f260SArchie Cobbs snprintf(msg, sizeof(msg), 462dc5df763SJoerg Wunsch "cmd %02x failed at in byte %d of %d\n", 463dc5df763SJoerg Wunsch cmd, n + 1, n_in); 4646182fdbdSPeter Wemm return fdc_err(fdc, msg); 465dc5df763SJoerg Wunsch } 466dc5df763SJoerg Wunsch } 467dc5df763SJoerg Wunsch 468dc5df763SJoerg Wunsch return 0; 469dc5df763SJoerg Wunsch } 470dc5df763SJoerg Wunsch 4716f4e0bebSPoul-Henning Kamp static int 472d66c374fSTor Egge enable_fifo(fdc_p fdc) 473d66c374fSTor Egge { 474d66c374fSTor Egge int i, j; 475d66c374fSTor Egge 476d66c374fSTor Egge if ((fdc->flags & FDC_HAS_FIFO) == 0) { 477d66c374fSTor Egge 478d66c374fSTor Egge /* 479d66c374fSTor Egge * XXX: 480d66c374fSTor Egge * Cannot use fd_cmd the normal way here, since 481d66c374fSTor Egge * this might be an invalid command. Thus we send the 482d66c374fSTor Egge * first byte, and check for an early turn of data directon. 483d66c374fSTor Egge */ 484d66c374fSTor Egge 4856182fdbdSPeter Wemm if (out_fdc(fdc, I8207X_CONFIGURE) < 0) 4866182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 487d66c374fSTor Egge 488d66c374fSTor Egge /* If command is invalid, return */ 489d66c374fSTor Egge j = 100000; 490427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) 491d66c374fSTor Egge != NE7_RQM && j-- > 0) 492d66c374fSTor Egge if (i == (NE7_DIO | NE7_RQM)) { 493d66c374fSTor Egge fdc_reset(fdc); 494d66c374fSTor Egge return FD_FAILED; 495d66c374fSTor Egge } 496d66c374fSTor Egge if (j<0 || 4976182fdbdSPeter Wemm fd_cmd(fdc, 3, 498d66c374fSTor Egge 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { 499d66c374fSTor Egge fdc_reset(fdc); 5006182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 501d66c374fSTor Egge } 502d66c374fSTor Egge fdc->flags |= FDC_HAS_FIFO; 503d66c374fSTor Egge return 0; 504d66c374fSTor Egge } 5056182fdbdSPeter Wemm if (fd_cmd(fdc, 4, 506d66c374fSTor Egge I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) 5076182fdbdSPeter Wemm return fdc_err(fdc, "Re-enable FIFO failed\n"); 508d66c374fSTor Egge return 0; 509d66c374fSTor Egge } 510d66c374fSTor Egge 511d66c374fSTor Egge static int 512dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p) 513dc5df763SJoerg Wunsch { 514dc5df763SJoerg Wunsch int st3; 515dc5df763SJoerg Wunsch 5166182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) 517dc5df763SJoerg Wunsch { 5186182fdbdSPeter Wemm return fdc_err(fdc, "Sense Drive Status failed\n"); 519dc5df763SJoerg Wunsch } 520dc5df763SJoerg Wunsch if (st3p) 521dc5df763SJoerg Wunsch *st3p = st3; 522dc5df763SJoerg Wunsch 523dc5df763SJoerg Wunsch return 0; 524dc5df763SJoerg Wunsch } 525dc5df763SJoerg Wunsch 5266f4e0bebSPoul-Henning Kamp static int 527dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp) 528dc5df763SJoerg Wunsch { 5296182fdbdSPeter Wemm int cyl, st0, ret; 530dc5df763SJoerg Wunsch 5316182fdbdSPeter Wemm ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); 5326182fdbdSPeter Wemm if (ret) { 5336182fdbdSPeter Wemm (void)fdc_err(fdc, 534dc5df763SJoerg Wunsch "sense intr err reading stat reg 0\n"); 535dc5df763SJoerg Wunsch return ret; 536dc5df763SJoerg Wunsch } 537dc5df763SJoerg Wunsch 538dc5df763SJoerg Wunsch if (st0p) 539dc5df763SJoerg Wunsch *st0p = st0; 540dc5df763SJoerg Wunsch 5416182fdbdSPeter Wemm if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { 542dc5df763SJoerg Wunsch /* 543dc5df763SJoerg Wunsch * There doesn't seem to have been an interrupt. 544dc5df763SJoerg Wunsch */ 545dc5df763SJoerg Wunsch return FD_NOT_VALID; 546dc5df763SJoerg Wunsch } 547dc5df763SJoerg Wunsch 5486182fdbdSPeter Wemm if (fd_in(fdc, &cyl) < 0) { 5496182fdbdSPeter Wemm return fdc_err(fdc, "can't get cyl num\n"); 550dc5df763SJoerg Wunsch } 551dc5df763SJoerg Wunsch 552dc5df763SJoerg Wunsch if (cylp) 553dc5df763SJoerg Wunsch *cylp = cyl; 554dc5df763SJoerg Wunsch 555dc5df763SJoerg Wunsch return 0; 556dc5df763SJoerg Wunsch } 557dc5df763SJoerg Wunsch 558dc5df763SJoerg Wunsch 5596f4e0bebSPoul-Henning Kamp static int 560dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu) 561dc5df763SJoerg Wunsch { 562dc5df763SJoerg Wunsch int i, ret; 563b5e8ce9fSBruce Evans 5646182fdbdSPeter Wemm for (i = 0; i < 7; i++) { 565b5e8ce9fSBruce Evans /* 566b5e8ce9fSBruce Evans * XXX types are poorly chosen. Only bytes can by read 567a838d83dSBruce Evans * from the hardware, but fdc->status[] wants u_ints and 568b5e8ce9fSBruce Evans * fd_in() gives ints. 569b5e8ce9fSBruce Evans */ 570b5e8ce9fSBruce Evans int status; 571b5e8ce9fSBruce Evans 5726182fdbdSPeter Wemm ret = fd_in(fdc, &status); 573b5e8ce9fSBruce Evans fdc->status[i] = status; 574b5e8ce9fSBruce Evans if (ret != 0) 575dc5df763SJoerg Wunsch break; 576dc5df763SJoerg Wunsch } 577dc5df763SJoerg Wunsch 578dc5df763SJoerg Wunsch if (ret == 0) 579dc5df763SJoerg Wunsch fdc->flags |= FDC_STAT_VALID; 580dc5df763SJoerg Wunsch else 581dc5df763SJoerg Wunsch fdc->flags &= ~FDC_STAT_VALID; 582dc5df763SJoerg Wunsch 583dc5df763SJoerg Wunsch return ret; 584dc5df763SJoerg Wunsch } 585dc5df763SJoerg Wunsch 5865b81b6b3SRodney W. Grimes /****************************************************************************/ 5875b81b6b3SRodney W. Grimes /* autoconfiguration stuff */ 5885b81b6b3SRodney W. Grimes /****************************************************************************/ 589dc5df763SJoerg Wunsch 5903a2f7427SDavid Greenman static int 59137286586SPeter Wemm fdc_alloc_resources(struct fdc_data *fdc) 5925b81b6b3SRodney W. Grimes { 59337286586SPeter Wemm device_t dev; 59437286586SPeter Wemm int ispnp; 5955b81b6b3SRodney W. Grimes 59637286586SPeter Wemm dev = fdc->fdc_dev; 59737286586SPeter Wemm ispnp = fdc->fdc_ispnp; 5986182fdbdSPeter Wemm fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; 5996182fdbdSPeter Wemm fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; 6006182fdbdSPeter Wemm 6016182fdbdSPeter Wemm fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, 6026182fdbdSPeter Wemm &fdc->rid_ioport, 0ul, ~0ul, 603427ccf00SDoug Rabson ispnp ? 1 : IO_FDCSIZE, 604427ccf00SDoug Rabson RF_ACTIVE); 6056182fdbdSPeter Wemm if (fdc->res_ioport == 0) { 606427ccf00SDoug Rabson device_printf(dev, "cannot reserve I/O port range\n"); 60737286586SPeter Wemm return ENXIO; 6086182fdbdSPeter Wemm } 609427ccf00SDoug Rabson fdc->portt = rman_get_bustag(fdc->res_ioport); 610427ccf00SDoug Rabson fdc->porth = rman_get_bushandle(fdc->res_ioport); 611427ccf00SDoug Rabson 612427ccf00SDoug Rabson /* 613a2639a18SPeter Wemm * Some bios' report the device at 0x3f2-0x3f5,0x3f7 and some at 614a2639a18SPeter Wemm * 0x3f0-0x3f5,0x3f7. We detect the former by checking the size 615a2639a18SPeter Wemm * and adjust the port address accordingly. 616a2639a18SPeter Wemm * 617a2639a18SPeter Wemm * And some (!!) report 0x3f2-0x3f5 and completely leave out the 618a2639a18SPeter Wemm * control register! It seems that some non-antique controller chips 619a2639a18SPeter Wemm * have a different method of programming the transfer speed which 620a2639a18SPeter Wemm * doesn't require the control register, but it's mighty bogus as the 621a2639a18SPeter Wemm * chip still responds to the address for the control register. 622427ccf00SDoug Rabson */ 623a2639a18SPeter Wemm if (ispnp) { 624b9da888fSPeter Wemm int cntport0; 625b9da888fSPeter Wemm int cntport1; 626b9da888fSPeter Wemm u_long ctlstart; 627b9da888fSPeter Wemm u_long ctlend; 628b9da888fSPeter Wemm 629b9da888fSPeter Wemm cntport0 = bus_get_resource_count(dev, SYS_RES_IOPORT, 0); 630b9da888fSPeter Wemm cntport1 = bus_get_resource_count(dev, SYS_RES_IOPORT, 1); 631b9da888fSPeter Wemm ctlstart = 0ul; 632b9da888fSPeter Wemm ctlend = ~0ul; 633b9da888fSPeter Wemm if (cntport0 == 4) 634427ccf00SDoug Rabson fdc->port_off = -2; 635b9da888fSPeter Wemm if (cntport1 == 0) { 636b9da888fSPeter Wemm /* GRRR, request a specific port */ 637b9da888fSPeter Wemm ctlstart = rman_get_start(fdc->res_ioport) + 638b9da888fSPeter Wemm fdc->port_off + 7; /* usually 0x3f7 */ 639b9da888fSPeter Wemm ctlend = ctlstart; 640b9da888fSPeter Wemm if (bootverbose) 641b9da888fSPeter Wemm device_printf(dev, "added missing ctrl port\n"); 642b9da888fSPeter Wemm } 643427ccf00SDoug Rabson fdc->flags |= FDC_ISPNP; 644427ccf00SDoug Rabson fdc->rid_ctl = 1; 645427ccf00SDoug Rabson fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, 646b9da888fSPeter Wemm &fdc->rid_ctl, ctlstart, 647b9da888fSPeter Wemm ctlend, 1, RF_ACTIVE); 648427ccf00SDoug Rabson if (fdc->res_ctl == 0) { 64937286586SPeter Wemm device_printf(dev, "cannot reserve I/O port range 2\n"); 65037286586SPeter Wemm return ENXIO; 651427ccf00SDoug Rabson } 652427ccf00SDoug Rabson fdc->ctlt = rman_get_bustag(fdc->res_ctl); 653427ccf00SDoug Rabson fdc->ctlh = rman_get_bushandle(fdc->res_ctl); 654427ccf00SDoug Rabson } 6556182fdbdSPeter Wemm 6566182fdbdSPeter Wemm fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, 6576182fdbdSPeter Wemm &fdc->rid_irq, 0ul, ~0ul, 1, 6586182fdbdSPeter Wemm RF_ACTIVE); 6596182fdbdSPeter Wemm if (fdc->res_irq == 0) { 660427ccf00SDoug Rabson device_printf(dev, "cannot reserve interrupt line\n"); 66137286586SPeter Wemm return ENXIO; 6626182fdbdSPeter Wemm } 6636182fdbdSPeter Wemm fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, 6646182fdbdSPeter Wemm &fdc->rid_drq, 0ul, ~0ul, 1, 6656182fdbdSPeter Wemm RF_ACTIVE); 6666182fdbdSPeter Wemm if (fdc->res_drq == 0) { 667427ccf00SDoug Rabson device_printf(dev, "cannot reserve DMA request line\n"); 66837286586SPeter Wemm return ENXIO; 6696182fdbdSPeter Wemm } 6706182fdbdSPeter Wemm fdc->dmachan = fdc->res_drq->r_start; 67137286586SPeter Wemm 67237286586SPeter Wemm return 0; 67337286586SPeter Wemm } 67437286586SPeter Wemm 67537286586SPeter Wemm static void 67637286586SPeter Wemm fdc_release_resources(struct fdc_data *fdc) 67737286586SPeter Wemm { 67837286586SPeter Wemm device_t dev; 67937286586SPeter Wemm 68037286586SPeter Wemm dev = fdc->fdc_dev; 68137286586SPeter Wemm if (fdc->res_irq != 0) { 68237286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 68337286586SPeter Wemm fdc->res_irq); 68437286586SPeter Wemm bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 68537286586SPeter Wemm fdc->res_irq); 68637286586SPeter Wemm } 68737286586SPeter Wemm if (fdc->res_ctl != 0) { 68837286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, 68937286586SPeter Wemm fdc->res_ctl); 69037286586SPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, 69137286586SPeter Wemm fdc->res_ctl); 69237286586SPeter Wemm } 69337286586SPeter Wemm if (fdc->res_ioport != 0) { 69437286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 69537286586SPeter Wemm fdc->res_ioport); 69637286586SPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 69737286586SPeter Wemm fdc->res_ioport); 69837286586SPeter Wemm } 69937286586SPeter Wemm if (fdc->res_drq != 0) { 70037286586SPeter Wemm bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 70137286586SPeter Wemm fdc->res_drq); 70237286586SPeter Wemm bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 70337286586SPeter Wemm fdc->res_drq); 70437286586SPeter Wemm } 70537286586SPeter Wemm } 70637286586SPeter Wemm 70737286586SPeter Wemm /****************************************************************************/ 70837286586SPeter Wemm /* autoconfiguration stuff */ 70937286586SPeter Wemm /****************************************************************************/ 71037286586SPeter Wemm 71137286586SPeter Wemm static struct isa_pnp_id fdc_ids[] = { 71237286586SPeter Wemm {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ 71337286586SPeter Wemm {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ 71437286586SPeter Wemm {0} 71537286586SPeter Wemm }; 71637286586SPeter Wemm 71737286586SPeter Wemm static int 71837286586SPeter Wemm fdc_read_ivar(device_t dev, device_t child, int which, u_long *result) 71937286586SPeter Wemm { 72037286586SPeter Wemm struct fdc_ivars *ivars = device_get_ivars(child); 72137286586SPeter Wemm 72237286586SPeter Wemm switch (which) { 72337286586SPeter Wemm case FDC_IVAR_FDUNIT: 72437286586SPeter Wemm *result = ivars->fdunit; 72537286586SPeter Wemm break; 72637286586SPeter Wemm default: 72737286586SPeter Wemm return ENOENT; 72837286586SPeter Wemm } 72937286586SPeter Wemm return 0; 73037286586SPeter Wemm } 73137286586SPeter Wemm 73237286586SPeter Wemm /* 73337286586SPeter Wemm * fdc controller section. 73437286586SPeter Wemm */ 73537286586SPeter Wemm static int 73637286586SPeter Wemm fdc_probe(device_t dev) 73737286586SPeter Wemm { 73837286586SPeter Wemm int error, ic_type; 73937286586SPeter Wemm struct fdc_data *fdc; 74037286586SPeter Wemm 74137286586SPeter Wemm fdc = device_get_softc(dev); 74237286586SPeter Wemm bzero(fdc, sizeof *fdc); 74337286586SPeter Wemm fdc->fdc_dev = dev; 74437286586SPeter Wemm 74537286586SPeter Wemm /* Check pnp ids */ 74637286586SPeter Wemm error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); 74737286586SPeter Wemm if (error == ENXIO) 74837286586SPeter Wemm return ENXIO; 74937286586SPeter Wemm fdc->fdc_ispnp = (error == 0); 75037286586SPeter Wemm 75137286586SPeter Wemm /* Attempt to allocate our resources for the duration of the probe */ 75237286586SPeter Wemm error = fdc_alloc_resources(fdc); 75337286586SPeter Wemm if (error) 75437286586SPeter Wemm goto out; 7555b81b6b3SRodney W. Grimes 75616111cedSAndrew Moore /* First - lets reset the floppy controller */ 757427ccf00SDoug Rabson fdout_wr(fdc, 0); 75816111cedSAndrew Moore DELAY(100); 759427ccf00SDoug Rabson fdout_wr(fdc, FDO_FRST); 76016111cedSAndrew Moore 7615b81b6b3SRodney W. Grimes /* see if it can handle a command */ 7626182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), 7636182fdbdSPeter Wemm NE7_SPEC_2(2, 0), 0)) { 7646182fdbdSPeter Wemm error = ENXIO; 7656182fdbdSPeter Wemm goto out; 7665b81b6b3SRodney W. Grimes } 7676182fdbdSPeter Wemm 7686182fdbdSPeter Wemm if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { 7696182fdbdSPeter Wemm ic_type = (u_char)ic_type; 7706182fdbdSPeter Wemm switch (ic_type) { 7716182fdbdSPeter Wemm case 0x80: 7726182fdbdSPeter Wemm device_set_desc(dev, "NEC 765 or clone"); 7736182fdbdSPeter Wemm fdc->fdct = FDC_NE765; 7746182fdbdSPeter Wemm break; 7756182fdbdSPeter Wemm case 0x81: 7766182fdbdSPeter Wemm device_set_desc(dev, "Intel 82077 or clone"); 7776182fdbdSPeter Wemm fdc->fdct = FDC_I82077; 7786182fdbdSPeter Wemm break; 7796182fdbdSPeter Wemm case 0x90: 7806182fdbdSPeter Wemm device_set_desc(dev, "NEC 72065B or clone"); 7816182fdbdSPeter Wemm fdc->fdct = FDC_NE72065; 7826182fdbdSPeter Wemm break; 7836182fdbdSPeter Wemm default: 7846182fdbdSPeter Wemm device_set_desc(dev, "generic floppy controller"); 7856182fdbdSPeter Wemm fdc->fdct = FDC_UNKNOWN; 7866182fdbdSPeter Wemm break; 7876182fdbdSPeter Wemm } 7886182fdbdSPeter Wemm } 7896182fdbdSPeter Wemm 7906182fdbdSPeter Wemm out: 79137286586SPeter Wemm fdc_release_resources(fdc); 7926182fdbdSPeter Wemm return (error); 7935b81b6b3SRodney W. Grimes } 7945b81b6b3SRodney W. Grimes 7955b81b6b3SRodney W. Grimes /* 79637286586SPeter Wemm * Add a child device to the fdc controller. It will then be probed etc. 7975b81b6b3SRodney W. Grimes */ 7986182fdbdSPeter Wemm static void 79937286586SPeter Wemm fdc_add_child(device_t dev, const char *name, int unit) 8005b81b6b3SRodney W. Grimes { 80137286586SPeter Wemm int disabled; 80237286586SPeter Wemm struct fdc_ivars *ivar; 8036182fdbdSPeter Wemm device_t child; 80492200632SGarrett Wollman 8056182fdbdSPeter Wemm ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); 80637286586SPeter Wemm if (ivar == NULL) 8076182fdbdSPeter Wemm return; 80837286586SPeter Wemm bzero(ivar, sizeof *ivar); 80937286586SPeter Wemm if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0) 81037286586SPeter Wemm ivar->fdunit = 0; 811fe0d4089SMatthew N. Dodd child = device_add_child(dev, name, unit); 81237286586SPeter Wemm if (child == NULL) 8136182fdbdSPeter Wemm return; 81437286586SPeter Wemm device_set_ivars(child, ivar); 815a97c75b7SDoug Rabson if (resource_int_value(name, unit, "disabled", &disabled) == 0 816a97c75b7SDoug Rabson && disabled != 0) 8176182fdbdSPeter Wemm device_disable(child); 8186182fdbdSPeter Wemm } 8196182fdbdSPeter Wemm 8206182fdbdSPeter Wemm static int 8216182fdbdSPeter Wemm fdc_attach(device_t dev) 8226182fdbdSPeter Wemm { 82337286586SPeter Wemm struct fdc_data *fdc; 82437286586SPeter Wemm int i, error; 825427ccf00SDoug Rabson 82637286586SPeter Wemm fdc = device_get_softc(dev); 82737286586SPeter Wemm error = fdc_alloc_resources(fdc); 82837286586SPeter Wemm if (error) { 82937286586SPeter Wemm device_printf(dev, "cannot re-aquire resources\n"); 83037286586SPeter Wemm return error; 83137286586SPeter Wemm } 83237286586SPeter Wemm error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, 83337286586SPeter Wemm INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr); 83437286586SPeter Wemm if (error) { 83537286586SPeter Wemm device_printf(dev, "cannot setup interrupt\n"); 83637286586SPeter Wemm return error; 83737286586SPeter Wemm } 83837286586SPeter Wemm fdc->fdcu = device_get_unit(dev); 8395b81b6b3SRodney W. Grimes fdc->flags |= FDC_ATTACHED; 8406182fdbdSPeter Wemm 841100f78bbSSujal Patel /* Acquire the DMA channel forever, The driver will do the rest */ 8426182fdbdSPeter Wemm /* XXX should integrate with rman */ 843100f78bbSSujal Patel isa_dma_acquire(fdc->dmachan); 844dd87702aSBruce Evans isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); 8455b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 8466182fdbdSPeter Wemm 8473a2f7427SDavid Greenman /* reset controller, turn motor off, clear fdout mirror reg */ 848427ccf00SDoug Rabson fdout_wr(fdc, ((fdc->fdout = 0))); 84902a19910SJustin T. Gibbs bufq_init(&fdc->head); 8505b81b6b3SRodney W. Grimes 8516182fdbdSPeter Wemm /* 85237286586SPeter Wemm * Probe and attach any children. We should probably detect 85337286586SPeter Wemm * devices from the BIOS unless overridden. 8546182fdbdSPeter Wemm */ 85537286586SPeter Wemm for (i = resource_query_string(-1, "at", device_get_nameunit(dev)); 85637286586SPeter Wemm i != -1; 85737286586SPeter Wemm i = resource_query_string(i, "at", device_get_nameunit(dev))) 85837286586SPeter Wemm fdc_add_child(dev, resource_query_name(i), 85937286586SPeter Wemm resource_query_unit(i)); 86037286586SPeter Wemm 8616182fdbdSPeter Wemm return (bus_generic_attach(dev)); 8626182fdbdSPeter Wemm } 8636182fdbdSPeter Wemm 86415317dd8SMatthew N. Dodd static int 8656182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child) 8666182fdbdSPeter Wemm { 86715317dd8SMatthew N. Dodd int retval = 0; 86815317dd8SMatthew N. Dodd 86915317dd8SMatthew N. Dodd retval += bus_print_child_header(me, child); 87015317dd8SMatthew N. Dodd retval += printf(" on %s drive %d\n", device_get_nameunit(me), 87137286586SPeter Wemm fdc_get_fdunit(child)); 87215317dd8SMatthew N. Dodd 87315317dd8SMatthew N. Dodd return (retval); 8746182fdbdSPeter Wemm } 8756182fdbdSPeter Wemm 87616e68fc6SPeter Wemm static device_method_t fdc_methods[] = { 87716e68fc6SPeter Wemm /* Device interface */ 87816e68fc6SPeter Wemm DEVMETHOD(device_probe, fdc_probe), 87916e68fc6SPeter Wemm DEVMETHOD(device_attach, fdc_attach), 88016e68fc6SPeter Wemm DEVMETHOD(device_detach, bus_generic_detach), 88116e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 88216e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), 88316e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), 88416e68fc6SPeter Wemm 88516e68fc6SPeter Wemm /* Bus interface */ 88616e68fc6SPeter Wemm DEVMETHOD(bus_print_child, fdc_print_child), 88737286586SPeter Wemm DEVMETHOD(bus_read_ivar, fdc_read_ivar), 88816e68fc6SPeter Wemm /* Our children never use any other bus interface methods. */ 88916e68fc6SPeter Wemm 89016e68fc6SPeter Wemm { 0, 0 } 89116e68fc6SPeter Wemm }; 89216e68fc6SPeter Wemm 89316e68fc6SPeter Wemm static driver_t fdc_driver = { 89416e68fc6SPeter Wemm "fdc", 89516e68fc6SPeter Wemm fdc_methods, 89616e68fc6SPeter Wemm sizeof(struct fdc_data) 89716e68fc6SPeter Wemm }; 89816e68fc6SPeter Wemm 89916e68fc6SPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); 90016e68fc6SPeter Wemm 90116e68fc6SPeter Wemm /******************************************************************/ 90216e68fc6SPeter Wemm /* 90316e68fc6SPeter Wemm * devices attached to the controller section. 90416e68fc6SPeter Wemm */ 9056182fdbdSPeter Wemm static int 9066182fdbdSPeter Wemm fd_probe(device_t dev) 9076182fdbdSPeter Wemm { 9086182fdbdSPeter Wemm int i; 9096182fdbdSPeter Wemm u_int fdt, st0, st3; 9106182fdbdSPeter Wemm struct fd_data *fd; 9116182fdbdSPeter Wemm struct fdc_data *fdc; 9126182fdbdSPeter Wemm fdsu_t fdsu; 9136182fdbdSPeter Wemm static int fd_fifo = 0; 9146182fdbdSPeter Wemm 9156182fdbdSPeter Wemm fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ 9166182fdbdSPeter Wemm fd = device_get_softc(dev); 9176182fdbdSPeter Wemm fdc = device_get_softc(device_get_parent(dev)); 9186182fdbdSPeter Wemm 9196182fdbdSPeter Wemm bzero(fd, sizeof *fd); 9206182fdbdSPeter Wemm fd->dev = dev; 9216182fdbdSPeter Wemm fd->fdc = fdc; 9226182fdbdSPeter Wemm fd->fdsu = fdsu; 9236182fdbdSPeter Wemm fd->fdu = device_get_unit(dev); 9246182fdbdSPeter Wemm 925a97c75b7SDoug Rabson #ifdef __i386__ 926b99f0a4aSAndrew Moore /* look up what bios thinks we have */ 9276182fdbdSPeter Wemm switch (fd->fdu) { 9286182fdbdSPeter Wemm case 0: 929062acdb7SDoug Rabson if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) 9300722d6abSJoerg Wunsch fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; 9310722d6abSJoerg Wunsch else 9320722d6abSJoerg Wunsch fdt = (rtcin(RTC_FDISKETTE) & 0xf0); 933b99f0a4aSAndrew Moore break; 9346182fdbdSPeter Wemm case 1: 9356182fdbdSPeter Wemm fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); 936dc5df763SJoerg Wunsch break; 937dc5df763SJoerg Wunsch default: 9386182fdbdSPeter Wemm fdt = RTCFDT_NONE; 939dc5df763SJoerg Wunsch break; 9406b7bd95bSJoerg Wunsch } 941a97c75b7SDoug Rabson #else 942a97c75b7SDoug Rabson fdt = RTCFDT_144M; /* XXX probably */ 943a97c75b7SDoug Rabson #endif 9446182fdbdSPeter Wemm 9456182fdbdSPeter Wemm /* is there a unit? */ 9466182fdbdSPeter Wemm if (fdt == RTCFDT_NONE) 9476182fdbdSPeter Wemm return (ENXIO); 9486182fdbdSPeter Wemm 9496182fdbdSPeter Wemm /* select it */ 9506182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNON); 9516182fdbdSPeter Wemm DELAY(1000000); /* 1 sec */ 9526182fdbdSPeter Wemm 9538de0675cSPeter Wemm /* XXX This doesn't work before the first set_motor() */ 9546182fdbdSPeter Wemm if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN 955e34c71eaSJoerg Wunsch && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 9566182fdbdSPeter Wemm && enable_fifo(fdc) == 0) { 9576182fdbdSPeter Wemm device_print_prettyname(device_get_parent(dev)); 9586182fdbdSPeter Wemm printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); 959d66c374fSTor Egge } 9606182fdbdSPeter Wemm fd_fifo = 1; 9616182fdbdSPeter Wemm 9626182fdbdSPeter Wemm if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) 9636182fdbdSPeter Wemm && (st3 & NE7_ST3_T0)) { 964dc5df763SJoerg Wunsch /* if at track 0, first seek inwards */ 965dc5df763SJoerg Wunsch /* seek some steps: */ 9666182fdbdSPeter Wemm fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); 967dc5df763SJoerg Wunsch DELAY(300000); /* ...wait a moment... */ 9686182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ 969dc5df763SJoerg Wunsch } 970dc5df763SJoerg Wunsch 971dc5df763SJoerg Wunsch /* If we're at track 0 first seek inwards. */ 9726182fdbdSPeter Wemm if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { 973dc5df763SJoerg Wunsch /* Seek some steps... */ 9746182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 975dc5df763SJoerg Wunsch /* ...wait a moment... */ 976dc5df763SJoerg Wunsch DELAY(300000); 977dc5df763SJoerg Wunsch /* make ctrlr happy: */ 9786182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); 979dc5df763SJoerg Wunsch } 980dc5df763SJoerg Wunsch } 981dc5df763SJoerg Wunsch 9826b7bd95bSJoerg Wunsch for (i = 0; i < 2; i++) { 9836b7bd95bSJoerg Wunsch /* 9846b7bd95bSJoerg Wunsch * we must recalibrate twice, just in case the 9856b7bd95bSJoerg Wunsch * heads have been beyond cylinder 76, since most 9866b7bd95bSJoerg Wunsch * FDCs still barf when attempting to recalibrate 9876b7bd95bSJoerg Wunsch * more than 77 steps 9886b7bd95bSJoerg Wunsch */ 989dc5df763SJoerg Wunsch /* go back to 0: */ 9906182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 9916b7bd95bSJoerg Wunsch /* a second being enough for full stroke seek*/ 9926b7bd95bSJoerg Wunsch DELAY(i == 0 ? 1000000 : 300000); 9935b81b6b3SRodney W. Grimes 9946b7bd95bSJoerg Wunsch /* anything responding? */ 995dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, 0) == 0 && 996dc5df763SJoerg Wunsch (st0 & NE7_ST0_EC) == 0) 9976b7bd95bSJoerg Wunsch break; /* already probed succesfully */ 9986b7bd95bSJoerg Wunsch } 999dc5df763SJoerg Wunsch } 10006b7bd95bSJoerg Wunsch 10016182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNOFF); 10023a2f7427SDavid Greenman 10033a2f7427SDavid Greenman if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ 10046182fdbdSPeter Wemm return (ENXIO); 10055b81b6b3SRodney W. Grimes 1006dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 1007b99f0a4aSAndrew Moore fd->fdc = fdc; 1008b99f0a4aSAndrew Moore fd->fdsu = fdsu; 10093a2f7427SDavid Greenman fd->options = 0; 101002a19910SJustin T. Gibbs callout_handle_init(&fd->toffhandle); 101102a19910SJustin T. Gibbs callout_handle_init(&fd->tohandle); 10125b81b6b3SRodney W. Grimes 1013b99f0a4aSAndrew Moore switch (fdt) { 10147ca0641bSAndrey A. Chernov case RTCFDT_12M: 10156182fdbdSPeter Wemm device_set_desc(dev, "1200-KB 5.25\" drive"); 1016b99f0a4aSAndrew Moore fd->type = FD_1200; 10177ca0641bSAndrey A. Chernov break; 10180722d6abSJoerg Wunsch case RTCFDT_144M | RTCFDT_144M_PRETENDED: 10196182fdbdSPeter Wemm device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); 10200722d6abSJoerg Wunsch fdt = RTCFDT_144M; 10216182fdbdSPeter Wemm fd->type = FD_1440; 10227ca0641bSAndrey A. Chernov case RTCFDT_144M: 10236182fdbdSPeter Wemm device_set_desc(dev, "1440-KB 3.5\" drive"); 1024b99f0a4aSAndrew Moore fd->type = FD_1440; 10257ca0641bSAndrey A. Chernov break; 1026290dd077SJoerg Wunsch case RTCFDT_288M: 102786a727d9SJoerg Wunsch case RTCFDT_288M_1: 10286182fdbdSPeter Wemm device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); 1029290dd077SJoerg Wunsch fd->type = FD_1440; 1030290dd077SJoerg Wunsch break; 10317ca0641bSAndrey A. Chernov case RTCFDT_360K: 10326182fdbdSPeter Wemm device_set_desc(dev, "360-KB 5.25\" drive"); 1033b99f0a4aSAndrew Moore fd->type = FD_360; 10347ca0641bSAndrey A. Chernov break; 1035ed2fa05eSAndrey A. Chernov case RTCFDT_720K: 10366182fdbdSPeter Wemm printf("720-KB 3.5\" drive"); 1037b99f0a4aSAndrew Moore fd->type = FD_720; 1038ed2fa05eSAndrey A. Chernov break; 10397ca0641bSAndrey A. Chernov default: 10406182fdbdSPeter Wemm return (ENXIO); 10415b81b6b3SRodney W. Grimes } 10426182fdbdSPeter Wemm return (0); 10436182fdbdSPeter Wemm } 10446182fdbdSPeter Wemm 10456182fdbdSPeter Wemm static int 10466182fdbdSPeter Wemm fd_attach(device_t dev) 10476182fdbdSPeter Wemm { 10486182fdbdSPeter Wemm struct fd_data *fd; 1049ada9bd8cSJulian Elischer #if 0 1050ada9bd8cSJulian Elischer int i; 1051ada9bd8cSJulian Elischer int mynor; 1052ada9bd8cSJulian Elischer int typemynor; 1053ada9bd8cSJulian Elischer int typesize; 1054ada9bd8cSJulian Elischer #endif 10556182fdbdSPeter Wemm 10566182fdbdSPeter Wemm fd = device_get_softc(dev); 10576182fdbdSPeter Wemm 1058475ad603SPeter Wemm cdevsw_add(&fd_cdevsw); /* XXX */ 1059ada9bd8cSJulian Elischer make_dev(&fd_cdevsw, (fd->fdu << 6), 1060ada9bd8cSJulian Elischer UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fd->fdu); 1061ada9bd8cSJulian Elischer 1062ada9bd8cSJulian Elischer #if 0 1063ada9bd8cSJulian Elischer /* Other make_dev() go here. */ 1064ada9bd8cSJulian Elischer #endif 1065ada9bd8cSJulian Elischer 1066671e2ceeSBruce Evans /* 1067b2dfb1f9SJustin T. Gibbs * Export the drive to the devstat interface. 1068671e2ceeSBruce Evans */ 10696182fdbdSPeter Wemm devstat_add_entry(&fd->device_stats, device_get_name(dev), 10706182fdbdSPeter Wemm device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, 10712a888f93SKenneth D. Merry DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, 10722a888f93SKenneth D. Merry DEVSTAT_PRIORITY_FD); 10736182fdbdSPeter Wemm return (0); 10745b81b6b3SRodney W. Grimes } 10755b81b6b3SRodney W. Grimes 107616e68fc6SPeter Wemm static device_method_t fd_methods[] = { 107716e68fc6SPeter Wemm /* Device interface */ 107816e68fc6SPeter Wemm DEVMETHOD(device_probe, fd_probe), 107916e68fc6SPeter Wemm DEVMETHOD(device_attach, fd_attach), 108016e68fc6SPeter Wemm DEVMETHOD(device_detach, bus_generic_detach), 108116e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 108216e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ 108316e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ 108416e68fc6SPeter Wemm 108516e68fc6SPeter Wemm { 0, 0 } 108616e68fc6SPeter Wemm }; 108716e68fc6SPeter Wemm 108816e68fc6SPeter Wemm static driver_t fd_driver = { 108916e68fc6SPeter Wemm "fd", 109016e68fc6SPeter Wemm fd_methods, 109116e68fc6SPeter Wemm sizeof(struct fd_data) 109216e68fc6SPeter Wemm }; 109316e68fc6SPeter Wemm 1094475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); 109516e68fc6SPeter Wemm 109616e68fc6SPeter Wemm /******************************************************************/ 109716e68fc6SPeter Wemm 109869acd21dSWarner Losh #ifdef FDC_YE 109969acd21dSWarner Losh /* 110069acd21dSWarner Losh * this is a subset of fdattach() optimized for the Y-E Data 110169acd21dSWarner Losh * PCMCIA floppy drive. 110269acd21dSWarner Losh */ 110369acd21dSWarner Losh static int yeattach(struct isa_device *dev) 110469acd21dSWarner Losh { 110569acd21dSWarner Losh fdcu_t fdcu = dev->id_unit; 110669acd21dSWarner Losh fdc_p fdc = fdc_data + fdcu; 110769acd21dSWarner Losh fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ 110869acd21dSWarner Losh fdu_t fdu; 110969acd21dSWarner Losh fd_p fd; 111069acd21dSWarner Losh int st0, st3, i; 111169acd21dSWarner Losh fdc->fdcu = fdcu; 111269acd21dSWarner Losh /* 11133b178206SWarner Losh * the FDC_NODMA flag is used to to indicate special PIO is used 111469acd21dSWarner Losh * instead of DMA 111569acd21dSWarner Losh */ 11163b178206SWarner Losh fdc->flags = FDC_ATTACHED|FDC_NODMA; 111769acd21dSWarner Losh fdc->state = DEVIDLE; 111869acd21dSWarner Losh /* reset controller, turn motor off, clear fdout mirror reg */ 1119427ccf00SDoug Rabson fdout_wr(fdc, ((fdc->fdout = 0))); 112069acd21dSWarner Losh bufq_init(&fdc->head); 112169acd21dSWarner Losh /* 112269acd21dSWarner Losh * assume 2 drives/ "normal" controller 112369acd21dSWarner Losh */ 112469acd21dSWarner Losh fdu = fdcu * 2; 112569acd21dSWarner Losh if (fdu >= NFD) { 112669acd21dSWarner Losh printf("fdu %d >= NFD\n",fdu); 112769acd21dSWarner Losh return(0); 112869acd21dSWarner Losh }; 112969acd21dSWarner Losh fd = &fd_data[fdu]; 113069acd21dSWarner Losh 113169acd21dSWarner Losh set_motor(fdcu, fdsu, TURNON); 113269acd21dSWarner Losh DELAY(1000000); /* 1 sec */ 113369acd21dSWarner Losh fdc->fdct = FDC_NE765; 113469acd21dSWarner Losh 113569acd21dSWarner Losh if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && 113669acd21dSWarner Losh (st3 & NE7_ST3_T0)) { 113769acd21dSWarner Losh /* if at track 0, first seek inwards */ 113869acd21dSWarner Losh /* seek some steps: */ 113969acd21dSWarner Losh (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); 114069acd21dSWarner Losh DELAY(300000); /* ...wait a moment... */ 114169acd21dSWarner Losh (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ 114269acd21dSWarner Losh } 114369acd21dSWarner Losh 114469acd21dSWarner Losh /* If we're at track 0 first seek inwards. */ 114569acd21dSWarner Losh if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { 114669acd21dSWarner Losh /* Seek some steps... */ 114769acd21dSWarner Losh if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 114869acd21dSWarner Losh /* ...wait a moment... */ 114969acd21dSWarner Losh DELAY(300000); 115069acd21dSWarner Losh /* make ctrlr happy: */ 115169acd21dSWarner Losh (void)fd_sense_int(fdc, 0, 0); 115269acd21dSWarner Losh } 115369acd21dSWarner Losh } 115469acd21dSWarner Losh 115569acd21dSWarner Losh for(i = 0; i < 2; i++) { 115669acd21dSWarner Losh /* 115769acd21dSWarner Losh * we must recalibrate twice, just in case the 115869acd21dSWarner Losh * heads have been beyond cylinder 76, since most 115969acd21dSWarner Losh * FDCs still barf when attempting to recalibrate 116069acd21dSWarner Losh * more than 77 steps 116169acd21dSWarner Losh */ 116269acd21dSWarner Losh /* go back to 0: */ 116369acd21dSWarner Losh if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 116469acd21dSWarner Losh /* a second being enough for full stroke seek*/ 116569acd21dSWarner Losh DELAY(i == 0? 1000000: 300000); 116669acd21dSWarner Losh 116769acd21dSWarner Losh /* anything responding? */ 116869acd21dSWarner Losh if (fd_sense_int(fdc, &st0, 0) == 0 && 116969acd21dSWarner Losh (st0 & NE7_ST0_EC) == 0) 117069acd21dSWarner Losh break; /* already probed succesfully */ 117169acd21dSWarner Losh } 117269acd21dSWarner Losh } 117369acd21dSWarner Losh 117469acd21dSWarner Losh set_motor(fdcu, fdsu, TURNOFF); 117569acd21dSWarner Losh 117669acd21dSWarner Losh if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ 117769acd21dSWarner Losh return(0); 117869acd21dSWarner Losh 117969acd21dSWarner Losh fd->track = FD_NO_TRACK; 118069acd21dSWarner Losh fd->fdc = fdc; 118169acd21dSWarner Losh fd->fdsu = fdsu; 118269acd21dSWarner Losh fd->options = 0; 118369acd21dSWarner Losh printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); 118469acd21dSWarner Losh fd->type = FD_1440; 118569acd21dSWarner Losh 118669acd21dSWarner Losh return (1); 118769acd21dSWarner Losh } 118869acd21dSWarner Losh #endif 118969acd21dSWarner Losh 11905b81b6b3SRodney W. Grimes /****************************************************************************/ 11915b81b6b3SRodney W. Grimes /* motor control stuff */ 11925e235068SJordan K. Hubbard /* remember to not deselect the drive we're working on */ 11935b81b6b3SRodney W. Grimes /****************************************************************************/ 11943a2f7427SDavid Greenman static void 11956182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon) 11965b81b6b3SRodney W. Grimes { 11976182fdbdSPeter Wemm int fdout = fdc->fdout; 11983a2f7427SDavid Greenman int needspecify = 0; 11993a2f7427SDavid Greenman 12003a2f7427SDavid Greenman if(turnon) { 12013a2f7427SDavid Greenman fdout &= ~FDO_FDSEL; 12023a2f7427SDavid Greenman fdout |= (FDO_MOEN0 << fdsu) + fdsu; 12033a2f7427SDavid Greenman } else 12043a2f7427SDavid Greenman fdout &= ~(FDO_MOEN0 << fdsu); 12053a2f7427SDavid Greenman 12065e235068SJordan K. Hubbard if(!turnon 12075e235068SJordan K. Hubbard && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) 12085e235068SJordan K. Hubbard /* gonna turn off the last drive, put FDC to bed */ 12095e235068SJordan K. Hubbard fdout &= ~ (FDO_FRST|FDO_FDMAEN); 12105e235068SJordan K. Hubbard else { 12113a2f7427SDavid Greenman /* make sure controller is selected and specified */ 12123a2f7427SDavid Greenman if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) 12133a2f7427SDavid Greenman needspecify = 1; 12143a2f7427SDavid Greenman fdout |= (FDO_FRST|FDO_FDMAEN); 12155b81b6b3SRodney W. Grimes } 12165b81b6b3SRodney W. Grimes 1217427ccf00SDoug Rabson fdout_wr(fdc, fdout); 12186182fdbdSPeter Wemm fdc->fdout = fdout; 12193a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdout); 12203a2f7427SDavid Greenman 12213a2f7427SDavid Greenman if (needspecify) { 1222dc8603e3SJoerg Wunsch /* 1223dc5df763SJoerg Wunsch * XXX 1224dc8603e3SJoerg Wunsch * special case: since we have just woken up the FDC 1225dc8603e3SJoerg Wunsch * from its sleep, we silently assume the command will 1226dc8603e3SJoerg Wunsch * be accepted, and do not test for a timeout 1227dc8603e3SJoerg Wunsch */ 12286182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1229dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1230dc5df763SJoerg Wunsch 0); 12316182fdbdSPeter Wemm if (fdc->flags & FDC_HAS_FIFO) 12326182fdbdSPeter Wemm (void) enable_fifo(fdc); 12333a2f7427SDavid Greenman } 12343a2f7427SDavid Greenman } 12353a2f7427SDavid Greenman 1236381fe1aaSGarrett Wollman static void 12376182fdbdSPeter Wemm fd_turnoff(void *xfd) 12385b81b6b3SRodney W. Grimes { 1239f5f7ba03SJordan K. Hubbard int s; 12406182fdbdSPeter Wemm fd_p fd = xfd; 1241dc16046fSJoerg Wunsch 12426182fdbdSPeter Wemm TRACE1("[fd%d: turnoff]", fd->fdu); 12438335c1b8SBruce Evans 12448335c1b8SBruce Evans /* 12458335c1b8SBruce Evans * Don't turn off the motor yet if the drive is active. 12468335c1b8SBruce Evans * XXX shouldn't even schedule turnoff until drive is inactive 12478335c1b8SBruce Evans * and nothing is queued on it. 12488335c1b8SBruce Evans */ 12496182fdbdSPeter Wemm if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { 12506182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 12518335c1b8SBruce Evans return; 12528335c1b8SBruce Evans } 12538335c1b8SBruce Evans 1254f5f7ba03SJordan K. Hubbard s = splbio(); 12555b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR; 12566182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNOFF); 1257f5f7ba03SJordan K. Hubbard splx(s); 12585b81b6b3SRodney W. Grimes } 12595b81b6b3SRodney W. Grimes 12603a2f7427SDavid Greenman static void 12616182fdbdSPeter Wemm fd_motor_on(void *xfd) 12625b81b6b3SRodney W. Grimes { 1263f5f7ba03SJordan K. Hubbard int s; 12646182fdbdSPeter Wemm fd_p fd = xfd; 1265f5f7ba03SJordan K. Hubbard 1266f5f7ba03SJordan K. Hubbard s = splbio(); 12675b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR_WAIT; 12685b81b6b3SRodney W. Grimes if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) 12695b81b6b3SRodney W. Grimes { 12706182fdbdSPeter Wemm fdc_intr(fd->fdc); 12715b81b6b3SRodney W. Grimes } 1272f5f7ba03SJordan K. Hubbard splx(s); 12735b81b6b3SRodney W. Grimes } 12745b81b6b3SRodney W. Grimes 12753a2f7427SDavid Greenman static void 12766182fdbdSPeter Wemm fd_turnon(fd_p fd) 12775b81b6b3SRodney W. Grimes { 12785b81b6b3SRodney W. Grimes if(!(fd->flags & FD_MOTOR)) 12795b81b6b3SRodney W. Grimes { 12803a2f7427SDavid Greenman fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); 12816182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNON); 12826182fdbdSPeter Wemm timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ 12835b81b6b3SRodney W. Grimes } 12845b81b6b3SRodney W. Grimes } 12855b81b6b3SRodney W. Grimes 1286381fe1aaSGarrett Wollman static void 1287dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc) 12885b81b6b3SRodney W. Grimes { 12893a2f7427SDavid Greenman /* Try a reset, keep motor on */ 1290427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 12913a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 12923a2f7427SDavid Greenman DELAY(100); 12933a2f7427SDavid Greenman /* enable FDC, but defer interrupts a moment */ 1294427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); 12953a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); 12963a2f7427SDavid Greenman DELAY(100); 1297427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout); 12983a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout); 12993a2f7427SDavid Greenman 1300dc5df763SJoerg Wunsch /* XXX after a reset, silently believe the FDC will accept commands */ 13016182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1302dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1303dc5df763SJoerg Wunsch 0); 1304d66c374fSTor Egge if (fdc->flags & FDC_HAS_FIFO) 1305d66c374fSTor Egge (void) enable_fifo(fdc); 13065b81b6b3SRodney W. Grimes } 13075b81b6b3SRodney W. Grimes 13085b81b6b3SRodney W. Grimes /****************************************************************************/ 13095b81b6b3SRodney W. Grimes /* fdc in/out */ 13105b81b6b3SRodney W. Grimes /****************************************************************************/ 13115b81b6b3SRodney W. Grimes int 13126182fdbdSPeter Wemm in_fdc(struct fdc_data *fdc) 13135b81b6b3SRodney W. Grimes { 13145b81b6b3SRodney W. Grimes int i, j = 100000; 1315427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) 13165b81b6b3SRodney W. Grimes != (NE7_DIO|NE7_RQM) && j-- > 0) 1317dc5df763SJoerg Wunsch if (i == NE7_RQM) 13186182fdbdSPeter Wemm return fdc_err(fdc, "ready for output in input\n"); 13195b81b6b3SRodney W. Grimes if (j <= 0) 13206182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); 1321d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 1322427ccf00SDoug Rabson i = fddata_rd(fdc); 13233a2f7427SDavid Greenman TRACE1("[FDDATA->0x%x]", (unsigned char)i); 13245b81b6b3SRodney W. Grimes return(i); 1325d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 1326427ccf00SDoug Rabson return fddata_rd(fdc); 1327d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 13285b81b6b3SRodney W. Grimes } 13295b81b6b3SRodney W. Grimes 1330dc5df763SJoerg Wunsch /* 1331dc5df763SJoerg Wunsch * fd_in: Like in_fdc, but allows you to see if it worked. 1332dc5df763SJoerg Wunsch */ 1333b5e8ce9fSBruce Evans static int 13346182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr) 1335dc5df763SJoerg Wunsch { 1336dc5df763SJoerg Wunsch int i, j = 100000; 1337427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) 1338dc5df763SJoerg Wunsch != (NE7_DIO|NE7_RQM) && j-- > 0) 1339dc5df763SJoerg Wunsch if (i == NE7_RQM) 13406182fdbdSPeter Wemm return fdc_err(fdc, "ready for output in input\n"); 1341dc5df763SJoerg Wunsch if (j <= 0) 13426182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); 1343d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 1344427ccf00SDoug Rabson i = fddata_rd(fdc); 1345dc5df763SJoerg Wunsch TRACE1("[FDDATA->0x%x]", (unsigned char)i); 1346dc5df763SJoerg Wunsch *ptr = i; 1347dc5df763SJoerg Wunsch return 0; 1348d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 1349427ccf00SDoug Rabson i = fddata_rd(fdc); 1350dc5df763SJoerg Wunsch if (ptr) 1351dc5df763SJoerg Wunsch *ptr = i; 1352dc5df763SJoerg Wunsch return 0; 1353d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 1354dc5df763SJoerg Wunsch } 1355dc5df763SJoerg Wunsch 1356dc5df763SJoerg Wunsch int 13576182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x) 13585b81b6b3SRodney W. Grimes { 13593b3837dbSRodney W. Grimes int i; 13605b81b6b3SRodney W. Grimes 13613b3837dbSRodney W. Grimes /* Check that the direction bit is set */ 13623b3837dbSRodney W. Grimes i = 100000; 1363427ccf00SDoug Rabson while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); 13646182fdbdSPeter Wemm if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); 13653b3837dbSRodney W. Grimes 13663b3837dbSRodney W. Grimes /* Check that the floppy controller is ready for a command */ 13673b3837dbSRodney W. Grimes i = 100000; 1368427ccf00SDoug Rabson while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); 136916b04b6aSJoerg Wunsch if (i <= 0) 13706182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); 13713b3837dbSRodney W. Grimes 13723b3837dbSRodney W. Grimes /* Send the command and return */ 1373427ccf00SDoug Rabson fddata_wr(fdc, x); 13743a2f7427SDavid Greenman TRACE1("[0x%x->FDDATA]", x); 13755b81b6b3SRodney W. Grimes return (0); 13765b81b6b3SRodney W. Grimes } 13775b81b6b3SRodney W. Grimes 13785b81b6b3SRodney W. Grimes /****************************************************************************/ 13795b81b6b3SRodney W. Grimes /* fdopen/fdclose */ 13805b81b6b3SRodney W. Grimes /****************************************************************************/ 1381381fe1aaSGarrett Wollman int 1382671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p) 13835b81b6b3SRodney W. Grimes { 13845b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 138520a29168SAndrey A. Chernov int type = FDTYPE(minor(dev)); 13866182fdbdSPeter Wemm fd_p fd; 1387b99f0a4aSAndrew Moore fdc_p fdc; 13885b81b6b3SRodney W. Grimes 13895b81b6b3SRodney W. Grimes /* check bounds */ 13906182fdbdSPeter Wemm if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) 1391b99f0a4aSAndrew Moore return (ENXIO); 13926182fdbdSPeter Wemm fdc = fd->fdc; 13936182fdbdSPeter Wemm if ((fdc == NULL) || (fd->type == NO_TYPE)) 1394b99f0a4aSAndrew Moore return (ENXIO); 1395b99f0a4aSAndrew Moore if (type > NUMDENS) 1396b99f0a4aSAndrew Moore return (ENXIO); 13977ca0641bSAndrey A. Chernov if (type == 0) 13986182fdbdSPeter Wemm type = fd->type; 13997ca0641bSAndrey A. Chernov else { 14003e425b96SJulian Elischer /* 14013e425b96SJulian Elischer * For each type of basic drive, make sure we are trying 14023e425b96SJulian Elischer * to open a type it can do, 14033e425b96SJulian Elischer */ 14046182fdbdSPeter Wemm if (type != fd->type) { 14056182fdbdSPeter Wemm switch (fd->type) { 14067ca0641bSAndrey A. Chernov case FD_360: 14077ca0641bSAndrey A. Chernov return (ENXIO); 1408ed2fa05eSAndrey A. Chernov case FD_720: 1409b39c878eSAndrey A. Chernov if ( type != FD_820 1410b39c878eSAndrey A. Chernov && type != FD_800 14116fb89845SKATO Takenori && type != FD_640 1412ed2fa05eSAndrey A. Chernov ) 1413ed2fa05eSAndrey A. Chernov return (ENXIO); 1414ed2fa05eSAndrey A. Chernov break; 14157ca0641bSAndrey A. Chernov case FD_1200: 1416b39c878eSAndrey A. Chernov switch (type) { 1417b39c878eSAndrey A. Chernov case FD_1480: 1418b39c878eSAndrey A. Chernov type = FD_1480in5_25; 1419fa4700b4SAndrey A. Chernov break; 14207ca0641bSAndrey A. Chernov case FD_1440: 1421b39c878eSAndrey A. Chernov type = FD_1440in5_25; 1422b39c878eSAndrey A. Chernov break; 14236fb89845SKATO Takenori case FD_1232: 14246fb89845SKATO Takenori break; 1425b39c878eSAndrey A. Chernov case FD_820: 1426b39c878eSAndrey A. Chernov type = FD_820in5_25; 1427b39c878eSAndrey A. Chernov break; 1428b39c878eSAndrey A. Chernov case FD_800: 1429b39c878eSAndrey A. Chernov type = FD_800in5_25; 1430b39c878eSAndrey A. Chernov break; 1431b39c878eSAndrey A. Chernov case FD_720: 1432b39c878eSAndrey A. Chernov type = FD_720in5_25; 1433b39c878eSAndrey A. Chernov break; 14346fb89845SKATO Takenori case FD_640: 14356fb89845SKATO Takenori type = FD_640in5_25; 14366fb89845SKATO Takenori break; 1437b39c878eSAndrey A. Chernov case FD_360: 1438b39c878eSAndrey A. Chernov type = FD_360in5_25; 1439b39c878eSAndrey A. Chernov break; 1440b39c878eSAndrey A. Chernov default: 1441b39c878eSAndrey A. Chernov return(ENXIO); 1442b39c878eSAndrey A. Chernov } 1443b39c878eSAndrey A. Chernov break; 1444b39c878eSAndrey A. Chernov case FD_1440: 1445b39c878eSAndrey A. Chernov if ( type != FD_1720 1446b39c878eSAndrey A. Chernov && type != FD_1480 1447ed2fa05eSAndrey A. Chernov && type != FD_1200 1448b39c878eSAndrey A. Chernov && type != FD_820 1449b39c878eSAndrey A. Chernov && type != FD_800 1450b39c878eSAndrey A. Chernov && type != FD_720 14516fb89845SKATO Takenori && type != FD_640 14527ca0641bSAndrey A. Chernov ) 1453dffff499SAndrey A. Chernov return(ENXIO); 1454fa4700b4SAndrey A. Chernov break; 14557ca0641bSAndrey A. Chernov } 14567ca0641bSAndrey A. Chernov } 1457fa4700b4SAndrey A. Chernov } 14586182fdbdSPeter Wemm fd->ft = fd_types + type - 1; 14596182fdbdSPeter Wemm fd->flags |= FD_OPEN; 14606182fdbdSPeter Wemm device_busy(fd->dev); 14616182fdbdSPeter Wemm device_busy(fd->fdc->fdc_dev); 14625b81b6b3SRodney W. Grimes return 0; 14635b81b6b3SRodney W. Grimes } 14645b81b6b3SRodney W. Grimes 1465381fe1aaSGarrett Wollman int 1466671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p) 14675b81b6b3SRodney W. Grimes { 14685b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 14696182fdbdSPeter Wemm struct fd_data *fd; 1470b99f0a4aSAndrew Moore 14716182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 14726182fdbdSPeter Wemm fd->flags &= ~FD_OPEN; 14736182fdbdSPeter Wemm fd->options &= ~FDOPT_NORETRY; 1474dc16046fSJoerg Wunsch 14755b81b6b3SRodney W. Grimes return (0); 14765b81b6b3SRodney W. Grimes } 14775b81b6b3SRodney W. Grimes 14783a2f7427SDavid Greenman /****************************************************************************/ 14793a2f7427SDavid Greenman /* fdstrategy */ 14803a2f7427SDavid Greenman /****************************************************************************/ 14813a2f7427SDavid Greenman void 14823a2f7427SDavid Greenman fdstrategy(struct buf *bp) 14833a2f7427SDavid Greenman { 1484bb6382faSJoerg Wunsch unsigned nblocks, blknum, cando; 14853a2f7427SDavid Greenman int s; 14863a2f7427SDavid Greenman fdu_t fdu; 14873a2f7427SDavid Greenman fdc_p fdc; 14883a2f7427SDavid Greenman fd_p fd; 14893a2f7427SDavid Greenman size_t fdblk; 14903a2f7427SDavid Greenman 14913a2f7427SDavid Greenman fdu = FDUNIT(minor(bp->b_dev)); 14926182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 14936182fdbdSPeter Wemm if (fd == 0) 14946182fdbdSPeter Wemm panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", 14956182fdbdSPeter Wemm (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); 14963a2f7427SDavid Greenman fdc = fd->fdc; 149769acd21dSWarner Losh if (fd->type == NO_TYPE) { 149869acd21dSWarner Losh bp->b_error = ENXIO; 149969acd21dSWarner Losh bp->b_flags |= B_ERROR; 15003b178206SWarner Losh goto bad; 150169acd21dSWarner Losh }; 15023a2f7427SDavid Greenman 1503d3628763SRodney W. Grimes fdblk = 128 << (fd->ft->secsize); 15043a2f7427SDavid Greenman if (!(bp->b_flags & B_FORMAT)) { 15056182fdbdSPeter Wemm if (bp->b_blkno < 0) { 1506dc5df763SJoerg Wunsch printf( 15076a0e6f42SRodney W. Grimes "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", 1508702c623aSPoul-Henning Kamp fdu, (u_long)bp->b_blkno, bp->b_bcount); 15093a2f7427SDavid Greenman bp->b_error = EINVAL; 15103a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 15113a2f7427SDavid Greenman goto bad; 15123a2f7427SDavid Greenman } 15133a2f7427SDavid Greenman if ((bp->b_bcount % fdblk) != 0) { 15143a2f7427SDavid Greenman bp->b_error = EINVAL; 15153a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 15163a2f7427SDavid Greenman goto bad; 15173a2f7427SDavid Greenman } 15183a2f7427SDavid Greenman } 15193a2f7427SDavid Greenman 15203a2f7427SDavid Greenman /* 15213a2f7427SDavid Greenman * Set up block calculations. 15223a2f7427SDavid Greenman */ 1523bb6382faSJoerg Wunsch if (bp->b_blkno > 20000000) { 1524bb6382faSJoerg Wunsch /* 1525bb6382faSJoerg Wunsch * Reject unreasonably high block number, prevent the 1526bb6382faSJoerg Wunsch * multiplication below from overflowing. 1527bb6382faSJoerg Wunsch */ 1528bb6382faSJoerg Wunsch bp->b_error = EINVAL; 15293a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 15303a2f7427SDavid Greenman goto bad; 15313a2f7427SDavid Greenman } 1532bb6382faSJoerg Wunsch blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; 1533bb6382faSJoerg Wunsch nblocks = fd->ft->size; 1534bb6382faSJoerg Wunsch bp->b_resid = 0; 1535bb6382faSJoerg Wunsch if (blknum + (bp->b_bcount / fdblk) > nblocks) { 1536bb6382faSJoerg Wunsch if (blknum <= nblocks) { 1537bb6382faSJoerg Wunsch cando = (nblocks - blknum) * fdblk; 1538bb6382faSJoerg Wunsch bp->b_resid = bp->b_bcount - cando; 1539bb6382faSJoerg Wunsch if (cando == 0) 1540bb6382faSJoerg Wunsch goto bad; /* not actually bad but EOF */ 1541bb6382faSJoerg Wunsch } else { 1542bb6382faSJoerg Wunsch bp->b_error = EINVAL; 1543bb6382faSJoerg Wunsch bp->b_flags |= B_ERROR; 1544bb6382faSJoerg Wunsch goto bad; 1545bb6382faSJoerg Wunsch } 1546bb6382faSJoerg Wunsch } 154727513ca7SBruce Evans bp->b_pblkno = bp->b_blkno; 15483a2f7427SDavid Greenman s = splbio(); 154902a19910SJustin T. Gibbs bufqdisksort(&fdc->head, bp); 15506182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ 1551b2dfb1f9SJustin T. Gibbs 1552b2dfb1f9SJustin T. Gibbs /* Tell devstat we are starting on the transaction */ 1553b2dfb1f9SJustin T. Gibbs devstat_start_transaction(&fd->device_stats); 1554b2dfb1f9SJustin T. Gibbs 15556182fdbdSPeter Wemm fdstart(fdc); 15563a2f7427SDavid Greenman splx(s); 15573a2f7427SDavid Greenman return; 15583a2f7427SDavid Greenman 15593a2f7427SDavid Greenman bad: 15603a2f7427SDavid Greenman biodone(bp); 15613a2f7427SDavid Greenman } 15623a2f7427SDavid Greenman 15635b81b6b3SRodney W. Grimes /***************************************************************\ 15645b81b6b3SRodney W. Grimes * fdstart * 15655b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy * 15665b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command * 15675b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more* 15685b81b6b3SRodney W. Grimes * work to do and picks up what we just added. * 15695b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it * 15705b81b6b3SRodney W. Grimes * will pick up our work when the present work completes * 15715b81b6b3SRodney W. Grimes \***************************************************************/ 1572381fe1aaSGarrett Wollman static void 15736182fdbdSPeter Wemm fdstart(struct fdc_data *fdc) 15745b81b6b3SRodney W. Grimes { 15755b81b6b3SRodney W. Grimes int s; 15765b81b6b3SRodney W. Grimes 15775b81b6b3SRodney W. Grimes s = splbio(); 15786182fdbdSPeter Wemm if(fdc->state == DEVIDLE) 15795b81b6b3SRodney W. Grimes { 15806182fdbdSPeter Wemm fdc_intr(fdc); 15815b81b6b3SRodney W. Grimes } 15825b81b6b3SRodney W. Grimes splx(s); 15835b81b6b3SRodney W. Grimes } 15845b81b6b3SRodney W. Grimes 1585381fe1aaSGarrett Wollman static void 15866182fdbdSPeter Wemm fd_iotimeout(void *xfdc) 15875b81b6b3SRodney W. Grimes { 15885c1a1eaeSBruce Evans fdc_p fdc; 1589f5f7ba03SJordan K. Hubbard int s; 15905b81b6b3SRodney W. Grimes 15916182fdbdSPeter Wemm fdc = xfdc; 15925c1a1eaeSBruce Evans TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); 15935b81b6b3SRodney W. Grimes 15943a2f7427SDavid Greenman /* 15953a2f7427SDavid Greenman * Due to IBM's brain-dead design, the FDC has a faked ready 15963a2f7427SDavid Greenman * signal, hardwired to ready == true. Thus, any command 15973a2f7427SDavid Greenman * issued if there's no diskette in the drive will _never_ 15983a2f7427SDavid Greenman * complete, and must be aborted by resetting the FDC. 15993a2f7427SDavid Greenman * Many thanks, Big Blue! 16005c1a1eaeSBruce Evans * The FDC must not be reset directly, since that would 16015c1a1eaeSBruce Evans * interfere with the state machine. Instead, pretend that 16025c1a1eaeSBruce Evans * the command completed but was invalid. The state machine 16035c1a1eaeSBruce Evans * will reset the FDC and retry once. 16043a2f7427SDavid Greenman */ 16053a2f7427SDavid Greenman s = splbio(); 16065c1a1eaeSBruce Evans fdc->status[0] = NE7_ST0_IC_IV; 16075c1a1eaeSBruce Evans fdc->flags &= ~FDC_STAT_VALID; 16085c1a1eaeSBruce Evans fdc->state = IOTIMEDOUT; 16096182fdbdSPeter Wemm fdc_intr(fdc); 1610f5f7ba03SJordan K. Hubbard splx(s); 16115b81b6b3SRodney W. Grimes } 16125b81b6b3SRodney W. Grimes 16135b81b6b3SRodney W. Grimes /* just ensure it has the right spl */ 1614381fe1aaSGarrett Wollman static void 16156182fdbdSPeter Wemm fd_pseudointr(void *xfdc) 16165b81b6b3SRodney W. Grimes { 16175b81b6b3SRodney W. Grimes int s; 16183a2f7427SDavid Greenman 16195b81b6b3SRodney W. Grimes s = splbio(); 16206182fdbdSPeter Wemm fdc_intr(xfdc); 16215b81b6b3SRodney W. Grimes splx(s); 16225b81b6b3SRodney W. Grimes } 16235b81b6b3SRodney W. Grimes 16245b81b6b3SRodney W. Grimes /***********************************************************************\ 16255b81b6b3SRodney W. Grimes * fdintr * 16265b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0 * 16275b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO * 16285b81b6b3SRodney W. Grimes \***********************************************************************/ 1629fe310de8SBruce Evans static void 16306182fdbdSPeter Wemm fdc_intr(void *xfdc) 16315b81b6b3SRodney W. Grimes { 16326182fdbdSPeter Wemm fdc_p fdc = xfdc; 16336182fdbdSPeter Wemm while(fdstate(fdc)) 1634381fe1aaSGarrett Wollman ; 16355b81b6b3SRodney W. Grimes } 16365b81b6b3SRodney W. Grimes 163769acd21dSWarner Losh /* 163869acd21dSWarner Losh * magic pseudo-DMA initialization for YE FDC. Sets count and 163969acd21dSWarner Losh * direction 164069acd21dSWarner Losh */ 16413b178206SWarner Losh #define SET_BCDR(fdc,wr,cnt,port) \ 16423b178206SWarner Losh bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port, \ 16433b178206SWarner Losh ((cnt)-1) & 0xff); \ 16443b178206SWarner Losh bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \ 16453b178206SWarner Losh ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))); 164669acd21dSWarner Losh 164769acd21dSWarner Losh /* 164869acd21dSWarner Losh * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy 164969acd21dSWarner Losh */ 16503b178206SWarner Losh static int fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) 165169acd21dSWarner Losh { 165269acd21dSWarner Losh u_char *cptr = (u_char *)addr; 165369acd21dSWarner Losh 165469acd21dSWarner Losh if (flags & B_READ) { 165569acd21dSWarner Losh if (fdc->state != PIOREAD) { 165669acd21dSWarner Losh fdc->state = PIOREAD; 165769acd21dSWarner Losh return(0); 165869acd21dSWarner Losh }; 16593b178206SWarner Losh SET_BCDR(fdc, 0, count, 0); 16603b178206SWarner Losh bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off + 16613b178206SWarner Losh FDC_YE_DATAPORT, cptr, count); 166269acd21dSWarner Losh } else { 16633b178206SWarner Losh bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off + 16643b178206SWarner Losh FDC_YE_DATAPORT, cptr, count); 16653b178206SWarner Losh SET_BCDR(fdc, 0, count, 0); 166669acd21dSWarner Losh }; 166769acd21dSWarner Losh return(1); 166869acd21dSWarner Losh } 166969acd21dSWarner Losh 16705b81b6b3SRodney W. Grimes /***********************************************************************\ 16715b81b6b3SRodney W. Grimes * The controller state machine. * 16725b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly * 16735b81b6b3SRodney W. Grimes \***********************************************************************/ 16743a2f7427SDavid Greenman static int 16756182fdbdSPeter Wemm fdstate(fdc_p fdc) 16765b81b6b3SRodney W. Grimes { 16775c1a1eaeSBruce Evans int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; 1678bb6382faSJoerg Wunsch unsigned blknum = 0, b_cylinder = 0; 16795b81b6b3SRodney W. Grimes fdu_t fdu = fdc->fdu; 16805b81b6b3SRodney W. Grimes fd_p fd; 168117542807SPoul-Henning Kamp register struct buf *bp; 1682b39c878eSAndrey A. Chernov struct fd_formb *finfo = NULL; 16833a2f7427SDavid Greenman size_t fdblk; 16845b81b6b3SRodney W. Grimes 1685e93e63cbSBruce Evans bp = fdc->bp; 1686e93e63cbSBruce Evans if (bp == NULL) { 168702a19910SJustin T. Gibbs bp = bufq_first(&fdc->head); 1688e93e63cbSBruce Evans if (bp != NULL) { 1689e93e63cbSBruce Evans bufq_remove(&fdc->head, bp); 1690e93e63cbSBruce Evans fdc->bp = bp; 1691e93e63cbSBruce Evans } 1692e93e63cbSBruce Evans } 1693e93e63cbSBruce Evans if (bp == NULL) { 16945b81b6b3SRodney W. Grimes /***********************************************\ 16955b81b6b3SRodney W. Grimes * nothing left for this controller to do * 16965b81b6b3SRodney W. Grimes * Force into the IDLE state, * 16975b81b6b3SRodney W. Grimes \***********************************************/ 16985b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 16996182fdbdSPeter Wemm if (fdc->fd) { 17006182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 17016182fdbdSPeter Wemm printf("unexpected valid fd pointer\n"); 17025b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 17035b81b6b3SRodney W. Grimes fdc->fdu = -1; 17045b81b6b3SRodney W. Grimes } 17056182fdbdSPeter Wemm TRACE1("[fdc%d IDLE]", fdc->fdcu); 17065b81b6b3SRodney W. Grimes return (0); 17075b81b6b3SRodney W. Grimes } 17085b81b6b3SRodney W. Grimes fdu = FDUNIT(minor(bp->b_dev)); 17096182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 17103a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 17116182fdbdSPeter Wemm if (fdc->fd && (fd != fdc->fd)) { 17126182fdbdSPeter Wemm device_print_prettyname(fd->dev); 17136182fdbdSPeter Wemm printf("confused fd pointers\n"); 17145b81b6b3SRodney W. Grimes } 17155b81b6b3SRodney W. Grimes read = bp->b_flags & B_READ; 1716b39c878eSAndrey A. Chernov format = bp->b_flags & B_FORMAT; 1717bb6382faSJoerg Wunsch if (format) { 1718ab3f7469SPoul-Henning Kamp finfo = (struct fd_formb *)bp->b_data; 1719bb6382faSJoerg Wunsch fd->skip = (char *)&(finfo->fd_formb_cylno(0)) 1720bb6382faSJoerg Wunsch - (char *)finfo; 1721bb6382faSJoerg Wunsch } 1722bb6382faSJoerg Wunsch if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { 17233e425b96SJulian Elischer blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + 1724bb6382faSJoerg Wunsch fd->skip/fdblk; 1725bb6382faSJoerg Wunsch b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1726bb6382faSJoerg Wunsch } 17275b81b6b3SRodney W. Grimes TRACE1("fd%d", fdu); 17285b81b6b3SRodney W. Grimes TRACE1("[%s]", fdstates[fdc->state]); 17295b81b6b3SRodney W. Grimes TRACE1("(0x%x)", fd->flags); 17306182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); 17316182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 17325b81b6b3SRodney W. Grimes switch (fdc->state) 17335b81b6b3SRodney W. Grimes { 17345b81b6b3SRodney W. Grimes case DEVIDLE: 17355b81b6b3SRodney W. Grimes case FINDWORK: /* we have found new work */ 17365b81b6b3SRodney W. Grimes fdc->retry = 0; 17375b81b6b3SRodney W. Grimes fd->skip = 0; 17385b81b6b3SRodney W. Grimes fdc->fd = fd; 17395b81b6b3SRodney W. Grimes fdc->fdu = fdu; 1740427ccf00SDoug Rabson fdctl_wr(fdc, fd->ft->trans); 17413a2f7427SDavid Greenman TRACE1("[0x%x->FDCTL]", fd->ft->trans); 17425b81b6b3SRodney W. Grimes /*******************************************************\ 17435b81b6b3SRodney W. Grimes * If the next drive has a motor startup pending, then * 1744dc733423SDag-Erling Smørgrav * it will start up in its own good time * 17455b81b6b3SRodney W. Grimes \*******************************************************/ 17466182fdbdSPeter Wemm if(fd->flags & FD_MOTOR_WAIT) { 17475b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 17485b81b6b3SRodney W. Grimes return (0); /* come back later */ 17495b81b6b3SRodney W. Grimes } 17505b81b6b3SRodney W. Grimes /*******************************************************\ 17515b81b6b3SRodney W. Grimes * Maybe if it's not starting, it SHOULD be starting * 17525b81b6b3SRodney W. Grimes \*******************************************************/ 17535b81b6b3SRodney W. Grimes if (!(fd->flags & FD_MOTOR)) 17545b81b6b3SRodney W. Grimes { 17555b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 17566182fdbdSPeter Wemm fd_turnon(fd); 17575b81b6b3SRodney W. Grimes return (0); 17585b81b6b3SRodney W. Grimes } 17595b81b6b3SRodney W. Grimes else /* at least make sure we are selected */ 17605b81b6b3SRodney W. Grimes { 17616182fdbdSPeter Wemm set_motor(fdc, fd->fdsu, TURNON); 17625b81b6b3SRodney W. Grimes } 17635c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 17645c1a1eaeSBruce Evans fdc->state = RESETCTLR; 17655c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 17665c1a1eaeSBruce Evans } else 17675e235068SJordan K. Hubbard fdc->state = DOSEEK; 17685b81b6b3SRodney W. Grimes break; 17695b81b6b3SRodney W. Grimes case DOSEEK: 1770bb6382faSJoerg Wunsch if (b_cylinder == (unsigned)fd->track) 17715b81b6b3SRodney W. Grimes { 17725b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 17735b81b6b3SRodney W. Grimes break; 17745b81b6b3SRodney W. Grimes } 17756182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, 1776bb6382faSJoerg Wunsch fd->fdsu, b_cylinder * fd->ft->steptrac, 1777dc5df763SJoerg Wunsch 0)) 1778dc8603e3SJoerg Wunsch { 1779dc8603e3SJoerg Wunsch /* 1780dc8603e3SJoerg Wunsch * seek command not accepted, looks like 1781dc8603e3SJoerg Wunsch * the FDC went off to the Saints... 1782dc8603e3SJoerg Wunsch */ 1783dc8603e3SJoerg Wunsch fdc->retry = 6; /* try a reset */ 17846182fdbdSPeter Wemm return(retrier(fdc)); 1785dc8603e3SJoerg Wunsch } 1786dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 17875b81b6b3SRodney W. Grimes fdc->state = SEEKWAIT; 17885b81b6b3SRodney W. Grimes return(0); /* will return later */ 17895b81b6b3SRodney W. Grimes case SEEKWAIT: 17905b81b6b3SRodney W. Grimes /* allow heads to settle */ 17916182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 16); 17925b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 17935b81b6b3SRodney W. Grimes return(0); /* will return later */ 17945b81b6b3SRodney W. Grimes case SEEKCOMPLETE : /* SEEK DONE, START DMA */ 17955b81b6b3SRodney W. Grimes /* Make sure seek really happened*/ 17966182fdbdSPeter Wemm if(fd->track == FD_NO_TRACK) { 1797bb6382faSJoerg Wunsch int descyl = b_cylinder * fd->ft->steptrac; 17983a2f7427SDavid Greenman do { 17993a2f7427SDavid Greenman /* 1800dc5df763SJoerg Wunsch * This might be a "ready changed" interrupt, 1801dc5df763SJoerg Wunsch * which cannot really happen since the 1802dc5df763SJoerg Wunsch * RDY pin is hardwired to + 5 volts. This 1803dc5df763SJoerg Wunsch * generally indicates a "bouncing" intr 1804dc5df763SJoerg Wunsch * line, so do one of the following: 1805dc5df763SJoerg Wunsch * 1806dc5df763SJoerg Wunsch * When running on an enhanced FDC that is 1807dc5df763SJoerg Wunsch * known to not go stuck after responding 1808dc5df763SJoerg Wunsch * with INVALID, fetch all interrupt states 1809dc5df763SJoerg Wunsch * until seeing either an INVALID or a 1810dc5df763SJoerg Wunsch * real interrupt condition. 1811dc5df763SJoerg Wunsch * 1812dc5df763SJoerg Wunsch * When running on a dumb old NE765, give 1813dc5df763SJoerg Wunsch * up immediately. The controller will 1814dc5df763SJoerg Wunsch * provide up to four dummy RC interrupt 1815dc5df763SJoerg Wunsch * conditions right after reset (for the 1816dc5df763SJoerg Wunsch * corresponding four drives), so this is 1817dc5df763SJoerg Wunsch * our only chance to get notice that it 1818dc5df763SJoerg Wunsch * was not the FDC that caused the interrupt. 18193a2f7427SDavid Greenman */ 1820dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) 1821dc5df763SJoerg Wunsch == FD_NOT_VALID) 1822dc5df763SJoerg Wunsch return 0; 1823dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 1824dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 1825dc5df763SJoerg Wunsch return 0; /* hope for a real intr */ 18263a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 1827dc5df763SJoerg Wunsch 18286182fdbdSPeter Wemm if (0 == descyl) { 1829dc5df763SJoerg Wunsch int failed = 0; 18303a2f7427SDavid Greenman /* 18313a2f7427SDavid Greenman * seek to cyl 0 requested; make sure we are 18323a2f7427SDavid Greenman * really there 18333a2f7427SDavid Greenman */ 1834dc5df763SJoerg Wunsch if (fd_sense_drive_status(fdc, &st3)) 1835dc5df763SJoerg Wunsch failed = 1; 18363a2f7427SDavid Greenman if ((st3 & NE7_ST3_T0) == 0) { 18373a2f7427SDavid Greenman printf( 18383a2f7427SDavid Greenman "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", 18393a2f7427SDavid Greenman fdu, st3, NE7_ST3BITS); 1840dc5df763SJoerg Wunsch failed = 1; 1841dc5df763SJoerg Wunsch } 1842dc5df763SJoerg Wunsch 18436182fdbdSPeter Wemm if (failed) { 18443a2f7427SDavid Greenman if(fdc->retry < 3) 18453a2f7427SDavid Greenman fdc->retry = 3; 18466182fdbdSPeter Wemm return (retrier(fdc)); 18473a2f7427SDavid Greenman } 18483a2f7427SDavid Greenman } 1849dc5df763SJoerg Wunsch 18506182fdbdSPeter Wemm if (cyl != descyl) { 18513a2f7427SDavid Greenman printf( 18523a2f7427SDavid Greenman "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", 18532d9d0204SRodney W. Grimes fdu, descyl, cyl, st0); 1854e5d7d243SBruce Evans if (fdc->retry < 3) 1855e5d7d243SBruce Evans fdc->retry = 3; 18566182fdbdSPeter Wemm return (retrier(fdc)); 18575b81b6b3SRodney W. Grimes } 18585b81b6b3SRodney W. Grimes } 18595b81b6b3SRodney W. Grimes 1860bb6382faSJoerg Wunsch fd->track = b_cylinder; 18613b178206SWarner Losh if (!(fdc->flags & FDC_NODMA)) 1862ab3f7469SPoul-Henning Kamp isa_dmastart(bp->b_flags, bp->b_data+fd->skip, 18633a2f7427SDavid Greenman format ? bp->b_bcount : fdblk, fdc->dmachan); 18645b81b6b3SRodney W. Grimes sectrac = fd->ft->sectrac; 18655b81b6b3SRodney W. Grimes sec = blknum % (sectrac * fd->ft->heads); 18665b81b6b3SRodney W. Grimes head = sec / sectrac; 18675b81b6b3SRodney W. Grimes sec = sec % sectrac + 1; 18683a2f7427SDavid Greenman fd->hddrv = ((head&1)<<2)+fdu; 18693a2f7427SDavid Greenman 18703a2f7427SDavid Greenman if(format || !read) 18713a2f7427SDavid Greenman { 18723a2f7427SDavid Greenman /* make sure the drive is writable */ 1873dc5df763SJoerg Wunsch if(fd_sense_drive_status(fdc, &st3) != 0) 1874dc8603e3SJoerg Wunsch { 1875dc8603e3SJoerg Wunsch /* stuck controller? */ 18765c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 18775c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 18785c1a1eaeSBruce Evans fdc->dmachan); 1879dc8603e3SJoerg Wunsch fdc->retry = 6; /* reset the beast */ 18806182fdbdSPeter Wemm return (retrier(fdc)); 1881dc8603e3SJoerg Wunsch } 18823a2f7427SDavid Greenman if(st3 & NE7_ST3_WP) 18833a2f7427SDavid Greenman { 18843a2f7427SDavid Greenman /* 18853a2f7427SDavid Greenman * XXX YES! this is ugly. 18863a2f7427SDavid Greenman * in order to force the current operation 18873a2f7427SDavid Greenman * to fail, we will have to fake an FDC 18883a2f7427SDavid Greenman * error - all error handling is done 18893a2f7427SDavid Greenman * by the retrier() 18903a2f7427SDavid Greenman */ 18913a2f7427SDavid Greenman fdc->status[0] = NE7_ST0_IC_AT; 18923a2f7427SDavid Greenman fdc->status[1] = NE7_ST1_NW; 18933a2f7427SDavid Greenman fdc->status[2] = 0; 18943a2f7427SDavid Greenman fdc->status[3] = fd->track; 18953a2f7427SDavid Greenman fdc->status[4] = head; 18963a2f7427SDavid Greenman fdc->status[5] = sec; 18973a2f7427SDavid Greenman fdc->retry = 8; /* break out immediately */ 18983a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; /* not really... */ 18993a2f7427SDavid Greenman return (1); 19003a2f7427SDavid Greenman } 19013a2f7427SDavid Greenman } 19025b81b6b3SRodney W. Grimes 19036182fdbdSPeter Wemm if (format) { 19043b178206SWarner Losh if (fdc->flags & FDC_NODMA) 19053b178206SWarner Losh (void)fdcpio(fdc,bp->b_flags, 190669acd21dSWarner Losh bp->b_data+fd->skip, 190769acd21dSWarner Losh bp->b_bcount); 1908b39c878eSAndrey A. Chernov /* formatting */ 19096182fdbdSPeter Wemm if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, 1910dc5df763SJoerg Wunsch finfo->fd_formb_secshift, 1911dc5df763SJoerg Wunsch finfo->fd_formb_nsecs, 1912dc5df763SJoerg Wunsch finfo->fd_formb_gaplen, 19136182fdbdSPeter Wemm finfo->fd_formb_fillbyte, 0)) { 1914dc8603e3SJoerg Wunsch /* controller fell over */ 19155c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 19165c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 19175c1a1eaeSBruce Evans fdc->dmachan); 1918dc8603e3SJoerg Wunsch fdc->retry = 6; 19196182fdbdSPeter Wemm return (retrier(fdc)); 1920dc8603e3SJoerg Wunsch } 19216182fdbdSPeter Wemm } else { 19223b178206SWarner Losh if (fdc->flags & FDC_NODMA) { 192369acd21dSWarner Losh /* 192469acd21dSWarner Losh * this seems to be necessary even when 192569acd21dSWarner Losh * reading data 192669acd21dSWarner Losh */ 19273b178206SWarner Losh SET_BCDR(fdc, 1, fdblk, 0); 192869acd21dSWarner Losh 192969acd21dSWarner Losh /* 193069acd21dSWarner Losh * perform the write pseudo-DMA before 193169acd21dSWarner Losh * the WRITE command is sent 193269acd21dSWarner Losh */ 193369acd21dSWarner Losh if (!read) 19343b178206SWarner Losh (void)fdcpio(fdc,bp->b_flags, 193569acd21dSWarner Losh bp->b_data+fd->skip, 193669acd21dSWarner Losh fdblk); 193769acd21dSWarner Losh } 19386182fdbdSPeter Wemm if (fd_cmd(fdc, 9, 1939dc5df763SJoerg Wunsch (read ? NE7CMD_READ : NE7CMD_WRITE), 1940dc5df763SJoerg Wunsch head << 2 | fdu, /* head & unit */ 1941dc5df763SJoerg Wunsch fd->track, /* track */ 1942dc5df763SJoerg Wunsch head, 1943dc5df763SJoerg Wunsch sec, /* sector + 1 */ 1944dc5df763SJoerg Wunsch fd->ft->secsize, /* sector size */ 1945dc5df763SJoerg Wunsch sectrac, /* sectors/track */ 1946dc5df763SJoerg Wunsch fd->ft->gap, /* gap size */ 1947dc5df763SJoerg Wunsch fd->ft->datalen, /* data length */ 19486182fdbdSPeter Wemm 0)) { 1949dc8603e3SJoerg Wunsch /* the beast is sleeping again */ 19505c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 19515c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 19525c1a1eaeSBruce Evans fdc->dmachan); 1953dc8603e3SJoerg Wunsch fdc->retry = 6; 19546182fdbdSPeter Wemm return (retrier(fdc)); 19555b81b6b3SRodney W. Grimes } 1956b39c878eSAndrey A. Chernov } 19573b178206SWarner Losh if (fdc->flags & FDC_NODMA) 195869acd21dSWarner Losh /* 195969acd21dSWarner Losh * if this is a read, then simply await interrupt 196069acd21dSWarner Losh * before performing PIO 196169acd21dSWarner Losh */ 19623b178206SWarner Losh if (read && !fdcpio(fdc,bp->b_flags, 196369acd21dSWarner Losh bp->b_data+fd->skip,fdblk)) { 19643b178206SWarner Losh fd->tohandle = timeout(fd_iotimeout, fdc, hz); 196569acd21dSWarner Losh return(0); /* will return later */ 196669acd21dSWarner Losh }; 196769acd21dSWarner Losh 196869acd21dSWarner Losh /* 196969acd21dSWarner Losh * write (or format) operation will fall through and 197069acd21dSWarner Losh * await completion interrupt 197169acd21dSWarner Losh */ 19725b81b6b3SRodney W. Grimes fdc->state = IOCOMPLETE; 19736182fdbdSPeter Wemm fd->tohandle = timeout(fd_iotimeout, fdc, hz); 19745b81b6b3SRodney W. Grimes return (0); /* will return later */ 197569acd21dSWarner Losh case PIOREAD: 197669acd21dSWarner Losh /* 197769acd21dSWarner Losh * actually perform the PIO read. The IOCOMPLETE case 197869acd21dSWarner Losh * removes the timeout for us. 197969acd21dSWarner Losh */ 19803b178206SWarner Losh (void)fdcpio(fdc,bp->b_flags,bp->b_data+fd->skip,fdblk); 198169acd21dSWarner Losh fdc->state = IOCOMPLETE; 198269acd21dSWarner Losh /* FALLTHROUGH */ 19835b81b6b3SRodney W. Grimes case IOCOMPLETE: /* IO DONE, post-analyze */ 19846182fdbdSPeter Wemm untimeout(fd_iotimeout, fdc, fd->tohandle); 1985dc5df763SJoerg Wunsch 19866182fdbdSPeter Wemm if (fd_read_status(fdc, fd->fdsu)) { 19875c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 19885c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 19895c1a1eaeSBruce Evans fdc->dmachan); 1990dc5df763SJoerg Wunsch if (fdc->retry < 6) 1991dc5df763SJoerg Wunsch fdc->retry = 6; /* force a reset */ 19926182fdbdSPeter Wemm return (retrier(fdc)); 19935b81b6b3SRodney W. Grimes } 1994dc5df763SJoerg Wunsch 19953a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; 1996dc5df763SJoerg Wunsch 19973a2f7427SDavid Greenman /* FALLTHROUGH */ 1998dc5df763SJoerg Wunsch 19993a2f7427SDavid Greenman case IOTIMEDOUT: 20003b178206SWarner Losh if (!(fdc->flags & FDC_NODMA)) 2001ab3f7469SPoul-Henning Kamp isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 20023a2f7427SDavid Greenman format ? bp->b_bcount : fdblk, fdc->dmachan); 20036182fdbdSPeter Wemm if (fdc->status[0] & NE7_ST0_IC) { 20043a2f7427SDavid Greenman if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 20053a2f7427SDavid Greenman && fdc->status[1] & NE7_ST1_OR) { 2006b39c878eSAndrey A. Chernov /* 20073a2f7427SDavid Greenman * DMA overrun. Someone hogged the bus 20083a2f7427SDavid Greenman * and didn't release it in time for the 20093a2f7427SDavid Greenman * next FDC transfer. 20103a2f7427SDavid Greenman * Just restart it, don't increment retry 20113a2f7427SDavid Greenman * count. (vak) 2012b39c878eSAndrey A. Chernov */ 2013b39c878eSAndrey A. Chernov fdc->state = SEEKCOMPLETE; 2014b39c878eSAndrey A. Chernov return (1); 2015b39c878eSAndrey A. Chernov } 20163a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV 20173a2f7427SDavid Greenman && fdc->retry < 6) 20183a2f7427SDavid Greenman fdc->retry = 6; /* force a reset */ 20193a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 20203a2f7427SDavid Greenman && fdc->status[2] & NE7_ST2_WC 20213a2f7427SDavid Greenman && fdc->retry < 3) 20223a2f7427SDavid Greenman fdc->retry = 3; /* force recalibrate */ 20236182fdbdSPeter Wemm return (retrier(fdc)); 20245b81b6b3SRodney W. Grimes } 20255b81b6b3SRodney W. Grimes /* All OK */ 20263a2f7427SDavid Greenman fd->skip += fdblk; 20276182fdbdSPeter Wemm if (!format && fd->skip < bp->b_bcount - bp->b_resid) { 20285b81b6b3SRodney W. Grimes /* set up next transfer */ 20295b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 20306182fdbdSPeter Wemm } else { 20315b81b6b3SRodney W. Grimes /* ALL DONE */ 20325b81b6b3SRodney W. Grimes fd->skip = 0; 2033e93e63cbSBruce Evans fdc->bp = NULL; 20342186cd9eSPoul-Henning Kamp devstat_end_transaction_buf(&fd->device_stats, bp); 20355b81b6b3SRodney W. Grimes biodone(bp); 20365b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 20375b81b6b3SRodney W. Grimes fdc->fdu = -1; 20385b81b6b3SRodney W. Grimes fdc->state = FINDWORK; 20395b81b6b3SRodney W. Grimes } 20405b81b6b3SRodney W. Grimes return (1); 20415b81b6b3SRodney W. Grimes case RESETCTLR: 20423a2f7427SDavid Greenman fdc_reset(fdc); 20435b81b6b3SRodney W. Grimes fdc->retry++; 20445c1a1eaeSBruce Evans fdc->state = RESETCOMPLETE; 20455c1a1eaeSBruce Evans return (0); 20465c1a1eaeSBruce Evans case RESETCOMPLETE: 20475c1a1eaeSBruce Evans /* 20485c1a1eaeSBruce Evans * Discard all the results from the reset so that they 20495c1a1eaeSBruce Evans * can't cause an unexpected interrupt later. 20505c1a1eaeSBruce Evans */ 20510e317d05SJoerg Wunsch for (i = 0; i < 4; i++) 20520e317d05SJoerg Wunsch (void)fd_sense_int(fdc, &st0, &cyl); 20535c1a1eaeSBruce Evans fdc->state = STARTRECAL; 20545c1a1eaeSBruce Evans /* Fall through. */ 20555c1a1eaeSBruce Evans case STARTRECAL: 20566182fdbdSPeter Wemm if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { 2057dc8603e3SJoerg Wunsch /* arrgl */ 2058dc8603e3SJoerg Wunsch fdc->retry = 6; 20596182fdbdSPeter Wemm return (retrier(fdc)); 2060dc8603e3SJoerg Wunsch } 20615b81b6b3SRodney W. Grimes fdc->state = RECALWAIT; 20625b81b6b3SRodney W. Grimes return (0); /* will return later */ 20635b81b6b3SRodney W. Grimes case RECALWAIT: 20645b81b6b3SRodney W. Grimes /* allow heads to settle */ 20656182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 8); 20665b81b6b3SRodney W. Grimes fdc->state = RECALCOMPLETE; 20675b81b6b3SRodney W. Grimes return (0); /* will return later */ 20685b81b6b3SRodney W. Grimes case RECALCOMPLETE: 20693a2f7427SDavid Greenman do { 20703a2f7427SDavid Greenman /* 2071dc5df763SJoerg Wunsch * See SEEKCOMPLETE for a comment on this: 20723a2f7427SDavid Greenman */ 2073dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) 2074dc5df763SJoerg Wunsch return 0; 2075dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 2076dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 2077dc5df763SJoerg Wunsch return 0; /* hope for a real intr */ 20783a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 20793a2f7427SDavid Greenman if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) 20805b81b6b3SRodney W. Grimes { 2081dc8603e3SJoerg Wunsch if(fdc->retry > 3) 2082dc8603e3SJoerg Wunsch /* 2083dc8603e3SJoerg Wunsch * a recalibrate from beyond cylinder 77 2084dc8603e3SJoerg Wunsch * will "fail" due to the FDC limitations; 2085dc8603e3SJoerg Wunsch * since people used to complain much about 2086dc8603e3SJoerg Wunsch * the failure message, try not logging 2087dc8603e3SJoerg Wunsch * this one if it seems to be the first 2088dc8603e3SJoerg Wunsch * time in a line 2089dc8603e3SJoerg Wunsch */ 2090dc8603e3SJoerg Wunsch printf("fd%d: recal failed ST0 %b cyl %d\n", 2091dc8603e3SJoerg Wunsch fdu, st0, NE7_ST0BITS, cyl); 20923a2f7427SDavid Greenman if(fdc->retry < 3) fdc->retry = 3; 20936182fdbdSPeter Wemm return (retrier(fdc)); 20945b81b6b3SRodney W. Grimes } 20955b81b6b3SRodney W. Grimes fd->track = 0; 20965b81b6b3SRodney W. Grimes /* Seek (probably) necessary */ 20975b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 20985b81b6b3SRodney W. Grimes return (1); /* will return immediatly */ 20995b81b6b3SRodney W. Grimes case MOTORWAIT: 21005b81b6b3SRodney W. Grimes if(fd->flags & FD_MOTOR_WAIT) 21015b81b6b3SRodney W. Grimes { 21025b81b6b3SRodney W. Grimes return (0); /* time's not up yet */ 21035b81b6b3SRodney W. Grimes } 21045c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 21055c1a1eaeSBruce Evans fdc->state = RESETCTLR; 21065c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 21075c1a1eaeSBruce Evans } else { 21085e235068SJordan K. Hubbard /* 21095c1a1eaeSBruce Evans * If all motors were off, then the controller was 21105c1a1eaeSBruce Evans * reset, so it has lost track of the current 21115c1a1eaeSBruce Evans * cylinder. Recalibrate to handle this case. 2112f86e4077SBruce Evans * But first, discard the results of the reset. 21135e235068SJordan K. Hubbard */ 2114f86e4077SBruce Evans fdc->state = RESETCOMPLETE; 21155c1a1eaeSBruce Evans } 21165b81b6b3SRodney W. Grimes return (1); /* will return immediatly */ 21175b81b6b3SRodney W. Grimes default: 21186182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 21196182fdbdSPeter Wemm printf("unexpected FD int->"); 2120dc5df763SJoerg Wunsch if (fd_read_status(fdc, fd->fdsu) == 0) 2121a838d83dSBruce Evans printf("FDC status :%x %x %x %x %x %x %x ", 21225b81b6b3SRodney W. Grimes fdc->status[0], 21235b81b6b3SRodney W. Grimes fdc->status[1], 21245b81b6b3SRodney W. Grimes fdc->status[2], 21255b81b6b3SRodney W. Grimes fdc->status[3], 21265b81b6b3SRodney W. Grimes fdc->status[4], 21275b81b6b3SRodney W. Grimes fdc->status[5], 21285b81b6b3SRodney W. Grimes fdc->status[6] ); 21293a2f7427SDavid Greenman else 2130dac0f2dbSJoerg Wunsch printf("No status available "); 2131dac0f2dbSJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) != 0) 2132dac0f2dbSJoerg Wunsch { 2133dac0f2dbSJoerg Wunsch printf("[controller is dead now]\n"); 21345b81b6b3SRodney W. Grimes return (0); 21355b81b6b3SRodney W. Grimes } 2136dac0f2dbSJoerg Wunsch printf("ST0 = %x, PCN = %x\n", st0, cyl); 2137dac0f2dbSJoerg Wunsch return (0); 2138dac0f2dbSJoerg Wunsch } 2139dac0f2dbSJoerg Wunsch /*XXX confusing: some branches return immediately, others end up here*/ 21405b81b6b3SRodney W. Grimes return (1); /* Come back immediatly to new state */ 21415b81b6b3SRodney W. Grimes } 21425b81b6b3SRodney W. Grimes 2143aaf08d94SGarrett Wollman static int 21446182fdbdSPeter Wemm retrier(struct fdc_data *fdc) 21455b81b6b3SRodney W. Grimes { 214617542807SPoul-Henning Kamp register struct buf *bp; 21476182fdbdSPeter Wemm struct fd_data *fd; 21486182fdbdSPeter Wemm int fdu; 21495b81b6b3SRodney W. Grimes 2150e93e63cbSBruce Evans bp = fdc->bp; 21515b81b6b3SRodney W. Grimes 21526182fdbdSPeter Wemm /* XXX shouldn't this be cached somewhere? */ 21536182fdbdSPeter Wemm fdu = FDUNIT(minor(bp->b_dev)); 21546182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 21556182fdbdSPeter Wemm if (fd->options & FDOPT_NORETRY) 21563a2f7427SDavid Greenman goto fail; 21576182fdbdSPeter Wemm 21586182fdbdSPeter Wemm switch (fdc->retry) { 21595b81b6b3SRodney W. Grimes case 0: case 1: case 2: 21605b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 21615b81b6b3SRodney W. Grimes break; 21625b81b6b3SRodney W. Grimes case 3: case 4: case 5: 21635b81b6b3SRodney W. Grimes fdc->state = STARTRECAL; 21645b81b6b3SRodney W. Grimes break; 21655b81b6b3SRodney W. Grimes case 6: 21665b81b6b3SRodney W. Grimes fdc->state = RESETCTLR; 21675b81b6b3SRodney W. Grimes break; 21685b81b6b3SRodney W. Grimes case 7: 21695b81b6b3SRodney W. Grimes break; 21705b81b6b3SRodney W. Grimes default: 21713a2f7427SDavid Greenman fail: 21725b81b6b3SRodney W. Grimes { 21737ca0641bSAndrey A. Chernov dev_t sav_b_dev = bp->b_dev; 21747ca0641bSAndrey A. Chernov /* Trick diskerr */ 21753a2f7427SDavid Greenman bp->b_dev = makedev(major(bp->b_dev), 21763a2f7427SDavid Greenman (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); 2177887ba12fSBruce Evans diskerr(bp, "hard error", LOG_PRINTF, 21783a2f7427SDavid Greenman fdc->fd->skip / DEV_BSIZE, 21793a2f7427SDavid Greenman (struct disklabel *)NULL); 21807ca0641bSAndrey A. Chernov bp->b_dev = sav_b_dev; 2181dc5df763SJoerg Wunsch if (fdc->flags & FDC_STAT_VALID) 2182dc5df763SJoerg Wunsch { 2183dc5df763SJoerg Wunsch printf( 2184a838d83dSBruce Evans " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", 2185dc5df763SJoerg Wunsch fdc->status[0], NE7_ST0BITS, 2186dc5df763SJoerg Wunsch fdc->status[1], NE7_ST1BITS, 2187dc5df763SJoerg Wunsch fdc->status[2], NE7_ST2BITS, 2188dc5df763SJoerg Wunsch fdc->status[3], fdc->status[4], 2189dc5df763SJoerg Wunsch fdc->status[5]); 2190dc5df763SJoerg Wunsch } 2191dc5df763SJoerg Wunsch else 2192dc5df763SJoerg Wunsch printf(" (No status)\n"); 21935b81b6b3SRodney W. Grimes } 21945b81b6b3SRodney W. Grimes bp->b_flags |= B_ERROR; 21955b81b6b3SRodney W. Grimes bp->b_error = EIO; 2196bb6382faSJoerg Wunsch bp->b_resid += bp->b_bcount - fdc->fd->skip; 2197e93e63cbSBruce Evans fdc->bp = NULL; 21985b81b6b3SRodney W. Grimes fdc->fd->skip = 0; 219911a0be87SPoul-Henning Kamp devstat_end_transaction_buf(&fdc->fd->device_stats, bp); 22005b81b6b3SRodney W. Grimes biodone(bp); 220192ed385aSRodney W. Grimes fdc->state = FINDWORK; 22025c1a1eaeSBruce Evans fdc->flags |= FDC_NEEDS_RESET; 22035b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 22045b81b6b3SRodney W. Grimes fdc->fdu = -1; 220592ed385aSRodney W. Grimes return (1); 22065b81b6b3SRodney W. Grimes } 22075b81b6b3SRodney W. Grimes fdc->retry++; 22085b81b6b3SRodney W. Grimes return (1); 22095b81b6b3SRodney W. Grimes } 22105b81b6b3SRodney W. Grimes 2211b39c878eSAndrey A. Chernov static int 2212b39c878eSAndrey A. Chernov fdformat(dev, finfo, p) 2213b39c878eSAndrey A. Chernov dev_t dev; 2214b39c878eSAndrey A. Chernov struct fd_formb *finfo; 2215b39c878eSAndrey A. Chernov struct proc *p; 2216b39c878eSAndrey A. Chernov { 2217b39c878eSAndrey A. Chernov fdu_t fdu; 2218b39c878eSAndrey A. Chernov fd_p fd; 2219b39c878eSAndrey A. Chernov 2220b39c878eSAndrey A. Chernov struct buf *bp; 2221b39c878eSAndrey A. Chernov int rv = 0, s; 22223a2f7427SDavid Greenman size_t fdblk; 2223b39c878eSAndrey A. Chernov 2224b39c878eSAndrey A. Chernov fdu = FDUNIT(minor(dev)); 22256182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 22263a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2227b39c878eSAndrey A. Chernov 2228b39c878eSAndrey A. Chernov /* set up a buffer header for fdstrategy() */ 2229b39c878eSAndrey A. Chernov bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); 2230b39c878eSAndrey A. Chernov if(bp == 0) 2231b39c878eSAndrey A. Chernov return ENOBUFS; 223282f5379bSJoerg Wunsch /* 223382f5379bSJoerg Wunsch * keep the process from being swapped 223482f5379bSJoerg Wunsch */ 22352ada239cSPeter Wemm PHOLD(p); 2236b39c878eSAndrey A. Chernov bzero((void *)bp, sizeof(struct buf)); 223767812eacSKirk McKusick BUF_LOCKINIT(bp); 223867812eacSKirk McKusick BUF_LOCK(bp, LK_EXCLUSIVE); 223967812eacSKirk McKusick bp->b_flags = B_PHYS | B_FORMAT; 2240b39c878eSAndrey A. Chernov 2241b39c878eSAndrey A. Chernov /* 2242b39c878eSAndrey A. Chernov * calculate a fake blkno, so fdstrategy() would initiate a 2243b39c878eSAndrey A. Chernov * seek to the requested cylinder 2244b39c878eSAndrey A. Chernov */ 2245b39c878eSAndrey A. Chernov bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) 22463a2f7427SDavid Greenman + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; 2247b39c878eSAndrey A. Chernov 2248b39c878eSAndrey A. Chernov bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; 2249ab3f7469SPoul-Henning Kamp bp->b_data = (caddr_t)finfo; 2250b39c878eSAndrey A. Chernov 2251b39c878eSAndrey A. Chernov /* now do the format */ 22523e425b96SJulian Elischer bp->b_dev = dev; 225349ff4debSPoul-Henning Kamp BUF_STRATEGY(bp, 0); 2254b39c878eSAndrey A. Chernov 2255b39c878eSAndrey A. Chernov /* ...and wait for it to complete */ 2256b39c878eSAndrey A. Chernov s = splbio(); 22576182fdbdSPeter Wemm while(!(bp->b_flags & B_DONE)) { 22585e235068SJordan K. Hubbard rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); 2259b39c878eSAndrey A. Chernov if (rv == EWOULDBLOCK) 2260b39c878eSAndrey A. Chernov break; 2261b39c878eSAndrey A. Chernov } 2262b39c878eSAndrey A. Chernov splx(s); 2263b39c878eSAndrey A. Chernov 226482f5379bSJoerg Wunsch if (rv == EWOULDBLOCK) { 2265b39c878eSAndrey A. Chernov /* timed out */ 2266b39c878eSAndrey A. Chernov rv = EIO; 226782f5379bSJoerg Wunsch biodone(bp); 226882f5379bSJoerg Wunsch } 22693a2f7427SDavid Greenman if (bp->b_flags & B_ERROR) 22703a2f7427SDavid Greenman rv = bp->b_error; 227182f5379bSJoerg Wunsch /* 227282f5379bSJoerg Wunsch * allow the process to be swapped 227382f5379bSJoerg Wunsch */ 22742ada239cSPeter Wemm PRELE(p); 227567812eacSKirk McKusick BUF_UNLOCK(bp); 227667812eacSKirk McKusick BUF_LOCKFREE(bp); 2277b39c878eSAndrey A. Chernov free(bp, M_TEMP); 2278b39c878eSAndrey A. Chernov return rv; 2279b39c878eSAndrey A. Chernov } 2280b39c878eSAndrey A. Chernov 2281f5f7ba03SJordan K. Hubbard /* 2282671e2ceeSBruce Evans * TODO: don't allocate buffer on stack. 2283f5f7ba03SJordan K. Hubbard */ 22845b81b6b3SRodney W. Grimes 22853e425b96SJulian Elischer static int 2286b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p) 2287f5f7ba03SJordan K. Hubbard dev_t dev; 2288ecbb00a2SDoug Rabson u_long cmd; 2289f5f7ba03SJordan K. Hubbard caddr_t addr; 2290f5f7ba03SJordan K. Hubbard int flag; 2291b39c878eSAndrey A. Chernov struct proc *p; 2292f5f7ba03SJordan K. Hubbard { 22933a2f7427SDavid Greenman fdu_t fdu = FDUNIT(minor(dev)); 22946182fdbdSPeter Wemm fd_p fd = devclass_get_softc(fd_devclass, fdu); 22953a2f7427SDavid Greenman size_t fdblk; 22963a2f7427SDavid Greenman 2297f5f7ba03SJordan K. Hubbard struct fd_type *fdt; 2298f5f7ba03SJordan K. Hubbard struct disklabel *dl; 2299f5f7ba03SJordan K. Hubbard char buffer[DEV_BSIZE]; 23003a2f7427SDavid Greenman int error = 0; 2301f5f7ba03SJordan K. Hubbard 23023a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2303f5f7ba03SJordan K. Hubbard 23046182fdbdSPeter Wemm switch (cmd) { 2305f5f7ba03SJordan K. Hubbard case DIOCGDINFO: 2306f5f7ba03SJordan K. Hubbard bzero(buffer, sizeof (buffer)); 2307f5f7ba03SJordan K. Hubbard dl = (struct disklabel *)buffer; 23083a2f7427SDavid Greenman dl->d_secsize = fdblk; 23096182fdbdSPeter Wemm fdt = fd->ft; 2310f5f7ba03SJordan K. Hubbard dl->d_secpercyl = fdt->size / fdt->tracks; 2311f5f7ba03SJordan K. Hubbard dl->d_type = DTYPE_FLOPPY; 2312f5f7ba03SJordan K. Hubbard 231349ff4debSPoul-Henning Kamp if (readdisklabel(dkmodpart(dev, RAW_PART), dl) 2314191e1a59SBruce Evans == NULL) 2315f5f7ba03SJordan K. Hubbard error = 0; 2316f5f7ba03SJordan K. Hubbard else 2317f5f7ba03SJordan K. Hubbard error = EINVAL; 2318f5f7ba03SJordan K. Hubbard 2319f5f7ba03SJordan K. Hubbard *(struct disklabel *)addr = *dl; 2320f5f7ba03SJordan K. Hubbard break; 2321f5f7ba03SJordan K. Hubbard 2322f5f7ba03SJordan K. Hubbard case DIOCSDINFO: 2323f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2324f5f7ba03SJordan K. Hubbard error = EBADF; 2325f5f7ba03SJordan K. Hubbard break; 2326f5f7ba03SJordan K. Hubbard 2327f5f7ba03SJordan K. Hubbard case DIOCWLABEL: 2328f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2329f5f7ba03SJordan K. Hubbard error = EBADF; 2330f5f7ba03SJordan K. Hubbard break; 2331f5f7ba03SJordan K. Hubbard 2332f5f7ba03SJordan K. Hubbard case DIOCWDINFO: 23336182fdbdSPeter Wemm if ((flag & FWRITE) == 0) { 2334f5f7ba03SJordan K. Hubbard error = EBADF; 2335f5f7ba03SJordan K. Hubbard break; 2336f5f7ba03SJordan K. Hubbard } 2337f5f7ba03SJordan K. Hubbard 2338f5f7ba03SJordan K. Hubbard dl = (struct disklabel *)addr; 2339f5f7ba03SJordan K. Hubbard 2340191e1a59SBruce Evans if ((error = setdisklabel((struct disklabel *)buffer, dl, 2341191e1a59SBruce Evans (u_long)0)) != 0) 2342f5f7ba03SJordan K. Hubbard break; 2343f5f7ba03SJordan K. Hubbard 234449ff4debSPoul-Henning Kamp error = writedisklabel(dev, (struct disklabel *)buffer); 2345b39c878eSAndrey A. Chernov break; 2346b39c878eSAndrey A. Chernov case FD_FORM: 2347b39c878eSAndrey A. Chernov if ((flag & FWRITE) == 0) 2348b39c878eSAndrey A. Chernov error = EBADF; /* must be opened for writing */ 2349b39c878eSAndrey A. Chernov else if (((struct fd_formb *)addr)->format_version != 2350b39c878eSAndrey A. Chernov FD_FORMAT_VERSION) 2351b39c878eSAndrey A. Chernov error = EINVAL; /* wrong version of formatting prog */ 2352b39c878eSAndrey A. Chernov else 2353b39c878eSAndrey A. Chernov error = fdformat(dev, (struct fd_formb *)addr, p); 2354b39c878eSAndrey A. Chernov break; 2355b39c878eSAndrey A. Chernov 2356b39c878eSAndrey A. Chernov case FD_GTYPE: /* get drive type */ 23573e425b96SJulian Elischer *(struct fd_type *)addr = *fd->ft; 2358f5f7ba03SJordan K. Hubbard break; 2359f5f7ba03SJordan K. Hubbard 23603a2f7427SDavid Greenman case FD_STYPE: /* set drive type */ 23613a2f7427SDavid Greenman /* this is considered harmful; only allow for superuser */ 2362f711d546SPoul-Henning Kamp if (suser(p) != 0) 23633a2f7427SDavid Greenman return EPERM; 23643e425b96SJulian Elischer *fd->ft = *(struct fd_type *)addr; 23653a2f7427SDavid Greenman break; 23663a2f7427SDavid Greenman 23673a2f7427SDavid Greenman case FD_GOPTS: /* get drive options */ 23683e425b96SJulian Elischer *(int *)addr = fd->options; 23693a2f7427SDavid Greenman break; 23703a2f7427SDavid Greenman 23713a2f7427SDavid Greenman case FD_SOPTS: /* set drive options */ 23723e425b96SJulian Elischer fd->options = *(int *)addr; 23733a2f7427SDavid Greenman break; 23743a2f7427SDavid Greenman 2375f5f7ba03SJordan K. Hubbard default: 23763a2f7427SDavid Greenman error = ENOTTY; 2377f5f7ba03SJordan K. Hubbard break; 2378f5f7ba03SJordan K. Hubbard } 2379f5f7ba03SJordan K. Hubbard return (error); 2380f5f7ba03SJordan K. Hubbard } 2381f5f7ba03SJordan K. Hubbard 23823a2f7427SDavid Greenman /* 23833a2f7427SDavid Greenman * Hello emacs, these are the 23843a2f7427SDavid Greenman * Local Variables: 23853a2f7427SDavid Greenman * c-indent-level: 8 23863a2f7427SDavid Greenman * c-continued-statement-offset: 8 23873a2f7427SDavid Greenman * c-continued-brace-offset: 0 23883a2f7427SDavid Greenman * c-brace-offset: -8 23893a2f7427SDavid Greenman * c-brace-imaginary-offset: 0 23903a2f7427SDavid Greenman * c-argdecl-indent: 8 23913a2f7427SDavid Greenman * c-label-offset: -8 23923a2f7427SDavid Greenman * c++-hanging-braces: 1 23933a2f7427SDavid Greenman * c++-access-specifier-offset: -8 23943a2f7427SDavid Greenman * c++-empty-arglist-indent: 8 23953a2f7427SDavid Greenman * c++-friend-offset: 0 23963a2f7427SDavid Greenman * End: 23973a2f7427SDavid Greenman */ 2398