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 85edfdec19SPeter Wemm #ifdef FDC_YE 86edfdec19SPeter Wemm #undef FDC_YE 87edfdec19SPeter Wemm #warning "fix FDC_YE! - newbus casualty" 88edfdec19SPeter Wemm #endif 89edfdec19SPeter Wemm 90b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */ 91b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX 925b81b6b3SRodney W. Grimes 930722d6abSJoerg Wunsch /* configuration flags */ 940722d6abSJoerg Wunsch #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ 9569acd21dSWarner Losh #ifdef FDC_YE 9669acd21dSWarner Losh #define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's 9769acd21dSWarner Losh a PCMCIA device */ 9869acd21dSWarner Losh #endif 99e34c71eaSJoerg Wunsch #define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */ 1000722d6abSJoerg Wunsch 1010722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */ 1020722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED 0x1000 1030722d6abSJoerg Wunsch 104dc5df763SJoerg Wunsch /* error returns for fd_cmd() */ 105dc5df763SJoerg Wunsch #define FD_FAILED -1 106dc5df763SJoerg Wunsch #define FD_NOT_VALID -2 107dc5df763SJoerg Wunsch #define FDC_ERRMAX 100 /* do not log more */ 108dc5df763SJoerg Wunsch 109b39c878eSAndrey A. Chernov #define NUMTYPES 14 110b39c878eSAndrey A. Chernov #define NUMDENS (NUMTYPES - 6) 1117ca0641bSAndrey A. Chernov 1123a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */ 113b99f0a4aSAndrew Moore #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ 114b99f0a4aSAndrew Moore #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ 115b99f0a4aSAndrew Moore #define FD_1720 1 116b99f0a4aSAndrew Moore #define FD_1480 2 117b99f0a4aSAndrew Moore #define FD_1440 3 118b99f0a4aSAndrew Moore #define FD_1200 4 119b99f0a4aSAndrew Moore #define FD_820 5 120b99f0a4aSAndrew Moore #define FD_800 6 121b99f0a4aSAndrew Moore #define FD_720 7 122b99f0a4aSAndrew Moore #define FD_360 8 123ed2fa05eSAndrey A. Chernov 124b99f0a4aSAndrew Moore #define FD_1480in5_25 9 125b99f0a4aSAndrew Moore #define FD_1440in5_25 10 126b99f0a4aSAndrew Moore #define FD_820in5_25 11 127b99f0a4aSAndrew Moore #define FD_800in5_25 12 128b99f0a4aSAndrew Moore #define FD_720in5_25 13 129b99f0a4aSAndrew Moore #define FD_360in5_25 14 130b99f0a4aSAndrew Moore 1317ca0641bSAndrey A. Chernov 1326f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] = 1335b81b6b3SRodney W. Grimes { 134126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ 135126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ 136126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ 137126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ 138126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ 139126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ 140126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ 141b0568305SAndrey A. Chernov { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ 142ed2fa05eSAndrey A. Chernov 143126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ 144126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ 145126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ 146126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ 147126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ 148126518a1SAndrey A. Chernov { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ 1495b81b6b3SRodney W. Grimes }; 1505b81b6b3SRodney W. Grimes 151b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2 /* 2 floppies */ 152dc16046fSJoerg Wunsch 1535b81b6b3SRodney W. Grimes /***********************************************************************\ 1545b81b6b3SRodney W. Grimes * Per controller structure. * 1555b81b6b3SRodney W. Grimes \***********************************************************************/ 1566182fdbdSPeter Wemm static devclass_t fdc_devclass; 1575b81b6b3SRodney W. Grimes 1585b81b6b3SRodney W. Grimes /***********************************************************************\ 1595b81b6b3SRodney W. Grimes * Per drive structure. * 160b99f0a4aSAndrew Moore * N per controller (DRVS_PER_CTLR) * 1615b81b6b3SRodney W. Grimes \***********************************************************************/ 1626182fdbdSPeter Wemm struct fd_data { 163b99f0a4aSAndrew Moore struct fdc_data *fdc; /* pointer to controller structure */ 1645b81b6b3SRodney W. Grimes int fdsu; /* this units number on this controller */ 1653a2f7427SDavid Greenman int type; /* Drive type (FD_1440...) */ 1665b81b6b3SRodney W. Grimes struct fd_type *ft; /* pointer to the type descriptor */ 1675b81b6b3SRodney W. Grimes int flags; 1685b81b6b3SRodney W. Grimes #define FD_OPEN 0x01 /* it's open */ 1695b81b6b3SRodney W. Grimes #define FD_ACTIVE 0x02 /* it's active */ 1705b81b6b3SRodney W. Grimes #define FD_MOTOR 0x04 /* motor should be on */ 1715b81b6b3SRodney W. Grimes #define FD_MOTOR_WAIT 0x08 /* motor coming up */ 1725b81b6b3SRodney W. Grimes int skip; 1735b81b6b3SRodney W. Grimes int hddrv; 174dc5df763SJoerg Wunsch #define FD_NO_TRACK -2 1755b81b6b3SRodney W. Grimes int track; /* where we think the head is */ 1763a2f7427SDavid Greenman int options; /* user configurable options, see ioctl_fd.h */ 17702a19910SJustin T. Gibbs struct callout_handle toffhandle; 17802a19910SJustin T. Gibbs struct callout_handle tohandle; 179b2dfb1f9SJustin T. Gibbs struct devstat device_stats; 1806182fdbdSPeter Wemm device_t dev; 1816182fdbdSPeter Wemm fdu_t fdu; 1826182fdbdSPeter Wemm }; 1836182fdbdSPeter Wemm static devclass_t fd_devclass; 1845b81b6b3SRodney W. Grimes 1855b81b6b3SRodney W. Grimes /***********************************************************************\ 1865b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used: * 1875b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question * 1885b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller * 1895b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number * 1905b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number * 1915b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit) * 1925b81b6b3SRodney W. Grimes \***********************************************************************/ 193b99f0a4aSAndrew Moore 19469acd21dSWarner Losh #ifdef FDC_YE 19569acd21dSWarner Losh #include "card.h" 19669acd21dSWarner Losh static int yeattach(struct isa_device *); 19769acd21dSWarner Losh #endif 19869acd21dSWarner Losh 1993a2f7427SDavid Greenman /* needed for ft driver, thus exported */ 2006182fdbdSPeter Wemm int in_fdc(struct fdc_data *); 2016182fdbdSPeter Wemm int out_fdc(struct fdc_data *, int); 2023a2f7427SDavid Greenman 2033a2f7427SDavid Greenman /* internal functions */ 2046182fdbdSPeter Wemm static void fdc_add_device(device_t, const char *, int); 2056182fdbdSPeter Wemm static void fdc_intr(void *); 2066182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int); 2073a2f7427SDavid Greenman # define TURNON 1 2083a2f7427SDavid Greenman # define TURNOFF 0 2093a2f7427SDavid Greenman static timeout_t fd_turnoff; 2103a2f7427SDavid Greenman static timeout_t fd_motor_on; 2116182fdbdSPeter Wemm static void fd_turnon(struct fd_data *); 2123a2f7427SDavid Greenman static void fdc_reset(fdc_p); 2136182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *); 2146182fdbdSPeter Wemm static void fdstart(struct fdc_data *); 2155c1a1eaeSBruce Evans static timeout_t fd_iotimeout; 2163a2f7427SDavid Greenman static timeout_t fd_pseudointr; 2176182fdbdSPeter Wemm static int fdstate(struct fdc_data *); 2186182fdbdSPeter Wemm static int retrier(struct fdc_data *); 2193a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *); 2203a2f7427SDavid Greenman 221d66c374fSTor Egge static int enable_fifo(fdc_p fdc); 222d66c374fSTor Egge 223d66c374fSTor Egge static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ 224d66c374fSTor Egge 225aaf08d94SGarrett Wollman 2265b81b6b3SRodney W. Grimes #define DEVIDLE 0 2275b81b6b3SRodney W. Grimes #define FINDWORK 1 2285b81b6b3SRodney W. Grimes #define DOSEEK 2 2295b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 3 2305b81b6b3SRodney W. Grimes #define IOCOMPLETE 4 2315b81b6b3SRodney W. Grimes #define RECALCOMPLETE 5 2325b81b6b3SRodney W. Grimes #define STARTRECAL 6 2335b81b6b3SRodney W. Grimes #define RESETCTLR 7 2345b81b6b3SRodney W. Grimes #define SEEKWAIT 8 2355b81b6b3SRodney W. Grimes #define RECALWAIT 9 2365b81b6b3SRodney W. Grimes #define MOTORWAIT 10 2375b81b6b3SRodney W. Grimes #define IOTIMEDOUT 11 2385c1a1eaeSBruce Evans #define RESETCOMPLETE 12 23969acd21dSWarner Losh #ifdef FDC_YE 24069acd21dSWarner Losh #define PIOREAD 13 24169acd21dSWarner Losh #endif 2425b81b6b3SRodney W. Grimes 243d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 244cba2a7c6SBruce Evans static char const * const fdstates[] = 2455b81b6b3SRodney W. Grimes { 2465b81b6b3SRodney W. Grimes "DEVIDLE", 2475b81b6b3SRodney W. Grimes "FINDWORK", 2485b81b6b3SRodney W. Grimes "DOSEEK", 2495b81b6b3SRodney W. Grimes "SEEKCOMPLETE", 2505b81b6b3SRodney W. Grimes "IOCOMPLETE", 2515b81b6b3SRodney W. Grimes "RECALCOMPLETE", 2525b81b6b3SRodney W. Grimes "STARTRECAL", 2535b81b6b3SRodney W. Grimes "RESETCTLR", 2545b81b6b3SRodney W. Grimes "SEEKWAIT", 2555b81b6b3SRodney W. Grimes "RECALWAIT", 2565b81b6b3SRodney W. Grimes "MOTORWAIT", 2575c1a1eaeSBruce Evans "IOTIMEDOUT", 2585c1a1eaeSBruce Evans "RESETCOMPLETE", 25969acd21dSWarner Losh #ifdef FDC_YE 260ff9607b0SBruce Evans "PIOREAD", 26169acd21dSWarner Losh #endif 2625b81b6b3SRodney W. Grimes }; 2635b81b6b3SRodney W. Grimes 2643a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */ 265cba2a7c6SBruce Evans static int volatile fd_debug = 0; 2665b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg) 2675b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) 268d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */ 2695b81b6b3SRodney W. Grimes #define TRACE0(arg) 2705b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) 271d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 2725b81b6b3SRodney W. Grimes 273427ccf00SDoug Rabson static void 274427ccf00SDoug Rabson fdout_wr(fdc_p fdc, u_int8_t v) 275427ccf00SDoug Rabson { 276427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); 277427ccf00SDoug Rabson } 278427ccf00SDoug Rabson 279427ccf00SDoug Rabson static u_int8_t 280427ccf00SDoug Rabson fdsts_rd(fdc_p fdc) 281427ccf00SDoug Rabson { 282427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); 283427ccf00SDoug Rabson } 284427ccf00SDoug Rabson 285427ccf00SDoug Rabson static void 286427ccf00SDoug Rabson fddata_wr(fdc_p fdc, u_int8_t v) 287427ccf00SDoug Rabson { 288427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); 289427ccf00SDoug Rabson } 290427ccf00SDoug Rabson 291427ccf00SDoug Rabson static u_int8_t 292427ccf00SDoug Rabson fddata_rd(fdc_p fdc) 293427ccf00SDoug Rabson { 294427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); 295427ccf00SDoug Rabson } 296427ccf00SDoug Rabson 297427ccf00SDoug Rabson static void 298427ccf00SDoug Rabson fdctl_wr(fdc_p fdc, u_int8_t v) 299427ccf00SDoug Rabson { 300427ccf00SDoug Rabson if (fdc->flags & FDC_ISPNP) 301427ccf00SDoug Rabson bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); 302427ccf00SDoug Rabson else 303427ccf00SDoug Rabson bus_space_write_1(fdc->portt, fdc->porth, FDCTL, v); 304427ccf00SDoug Rabson } 305427ccf00SDoug Rabson 306427ccf00SDoug Rabson #if 0 307427ccf00SDoug Rabson 308427ccf00SDoug Rabson static u_int8_t 309427ccf00SDoug Rabson fdin_rd(fdc_p fdc) 310427ccf00SDoug Rabson { 311427ccf00SDoug Rabson return bus_space_read_1(fdc->portt, fdc->porth, FDIN); 312427ccf00SDoug Rabson } 313427ccf00SDoug Rabson 314427ccf00SDoug Rabson #endif 315427ccf00SDoug Rabson 31669acd21dSWarner Losh #ifdef FDC_YE 31769acd21dSWarner Losh #if NCARD > 0 31869acd21dSWarner Losh #include <sys/select.h> 319d17e4ee6SPeter Wemm #include <sys/module.h> 32069acd21dSWarner Losh #include <pccard/cardinfo.h> 32169acd21dSWarner Losh #include <pccard/driver.h> 32269acd21dSWarner Losh #include <pccard/slot.h> 32369acd21dSWarner Losh 32469acd21dSWarner Losh /* 32569acd21dSWarner Losh * PC-Card (PCMCIA) specific code. 32669acd21dSWarner Losh */ 32769acd21dSWarner Losh static int yeinit(struct pccard_devinfo *); /* init device */ 32869acd21dSWarner Losh static void yeunload(struct pccard_devinfo *); /* Disable driver */ 32969acd21dSWarner Losh static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ 33069acd21dSWarner Losh 331d17e4ee6SPeter Wemm PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); 33269acd21dSWarner Losh 33369acd21dSWarner Losh /* 33469acd21dSWarner Losh * this is the secret PIO data port (offset from base) 33569acd21dSWarner Losh */ 33669acd21dSWarner Losh #define FDC_YE_DATAPORT 6 33769acd21dSWarner Losh 33869acd21dSWarner Losh /* 33969acd21dSWarner Losh * Initialize the device - called from Slot manager. 34069acd21dSWarner Losh */ 34169acd21dSWarner Losh static int yeinit(struct pccard_devinfo *devi) 34269acd21dSWarner Losh { 34369acd21dSWarner Losh fdc_p fdc = &fdc_data[devi->isahd.id_unit]; 34469acd21dSWarner Losh 34569acd21dSWarner Losh fdc->baseport = devi->isahd.id_iobase; 34669acd21dSWarner Losh /* 34769acd21dSWarner Losh * reset controller 34869acd21dSWarner Losh */ 349427ccf00SDoug Rabson fdout_wr(fdc, 0); 35069acd21dSWarner Losh DELAY(100); 351427ccf00SDoug Rabson fdout_wr(fdc, FDO_FRST); 35269acd21dSWarner Losh 35369acd21dSWarner Losh /* 35469acd21dSWarner Losh * wire into system 35569acd21dSWarner Losh */ 35669acd21dSWarner Losh if (yeattach(&devi->isahd) == 0) 35769acd21dSWarner Losh return(ENXIO); 35869acd21dSWarner Losh 35969acd21dSWarner Losh return(0); 36069acd21dSWarner Losh } 36169acd21dSWarner Losh 36269acd21dSWarner Losh /* 36369acd21dSWarner Losh * yeunload - unload the driver and clear the table. 36469acd21dSWarner Losh * XXX TODO: 36569acd21dSWarner Losh * This is usually called when the card is ejected, but 36669acd21dSWarner Losh * can be caused by a modunload of a controller driver. 36769acd21dSWarner Losh * The idea is to reset the driver's view of the device 36869acd21dSWarner Losh * and ensure that any driver entry points such as 36969acd21dSWarner Losh * read and write do not hang. 37069acd21dSWarner Losh */ 37169acd21dSWarner Losh static void yeunload(struct pccard_devinfo *devi) 37269acd21dSWarner Losh { 37369acd21dSWarner Losh if (fd_data[devi->isahd.id_unit].type == NO_TYPE) 37469acd21dSWarner Losh return; 37569acd21dSWarner Losh 37669acd21dSWarner Losh /* 37769acd21dSWarner Losh * this prevents Fdopen() and fdstrategy() from attempting 37869acd21dSWarner Losh * to access unloaded controller 37969acd21dSWarner Losh */ 38069acd21dSWarner Losh fd_data[devi->isahd.id_unit].type = NO_TYPE; 38169acd21dSWarner Losh 38269acd21dSWarner Losh printf("fdc%d: unload\n", devi->isahd.id_unit); 38369acd21dSWarner Losh } 38469acd21dSWarner Losh 38569acd21dSWarner Losh /* 38669acd21dSWarner Losh * yeintr - Shared interrupt called from 38769acd21dSWarner Losh * front end of PC-Card handler. 38869acd21dSWarner Losh */ 38969acd21dSWarner Losh static int yeintr(struct pccard_devinfo *devi) 39069acd21dSWarner Losh { 39169acd21dSWarner Losh fdintr((fdcu_t)devi->isahd.id_unit); 39269acd21dSWarner Losh return(1); 39369acd21dSWarner Losh } 39469acd21dSWarner Losh #endif /* NCARD > 0 */ 39569acd21dSWarner Losh #endif /* FDC_YE */ 39669acd21dSWarner Losh 39787f6c662SJulian Elischer static d_open_t Fdopen; /* NOTE, not fdopen */ 39887f6c662SJulian Elischer static d_close_t fdclose; 39987f6c662SJulian Elischer static d_ioctl_t fdioctl; 40087f6c662SJulian Elischer static d_strategy_t fdstrategy; 40187f6c662SJulian Elischer 40287f6c662SJulian Elischer #define CDEV_MAJOR 9 40387f6c662SJulian Elischer #define BDEV_MAJOR 2 404f7ea2f55SJulian Elischer 4054e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = { 4064e2f199eSPoul-Henning Kamp /* open */ Fdopen, 4074e2f199eSPoul-Henning Kamp /* close */ fdclose, 4084e2f199eSPoul-Henning Kamp /* read */ physread, 4094e2f199eSPoul-Henning Kamp /* write */ physwrite, 4104e2f199eSPoul-Henning Kamp /* ioctl */ fdioctl, 4114e2f199eSPoul-Henning Kamp /* poll */ nopoll, 4124e2f199eSPoul-Henning Kamp /* mmap */ nommap, 4134e2f199eSPoul-Henning Kamp /* strategy */ fdstrategy, 4144e2f199eSPoul-Henning Kamp /* name */ "fd", 4154e2f199eSPoul-Henning Kamp /* maj */ CDEV_MAJOR, 4164e2f199eSPoul-Henning Kamp /* dump */ nodump, 4174e2f199eSPoul-Henning Kamp /* psize */ nopsize, 4184e2f199eSPoul-Henning Kamp /* flags */ D_DISK, 4194e2f199eSPoul-Henning Kamp /* bmaj */ BDEV_MAJOR 4204e2f199eSPoul-Henning Kamp }; 4214e2f199eSPoul-Henning Kamp 422dc5df763SJoerg Wunsch static int 4236182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s) 424dc5df763SJoerg Wunsch { 4256182fdbdSPeter Wemm fdc->fdc_errs++; 42616b04b6aSJoerg Wunsch if (s) { 4276182fdbdSPeter Wemm if (fdc->fdc_errs < FDC_ERRMAX) { 4286182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 4296182fdbdSPeter Wemm printf("%s", s); 4306182fdbdSPeter Wemm } else if (fdc->fdc_errs == FDC_ERRMAX) { 4316182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 4326182fdbdSPeter Wemm printf("too many errors, not logging any more\n"); 4336182fdbdSPeter Wemm } 43416b04b6aSJoerg Wunsch } 435dc5df763SJoerg Wunsch 436dc5df763SJoerg Wunsch return FD_FAILED; 437dc5df763SJoerg Wunsch } 438dc5df763SJoerg Wunsch 439dc5df763SJoerg Wunsch /* 440dc5df763SJoerg Wunsch * fd_cmd: Send a command to the chip. Takes a varargs with this structure: 441dc5df763SJoerg Wunsch * Unit number, 442dc5df763SJoerg Wunsch * # of output bytes, output bytes as ints ..., 443dc5df763SJoerg Wunsch * # of input bytes, input bytes as ints ... 444dc5df763SJoerg Wunsch */ 4456f4e0bebSPoul-Henning Kamp static int 4466182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...) 447dc5df763SJoerg Wunsch { 448dc5df763SJoerg Wunsch u_char cmd; 449dc5df763SJoerg Wunsch int n_in; 450dc5df763SJoerg Wunsch int n; 451dc5df763SJoerg Wunsch va_list ap; 452dc5df763SJoerg Wunsch 453dc5df763SJoerg Wunsch va_start(ap, n_out); 454dc5df763SJoerg Wunsch cmd = (u_char)(va_arg(ap, int)); 455dc5df763SJoerg Wunsch va_end(ap); 456dc5df763SJoerg Wunsch va_start(ap, n_out); 457dc5df763SJoerg Wunsch for (n = 0; n < n_out; n++) 458dc5df763SJoerg Wunsch { 4596182fdbdSPeter Wemm if (out_fdc(fdc, va_arg(ap, int)) < 0) 460dc5df763SJoerg Wunsch { 461dc5df763SJoerg Wunsch char msg[50]; 4622127f260SArchie Cobbs snprintf(msg, sizeof(msg), 463dc5df763SJoerg Wunsch "cmd %x failed at out byte %d of %d\n", 464dc5df763SJoerg Wunsch cmd, n + 1, n_out); 4656182fdbdSPeter Wemm return fdc_err(fdc, msg); 466dc5df763SJoerg Wunsch } 467dc5df763SJoerg Wunsch } 468dc5df763SJoerg Wunsch n_in = va_arg(ap, int); 469dc5df763SJoerg Wunsch for (n = 0; n < n_in; n++) 470dc5df763SJoerg Wunsch { 471dc5df763SJoerg Wunsch int *ptr = va_arg(ap, int *); 4726182fdbdSPeter Wemm if (fd_in(fdc, ptr) < 0) 473dc5df763SJoerg Wunsch { 474dc5df763SJoerg Wunsch char msg[50]; 4752127f260SArchie Cobbs snprintf(msg, sizeof(msg), 476dc5df763SJoerg Wunsch "cmd %02x failed at in byte %d of %d\n", 477dc5df763SJoerg Wunsch cmd, n + 1, n_in); 4786182fdbdSPeter Wemm return fdc_err(fdc, msg); 479dc5df763SJoerg Wunsch } 480dc5df763SJoerg Wunsch } 481dc5df763SJoerg Wunsch 482dc5df763SJoerg Wunsch return 0; 483dc5df763SJoerg Wunsch } 484dc5df763SJoerg Wunsch 4856f4e0bebSPoul-Henning Kamp static int 486d66c374fSTor Egge enable_fifo(fdc_p fdc) 487d66c374fSTor Egge { 488d66c374fSTor Egge int i, j; 489d66c374fSTor Egge 490d66c374fSTor Egge if ((fdc->flags & FDC_HAS_FIFO) == 0) { 491d66c374fSTor Egge 492d66c374fSTor Egge /* 493d66c374fSTor Egge * XXX: 494d66c374fSTor Egge * Cannot use fd_cmd the normal way here, since 495d66c374fSTor Egge * this might be an invalid command. Thus we send the 496d66c374fSTor Egge * first byte, and check for an early turn of data directon. 497d66c374fSTor Egge */ 498d66c374fSTor Egge 4996182fdbdSPeter Wemm if (out_fdc(fdc, I8207X_CONFIGURE) < 0) 5006182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 501d66c374fSTor Egge 502d66c374fSTor Egge /* If command is invalid, return */ 503d66c374fSTor Egge j = 100000; 504427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) 505d66c374fSTor Egge != NE7_RQM && j-- > 0) 506d66c374fSTor Egge if (i == (NE7_DIO | NE7_RQM)) { 507d66c374fSTor Egge fdc_reset(fdc); 508d66c374fSTor Egge return FD_FAILED; 509d66c374fSTor Egge } 510d66c374fSTor Egge if (j<0 || 5116182fdbdSPeter Wemm fd_cmd(fdc, 3, 512d66c374fSTor Egge 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { 513d66c374fSTor Egge fdc_reset(fdc); 5146182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 515d66c374fSTor Egge } 516d66c374fSTor Egge fdc->flags |= FDC_HAS_FIFO; 517d66c374fSTor Egge return 0; 518d66c374fSTor Egge } 5196182fdbdSPeter Wemm if (fd_cmd(fdc, 4, 520d66c374fSTor Egge I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) 5216182fdbdSPeter Wemm return fdc_err(fdc, "Re-enable FIFO failed\n"); 522d66c374fSTor Egge return 0; 523d66c374fSTor Egge } 524d66c374fSTor Egge 525d66c374fSTor Egge static int 526dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p) 527dc5df763SJoerg Wunsch { 528dc5df763SJoerg Wunsch int st3; 529dc5df763SJoerg Wunsch 5306182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) 531dc5df763SJoerg Wunsch { 5326182fdbdSPeter Wemm return fdc_err(fdc, "Sense Drive Status failed\n"); 533dc5df763SJoerg Wunsch } 534dc5df763SJoerg Wunsch if (st3p) 535dc5df763SJoerg Wunsch *st3p = st3; 536dc5df763SJoerg Wunsch 537dc5df763SJoerg Wunsch return 0; 538dc5df763SJoerg Wunsch } 539dc5df763SJoerg Wunsch 5406f4e0bebSPoul-Henning Kamp static int 541dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp) 542dc5df763SJoerg Wunsch { 5436182fdbdSPeter Wemm int cyl, st0, ret; 544dc5df763SJoerg Wunsch 5456182fdbdSPeter Wemm ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); 5466182fdbdSPeter Wemm if (ret) { 5476182fdbdSPeter Wemm (void)fdc_err(fdc, 548dc5df763SJoerg Wunsch "sense intr err reading stat reg 0\n"); 549dc5df763SJoerg Wunsch return ret; 550dc5df763SJoerg Wunsch } 551dc5df763SJoerg Wunsch 552dc5df763SJoerg Wunsch if (st0p) 553dc5df763SJoerg Wunsch *st0p = st0; 554dc5df763SJoerg Wunsch 5556182fdbdSPeter Wemm if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { 556dc5df763SJoerg Wunsch /* 557dc5df763SJoerg Wunsch * There doesn't seem to have been an interrupt. 558dc5df763SJoerg Wunsch */ 559dc5df763SJoerg Wunsch return FD_NOT_VALID; 560dc5df763SJoerg Wunsch } 561dc5df763SJoerg Wunsch 5626182fdbdSPeter Wemm if (fd_in(fdc, &cyl) < 0) { 5636182fdbdSPeter Wemm return fdc_err(fdc, "can't get cyl num\n"); 564dc5df763SJoerg Wunsch } 565dc5df763SJoerg Wunsch 566dc5df763SJoerg Wunsch if (cylp) 567dc5df763SJoerg Wunsch *cylp = cyl; 568dc5df763SJoerg Wunsch 569dc5df763SJoerg Wunsch return 0; 570dc5df763SJoerg Wunsch } 571dc5df763SJoerg Wunsch 572dc5df763SJoerg Wunsch 5736f4e0bebSPoul-Henning Kamp static int 574dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu) 575dc5df763SJoerg Wunsch { 576dc5df763SJoerg Wunsch int i, ret; 577b5e8ce9fSBruce Evans 5786182fdbdSPeter Wemm for (i = 0; i < 7; i++) { 579b5e8ce9fSBruce Evans /* 580b5e8ce9fSBruce Evans * XXX types are poorly chosen. Only bytes can by read 581a838d83dSBruce Evans * from the hardware, but fdc->status[] wants u_ints and 582b5e8ce9fSBruce Evans * fd_in() gives ints. 583b5e8ce9fSBruce Evans */ 584b5e8ce9fSBruce Evans int status; 585b5e8ce9fSBruce Evans 5866182fdbdSPeter Wemm ret = fd_in(fdc, &status); 587b5e8ce9fSBruce Evans fdc->status[i] = status; 588b5e8ce9fSBruce Evans if (ret != 0) 589dc5df763SJoerg Wunsch break; 590dc5df763SJoerg Wunsch } 591dc5df763SJoerg Wunsch 592dc5df763SJoerg Wunsch if (ret == 0) 593dc5df763SJoerg Wunsch fdc->flags |= FDC_STAT_VALID; 594dc5df763SJoerg Wunsch else 595dc5df763SJoerg Wunsch fdc->flags &= ~FDC_STAT_VALID; 596dc5df763SJoerg Wunsch 597dc5df763SJoerg Wunsch return ret; 598dc5df763SJoerg Wunsch } 599dc5df763SJoerg Wunsch 6005b81b6b3SRodney W. Grimes /****************************************************************************/ 6015b81b6b3SRodney W. Grimes /* autoconfiguration stuff */ 6025b81b6b3SRodney W. Grimes /****************************************************************************/ 603dc5df763SJoerg Wunsch 604427ccf00SDoug Rabson static struct isa_pnp_id fdc_ids[] = { 605427ccf00SDoug Rabson {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ 606427ccf00SDoug Rabson {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ 607427ccf00SDoug Rabson {0} 608427ccf00SDoug Rabson }; 609427ccf00SDoug Rabson 610427ccf00SDoug Rabson 61116e68fc6SPeter Wemm /* 61216e68fc6SPeter Wemm * fdc controller section. 61316e68fc6SPeter Wemm */ 6143a2f7427SDavid Greenman static int 6156182fdbdSPeter Wemm fdc_probe(device_t dev) 6165b81b6b3SRodney W. Grimes { 617427ccf00SDoug Rabson int error, ispnp, ic_type; 6186182fdbdSPeter Wemm struct fdc_data *fdc; 6195b81b6b3SRodney W. Grimes 620427ccf00SDoug Rabson /* Check pnp ids */ 621427ccf00SDoug Rabson error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); 622427ccf00SDoug Rabson if (error == ENXIO) 623427ccf00SDoug Rabson return ENXIO; 624427ccf00SDoug Rabson ispnp = (error == 0); 625a97c75b7SDoug Rabson 6266182fdbdSPeter Wemm fdc = device_get_softc(dev); 6276182fdbdSPeter Wemm bzero(fdc, sizeof *fdc); 6286182fdbdSPeter Wemm fdc->fdc_dev = dev; 6296182fdbdSPeter Wemm fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; 6306182fdbdSPeter Wemm fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; 6316182fdbdSPeter Wemm 6326182fdbdSPeter Wemm fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, 6336182fdbdSPeter Wemm &fdc->rid_ioport, 0ul, ~0ul, 634427ccf00SDoug Rabson ispnp ? 1 : IO_FDCSIZE, 635427ccf00SDoug Rabson RF_ACTIVE); 6366182fdbdSPeter Wemm if (fdc->res_ioport == 0) { 637427ccf00SDoug Rabson device_printf(dev, "cannot reserve I/O port range\n"); 6386182fdbdSPeter Wemm error = ENXIO; 6396182fdbdSPeter Wemm goto out; 6406182fdbdSPeter Wemm } 641427ccf00SDoug Rabson fdc->portt = rman_get_bustag(fdc->res_ioport); 642427ccf00SDoug Rabson fdc->porth = rman_get_bushandle(fdc->res_ioport); 643427ccf00SDoug Rabson 644427ccf00SDoug Rabson /* 645a2639a18SPeter Wemm * Some bios' report the device at 0x3f2-0x3f5,0x3f7 and some at 646a2639a18SPeter Wemm * 0x3f0-0x3f5,0x3f7. We detect the former by checking the size 647a2639a18SPeter Wemm * and adjust the port address accordingly. 648a2639a18SPeter Wemm * 649a2639a18SPeter Wemm * And some (!!) report 0x3f2-0x3f5 and completely leave out the 650a2639a18SPeter Wemm * control register! It seems that some non-antique controller chips 651a2639a18SPeter Wemm * have a different method of programming the transfer speed which 652a2639a18SPeter Wemm * doesn't require the control register, but it's mighty bogus as the 653a2639a18SPeter Wemm * chip still responds to the address for the control register. 654a2639a18SPeter Wemm * This hack is truely evil as we use the 6th port in a 4-port chunk. 655427ccf00SDoug Rabson */ 656a2639a18SPeter Wemm /* 0x3f2-0x3f5 */ 657a2639a18SPeter Wemm if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4 && 658a2639a18SPeter Wemm bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { 659a2639a18SPeter Wemm fdc->port_off = -2; 660a2639a18SPeter Wemm ispnp = 0; /* hack, don't reserve second port chunk */ 661a2639a18SPeter Wemm } 662a2639a18SPeter Wemm /* 0x3f0-0x3f5 */ 663a2639a18SPeter Wemm if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 6 && 664a2639a18SPeter Wemm bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { 665a2639a18SPeter Wemm ispnp = 0; /* hack, don't reserve second port chunk */ 666a2639a18SPeter Wemm } 667a2639a18SPeter Wemm if (ispnp) { 668427ccf00SDoug Rabson if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) 669427ccf00SDoug Rabson fdc->port_off = -2; 670427ccf00SDoug Rabson fdc->flags |= FDC_ISPNP; 671427ccf00SDoug Rabson fdc->rid_ctl = 1; 672427ccf00SDoug Rabson fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, 673427ccf00SDoug Rabson &fdc->rid_ctl, 0ul, ~0ul, 674427ccf00SDoug Rabson 1, RF_ACTIVE); 675427ccf00SDoug Rabson if (fdc->res_ctl == 0) { 676427ccf00SDoug Rabson device_printf(dev, "cannot reserve I/O port range\n"); 677427ccf00SDoug Rabson error = ENXIO; 678427ccf00SDoug Rabson goto out; 679427ccf00SDoug Rabson } 680427ccf00SDoug Rabson fdc->ctlt = rman_get_bustag(fdc->res_ctl); 681427ccf00SDoug Rabson fdc->ctlh = rman_get_bushandle(fdc->res_ctl); 682427ccf00SDoug Rabson } 6836182fdbdSPeter Wemm 6846182fdbdSPeter Wemm fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, 6856182fdbdSPeter Wemm &fdc->rid_irq, 0ul, ~0ul, 1, 6866182fdbdSPeter Wemm RF_ACTIVE); 6876182fdbdSPeter Wemm if (fdc->res_irq == 0) { 688427ccf00SDoug Rabson device_printf(dev, "cannot reserve interrupt line\n"); 6896182fdbdSPeter Wemm error = ENXIO; 6906182fdbdSPeter Wemm goto out; 6916182fdbdSPeter Wemm } 6926182fdbdSPeter Wemm fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, 6936182fdbdSPeter Wemm &fdc->rid_drq, 0ul, ~0ul, 1, 6946182fdbdSPeter Wemm RF_ACTIVE); 6956182fdbdSPeter Wemm if (fdc->res_drq == 0) { 696427ccf00SDoug Rabson device_printf(dev, "cannot reserve DMA request line\n"); 6976182fdbdSPeter Wemm error = ENXIO; 6986182fdbdSPeter Wemm goto out; 6996182fdbdSPeter Wemm } 7006182fdbdSPeter Wemm fdc->dmachan = fdc->res_drq->r_start; 701566643e3SDoug Rabson error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, 702566643e3SDoug Rabson INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr); 7035b81b6b3SRodney W. Grimes 70416111cedSAndrew Moore /* First - lets reset the floppy controller */ 705427ccf00SDoug Rabson fdout_wr(fdc, 0); 70616111cedSAndrew Moore DELAY(100); 707427ccf00SDoug Rabson fdout_wr(fdc, FDO_FRST); 70816111cedSAndrew Moore 7095b81b6b3SRodney W. Grimes /* see if it can handle a command */ 7106182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), 7116182fdbdSPeter Wemm NE7_SPEC_2(2, 0), 0)) { 7126182fdbdSPeter Wemm error = ENXIO; 7136182fdbdSPeter Wemm goto out; 7145b81b6b3SRodney W. Grimes } 7156182fdbdSPeter Wemm 7166182fdbdSPeter Wemm if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { 7176182fdbdSPeter Wemm ic_type = (u_char)ic_type; 7186182fdbdSPeter Wemm switch (ic_type) { 7196182fdbdSPeter Wemm case 0x80: 7206182fdbdSPeter Wemm device_set_desc(dev, "NEC 765 or clone"); 7216182fdbdSPeter Wemm fdc->fdct = FDC_NE765; 7226182fdbdSPeter Wemm break; 7236182fdbdSPeter Wemm case 0x81: 7246182fdbdSPeter Wemm device_set_desc(dev, "Intel 82077 or clone"); 7256182fdbdSPeter Wemm fdc->fdct = FDC_I82077; 7266182fdbdSPeter Wemm break; 7276182fdbdSPeter Wemm case 0x90: 7286182fdbdSPeter Wemm device_set_desc(dev, "NEC 72065B or clone"); 7296182fdbdSPeter Wemm fdc->fdct = FDC_NE72065; 7306182fdbdSPeter Wemm break; 7316182fdbdSPeter Wemm default: 7326182fdbdSPeter Wemm device_set_desc(dev, "generic floppy controller"); 7336182fdbdSPeter Wemm fdc->fdct = FDC_UNKNOWN; 7346182fdbdSPeter Wemm break; 7356182fdbdSPeter Wemm } 7366182fdbdSPeter Wemm } 7376182fdbdSPeter Wemm 73869acd21dSWarner Losh #ifdef FDC_YE 73969acd21dSWarner Losh /* 74069acd21dSWarner Losh * don't succeed on probe; wait 74169acd21dSWarner Losh * for PCCARD subsystem to do it 74269acd21dSWarner Losh */ 743e34c71eaSJoerg Wunsch if (device_get_flags(fdc->fdc_dev) & FDC_IS_PCMCIA) 74469acd21dSWarner Losh return(0); 74569acd21dSWarner Losh #endif 7466182fdbdSPeter Wemm return (0); 7476182fdbdSPeter Wemm 7486182fdbdSPeter Wemm out: 7496182fdbdSPeter Wemm if (fdc->fdc_intr) 7506182fdbdSPeter Wemm BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, 7516182fdbdSPeter Wemm fdc->fdc_intr); 7526182fdbdSPeter Wemm if (fdc->res_irq != 0) { 7536182fdbdSPeter Wemm bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 7546182fdbdSPeter Wemm fdc->res_irq); 7556182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 7566182fdbdSPeter Wemm fdc->res_irq); 7576182fdbdSPeter Wemm } 758427ccf00SDoug Rabson if (fdc->res_ctl != 0) { 759427ccf00SDoug Rabson bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, 760427ccf00SDoug Rabson fdc->res_ctl); 761427ccf00SDoug Rabson bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, 762427ccf00SDoug Rabson fdc->res_ctl); 763427ccf00SDoug Rabson } 7646182fdbdSPeter Wemm if (fdc->res_ioport != 0) { 7656182fdbdSPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 7666182fdbdSPeter Wemm fdc->res_ioport); 7676182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 7686182fdbdSPeter Wemm fdc->res_ioport); 7696182fdbdSPeter Wemm } 7706182fdbdSPeter Wemm if (fdc->res_drq != 0) { 7716182fdbdSPeter Wemm bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 7726182fdbdSPeter Wemm fdc->res_drq); 7736182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 7746182fdbdSPeter Wemm fdc->res_drq); 7756182fdbdSPeter Wemm } 7766182fdbdSPeter Wemm return (error); 7775b81b6b3SRodney W. Grimes } 7785b81b6b3SRodney W. Grimes 7795b81b6b3SRodney W. Grimes /* 7806182fdbdSPeter Wemm * Aped dfr@freebsd.org's isa_add_device(). 7815b81b6b3SRodney W. Grimes */ 7826182fdbdSPeter Wemm static void 7836182fdbdSPeter Wemm fdc_add_device(device_t dev, const char *name, int unit) 7845b81b6b3SRodney W. Grimes { 7856182fdbdSPeter Wemm int disabled, *ivar; 7866182fdbdSPeter Wemm device_t child; 78792200632SGarrett Wollman 7886182fdbdSPeter Wemm ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); 7896182fdbdSPeter Wemm if (ivar == 0) 7906182fdbdSPeter Wemm return; 791a97c75b7SDoug Rabson if (resource_int_value(name, unit, "drive", ivar) != 0) 7926182fdbdSPeter Wemm *ivar = 0; 793fe0d4089SMatthew N. Dodd child = device_add_child(dev, name, unit); 794fe0d4089SMatthew N. Dodd device_set_ivars(child, ivar); 7956182fdbdSPeter Wemm if (child == 0) 7966182fdbdSPeter Wemm return; 797a97c75b7SDoug Rabson if (resource_int_value(name, unit, "disabled", &disabled) == 0 798a97c75b7SDoug Rabson && disabled != 0) 7996182fdbdSPeter Wemm device_disable(child); 8006182fdbdSPeter Wemm } 8016182fdbdSPeter Wemm 8026182fdbdSPeter Wemm static int 8036182fdbdSPeter Wemm fdc_attach(device_t dev) 8046182fdbdSPeter Wemm { 8056182fdbdSPeter Wemm struct fdc_data *fdc = device_get_softc(dev); 8066182fdbdSPeter Wemm fdcu_t fdcu = device_get_unit(dev); 807427ccf00SDoug Rabson int i; 808427ccf00SDoug Rabson 809427ccf00SDoug Rabson for (i = resource_query_string(-1, "at", device_get_nameunit(dev)); 810427ccf00SDoug Rabson i != -1; 811427ccf00SDoug Rabson i = resource_query_string(i, "at", device_get_nameunit(dev))) 812427ccf00SDoug Rabson fdc_add_device(dev, resource_query_name(i), 813427ccf00SDoug Rabson resource_query_unit(i)); 8146182fdbdSPeter Wemm 8155b81b6b3SRodney W. Grimes fdc->fdcu = fdcu; 8165b81b6b3SRodney W. Grimes fdc->flags |= FDC_ATTACHED; 8176182fdbdSPeter Wemm 818100f78bbSSujal Patel /* Acquire the DMA channel forever, The driver will do the rest */ 8196182fdbdSPeter Wemm /* XXX should integrate with rman */ 820100f78bbSSujal Patel isa_dma_acquire(fdc->dmachan); 821dd87702aSBruce Evans isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); 8225b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 8236182fdbdSPeter Wemm 8243a2f7427SDavid Greenman /* reset controller, turn motor off, clear fdout mirror reg */ 825427ccf00SDoug Rabson fdout_wr(fdc, ((fdc->fdout = 0))); 82602a19910SJustin T. Gibbs bufq_init(&fdc->head); 8275b81b6b3SRodney W. Grimes 8286182fdbdSPeter Wemm /* 8296182fdbdSPeter Wemm * Probe and attach any children as were configured above. 8306182fdbdSPeter Wemm */ 8316182fdbdSPeter Wemm return (bus_generic_attach(dev)); 8326182fdbdSPeter Wemm } 8336182fdbdSPeter Wemm 83415317dd8SMatthew N. Dodd static int 8356182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child) 8366182fdbdSPeter Wemm { 83715317dd8SMatthew N. Dodd int retval = 0; 83815317dd8SMatthew N. Dodd 83915317dd8SMatthew N. Dodd retval += bus_print_child_header(me, child); 84015317dd8SMatthew N. Dodd retval += printf(" on %s drive %d\n", device_get_nameunit(me), 8416182fdbdSPeter Wemm *(int *)device_get_ivars(child)); 84215317dd8SMatthew N. Dodd 84315317dd8SMatthew N. Dodd return (retval); 8446182fdbdSPeter Wemm } 8456182fdbdSPeter Wemm 84616e68fc6SPeter Wemm static device_method_t fdc_methods[] = { 84716e68fc6SPeter Wemm /* Device interface */ 84816e68fc6SPeter Wemm DEVMETHOD(device_probe, fdc_probe), 84916e68fc6SPeter Wemm DEVMETHOD(device_attach, fdc_attach), 85016e68fc6SPeter Wemm DEVMETHOD(device_detach, bus_generic_detach), 85116e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 85216e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), 85316e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), 85416e68fc6SPeter Wemm 85516e68fc6SPeter Wemm /* Bus interface */ 85616e68fc6SPeter Wemm DEVMETHOD(bus_print_child, fdc_print_child), 85716e68fc6SPeter Wemm /* Our children never use any other bus interface methods. */ 85816e68fc6SPeter Wemm 85916e68fc6SPeter Wemm { 0, 0 } 86016e68fc6SPeter Wemm }; 86116e68fc6SPeter Wemm 86216e68fc6SPeter Wemm static driver_t fdc_driver = { 86316e68fc6SPeter Wemm "fdc", 86416e68fc6SPeter Wemm fdc_methods, 86516e68fc6SPeter Wemm sizeof(struct fdc_data) 86616e68fc6SPeter Wemm }; 86716e68fc6SPeter Wemm 86816e68fc6SPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); 86916e68fc6SPeter Wemm 87016e68fc6SPeter Wemm /******************************************************************/ 87116e68fc6SPeter Wemm /* 87216e68fc6SPeter Wemm * devices attached to the controller section. 87316e68fc6SPeter Wemm */ 8746182fdbdSPeter Wemm static int 8756182fdbdSPeter Wemm fd_probe(device_t dev) 8766182fdbdSPeter Wemm { 8776182fdbdSPeter Wemm int i; 8786182fdbdSPeter Wemm u_int fdt, st0, st3; 8796182fdbdSPeter Wemm struct fd_data *fd; 8806182fdbdSPeter Wemm struct fdc_data *fdc; 8816182fdbdSPeter Wemm fdsu_t fdsu; 8826182fdbdSPeter Wemm static int fd_fifo = 0; 8836182fdbdSPeter Wemm 8846182fdbdSPeter Wemm fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ 8856182fdbdSPeter Wemm fd = device_get_softc(dev); 8866182fdbdSPeter Wemm fdc = device_get_softc(device_get_parent(dev)); 8876182fdbdSPeter Wemm 8886182fdbdSPeter Wemm bzero(fd, sizeof *fd); 8896182fdbdSPeter Wemm fd->dev = dev; 8906182fdbdSPeter Wemm fd->fdc = fdc; 8916182fdbdSPeter Wemm fd->fdsu = fdsu; 8926182fdbdSPeter Wemm fd->fdu = device_get_unit(dev); 8936182fdbdSPeter Wemm 894a97c75b7SDoug Rabson #ifdef __i386__ 895b99f0a4aSAndrew Moore /* look up what bios thinks we have */ 8966182fdbdSPeter Wemm switch (fd->fdu) { 8976182fdbdSPeter Wemm case 0: 898062acdb7SDoug Rabson if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) 8990722d6abSJoerg Wunsch fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; 9000722d6abSJoerg Wunsch else 9010722d6abSJoerg Wunsch fdt = (rtcin(RTC_FDISKETTE) & 0xf0); 902b99f0a4aSAndrew Moore break; 9036182fdbdSPeter Wemm case 1: 9046182fdbdSPeter Wemm fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); 905dc5df763SJoerg Wunsch break; 906dc5df763SJoerg Wunsch default: 9076182fdbdSPeter Wemm fdt = RTCFDT_NONE; 908dc5df763SJoerg Wunsch break; 9096b7bd95bSJoerg Wunsch } 910a97c75b7SDoug Rabson #else 911a97c75b7SDoug Rabson fdt = RTCFDT_144M; /* XXX probably */ 912a97c75b7SDoug Rabson #endif 9136182fdbdSPeter Wemm 9146182fdbdSPeter Wemm /* is there a unit? */ 9156182fdbdSPeter Wemm if (fdt == RTCFDT_NONE) 9166182fdbdSPeter Wemm return (ENXIO); 9176182fdbdSPeter Wemm 9186182fdbdSPeter Wemm /* select it */ 9196182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNON); 9206182fdbdSPeter Wemm DELAY(1000000); /* 1 sec */ 9216182fdbdSPeter Wemm 9228de0675cSPeter Wemm /* XXX This doesn't work before the first set_motor() */ 9236182fdbdSPeter Wemm if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN 924e34c71eaSJoerg Wunsch && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 9256182fdbdSPeter Wemm && enable_fifo(fdc) == 0) { 9266182fdbdSPeter Wemm device_print_prettyname(device_get_parent(dev)); 9276182fdbdSPeter Wemm printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); 928d66c374fSTor Egge } 9296182fdbdSPeter Wemm fd_fifo = 1; 9306182fdbdSPeter Wemm 9316182fdbdSPeter Wemm if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) 9326182fdbdSPeter Wemm && (st3 & NE7_ST3_T0)) { 933dc5df763SJoerg Wunsch /* if at track 0, first seek inwards */ 934dc5df763SJoerg Wunsch /* seek some steps: */ 9356182fdbdSPeter Wemm fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); 936dc5df763SJoerg Wunsch DELAY(300000); /* ...wait a moment... */ 9376182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ 938dc5df763SJoerg Wunsch } 939dc5df763SJoerg Wunsch 940dc5df763SJoerg Wunsch /* If we're at track 0 first seek inwards. */ 9416182fdbdSPeter Wemm if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { 942dc5df763SJoerg Wunsch /* Seek some steps... */ 9436182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 944dc5df763SJoerg Wunsch /* ...wait a moment... */ 945dc5df763SJoerg Wunsch DELAY(300000); 946dc5df763SJoerg Wunsch /* make ctrlr happy: */ 9476182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); 948dc5df763SJoerg Wunsch } 949dc5df763SJoerg Wunsch } 950dc5df763SJoerg Wunsch 9516b7bd95bSJoerg Wunsch for (i = 0; i < 2; i++) { 9526b7bd95bSJoerg Wunsch /* 9536b7bd95bSJoerg Wunsch * we must recalibrate twice, just in case the 9546b7bd95bSJoerg Wunsch * heads have been beyond cylinder 76, since most 9556b7bd95bSJoerg Wunsch * FDCs still barf when attempting to recalibrate 9566b7bd95bSJoerg Wunsch * more than 77 steps 9576b7bd95bSJoerg Wunsch */ 958dc5df763SJoerg Wunsch /* go back to 0: */ 9596182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 9606b7bd95bSJoerg Wunsch /* a second being enough for full stroke seek*/ 9616b7bd95bSJoerg Wunsch DELAY(i == 0 ? 1000000 : 300000); 9625b81b6b3SRodney W. Grimes 9636b7bd95bSJoerg Wunsch /* anything responding? */ 964dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, 0) == 0 && 965dc5df763SJoerg Wunsch (st0 & NE7_ST0_EC) == 0) 9666b7bd95bSJoerg Wunsch break; /* already probed succesfully */ 9676b7bd95bSJoerg Wunsch } 968dc5df763SJoerg Wunsch } 9696b7bd95bSJoerg Wunsch 9706182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNOFF); 9713a2f7427SDavid Greenman 9723a2f7427SDavid Greenman if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ 9736182fdbdSPeter Wemm return (ENXIO); 9745b81b6b3SRodney W. Grimes 975dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 976b99f0a4aSAndrew Moore fd->fdc = fdc; 977b99f0a4aSAndrew Moore fd->fdsu = fdsu; 9783a2f7427SDavid Greenman fd->options = 0; 97902a19910SJustin T. Gibbs callout_handle_init(&fd->toffhandle); 98002a19910SJustin T. Gibbs callout_handle_init(&fd->tohandle); 9815b81b6b3SRodney W. Grimes 982b99f0a4aSAndrew Moore switch (fdt) { 9837ca0641bSAndrey A. Chernov case RTCFDT_12M: 9846182fdbdSPeter Wemm device_set_desc(dev, "1200-KB 5.25\" drive"); 985b99f0a4aSAndrew Moore fd->type = FD_1200; 9867ca0641bSAndrey A. Chernov break; 9870722d6abSJoerg Wunsch case RTCFDT_144M | RTCFDT_144M_PRETENDED: 9886182fdbdSPeter Wemm device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); 9890722d6abSJoerg Wunsch fdt = RTCFDT_144M; 9906182fdbdSPeter Wemm fd->type = FD_1440; 9917ca0641bSAndrey A. Chernov case RTCFDT_144M: 9926182fdbdSPeter Wemm device_set_desc(dev, "1440-KB 3.5\" drive"); 993b99f0a4aSAndrew Moore fd->type = FD_1440; 9947ca0641bSAndrey A. Chernov break; 995290dd077SJoerg Wunsch case RTCFDT_288M: 99686a727d9SJoerg Wunsch case RTCFDT_288M_1: 9976182fdbdSPeter Wemm device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); 998290dd077SJoerg Wunsch fd->type = FD_1440; 999290dd077SJoerg Wunsch break; 10007ca0641bSAndrey A. Chernov case RTCFDT_360K: 10016182fdbdSPeter Wemm device_set_desc(dev, "360-KB 5.25\" drive"); 1002b99f0a4aSAndrew Moore fd->type = FD_360; 10037ca0641bSAndrey A. Chernov break; 1004ed2fa05eSAndrey A. Chernov case RTCFDT_720K: 10056182fdbdSPeter Wemm printf("720-KB 3.5\" drive"); 1006b99f0a4aSAndrew Moore fd->type = FD_720; 1007ed2fa05eSAndrey A. Chernov break; 10087ca0641bSAndrey A. Chernov default: 10096182fdbdSPeter Wemm return (ENXIO); 10105b81b6b3SRodney W. Grimes } 10116182fdbdSPeter Wemm return (0); 10126182fdbdSPeter Wemm } 10136182fdbdSPeter Wemm 10146182fdbdSPeter Wemm static int 10156182fdbdSPeter Wemm fd_attach(device_t dev) 10166182fdbdSPeter Wemm { 10176182fdbdSPeter Wemm struct fd_data *fd; 1018ada9bd8cSJulian Elischer #if 0 1019ada9bd8cSJulian Elischer int i; 1020ada9bd8cSJulian Elischer int mynor; 1021ada9bd8cSJulian Elischer int typemynor; 1022ada9bd8cSJulian Elischer int typesize; 1023ada9bd8cSJulian Elischer #endif 10246182fdbdSPeter Wemm 10256182fdbdSPeter Wemm fd = device_get_softc(dev); 10266182fdbdSPeter Wemm 1027475ad603SPeter Wemm cdevsw_add(&fd_cdevsw); /* XXX */ 1028ada9bd8cSJulian Elischer make_dev(&fd_cdevsw, (fd->fdu << 6), 1029ada9bd8cSJulian Elischer UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fd->fdu); 1030ada9bd8cSJulian Elischer 1031ada9bd8cSJulian Elischer #if 0 1032ada9bd8cSJulian Elischer /* Other make_dev() go here. */ 1033ada9bd8cSJulian Elischer #endif 1034ada9bd8cSJulian Elischer 1035671e2ceeSBruce Evans /* 1036b2dfb1f9SJustin T. Gibbs * Export the drive to the devstat interface. 1037671e2ceeSBruce Evans */ 10386182fdbdSPeter Wemm devstat_add_entry(&fd->device_stats, device_get_name(dev), 10396182fdbdSPeter Wemm device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, 10402a888f93SKenneth D. Merry DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, 10412a888f93SKenneth D. Merry DEVSTAT_PRIORITY_FD); 10426182fdbdSPeter Wemm return (0); 10435b81b6b3SRodney W. Grimes } 10445b81b6b3SRodney W. Grimes 104516e68fc6SPeter Wemm static device_method_t fd_methods[] = { 104616e68fc6SPeter Wemm /* Device interface */ 104716e68fc6SPeter Wemm DEVMETHOD(device_probe, fd_probe), 104816e68fc6SPeter Wemm DEVMETHOD(device_attach, fd_attach), 104916e68fc6SPeter Wemm DEVMETHOD(device_detach, bus_generic_detach), 105016e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 105116e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ 105216e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ 105316e68fc6SPeter Wemm 105416e68fc6SPeter Wemm { 0, 0 } 105516e68fc6SPeter Wemm }; 105616e68fc6SPeter Wemm 105716e68fc6SPeter Wemm static driver_t fd_driver = { 105816e68fc6SPeter Wemm "fd", 105916e68fc6SPeter Wemm fd_methods, 106016e68fc6SPeter Wemm sizeof(struct fd_data) 106116e68fc6SPeter Wemm }; 106216e68fc6SPeter Wemm 1063475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); 106416e68fc6SPeter Wemm 106516e68fc6SPeter Wemm /******************************************************************/ 106616e68fc6SPeter Wemm 106769acd21dSWarner Losh #ifdef FDC_YE 106869acd21dSWarner Losh /* 106969acd21dSWarner Losh * this is a subset of fdattach() optimized for the Y-E Data 107069acd21dSWarner Losh * PCMCIA floppy drive. 107169acd21dSWarner Losh */ 107269acd21dSWarner Losh static int yeattach(struct isa_device *dev) 107369acd21dSWarner Losh { 107469acd21dSWarner Losh fdcu_t fdcu = dev->id_unit; 107569acd21dSWarner Losh fdc_p fdc = fdc_data + fdcu; 107669acd21dSWarner Losh fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ 107769acd21dSWarner Losh fdu_t fdu; 107869acd21dSWarner Losh fd_p fd; 107969acd21dSWarner Losh int st0, st3, i; 108069acd21dSWarner Losh fdc->fdcu = fdcu; 108169acd21dSWarner Losh /* 108269acd21dSWarner Losh * the FDC_PCMCIA flag is used to to indicate special PIO is used 108369acd21dSWarner Losh * instead of DMA 108469acd21dSWarner Losh */ 108569acd21dSWarner Losh fdc->flags = FDC_ATTACHED|FDC_PCMCIA; 108669acd21dSWarner Losh fdc->state = DEVIDLE; 108769acd21dSWarner Losh /* reset controller, turn motor off, clear fdout mirror reg */ 1088427ccf00SDoug Rabson fdout_wr(fdc, ((fdc->fdout = 0))); 108969acd21dSWarner Losh bufq_init(&fdc->head); 109069acd21dSWarner Losh /* 109169acd21dSWarner Losh * assume 2 drives/ "normal" controller 109269acd21dSWarner Losh */ 109369acd21dSWarner Losh fdu = fdcu * 2; 109469acd21dSWarner Losh if (fdu >= NFD) { 109569acd21dSWarner Losh printf("fdu %d >= NFD\n",fdu); 109669acd21dSWarner Losh return(0); 109769acd21dSWarner Losh }; 109869acd21dSWarner Losh fd = &fd_data[fdu]; 109969acd21dSWarner Losh 110069acd21dSWarner Losh set_motor(fdcu, fdsu, TURNON); 110169acd21dSWarner Losh DELAY(1000000); /* 1 sec */ 110269acd21dSWarner Losh fdc->fdct = FDC_NE765; 110369acd21dSWarner Losh 110469acd21dSWarner Losh if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && 110569acd21dSWarner Losh (st3 & NE7_ST3_T0)) { 110669acd21dSWarner Losh /* if at track 0, first seek inwards */ 110769acd21dSWarner Losh /* seek some steps: */ 110869acd21dSWarner Losh (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); 110969acd21dSWarner Losh DELAY(300000); /* ...wait a moment... */ 111069acd21dSWarner Losh (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ 111169acd21dSWarner Losh } 111269acd21dSWarner Losh 111369acd21dSWarner Losh /* If we're at track 0 first seek inwards. */ 111469acd21dSWarner Losh if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { 111569acd21dSWarner Losh /* Seek some steps... */ 111669acd21dSWarner Losh if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 111769acd21dSWarner Losh /* ...wait a moment... */ 111869acd21dSWarner Losh DELAY(300000); 111969acd21dSWarner Losh /* make ctrlr happy: */ 112069acd21dSWarner Losh (void)fd_sense_int(fdc, 0, 0); 112169acd21dSWarner Losh } 112269acd21dSWarner Losh } 112369acd21dSWarner Losh 112469acd21dSWarner Losh for(i = 0; i < 2; i++) { 112569acd21dSWarner Losh /* 112669acd21dSWarner Losh * we must recalibrate twice, just in case the 112769acd21dSWarner Losh * heads have been beyond cylinder 76, since most 112869acd21dSWarner Losh * FDCs still barf when attempting to recalibrate 112969acd21dSWarner Losh * more than 77 steps 113069acd21dSWarner Losh */ 113169acd21dSWarner Losh /* go back to 0: */ 113269acd21dSWarner Losh if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 113369acd21dSWarner Losh /* a second being enough for full stroke seek*/ 113469acd21dSWarner Losh DELAY(i == 0? 1000000: 300000); 113569acd21dSWarner Losh 113669acd21dSWarner Losh /* anything responding? */ 113769acd21dSWarner Losh if (fd_sense_int(fdc, &st0, 0) == 0 && 113869acd21dSWarner Losh (st0 & NE7_ST0_EC) == 0) 113969acd21dSWarner Losh break; /* already probed succesfully */ 114069acd21dSWarner Losh } 114169acd21dSWarner Losh } 114269acd21dSWarner Losh 114369acd21dSWarner Losh set_motor(fdcu, fdsu, TURNOFF); 114469acd21dSWarner Losh 114569acd21dSWarner Losh if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ 114669acd21dSWarner Losh return(0); 114769acd21dSWarner Losh 114869acd21dSWarner Losh fd->track = FD_NO_TRACK; 114969acd21dSWarner Losh fd->fdc = fdc; 115069acd21dSWarner Losh fd->fdsu = fdsu; 115169acd21dSWarner Losh fd->options = 0; 115269acd21dSWarner Losh printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); 115369acd21dSWarner Losh fd->type = FD_1440; 115469acd21dSWarner Losh 115569acd21dSWarner Losh return (1); 115669acd21dSWarner Losh } 115769acd21dSWarner Losh #endif 115869acd21dSWarner Losh 11595b81b6b3SRodney W. Grimes /****************************************************************************/ 11605b81b6b3SRodney W. Grimes /* motor control stuff */ 11615e235068SJordan K. Hubbard /* remember to not deselect the drive we're working on */ 11625b81b6b3SRodney W. Grimes /****************************************************************************/ 11633a2f7427SDavid Greenman static void 11646182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon) 11655b81b6b3SRodney W. Grimes { 11666182fdbdSPeter Wemm int fdout = fdc->fdout; 11673a2f7427SDavid Greenman int needspecify = 0; 11683a2f7427SDavid Greenman 11693a2f7427SDavid Greenman if(turnon) { 11703a2f7427SDavid Greenman fdout &= ~FDO_FDSEL; 11713a2f7427SDavid Greenman fdout |= (FDO_MOEN0 << fdsu) + fdsu; 11723a2f7427SDavid Greenman } else 11733a2f7427SDavid Greenman fdout &= ~(FDO_MOEN0 << fdsu); 11743a2f7427SDavid Greenman 11755e235068SJordan K. Hubbard if(!turnon 11765e235068SJordan K. Hubbard && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) 11775e235068SJordan K. Hubbard /* gonna turn off the last drive, put FDC to bed */ 11785e235068SJordan K. Hubbard fdout &= ~ (FDO_FRST|FDO_FDMAEN); 11795e235068SJordan K. Hubbard else { 11803a2f7427SDavid Greenman /* make sure controller is selected and specified */ 11813a2f7427SDavid Greenman if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) 11823a2f7427SDavid Greenman needspecify = 1; 11833a2f7427SDavid Greenman fdout |= (FDO_FRST|FDO_FDMAEN); 11845b81b6b3SRodney W. Grimes } 11855b81b6b3SRodney W. Grimes 1186427ccf00SDoug Rabson fdout_wr(fdc, fdout); 11876182fdbdSPeter Wemm fdc->fdout = fdout; 11883a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdout); 11893a2f7427SDavid Greenman 11903a2f7427SDavid Greenman if (needspecify) { 1191dc8603e3SJoerg Wunsch /* 1192dc5df763SJoerg Wunsch * XXX 1193dc8603e3SJoerg Wunsch * special case: since we have just woken up the FDC 1194dc8603e3SJoerg Wunsch * from its sleep, we silently assume the command will 1195dc8603e3SJoerg Wunsch * be accepted, and do not test for a timeout 1196dc8603e3SJoerg Wunsch */ 11976182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1198dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1199dc5df763SJoerg Wunsch 0); 12006182fdbdSPeter Wemm if (fdc->flags & FDC_HAS_FIFO) 12016182fdbdSPeter Wemm (void) enable_fifo(fdc); 12023a2f7427SDavid Greenman } 12033a2f7427SDavid Greenman } 12043a2f7427SDavid Greenman 1205381fe1aaSGarrett Wollman static void 12066182fdbdSPeter Wemm fd_turnoff(void *xfd) 12075b81b6b3SRodney W. Grimes { 1208f5f7ba03SJordan K. Hubbard int s; 12096182fdbdSPeter Wemm fd_p fd = xfd; 1210dc16046fSJoerg Wunsch 12116182fdbdSPeter Wemm TRACE1("[fd%d: turnoff]", fd->fdu); 12128335c1b8SBruce Evans 12138335c1b8SBruce Evans /* 12148335c1b8SBruce Evans * Don't turn off the motor yet if the drive is active. 12158335c1b8SBruce Evans * XXX shouldn't even schedule turnoff until drive is inactive 12168335c1b8SBruce Evans * and nothing is queued on it. 12178335c1b8SBruce Evans */ 12186182fdbdSPeter Wemm if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { 12196182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 12208335c1b8SBruce Evans return; 12218335c1b8SBruce Evans } 12228335c1b8SBruce Evans 1223f5f7ba03SJordan K. Hubbard s = splbio(); 12245b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR; 12256182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNOFF); 1226f5f7ba03SJordan K. Hubbard splx(s); 12275b81b6b3SRodney W. Grimes } 12285b81b6b3SRodney W. Grimes 12293a2f7427SDavid Greenman static void 12306182fdbdSPeter Wemm fd_motor_on(void *xfd) 12315b81b6b3SRodney W. Grimes { 1232f5f7ba03SJordan K. Hubbard int s; 12336182fdbdSPeter Wemm fd_p fd = xfd; 1234f5f7ba03SJordan K. Hubbard 1235f5f7ba03SJordan K. Hubbard s = splbio(); 12365b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR_WAIT; 12375b81b6b3SRodney W. Grimes if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) 12385b81b6b3SRodney W. Grimes { 12396182fdbdSPeter Wemm fdc_intr(fd->fdc); 12405b81b6b3SRodney W. Grimes } 1241f5f7ba03SJordan K. Hubbard splx(s); 12425b81b6b3SRodney W. Grimes } 12435b81b6b3SRodney W. Grimes 12443a2f7427SDavid Greenman static void 12456182fdbdSPeter Wemm fd_turnon(fd_p fd) 12465b81b6b3SRodney W. Grimes { 12475b81b6b3SRodney W. Grimes if(!(fd->flags & FD_MOTOR)) 12485b81b6b3SRodney W. Grimes { 12493a2f7427SDavid Greenman fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); 12506182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNON); 12516182fdbdSPeter Wemm timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ 12525b81b6b3SRodney W. Grimes } 12535b81b6b3SRodney W. Grimes } 12545b81b6b3SRodney W. Grimes 1255381fe1aaSGarrett Wollman static void 1256dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc) 12575b81b6b3SRodney W. Grimes { 12583a2f7427SDavid Greenman /* Try a reset, keep motor on */ 1259427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 12603a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 12613a2f7427SDavid Greenman DELAY(100); 12623a2f7427SDavid Greenman /* enable FDC, but defer interrupts a moment */ 1263427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); 12643a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); 12653a2f7427SDavid Greenman DELAY(100); 1266427ccf00SDoug Rabson fdout_wr(fdc, fdc->fdout); 12673a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout); 12683a2f7427SDavid Greenman 1269dc5df763SJoerg Wunsch /* XXX after a reset, silently believe the FDC will accept commands */ 12706182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1271dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1272dc5df763SJoerg Wunsch 0); 1273d66c374fSTor Egge if (fdc->flags & FDC_HAS_FIFO) 1274d66c374fSTor Egge (void) enable_fifo(fdc); 12755b81b6b3SRodney W. Grimes } 12765b81b6b3SRodney W. Grimes 12775b81b6b3SRodney W. Grimes /****************************************************************************/ 12785b81b6b3SRodney W. Grimes /* fdc in/out */ 12795b81b6b3SRodney W. Grimes /****************************************************************************/ 12805b81b6b3SRodney W. Grimes int 12816182fdbdSPeter Wemm in_fdc(struct fdc_data *fdc) 12825b81b6b3SRodney W. Grimes { 12835b81b6b3SRodney W. Grimes int i, j = 100000; 1284427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) 12855b81b6b3SRodney W. Grimes != (NE7_DIO|NE7_RQM) && j-- > 0) 1286dc5df763SJoerg Wunsch if (i == NE7_RQM) 12876182fdbdSPeter Wemm return fdc_err(fdc, "ready for output in input\n"); 12885b81b6b3SRodney W. Grimes if (j <= 0) 12896182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); 1290d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 1291427ccf00SDoug Rabson i = fddata_rd(fdc); 12923a2f7427SDavid Greenman TRACE1("[FDDATA->0x%x]", (unsigned char)i); 12935b81b6b3SRodney W. Grimes return(i); 1294d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 1295427ccf00SDoug Rabson return fddata_rd(fdc); 1296d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 12975b81b6b3SRodney W. Grimes } 12985b81b6b3SRodney W. Grimes 1299dc5df763SJoerg Wunsch /* 1300dc5df763SJoerg Wunsch * fd_in: Like in_fdc, but allows you to see if it worked. 1301dc5df763SJoerg Wunsch */ 1302b5e8ce9fSBruce Evans static int 13036182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr) 1304dc5df763SJoerg Wunsch { 1305dc5df763SJoerg Wunsch int i, j = 100000; 1306427ccf00SDoug Rabson while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) 1307dc5df763SJoerg Wunsch != (NE7_DIO|NE7_RQM) && j-- > 0) 1308dc5df763SJoerg Wunsch if (i == NE7_RQM) 13096182fdbdSPeter Wemm return fdc_err(fdc, "ready for output in input\n"); 1310dc5df763SJoerg Wunsch if (j <= 0) 13116182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); 1312d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 1313427ccf00SDoug Rabson i = fddata_rd(fdc); 1314dc5df763SJoerg Wunsch TRACE1("[FDDATA->0x%x]", (unsigned char)i); 1315dc5df763SJoerg Wunsch *ptr = i; 1316dc5df763SJoerg Wunsch return 0; 1317d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 1318427ccf00SDoug Rabson i = fddata_rd(fdc); 1319dc5df763SJoerg Wunsch if (ptr) 1320dc5df763SJoerg Wunsch *ptr = i; 1321dc5df763SJoerg Wunsch return 0; 1322d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 1323dc5df763SJoerg Wunsch } 1324dc5df763SJoerg Wunsch 1325dc5df763SJoerg Wunsch int 13266182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x) 13275b81b6b3SRodney W. Grimes { 13283b3837dbSRodney W. Grimes int i; 13295b81b6b3SRodney W. Grimes 13303b3837dbSRodney W. Grimes /* Check that the direction bit is set */ 13313b3837dbSRodney W. Grimes i = 100000; 1332427ccf00SDoug Rabson while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); 13336182fdbdSPeter Wemm if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); 13343b3837dbSRodney W. Grimes 13353b3837dbSRodney W. Grimes /* Check that the floppy controller is ready for a command */ 13363b3837dbSRodney W. Grimes i = 100000; 1337427ccf00SDoug Rabson while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); 133816b04b6aSJoerg Wunsch if (i <= 0) 13396182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); 13403b3837dbSRodney W. Grimes 13413b3837dbSRodney W. Grimes /* Send the command and return */ 1342427ccf00SDoug Rabson fddata_wr(fdc, x); 13433a2f7427SDavid Greenman TRACE1("[0x%x->FDDATA]", x); 13445b81b6b3SRodney W. Grimes return (0); 13455b81b6b3SRodney W. Grimes } 13465b81b6b3SRodney W. Grimes 13475b81b6b3SRodney W. Grimes /****************************************************************************/ 13485b81b6b3SRodney W. Grimes /* fdopen/fdclose */ 13495b81b6b3SRodney W. Grimes /****************************************************************************/ 1350381fe1aaSGarrett Wollman int 1351671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p) 13525b81b6b3SRodney W. Grimes { 13535b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 135420a29168SAndrey A. Chernov int type = FDTYPE(minor(dev)); 13556182fdbdSPeter Wemm fd_p fd; 1356b99f0a4aSAndrew Moore fdc_p fdc; 13575b81b6b3SRodney W. Grimes 13585b81b6b3SRodney W. Grimes /* check bounds */ 13596182fdbdSPeter Wemm if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) 1360b99f0a4aSAndrew Moore return (ENXIO); 13616182fdbdSPeter Wemm fdc = fd->fdc; 13626182fdbdSPeter Wemm if ((fdc == NULL) || (fd->type == NO_TYPE)) 1363b99f0a4aSAndrew Moore return (ENXIO); 1364b99f0a4aSAndrew Moore if (type > NUMDENS) 1365b99f0a4aSAndrew Moore return (ENXIO); 13667ca0641bSAndrey A. Chernov if (type == 0) 13676182fdbdSPeter Wemm type = fd->type; 13687ca0641bSAndrey A. Chernov else { 13693e425b96SJulian Elischer /* 13703e425b96SJulian Elischer * For each type of basic drive, make sure we are trying 13713e425b96SJulian Elischer * to open a type it can do, 13723e425b96SJulian Elischer */ 13736182fdbdSPeter Wemm if (type != fd->type) { 13746182fdbdSPeter Wemm switch (fd->type) { 13757ca0641bSAndrey A. Chernov case FD_360: 13767ca0641bSAndrey A. Chernov return (ENXIO); 1377ed2fa05eSAndrey A. Chernov case FD_720: 1378b39c878eSAndrey A. Chernov if ( type != FD_820 1379b39c878eSAndrey A. Chernov && type != FD_800 1380ed2fa05eSAndrey A. Chernov ) 1381ed2fa05eSAndrey A. Chernov return (ENXIO); 1382ed2fa05eSAndrey A. Chernov break; 13837ca0641bSAndrey A. Chernov case FD_1200: 1384b39c878eSAndrey A. Chernov switch (type) { 1385b39c878eSAndrey A. Chernov case FD_1480: 1386b39c878eSAndrey A. Chernov type = FD_1480in5_25; 1387fa4700b4SAndrey A. Chernov break; 13887ca0641bSAndrey A. Chernov case FD_1440: 1389b39c878eSAndrey A. Chernov type = FD_1440in5_25; 1390b39c878eSAndrey A. Chernov break; 1391b39c878eSAndrey A. Chernov case FD_820: 1392b39c878eSAndrey A. Chernov type = FD_820in5_25; 1393b39c878eSAndrey A. Chernov break; 1394b39c878eSAndrey A. Chernov case FD_800: 1395b39c878eSAndrey A. Chernov type = FD_800in5_25; 1396b39c878eSAndrey A. Chernov break; 1397b39c878eSAndrey A. Chernov case FD_720: 1398b39c878eSAndrey A. Chernov type = FD_720in5_25; 1399b39c878eSAndrey A. Chernov break; 1400b39c878eSAndrey A. Chernov case FD_360: 1401b39c878eSAndrey A. Chernov type = FD_360in5_25; 1402b39c878eSAndrey A. Chernov break; 1403b39c878eSAndrey A. Chernov default: 1404b39c878eSAndrey A. Chernov return(ENXIO); 1405b39c878eSAndrey A. Chernov } 1406b39c878eSAndrey A. Chernov break; 1407b39c878eSAndrey A. Chernov case FD_1440: 1408b39c878eSAndrey A. Chernov if ( type != FD_1720 1409b39c878eSAndrey A. Chernov && type != FD_1480 1410ed2fa05eSAndrey A. Chernov && type != FD_1200 1411b39c878eSAndrey A. Chernov && type != FD_820 1412b39c878eSAndrey A. Chernov && type != FD_800 1413b39c878eSAndrey A. Chernov && type != FD_720 14147ca0641bSAndrey A. Chernov ) 1415dffff499SAndrey A. Chernov return(ENXIO); 1416fa4700b4SAndrey A. Chernov break; 14177ca0641bSAndrey A. Chernov } 14187ca0641bSAndrey A. Chernov } 1419fa4700b4SAndrey A. Chernov } 14206182fdbdSPeter Wemm fd->ft = fd_types + type - 1; 14216182fdbdSPeter Wemm fd->flags |= FD_OPEN; 14226182fdbdSPeter Wemm device_busy(fd->dev); 14236182fdbdSPeter Wemm device_busy(fd->fdc->fdc_dev); 14245b81b6b3SRodney W. Grimes return 0; 14255b81b6b3SRodney W. Grimes } 14265b81b6b3SRodney W. Grimes 1427381fe1aaSGarrett Wollman int 1428671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p) 14295b81b6b3SRodney W. Grimes { 14305b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 14316182fdbdSPeter Wemm struct fd_data *fd; 1432b99f0a4aSAndrew Moore 14336182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 14346182fdbdSPeter Wemm fd->flags &= ~FD_OPEN; 14356182fdbdSPeter Wemm fd->options &= ~FDOPT_NORETRY; 1436dc16046fSJoerg Wunsch 14375b81b6b3SRodney W. Grimes return (0); 14385b81b6b3SRodney W. Grimes } 14395b81b6b3SRodney W. Grimes 14403a2f7427SDavid Greenman /****************************************************************************/ 14413a2f7427SDavid Greenman /* fdstrategy */ 14423a2f7427SDavid Greenman /****************************************************************************/ 14433a2f7427SDavid Greenman void 14443a2f7427SDavid Greenman fdstrategy(struct buf *bp) 14453a2f7427SDavid Greenman { 1446bb6382faSJoerg Wunsch unsigned nblocks, blknum, cando; 14473a2f7427SDavid Greenman int s; 14483a2f7427SDavid Greenman fdu_t fdu; 14493a2f7427SDavid Greenman fdc_p fdc; 14503a2f7427SDavid Greenman fd_p fd; 14513a2f7427SDavid Greenman size_t fdblk; 14523a2f7427SDavid Greenman 14533a2f7427SDavid Greenman fdu = FDUNIT(minor(bp->b_dev)); 14546182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 14556182fdbdSPeter Wemm if (fd == 0) 14566182fdbdSPeter Wemm panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", 14576182fdbdSPeter Wemm (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); 14583a2f7427SDavid Greenman fdc = fd->fdc; 145969acd21dSWarner Losh #ifdef FDC_YE 146069acd21dSWarner Losh if (fd->type == NO_TYPE) { 146169acd21dSWarner Losh bp->b_error = ENXIO; 146269acd21dSWarner Losh bp->b_flags |= B_ERROR; 146369acd21dSWarner Losh /* 146469acd21dSWarner Losh * I _refuse_ to use a goto 146569acd21dSWarner Losh */ 146669acd21dSWarner Losh biodone(bp); 146769acd21dSWarner Losh return; 146869acd21dSWarner Losh }; 146969acd21dSWarner Losh #endif 14703a2f7427SDavid Greenman 1471d3628763SRodney W. Grimes fdblk = 128 << (fd->ft->secsize); 14723a2f7427SDavid Greenman if (!(bp->b_flags & B_FORMAT)) { 14736182fdbdSPeter Wemm if (bp->b_blkno < 0) { 1474dc5df763SJoerg Wunsch printf( 14756a0e6f42SRodney W. Grimes "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", 1476702c623aSPoul-Henning Kamp fdu, (u_long)bp->b_blkno, bp->b_bcount); 14773a2f7427SDavid Greenman bp->b_error = EINVAL; 14783a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 14793a2f7427SDavid Greenman goto bad; 14803a2f7427SDavid Greenman } 14813a2f7427SDavid Greenman if ((bp->b_bcount % fdblk) != 0) { 14823a2f7427SDavid Greenman bp->b_error = EINVAL; 14833a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 14843a2f7427SDavid Greenman goto bad; 14853a2f7427SDavid Greenman } 14863a2f7427SDavid Greenman } 14873a2f7427SDavid Greenman 14883a2f7427SDavid Greenman /* 14893a2f7427SDavid Greenman * Set up block calculations. 14903a2f7427SDavid Greenman */ 1491bb6382faSJoerg Wunsch if (bp->b_blkno > 20000000) { 1492bb6382faSJoerg Wunsch /* 1493bb6382faSJoerg Wunsch * Reject unreasonably high block number, prevent the 1494bb6382faSJoerg Wunsch * multiplication below from overflowing. 1495bb6382faSJoerg Wunsch */ 1496bb6382faSJoerg Wunsch bp->b_error = EINVAL; 14973a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 14983a2f7427SDavid Greenman goto bad; 14993a2f7427SDavid Greenman } 1500bb6382faSJoerg Wunsch blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; 1501bb6382faSJoerg Wunsch nblocks = fd->ft->size; 1502bb6382faSJoerg Wunsch bp->b_resid = 0; 1503bb6382faSJoerg Wunsch if (blknum + (bp->b_bcount / fdblk) > nblocks) { 1504bb6382faSJoerg Wunsch if (blknum <= nblocks) { 1505bb6382faSJoerg Wunsch cando = (nblocks - blknum) * fdblk; 1506bb6382faSJoerg Wunsch bp->b_resid = bp->b_bcount - cando; 1507bb6382faSJoerg Wunsch if (cando == 0) 1508bb6382faSJoerg Wunsch goto bad; /* not actually bad but EOF */ 1509bb6382faSJoerg Wunsch } else { 1510bb6382faSJoerg Wunsch bp->b_error = EINVAL; 1511bb6382faSJoerg Wunsch bp->b_flags |= B_ERROR; 1512bb6382faSJoerg Wunsch goto bad; 1513bb6382faSJoerg Wunsch } 1514bb6382faSJoerg Wunsch } 151527513ca7SBruce Evans bp->b_pblkno = bp->b_blkno; 15163a2f7427SDavid Greenman s = splbio(); 151702a19910SJustin T. Gibbs bufqdisksort(&fdc->head, bp); 15186182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ 1519b2dfb1f9SJustin T. Gibbs 1520b2dfb1f9SJustin T. Gibbs /* Tell devstat we are starting on the transaction */ 1521b2dfb1f9SJustin T. Gibbs devstat_start_transaction(&fd->device_stats); 1522b2dfb1f9SJustin T. Gibbs 15236182fdbdSPeter Wemm fdstart(fdc); 15243a2f7427SDavid Greenman splx(s); 15253a2f7427SDavid Greenman return; 15263a2f7427SDavid Greenman 15273a2f7427SDavid Greenman bad: 15283a2f7427SDavid Greenman biodone(bp); 15293a2f7427SDavid Greenman } 15303a2f7427SDavid Greenman 15315b81b6b3SRodney W. Grimes /***************************************************************\ 15325b81b6b3SRodney W. Grimes * fdstart * 15335b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy * 15345b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command * 15355b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more* 15365b81b6b3SRodney W. Grimes * work to do and picks up what we just added. * 15375b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it * 15385b81b6b3SRodney W. Grimes * will pick up our work when the present work completes * 15395b81b6b3SRodney W. Grimes \***************************************************************/ 1540381fe1aaSGarrett Wollman static void 15416182fdbdSPeter Wemm fdstart(struct fdc_data *fdc) 15425b81b6b3SRodney W. Grimes { 15435b81b6b3SRodney W. Grimes int s; 15445b81b6b3SRodney W. Grimes 15455b81b6b3SRodney W. Grimes s = splbio(); 15466182fdbdSPeter Wemm if(fdc->state == DEVIDLE) 15475b81b6b3SRodney W. Grimes { 15486182fdbdSPeter Wemm fdc_intr(fdc); 15495b81b6b3SRodney W. Grimes } 15505b81b6b3SRodney W. Grimes splx(s); 15515b81b6b3SRodney W. Grimes } 15525b81b6b3SRodney W. Grimes 1553381fe1aaSGarrett Wollman static void 15546182fdbdSPeter Wemm fd_iotimeout(void *xfdc) 15555b81b6b3SRodney W. Grimes { 15565c1a1eaeSBruce Evans fdc_p fdc; 1557f5f7ba03SJordan K. Hubbard int s; 15585b81b6b3SRodney W. Grimes 15596182fdbdSPeter Wemm fdc = xfdc; 15605c1a1eaeSBruce Evans TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); 15615b81b6b3SRodney W. Grimes 15623a2f7427SDavid Greenman /* 15633a2f7427SDavid Greenman * Due to IBM's brain-dead design, the FDC has a faked ready 15643a2f7427SDavid Greenman * signal, hardwired to ready == true. Thus, any command 15653a2f7427SDavid Greenman * issued if there's no diskette in the drive will _never_ 15663a2f7427SDavid Greenman * complete, and must be aborted by resetting the FDC. 15673a2f7427SDavid Greenman * Many thanks, Big Blue! 15685c1a1eaeSBruce Evans * The FDC must not be reset directly, since that would 15695c1a1eaeSBruce Evans * interfere with the state machine. Instead, pretend that 15705c1a1eaeSBruce Evans * the command completed but was invalid. The state machine 15715c1a1eaeSBruce Evans * will reset the FDC and retry once. 15723a2f7427SDavid Greenman */ 15733a2f7427SDavid Greenman s = splbio(); 15745c1a1eaeSBruce Evans fdc->status[0] = NE7_ST0_IC_IV; 15755c1a1eaeSBruce Evans fdc->flags &= ~FDC_STAT_VALID; 15765c1a1eaeSBruce Evans fdc->state = IOTIMEDOUT; 15776182fdbdSPeter Wemm fdc_intr(fdc); 1578f5f7ba03SJordan K. Hubbard splx(s); 15795b81b6b3SRodney W. Grimes } 15805b81b6b3SRodney W. Grimes 15815b81b6b3SRodney W. Grimes /* just ensure it has the right spl */ 1582381fe1aaSGarrett Wollman static void 15836182fdbdSPeter Wemm fd_pseudointr(void *xfdc) 15845b81b6b3SRodney W. Grimes { 15855b81b6b3SRodney W. Grimes int s; 15863a2f7427SDavid Greenman 15875b81b6b3SRodney W. Grimes s = splbio(); 15886182fdbdSPeter Wemm fdc_intr(xfdc); 15895b81b6b3SRodney W. Grimes splx(s); 15905b81b6b3SRodney W. Grimes } 15915b81b6b3SRodney W. Grimes 15925b81b6b3SRodney W. Grimes /***********************************************************************\ 15935b81b6b3SRodney W. Grimes * fdintr * 15945b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0 * 15955b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO * 15965b81b6b3SRodney W. Grimes \***********************************************************************/ 1597fe310de8SBruce Evans static void 15986182fdbdSPeter Wemm fdc_intr(void *xfdc) 15995b81b6b3SRodney W. Grimes { 16006182fdbdSPeter Wemm fdc_p fdc = xfdc; 16016182fdbdSPeter Wemm while(fdstate(fdc)) 1602381fe1aaSGarrett Wollman ; 16035b81b6b3SRodney W. Grimes } 16045b81b6b3SRodney W. Grimes 160569acd21dSWarner Losh #ifdef FDC_YE 160669acd21dSWarner Losh /* 160769acd21dSWarner Losh * magic pseudo-DMA initialization for YE FDC. Sets count and 160869acd21dSWarner Losh * direction 160969acd21dSWarner Losh */ 161069acd21dSWarner Losh #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ 161169acd21dSWarner Losh outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) 161269acd21dSWarner Losh 161369acd21dSWarner Losh /* 161469acd21dSWarner Losh * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy 161569acd21dSWarner Losh */ 161669acd21dSWarner Losh static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) 161769acd21dSWarner Losh { 161869acd21dSWarner Losh u_char *cptr = (u_char *)addr; 161969acd21dSWarner Losh fdc_p fdc = &fdc_data[fdcu]; 162069acd21dSWarner Losh int io = fdc->baseport; 162169acd21dSWarner Losh 162269acd21dSWarner Losh if (flags & B_READ) { 162369acd21dSWarner Losh if (fdc->state != PIOREAD) { 162469acd21dSWarner Losh fdc->state = PIOREAD; 162569acd21dSWarner Losh return(0); 162669acd21dSWarner Losh }; 162769acd21dSWarner Losh SET_BCDR(0,count,io); 162869acd21dSWarner Losh insb(io+FDC_YE_DATAPORT,cptr,count); 162969acd21dSWarner Losh } else { 163069acd21dSWarner Losh outsb(io+FDC_YE_DATAPORT,cptr,count); 163169acd21dSWarner Losh SET_BCDR(0,count,io); 163269acd21dSWarner Losh }; 163369acd21dSWarner Losh return(1); 163469acd21dSWarner Losh } 163569acd21dSWarner Losh #endif /* FDC_YE */ 163669acd21dSWarner Losh 16375b81b6b3SRodney W. Grimes /***********************************************************************\ 16385b81b6b3SRodney W. Grimes * The controller state machine. * 16395b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly * 16405b81b6b3SRodney W. Grimes \***********************************************************************/ 16413a2f7427SDavid Greenman static int 16426182fdbdSPeter Wemm fdstate(fdc_p fdc) 16435b81b6b3SRodney W. Grimes { 16445c1a1eaeSBruce Evans int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; 1645bb6382faSJoerg Wunsch unsigned blknum = 0, b_cylinder = 0; 16465b81b6b3SRodney W. Grimes fdu_t fdu = fdc->fdu; 16475b81b6b3SRodney W. Grimes fd_p fd; 164817542807SPoul-Henning Kamp register struct buf *bp; 1649b39c878eSAndrey A. Chernov struct fd_formb *finfo = NULL; 16503a2f7427SDavid Greenman size_t fdblk; 16515b81b6b3SRodney W. Grimes 1652e93e63cbSBruce Evans bp = fdc->bp; 1653e93e63cbSBruce Evans if (bp == NULL) { 165402a19910SJustin T. Gibbs bp = bufq_first(&fdc->head); 1655e93e63cbSBruce Evans if (bp != NULL) { 1656e93e63cbSBruce Evans bufq_remove(&fdc->head, bp); 1657e93e63cbSBruce Evans fdc->bp = bp; 1658e93e63cbSBruce Evans } 1659e93e63cbSBruce Evans } 1660e93e63cbSBruce Evans if (bp == NULL) { 16615b81b6b3SRodney W. Grimes /***********************************************\ 16625b81b6b3SRodney W. Grimes * nothing left for this controller to do * 16635b81b6b3SRodney W. Grimes * Force into the IDLE state, * 16645b81b6b3SRodney W. Grimes \***********************************************/ 16655b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 16666182fdbdSPeter Wemm if (fdc->fd) { 16676182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 16686182fdbdSPeter Wemm printf("unexpected valid fd pointer\n"); 16695b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 16705b81b6b3SRodney W. Grimes fdc->fdu = -1; 16715b81b6b3SRodney W. Grimes } 16726182fdbdSPeter Wemm TRACE1("[fdc%d IDLE]", fdc->fdcu); 16735b81b6b3SRodney W. Grimes return (0); 16745b81b6b3SRodney W. Grimes } 16755b81b6b3SRodney W. Grimes fdu = FDUNIT(minor(bp->b_dev)); 16766182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 16773a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 16786182fdbdSPeter Wemm if (fdc->fd && (fd != fdc->fd)) { 16796182fdbdSPeter Wemm device_print_prettyname(fd->dev); 16806182fdbdSPeter Wemm printf("confused fd pointers\n"); 16815b81b6b3SRodney W. Grimes } 16825b81b6b3SRodney W. Grimes read = bp->b_flags & B_READ; 1683b39c878eSAndrey A. Chernov format = bp->b_flags & B_FORMAT; 1684bb6382faSJoerg Wunsch if (format) { 1685ab3f7469SPoul-Henning Kamp finfo = (struct fd_formb *)bp->b_data; 1686bb6382faSJoerg Wunsch fd->skip = (char *)&(finfo->fd_formb_cylno(0)) 1687bb6382faSJoerg Wunsch - (char *)finfo; 1688bb6382faSJoerg Wunsch } 1689bb6382faSJoerg Wunsch if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { 16903e425b96SJulian Elischer blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + 1691bb6382faSJoerg Wunsch fd->skip/fdblk; 1692bb6382faSJoerg Wunsch b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1693bb6382faSJoerg Wunsch } 16945b81b6b3SRodney W. Grimes TRACE1("fd%d", fdu); 16955b81b6b3SRodney W. Grimes TRACE1("[%s]", fdstates[fdc->state]); 16965b81b6b3SRodney W. Grimes TRACE1("(0x%x)", fd->flags); 16976182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); 16986182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 16995b81b6b3SRodney W. Grimes switch (fdc->state) 17005b81b6b3SRodney W. Grimes { 17015b81b6b3SRodney W. Grimes case DEVIDLE: 17025b81b6b3SRodney W. Grimes case FINDWORK: /* we have found new work */ 17035b81b6b3SRodney W. Grimes fdc->retry = 0; 17045b81b6b3SRodney W. Grimes fd->skip = 0; 17055b81b6b3SRodney W. Grimes fdc->fd = fd; 17065b81b6b3SRodney W. Grimes fdc->fdu = fdu; 1707427ccf00SDoug Rabson fdctl_wr(fdc, fd->ft->trans); 17083a2f7427SDavid Greenman TRACE1("[0x%x->FDCTL]", fd->ft->trans); 17095b81b6b3SRodney W. Grimes /*******************************************************\ 17105b81b6b3SRodney W. Grimes * If the next drive has a motor startup pending, then * 1711dc733423SDag-Erling Smørgrav * it will start up in its own good time * 17125b81b6b3SRodney W. Grimes \*******************************************************/ 17136182fdbdSPeter Wemm if(fd->flags & FD_MOTOR_WAIT) { 17145b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 17155b81b6b3SRodney W. Grimes return (0); /* come back later */ 17165b81b6b3SRodney W. Grimes } 17175b81b6b3SRodney W. Grimes /*******************************************************\ 17185b81b6b3SRodney W. Grimes * Maybe if it's not starting, it SHOULD be starting * 17195b81b6b3SRodney W. Grimes \*******************************************************/ 17205b81b6b3SRodney W. Grimes if (!(fd->flags & FD_MOTOR)) 17215b81b6b3SRodney W. Grimes { 17225b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 17236182fdbdSPeter Wemm fd_turnon(fd); 17245b81b6b3SRodney W. Grimes return (0); 17255b81b6b3SRodney W. Grimes } 17265b81b6b3SRodney W. Grimes else /* at least make sure we are selected */ 17275b81b6b3SRodney W. Grimes { 17286182fdbdSPeter Wemm set_motor(fdc, fd->fdsu, TURNON); 17295b81b6b3SRodney W. Grimes } 17305c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 17315c1a1eaeSBruce Evans fdc->state = RESETCTLR; 17325c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 17335c1a1eaeSBruce Evans } else 17345e235068SJordan K. Hubbard fdc->state = DOSEEK; 17355b81b6b3SRodney W. Grimes break; 17365b81b6b3SRodney W. Grimes case DOSEEK: 1737bb6382faSJoerg Wunsch if (b_cylinder == (unsigned)fd->track) 17385b81b6b3SRodney W. Grimes { 17395b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 17405b81b6b3SRodney W. Grimes break; 17415b81b6b3SRodney W. Grimes } 17426182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, 1743bb6382faSJoerg Wunsch fd->fdsu, b_cylinder * fd->ft->steptrac, 1744dc5df763SJoerg Wunsch 0)) 1745dc8603e3SJoerg Wunsch { 1746dc8603e3SJoerg Wunsch /* 1747dc8603e3SJoerg Wunsch * seek command not accepted, looks like 1748dc8603e3SJoerg Wunsch * the FDC went off to the Saints... 1749dc8603e3SJoerg Wunsch */ 1750dc8603e3SJoerg Wunsch fdc->retry = 6; /* try a reset */ 17516182fdbdSPeter Wemm return(retrier(fdc)); 1752dc8603e3SJoerg Wunsch } 1753dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 17545b81b6b3SRodney W. Grimes fdc->state = SEEKWAIT; 17555b81b6b3SRodney W. Grimes return(0); /* will return later */ 17565b81b6b3SRodney W. Grimes case SEEKWAIT: 17575b81b6b3SRodney W. Grimes /* allow heads to settle */ 17586182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 16); 17595b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 17605b81b6b3SRodney W. Grimes return(0); /* will return later */ 17615b81b6b3SRodney W. Grimes case SEEKCOMPLETE : /* SEEK DONE, START DMA */ 17625b81b6b3SRodney W. Grimes /* Make sure seek really happened*/ 17636182fdbdSPeter Wemm if(fd->track == FD_NO_TRACK) { 1764bb6382faSJoerg Wunsch int descyl = b_cylinder * fd->ft->steptrac; 17653a2f7427SDavid Greenman do { 17663a2f7427SDavid Greenman /* 1767dc5df763SJoerg Wunsch * This might be a "ready changed" interrupt, 1768dc5df763SJoerg Wunsch * which cannot really happen since the 1769dc5df763SJoerg Wunsch * RDY pin is hardwired to + 5 volts. This 1770dc5df763SJoerg Wunsch * generally indicates a "bouncing" intr 1771dc5df763SJoerg Wunsch * line, so do one of the following: 1772dc5df763SJoerg Wunsch * 1773dc5df763SJoerg Wunsch * When running on an enhanced FDC that is 1774dc5df763SJoerg Wunsch * known to not go stuck after responding 1775dc5df763SJoerg Wunsch * with INVALID, fetch all interrupt states 1776dc5df763SJoerg Wunsch * until seeing either an INVALID or a 1777dc5df763SJoerg Wunsch * real interrupt condition. 1778dc5df763SJoerg Wunsch * 1779dc5df763SJoerg Wunsch * When running on a dumb old NE765, give 1780dc5df763SJoerg Wunsch * up immediately. The controller will 1781dc5df763SJoerg Wunsch * provide up to four dummy RC interrupt 1782dc5df763SJoerg Wunsch * conditions right after reset (for the 1783dc5df763SJoerg Wunsch * corresponding four drives), so this is 1784dc5df763SJoerg Wunsch * our only chance to get notice that it 1785dc5df763SJoerg Wunsch * was not the FDC that caused the interrupt. 17863a2f7427SDavid Greenman */ 1787dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) 1788dc5df763SJoerg Wunsch == FD_NOT_VALID) 1789dc5df763SJoerg Wunsch return 0; 1790dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 1791dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 1792dc5df763SJoerg Wunsch return 0; /* hope for a real intr */ 17933a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 1794dc5df763SJoerg Wunsch 17956182fdbdSPeter Wemm if (0 == descyl) { 1796dc5df763SJoerg Wunsch int failed = 0; 17973a2f7427SDavid Greenman /* 17983a2f7427SDavid Greenman * seek to cyl 0 requested; make sure we are 17993a2f7427SDavid Greenman * really there 18003a2f7427SDavid Greenman */ 1801dc5df763SJoerg Wunsch if (fd_sense_drive_status(fdc, &st3)) 1802dc5df763SJoerg Wunsch failed = 1; 18033a2f7427SDavid Greenman if ((st3 & NE7_ST3_T0) == 0) { 18043a2f7427SDavid Greenman printf( 18053a2f7427SDavid Greenman "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", 18063a2f7427SDavid Greenman fdu, st3, NE7_ST3BITS); 1807dc5df763SJoerg Wunsch failed = 1; 1808dc5df763SJoerg Wunsch } 1809dc5df763SJoerg Wunsch 18106182fdbdSPeter Wemm if (failed) { 18113a2f7427SDavid Greenman if(fdc->retry < 3) 18123a2f7427SDavid Greenman fdc->retry = 3; 18136182fdbdSPeter Wemm return (retrier(fdc)); 18143a2f7427SDavid Greenman } 18153a2f7427SDavid Greenman } 1816dc5df763SJoerg Wunsch 18176182fdbdSPeter Wemm if (cyl != descyl) { 18183a2f7427SDavid Greenman printf( 18193a2f7427SDavid Greenman "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", 18202d9d0204SRodney W. Grimes fdu, descyl, cyl, st0); 1821e5d7d243SBruce Evans if (fdc->retry < 3) 1822e5d7d243SBruce Evans fdc->retry = 3; 18236182fdbdSPeter Wemm return (retrier(fdc)); 18245b81b6b3SRodney W. Grimes } 18255b81b6b3SRodney W. Grimes } 18265b81b6b3SRodney W. Grimes 1827bb6382faSJoerg Wunsch fd->track = b_cylinder; 182869acd21dSWarner Losh #ifdef FDC_YE 182969acd21dSWarner Losh if (!(fdc->flags & FDC_PCMCIA)) 183069acd21dSWarner Losh #endif 1831ab3f7469SPoul-Henning Kamp isa_dmastart(bp->b_flags, bp->b_data+fd->skip, 18323a2f7427SDavid Greenman format ? bp->b_bcount : fdblk, fdc->dmachan); 18335b81b6b3SRodney W. Grimes sectrac = fd->ft->sectrac; 18345b81b6b3SRodney W. Grimes sec = blknum % (sectrac * fd->ft->heads); 18355b81b6b3SRodney W. Grimes head = sec / sectrac; 18365b81b6b3SRodney W. Grimes sec = sec % sectrac + 1; 18373a2f7427SDavid Greenman fd->hddrv = ((head&1)<<2)+fdu; 18383a2f7427SDavid Greenman 18393a2f7427SDavid Greenman if(format || !read) 18403a2f7427SDavid Greenman { 18413a2f7427SDavid Greenman /* make sure the drive is writable */ 1842dc5df763SJoerg Wunsch if(fd_sense_drive_status(fdc, &st3) != 0) 1843dc8603e3SJoerg Wunsch { 1844dc8603e3SJoerg Wunsch /* stuck controller? */ 18455c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 18465c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 18475c1a1eaeSBruce Evans fdc->dmachan); 1848dc8603e3SJoerg Wunsch fdc->retry = 6; /* reset the beast */ 18496182fdbdSPeter Wemm return (retrier(fdc)); 1850dc8603e3SJoerg Wunsch } 18513a2f7427SDavid Greenman if(st3 & NE7_ST3_WP) 18523a2f7427SDavid Greenman { 18533a2f7427SDavid Greenman /* 18543a2f7427SDavid Greenman * XXX YES! this is ugly. 18553a2f7427SDavid Greenman * in order to force the current operation 18563a2f7427SDavid Greenman * to fail, we will have to fake an FDC 18573a2f7427SDavid Greenman * error - all error handling is done 18583a2f7427SDavid Greenman * by the retrier() 18593a2f7427SDavid Greenman */ 18603a2f7427SDavid Greenman fdc->status[0] = NE7_ST0_IC_AT; 18613a2f7427SDavid Greenman fdc->status[1] = NE7_ST1_NW; 18623a2f7427SDavid Greenman fdc->status[2] = 0; 18633a2f7427SDavid Greenman fdc->status[3] = fd->track; 18643a2f7427SDavid Greenman fdc->status[4] = head; 18653a2f7427SDavid Greenman fdc->status[5] = sec; 18663a2f7427SDavid Greenman fdc->retry = 8; /* break out immediately */ 18673a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; /* not really... */ 18683a2f7427SDavid Greenman return (1); 18693a2f7427SDavid Greenman } 18703a2f7427SDavid Greenman } 18715b81b6b3SRodney W. Grimes 18726182fdbdSPeter Wemm if (format) { 187369acd21dSWarner Losh #ifdef FDC_YE 187469acd21dSWarner Losh if (fdc->flags & FDC_PCMCIA) 187569acd21dSWarner Losh (void)fdcpio(fdcu,bp->b_flags, 187669acd21dSWarner Losh bp->b_data+fd->skip, 187769acd21dSWarner Losh bp->b_bcount); 187869acd21dSWarner Losh #endif 1879b39c878eSAndrey A. Chernov /* formatting */ 18806182fdbdSPeter Wemm if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, 1881dc5df763SJoerg Wunsch finfo->fd_formb_secshift, 1882dc5df763SJoerg Wunsch finfo->fd_formb_nsecs, 1883dc5df763SJoerg Wunsch finfo->fd_formb_gaplen, 18846182fdbdSPeter Wemm finfo->fd_formb_fillbyte, 0)) { 1885dc8603e3SJoerg Wunsch /* controller fell over */ 18865c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 18875c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 18885c1a1eaeSBruce Evans fdc->dmachan); 1889dc8603e3SJoerg Wunsch fdc->retry = 6; 18906182fdbdSPeter Wemm return (retrier(fdc)); 1891dc8603e3SJoerg Wunsch } 18926182fdbdSPeter Wemm } else { 189369acd21dSWarner Losh #ifdef FDC_YE 189469acd21dSWarner Losh if (fdc->flags & FDC_PCMCIA) { 189569acd21dSWarner Losh /* 189669acd21dSWarner Losh * this seems to be necessary even when 189769acd21dSWarner Losh * reading data 189869acd21dSWarner Losh */ 189969acd21dSWarner Losh SET_BCDR(1,fdblk,fdc->baseport); 190069acd21dSWarner Losh 190169acd21dSWarner Losh /* 190269acd21dSWarner Losh * perform the write pseudo-DMA before 190369acd21dSWarner Losh * the WRITE command is sent 190469acd21dSWarner Losh */ 190569acd21dSWarner Losh if (!read) 190669acd21dSWarner Losh (void)fdcpio(fdcu,bp->b_flags, 190769acd21dSWarner Losh bp->b_data+fd->skip, 190869acd21dSWarner Losh fdblk); 190969acd21dSWarner Losh } 191069acd21dSWarner Losh #endif 19116182fdbdSPeter Wemm if (fd_cmd(fdc, 9, 1912dc5df763SJoerg Wunsch (read ? NE7CMD_READ : NE7CMD_WRITE), 1913dc5df763SJoerg Wunsch head << 2 | fdu, /* head & unit */ 1914dc5df763SJoerg Wunsch fd->track, /* track */ 1915dc5df763SJoerg Wunsch head, 1916dc5df763SJoerg Wunsch sec, /* sector + 1 */ 1917dc5df763SJoerg Wunsch fd->ft->secsize, /* sector size */ 1918dc5df763SJoerg Wunsch sectrac, /* sectors/track */ 1919dc5df763SJoerg Wunsch fd->ft->gap, /* gap size */ 1920dc5df763SJoerg Wunsch fd->ft->datalen, /* data length */ 19216182fdbdSPeter Wemm 0)) { 1922dc8603e3SJoerg Wunsch /* the beast is sleeping again */ 19235c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 19245c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 19255c1a1eaeSBruce Evans fdc->dmachan); 1926dc8603e3SJoerg Wunsch fdc->retry = 6; 19276182fdbdSPeter Wemm return (retrier(fdc)); 19285b81b6b3SRodney W. Grimes } 1929b39c878eSAndrey A. Chernov } 193069acd21dSWarner Losh #ifdef FDC_YE 193169acd21dSWarner Losh if (fdc->flags & FDC_PCMCIA) 193269acd21dSWarner Losh /* 193369acd21dSWarner Losh * if this is a read, then simply await interrupt 193469acd21dSWarner Losh * before performing PIO 193569acd21dSWarner Losh */ 193669acd21dSWarner Losh if (read && !fdcpio(fdcu,bp->b_flags, 193769acd21dSWarner Losh bp->b_data+fd->skip,fdblk)) { 193869acd21dSWarner Losh fd->tohandle = timeout(fd_iotimeout, 193969acd21dSWarner Losh (caddr_t)fdcu, hz); 194069acd21dSWarner Losh return(0); /* will return later */ 194169acd21dSWarner Losh }; 194269acd21dSWarner Losh 194369acd21dSWarner Losh /* 194469acd21dSWarner Losh * write (or format) operation will fall through and 194569acd21dSWarner Losh * await completion interrupt 194669acd21dSWarner Losh */ 194769acd21dSWarner Losh #endif 19485b81b6b3SRodney W. Grimes fdc->state = IOCOMPLETE; 19496182fdbdSPeter Wemm fd->tohandle = timeout(fd_iotimeout, fdc, hz); 19505b81b6b3SRodney W. Grimes return (0); /* will return later */ 195169acd21dSWarner Losh #ifdef FDC_YE 195269acd21dSWarner Losh case PIOREAD: 195369acd21dSWarner Losh /* 195469acd21dSWarner Losh * actually perform the PIO read. The IOCOMPLETE case 195569acd21dSWarner Losh * removes the timeout for us. 195669acd21dSWarner Losh */ 195769acd21dSWarner Losh (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); 195869acd21dSWarner Losh fdc->state = IOCOMPLETE; 195969acd21dSWarner Losh /* FALLTHROUGH */ 196069acd21dSWarner Losh #endif 19615b81b6b3SRodney W. Grimes case IOCOMPLETE: /* IO DONE, post-analyze */ 19626182fdbdSPeter Wemm untimeout(fd_iotimeout, fdc, fd->tohandle); 1963dc5df763SJoerg Wunsch 19646182fdbdSPeter Wemm if (fd_read_status(fdc, fd->fdsu)) { 19655c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 19665c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 19675c1a1eaeSBruce Evans fdc->dmachan); 1968dc5df763SJoerg Wunsch if (fdc->retry < 6) 1969dc5df763SJoerg Wunsch fdc->retry = 6; /* force a reset */ 19706182fdbdSPeter Wemm return (retrier(fdc)); 19715b81b6b3SRodney W. Grimes } 1972dc5df763SJoerg Wunsch 19733a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; 1974dc5df763SJoerg Wunsch 19753a2f7427SDavid Greenman /* FALLTHROUGH */ 1976dc5df763SJoerg Wunsch 19773a2f7427SDavid Greenman case IOTIMEDOUT: 197869acd21dSWarner Losh #ifdef FDC_YE 197969acd21dSWarner Losh if (!(fdc->flags & FDC_PCMCIA)) 198069acd21dSWarner Losh #endif 1981ab3f7469SPoul-Henning Kamp isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 19823a2f7427SDavid Greenman format ? bp->b_bcount : fdblk, fdc->dmachan); 19836182fdbdSPeter Wemm if (fdc->status[0] & NE7_ST0_IC) { 19843a2f7427SDavid Greenman if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 19853a2f7427SDavid Greenman && fdc->status[1] & NE7_ST1_OR) { 1986b39c878eSAndrey A. Chernov /* 19873a2f7427SDavid Greenman * DMA overrun. Someone hogged the bus 19883a2f7427SDavid Greenman * and didn't release it in time for the 19893a2f7427SDavid Greenman * next FDC transfer. 19903a2f7427SDavid Greenman * Just restart it, don't increment retry 19913a2f7427SDavid Greenman * count. (vak) 1992b39c878eSAndrey A. Chernov */ 1993b39c878eSAndrey A. Chernov fdc->state = SEEKCOMPLETE; 1994b39c878eSAndrey A. Chernov return (1); 1995b39c878eSAndrey A. Chernov } 19963a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV 19973a2f7427SDavid Greenman && fdc->retry < 6) 19983a2f7427SDavid Greenman fdc->retry = 6; /* force a reset */ 19993a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 20003a2f7427SDavid Greenman && fdc->status[2] & NE7_ST2_WC 20013a2f7427SDavid Greenman && fdc->retry < 3) 20023a2f7427SDavid Greenman fdc->retry = 3; /* force recalibrate */ 20036182fdbdSPeter Wemm return (retrier(fdc)); 20045b81b6b3SRodney W. Grimes } 20055b81b6b3SRodney W. Grimes /* All OK */ 20063a2f7427SDavid Greenman fd->skip += fdblk; 20076182fdbdSPeter Wemm if (!format && fd->skip < bp->b_bcount - bp->b_resid) { 20085b81b6b3SRodney W. Grimes /* set up next transfer */ 20095b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 20106182fdbdSPeter Wemm } else { 20115b81b6b3SRodney W. Grimes /* ALL DONE */ 20125b81b6b3SRodney W. Grimes fd->skip = 0; 2013e93e63cbSBruce Evans fdc->bp = NULL; 20142186cd9eSPoul-Henning Kamp devstat_end_transaction_buf(&fd->device_stats, bp); 20155b81b6b3SRodney W. Grimes biodone(bp); 20165b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 20175b81b6b3SRodney W. Grimes fdc->fdu = -1; 20185b81b6b3SRodney W. Grimes fdc->state = FINDWORK; 20195b81b6b3SRodney W. Grimes } 20205b81b6b3SRodney W. Grimes return (1); 20215b81b6b3SRodney W. Grimes case RESETCTLR: 20223a2f7427SDavid Greenman fdc_reset(fdc); 20235b81b6b3SRodney W. Grimes fdc->retry++; 20245c1a1eaeSBruce Evans fdc->state = RESETCOMPLETE; 20255c1a1eaeSBruce Evans return (0); 20265c1a1eaeSBruce Evans case RESETCOMPLETE: 20275c1a1eaeSBruce Evans /* 20285c1a1eaeSBruce Evans * Discard all the results from the reset so that they 20295c1a1eaeSBruce Evans * can't cause an unexpected interrupt later. 20305c1a1eaeSBruce Evans */ 20310e317d05SJoerg Wunsch for (i = 0; i < 4; i++) 20320e317d05SJoerg Wunsch (void)fd_sense_int(fdc, &st0, &cyl); 20335c1a1eaeSBruce Evans fdc->state = STARTRECAL; 20345c1a1eaeSBruce Evans /* Fall through. */ 20355c1a1eaeSBruce Evans case STARTRECAL: 20366182fdbdSPeter Wemm if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { 2037dc8603e3SJoerg Wunsch /* arrgl */ 2038dc8603e3SJoerg Wunsch fdc->retry = 6; 20396182fdbdSPeter Wemm return (retrier(fdc)); 2040dc8603e3SJoerg Wunsch } 20415b81b6b3SRodney W. Grimes fdc->state = RECALWAIT; 20425b81b6b3SRodney W. Grimes return (0); /* will return later */ 20435b81b6b3SRodney W. Grimes case RECALWAIT: 20445b81b6b3SRodney W. Grimes /* allow heads to settle */ 20456182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 8); 20465b81b6b3SRodney W. Grimes fdc->state = RECALCOMPLETE; 20475b81b6b3SRodney W. Grimes return (0); /* will return later */ 20485b81b6b3SRodney W. Grimes case RECALCOMPLETE: 20493a2f7427SDavid Greenman do { 20503a2f7427SDavid Greenman /* 2051dc5df763SJoerg Wunsch * See SEEKCOMPLETE for a comment on this: 20523a2f7427SDavid Greenman */ 2053dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) 2054dc5df763SJoerg Wunsch return 0; 2055dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 2056dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 2057dc5df763SJoerg Wunsch return 0; /* hope for a real intr */ 20583a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 20593a2f7427SDavid Greenman if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) 20605b81b6b3SRodney W. Grimes { 2061dc8603e3SJoerg Wunsch if(fdc->retry > 3) 2062dc8603e3SJoerg Wunsch /* 2063dc8603e3SJoerg Wunsch * a recalibrate from beyond cylinder 77 2064dc8603e3SJoerg Wunsch * will "fail" due to the FDC limitations; 2065dc8603e3SJoerg Wunsch * since people used to complain much about 2066dc8603e3SJoerg Wunsch * the failure message, try not logging 2067dc8603e3SJoerg Wunsch * this one if it seems to be the first 2068dc8603e3SJoerg Wunsch * time in a line 2069dc8603e3SJoerg Wunsch */ 2070dc8603e3SJoerg Wunsch printf("fd%d: recal failed ST0 %b cyl %d\n", 2071dc8603e3SJoerg Wunsch fdu, st0, NE7_ST0BITS, cyl); 20723a2f7427SDavid Greenman if(fdc->retry < 3) fdc->retry = 3; 20736182fdbdSPeter Wemm return (retrier(fdc)); 20745b81b6b3SRodney W. Grimes } 20755b81b6b3SRodney W. Grimes fd->track = 0; 20765b81b6b3SRodney W. Grimes /* Seek (probably) necessary */ 20775b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 20785b81b6b3SRodney W. Grimes return (1); /* will return immediatly */ 20795b81b6b3SRodney W. Grimes case MOTORWAIT: 20805b81b6b3SRodney W. Grimes if(fd->flags & FD_MOTOR_WAIT) 20815b81b6b3SRodney W. Grimes { 20825b81b6b3SRodney W. Grimes return (0); /* time's not up yet */ 20835b81b6b3SRodney W. Grimes } 20845c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 20855c1a1eaeSBruce Evans fdc->state = RESETCTLR; 20865c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 20875c1a1eaeSBruce Evans } else { 20885e235068SJordan K. Hubbard /* 20895c1a1eaeSBruce Evans * If all motors were off, then the controller was 20905c1a1eaeSBruce Evans * reset, so it has lost track of the current 20915c1a1eaeSBruce Evans * cylinder. Recalibrate to handle this case. 2092f86e4077SBruce Evans * But first, discard the results of the reset. 20935e235068SJordan K. Hubbard */ 2094f86e4077SBruce Evans fdc->state = RESETCOMPLETE; 20955c1a1eaeSBruce Evans } 20965b81b6b3SRodney W. Grimes return (1); /* will return immediatly */ 20975b81b6b3SRodney W. Grimes default: 20986182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 20996182fdbdSPeter Wemm printf("unexpected FD int->"); 2100dc5df763SJoerg Wunsch if (fd_read_status(fdc, fd->fdsu) == 0) 2101a838d83dSBruce Evans printf("FDC status :%x %x %x %x %x %x %x ", 21025b81b6b3SRodney W. Grimes fdc->status[0], 21035b81b6b3SRodney W. Grimes fdc->status[1], 21045b81b6b3SRodney W. Grimes fdc->status[2], 21055b81b6b3SRodney W. Grimes fdc->status[3], 21065b81b6b3SRodney W. Grimes fdc->status[4], 21075b81b6b3SRodney W. Grimes fdc->status[5], 21085b81b6b3SRodney W. Grimes fdc->status[6] ); 21093a2f7427SDavid Greenman else 2110dac0f2dbSJoerg Wunsch printf("No status available "); 2111dac0f2dbSJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) != 0) 2112dac0f2dbSJoerg Wunsch { 2113dac0f2dbSJoerg Wunsch printf("[controller is dead now]\n"); 21145b81b6b3SRodney W. Grimes return (0); 21155b81b6b3SRodney W. Grimes } 2116dac0f2dbSJoerg Wunsch printf("ST0 = %x, PCN = %x\n", st0, cyl); 2117dac0f2dbSJoerg Wunsch return (0); 2118dac0f2dbSJoerg Wunsch } 2119dac0f2dbSJoerg Wunsch /*XXX confusing: some branches return immediately, others end up here*/ 21205b81b6b3SRodney W. Grimes return (1); /* Come back immediatly to new state */ 21215b81b6b3SRodney W. Grimes } 21225b81b6b3SRodney W. Grimes 2123aaf08d94SGarrett Wollman static int 21246182fdbdSPeter Wemm retrier(struct fdc_data *fdc) 21255b81b6b3SRodney W. Grimes { 212617542807SPoul-Henning Kamp register struct buf *bp; 21276182fdbdSPeter Wemm struct fd_data *fd; 21286182fdbdSPeter Wemm int fdu; 21295b81b6b3SRodney W. Grimes 2130e93e63cbSBruce Evans bp = fdc->bp; 21315b81b6b3SRodney W. Grimes 21326182fdbdSPeter Wemm /* XXX shouldn't this be cached somewhere? */ 21336182fdbdSPeter Wemm fdu = FDUNIT(minor(bp->b_dev)); 21346182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 21356182fdbdSPeter Wemm if (fd->options & FDOPT_NORETRY) 21363a2f7427SDavid Greenman goto fail; 21376182fdbdSPeter Wemm 21386182fdbdSPeter Wemm switch (fdc->retry) { 21395b81b6b3SRodney W. Grimes case 0: case 1: case 2: 21405b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 21415b81b6b3SRodney W. Grimes break; 21425b81b6b3SRodney W. Grimes case 3: case 4: case 5: 21435b81b6b3SRodney W. Grimes fdc->state = STARTRECAL; 21445b81b6b3SRodney W. Grimes break; 21455b81b6b3SRodney W. Grimes case 6: 21465b81b6b3SRodney W. Grimes fdc->state = RESETCTLR; 21475b81b6b3SRodney W. Grimes break; 21485b81b6b3SRodney W. Grimes case 7: 21495b81b6b3SRodney W. Grimes break; 21505b81b6b3SRodney W. Grimes default: 21513a2f7427SDavid Greenman fail: 21525b81b6b3SRodney W. Grimes { 21537ca0641bSAndrey A. Chernov dev_t sav_b_dev = bp->b_dev; 21547ca0641bSAndrey A. Chernov /* Trick diskerr */ 21553a2f7427SDavid Greenman bp->b_dev = makedev(major(bp->b_dev), 21563a2f7427SDavid Greenman (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); 2157887ba12fSBruce Evans diskerr(bp, "hard error", LOG_PRINTF, 21583a2f7427SDavid Greenman fdc->fd->skip / DEV_BSIZE, 21593a2f7427SDavid Greenman (struct disklabel *)NULL); 21607ca0641bSAndrey A. Chernov bp->b_dev = sav_b_dev; 2161dc5df763SJoerg Wunsch if (fdc->flags & FDC_STAT_VALID) 2162dc5df763SJoerg Wunsch { 2163dc5df763SJoerg Wunsch printf( 2164a838d83dSBruce Evans " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", 2165dc5df763SJoerg Wunsch fdc->status[0], NE7_ST0BITS, 2166dc5df763SJoerg Wunsch fdc->status[1], NE7_ST1BITS, 2167dc5df763SJoerg Wunsch fdc->status[2], NE7_ST2BITS, 2168dc5df763SJoerg Wunsch fdc->status[3], fdc->status[4], 2169dc5df763SJoerg Wunsch fdc->status[5]); 2170dc5df763SJoerg Wunsch } 2171dc5df763SJoerg Wunsch else 2172dc5df763SJoerg Wunsch printf(" (No status)\n"); 21735b81b6b3SRodney W. Grimes } 21745b81b6b3SRodney W. Grimes bp->b_flags |= B_ERROR; 21755b81b6b3SRodney W. Grimes bp->b_error = EIO; 2176bb6382faSJoerg Wunsch bp->b_resid += bp->b_bcount - fdc->fd->skip; 2177e93e63cbSBruce Evans fdc->bp = NULL; 21785b81b6b3SRodney W. Grimes fdc->fd->skip = 0; 217911a0be87SPoul-Henning Kamp devstat_end_transaction_buf(&fdc->fd->device_stats, bp); 21805b81b6b3SRodney W. Grimes biodone(bp); 218192ed385aSRodney W. Grimes fdc->state = FINDWORK; 21825c1a1eaeSBruce Evans fdc->flags |= FDC_NEEDS_RESET; 21835b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 21845b81b6b3SRodney W. Grimes fdc->fdu = -1; 218592ed385aSRodney W. Grimes return (1); 21865b81b6b3SRodney W. Grimes } 21875b81b6b3SRodney W. Grimes fdc->retry++; 21885b81b6b3SRodney W. Grimes return (1); 21895b81b6b3SRodney W. Grimes } 21905b81b6b3SRodney W. Grimes 2191b39c878eSAndrey A. Chernov static int 2192b39c878eSAndrey A. Chernov fdformat(dev, finfo, p) 2193b39c878eSAndrey A. Chernov dev_t dev; 2194b39c878eSAndrey A. Chernov struct fd_formb *finfo; 2195b39c878eSAndrey A. Chernov struct proc *p; 2196b39c878eSAndrey A. Chernov { 2197b39c878eSAndrey A. Chernov fdu_t fdu; 2198b39c878eSAndrey A. Chernov fd_p fd; 2199b39c878eSAndrey A. Chernov 2200b39c878eSAndrey A. Chernov struct buf *bp; 2201b39c878eSAndrey A. Chernov int rv = 0, s; 22023a2f7427SDavid Greenman size_t fdblk; 2203b39c878eSAndrey A. Chernov 2204b39c878eSAndrey A. Chernov fdu = FDUNIT(minor(dev)); 22056182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 22063a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2207b39c878eSAndrey A. Chernov 2208b39c878eSAndrey A. Chernov /* set up a buffer header for fdstrategy() */ 2209b39c878eSAndrey A. Chernov bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); 2210b39c878eSAndrey A. Chernov if(bp == 0) 2211b39c878eSAndrey A. Chernov return ENOBUFS; 221282f5379bSJoerg Wunsch /* 221382f5379bSJoerg Wunsch * keep the process from being swapped 221482f5379bSJoerg Wunsch */ 22152ada239cSPeter Wemm PHOLD(p); 2216b39c878eSAndrey A. Chernov bzero((void *)bp, sizeof(struct buf)); 221767812eacSKirk McKusick BUF_LOCKINIT(bp); 221867812eacSKirk McKusick BUF_LOCK(bp, LK_EXCLUSIVE); 221967812eacSKirk McKusick bp->b_flags = B_PHYS | B_FORMAT; 2220b39c878eSAndrey A. Chernov 2221b39c878eSAndrey A. Chernov /* 2222b39c878eSAndrey A. Chernov * calculate a fake blkno, so fdstrategy() would initiate a 2223b39c878eSAndrey A. Chernov * seek to the requested cylinder 2224b39c878eSAndrey A. Chernov */ 2225b39c878eSAndrey A. Chernov bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) 22263a2f7427SDavid Greenman + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; 2227b39c878eSAndrey A. Chernov 2228b39c878eSAndrey A. Chernov bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; 2229ab3f7469SPoul-Henning Kamp bp->b_data = (caddr_t)finfo; 2230b39c878eSAndrey A. Chernov 2231b39c878eSAndrey A. Chernov /* now do the format */ 22323e425b96SJulian Elischer bp->b_dev = dev; 223349ff4debSPoul-Henning Kamp BUF_STRATEGY(bp, 0); 2234b39c878eSAndrey A. Chernov 2235b39c878eSAndrey A. Chernov /* ...and wait for it to complete */ 2236b39c878eSAndrey A. Chernov s = splbio(); 22376182fdbdSPeter Wemm while(!(bp->b_flags & B_DONE)) { 22385e235068SJordan K. Hubbard rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); 2239b39c878eSAndrey A. Chernov if (rv == EWOULDBLOCK) 2240b39c878eSAndrey A. Chernov break; 2241b39c878eSAndrey A. Chernov } 2242b39c878eSAndrey A. Chernov splx(s); 2243b39c878eSAndrey A. Chernov 224482f5379bSJoerg Wunsch if (rv == EWOULDBLOCK) { 2245b39c878eSAndrey A. Chernov /* timed out */ 2246b39c878eSAndrey A. Chernov rv = EIO; 224782f5379bSJoerg Wunsch biodone(bp); 224882f5379bSJoerg Wunsch } 22493a2f7427SDavid Greenman if (bp->b_flags & B_ERROR) 22503a2f7427SDavid Greenman rv = bp->b_error; 225182f5379bSJoerg Wunsch /* 225282f5379bSJoerg Wunsch * allow the process to be swapped 225382f5379bSJoerg Wunsch */ 22542ada239cSPeter Wemm PRELE(p); 225567812eacSKirk McKusick BUF_UNLOCK(bp); 225667812eacSKirk McKusick BUF_LOCKFREE(bp); 2257b39c878eSAndrey A. Chernov free(bp, M_TEMP); 2258b39c878eSAndrey A. Chernov return rv; 2259b39c878eSAndrey A. Chernov } 2260b39c878eSAndrey A. Chernov 2261f5f7ba03SJordan K. Hubbard /* 2262671e2ceeSBruce Evans * TODO: don't allocate buffer on stack. 2263f5f7ba03SJordan K. Hubbard */ 22645b81b6b3SRodney W. Grimes 22653e425b96SJulian Elischer static int 2266b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p) 2267f5f7ba03SJordan K. Hubbard dev_t dev; 2268ecbb00a2SDoug Rabson u_long cmd; 2269f5f7ba03SJordan K. Hubbard caddr_t addr; 2270f5f7ba03SJordan K. Hubbard int flag; 2271b39c878eSAndrey A. Chernov struct proc *p; 2272f5f7ba03SJordan K. Hubbard { 22733a2f7427SDavid Greenman fdu_t fdu = FDUNIT(minor(dev)); 22746182fdbdSPeter Wemm fd_p fd = devclass_get_softc(fd_devclass, fdu); 22753a2f7427SDavid Greenman size_t fdblk; 22763a2f7427SDavid Greenman 2277f5f7ba03SJordan K. Hubbard struct fd_type *fdt; 2278f5f7ba03SJordan K. Hubbard struct disklabel *dl; 2279f5f7ba03SJordan K. Hubbard char buffer[DEV_BSIZE]; 22803a2f7427SDavid Greenman int error = 0; 2281f5f7ba03SJordan K. Hubbard 22823a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2283f5f7ba03SJordan K. Hubbard 22846182fdbdSPeter Wemm switch (cmd) { 2285f5f7ba03SJordan K. Hubbard case DIOCGDINFO: 2286f5f7ba03SJordan K. Hubbard bzero(buffer, sizeof (buffer)); 2287f5f7ba03SJordan K. Hubbard dl = (struct disklabel *)buffer; 22883a2f7427SDavid Greenman dl->d_secsize = fdblk; 22896182fdbdSPeter Wemm fdt = fd->ft; 2290f5f7ba03SJordan K. Hubbard dl->d_secpercyl = fdt->size / fdt->tracks; 2291f5f7ba03SJordan K. Hubbard dl->d_type = DTYPE_FLOPPY; 2292f5f7ba03SJordan K. Hubbard 229349ff4debSPoul-Henning Kamp if (readdisklabel(dkmodpart(dev, RAW_PART), dl) 2294191e1a59SBruce Evans == NULL) 2295f5f7ba03SJordan K. Hubbard error = 0; 2296f5f7ba03SJordan K. Hubbard else 2297f5f7ba03SJordan K. Hubbard error = EINVAL; 2298f5f7ba03SJordan K. Hubbard 2299f5f7ba03SJordan K. Hubbard *(struct disklabel *)addr = *dl; 2300f5f7ba03SJordan K. Hubbard break; 2301f5f7ba03SJordan K. Hubbard 2302f5f7ba03SJordan K. Hubbard case DIOCSDINFO: 2303f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2304f5f7ba03SJordan K. Hubbard error = EBADF; 2305f5f7ba03SJordan K. Hubbard break; 2306f5f7ba03SJordan K. Hubbard 2307f5f7ba03SJordan K. Hubbard case DIOCWLABEL: 2308f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2309f5f7ba03SJordan K. Hubbard error = EBADF; 2310f5f7ba03SJordan K. Hubbard break; 2311f5f7ba03SJordan K. Hubbard 2312f5f7ba03SJordan K. Hubbard case DIOCWDINFO: 23136182fdbdSPeter Wemm if ((flag & FWRITE) == 0) { 2314f5f7ba03SJordan K. Hubbard error = EBADF; 2315f5f7ba03SJordan K. Hubbard break; 2316f5f7ba03SJordan K. Hubbard } 2317f5f7ba03SJordan K. Hubbard 2318f5f7ba03SJordan K. Hubbard dl = (struct disklabel *)addr; 2319f5f7ba03SJordan K. Hubbard 2320191e1a59SBruce Evans if ((error = setdisklabel((struct disklabel *)buffer, dl, 2321191e1a59SBruce Evans (u_long)0)) != 0) 2322f5f7ba03SJordan K. Hubbard break; 2323f5f7ba03SJordan K. Hubbard 232449ff4debSPoul-Henning Kamp error = writedisklabel(dev, (struct disklabel *)buffer); 2325b39c878eSAndrey A. Chernov break; 2326b39c878eSAndrey A. Chernov case FD_FORM: 2327b39c878eSAndrey A. Chernov if ((flag & FWRITE) == 0) 2328b39c878eSAndrey A. Chernov error = EBADF; /* must be opened for writing */ 2329b39c878eSAndrey A. Chernov else if (((struct fd_formb *)addr)->format_version != 2330b39c878eSAndrey A. Chernov FD_FORMAT_VERSION) 2331b39c878eSAndrey A. Chernov error = EINVAL; /* wrong version of formatting prog */ 2332b39c878eSAndrey A. Chernov else 2333b39c878eSAndrey A. Chernov error = fdformat(dev, (struct fd_formb *)addr, p); 2334b39c878eSAndrey A. Chernov break; 2335b39c878eSAndrey A. Chernov 2336b39c878eSAndrey A. Chernov case FD_GTYPE: /* get drive type */ 23373e425b96SJulian Elischer *(struct fd_type *)addr = *fd->ft; 2338f5f7ba03SJordan K. Hubbard break; 2339f5f7ba03SJordan K. Hubbard 23403a2f7427SDavid Greenman case FD_STYPE: /* set drive type */ 23413a2f7427SDavid Greenman /* this is considered harmful; only allow for superuser */ 2342f711d546SPoul-Henning Kamp if (suser(p) != 0) 23433a2f7427SDavid Greenman return EPERM; 23443e425b96SJulian Elischer *fd->ft = *(struct fd_type *)addr; 23453a2f7427SDavid Greenman break; 23463a2f7427SDavid Greenman 23473a2f7427SDavid Greenman case FD_GOPTS: /* get drive options */ 23483e425b96SJulian Elischer *(int *)addr = fd->options; 23493a2f7427SDavid Greenman break; 23503a2f7427SDavid Greenman 23513a2f7427SDavid Greenman case FD_SOPTS: /* set drive options */ 23523e425b96SJulian Elischer fd->options = *(int *)addr; 23533a2f7427SDavid Greenman break; 23543a2f7427SDavid Greenman 2355f5f7ba03SJordan K. Hubbard default: 23563a2f7427SDavid Greenman error = ENOTTY; 2357f5f7ba03SJordan K. Hubbard break; 2358f5f7ba03SJordan K. Hubbard } 2359f5f7ba03SJordan K. Hubbard return (error); 2360f5f7ba03SJordan K. Hubbard } 2361f5f7ba03SJordan K. Hubbard 23623a2f7427SDavid Greenman /* 23633a2f7427SDavid Greenman * Hello emacs, these are the 23643a2f7427SDavid Greenman * Local Variables: 23653a2f7427SDavid Greenman * c-indent-level: 8 23663a2f7427SDavid Greenman * c-continued-statement-offset: 8 23673a2f7427SDavid Greenman * c-continued-brace-offset: 0 23683a2f7427SDavid Greenman * c-brace-offset: -8 23693a2f7427SDavid Greenman * c-brace-imaginary-offset: 0 23703a2f7427SDavid Greenman * c-argdecl-indent: 8 23713a2f7427SDavid Greenman * c-label-offset: -8 23723a2f7427SDavid Greenman * c++-hanging-braces: 1 23733a2f7427SDavid Greenman * c++-access-specifier-offset: -8 23743a2f7427SDavid Greenman * c++-empty-arglist-indent: 8 23753a2f7427SDavid Greenman * c++-friend-offset: 0 23763a2f7427SDavid Greenman * End: 23773a2f7427SDavid Greenman */ 2378