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 545b81b6b3SRodney W. Grimes #include "fd.h" 55d2fb4892SJoerg Wunsch #include "opt_fdc.h" 565b81b6b3SRodney W. Grimes 57b99f0a4aSAndrew Moore #if NFDC > 0 58b99f0a4aSAndrew Moore 59b99f0a4aSAndrew Moore #include <sys/param.h> 60b99f0a4aSAndrew Moore #include <sys/systm.h> 61b99f0a4aSAndrew Moore #include <sys/kernel.h> 62b99f0a4aSAndrew Moore #include <sys/buf.h> 636182fdbdSPeter Wemm #include <sys/bus.h> 646182fdbdSPeter Wemm #include <sys/conf.h> 656182fdbdSPeter Wemm #include <sys/disklabel.h> 66b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h> 676182fdbdSPeter Wemm #include <sys/fcntl.h> 68b99f0a4aSAndrew Moore #include <sys/malloc.h> 696182fdbdSPeter Wemm #include <sys/module.h> 703a2f7427SDavid Greenman #include <sys/proc.h> 71b99f0a4aSAndrew Moore #include <sys/syslog.h> 726182fdbdSPeter Wemm 736182fdbdSPeter Wemm #include <sys/bus.h> 746182fdbdSPeter Wemm #include <machine/bus.h> 756182fdbdSPeter Wemm #include <sys/rman.h> 766182fdbdSPeter Wemm 776182fdbdSPeter Wemm #include <machine/clock.h> 786182fdbdSPeter Wemm #include <machine/ioctl_fd.h> 796182fdbdSPeter Wemm #include <machine/resource.h> 80dc5df763SJoerg Wunsch #include <machine/stdarg.h> 816182fdbdSPeter Wemm 826182fdbdSPeter Wemm #include <isa/isavar.h> 83a97c75b7SDoug Rabson #include <isa/isareg.h> 84a97c75b7SDoug Rabson #include <isa/fdreg.h> 85a97c75b7SDoug Rabson #include <isa/fdc.h> 86a97c75b7SDoug Rabson #include <isa/rtc.h> 876182fdbdSPeter Wemm 88edfdec19SPeter Wemm #ifdef FDC_YE 89edfdec19SPeter Wemm #undef FDC_YE 90edfdec19SPeter Wemm #warning "fix FDC_YE! - newbus casualty" 91edfdec19SPeter Wemm #endif 92edfdec19SPeter Wemm 93b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */ 94b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX 955b81b6b3SRodney W. Grimes 960722d6abSJoerg Wunsch /* configuration flags */ 970722d6abSJoerg Wunsch #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ 9869acd21dSWarner Losh #ifdef FDC_YE 9969acd21dSWarner Losh #define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's 10069acd21dSWarner Losh a PCMCIA device */ 10169acd21dSWarner Losh #endif 1020722d6abSJoerg Wunsch 1030722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */ 1040722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED 0x1000 1050722d6abSJoerg Wunsch 106dc5df763SJoerg Wunsch /* error returns for fd_cmd() */ 107dc5df763SJoerg Wunsch #define FD_FAILED -1 108dc5df763SJoerg Wunsch #define FD_NOT_VALID -2 109dc5df763SJoerg Wunsch #define FDC_ERRMAX 100 /* do not log more */ 110dc5df763SJoerg Wunsch 111b39c878eSAndrey A. Chernov #define NUMTYPES 14 112b39c878eSAndrey A. Chernov #define NUMDENS (NUMTYPES - 6) 1137ca0641bSAndrey A. Chernov 1143a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */ 115b99f0a4aSAndrew Moore #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ 116b99f0a4aSAndrew Moore #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ 117b99f0a4aSAndrew Moore #define FD_1720 1 118b99f0a4aSAndrew Moore #define FD_1480 2 119b99f0a4aSAndrew Moore #define FD_1440 3 120b99f0a4aSAndrew Moore #define FD_1200 4 121b99f0a4aSAndrew Moore #define FD_820 5 122b99f0a4aSAndrew Moore #define FD_800 6 123b99f0a4aSAndrew Moore #define FD_720 7 124b99f0a4aSAndrew Moore #define FD_360 8 125ed2fa05eSAndrey A. Chernov 126b99f0a4aSAndrew Moore #define FD_1480in5_25 9 127b99f0a4aSAndrew Moore #define FD_1440in5_25 10 128b99f0a4aSAndrew Moore #define FD_820in5_25 11 129b99f0a4aSAndrew Moore #define FD_800in5_25 12 130b99f0a4aSAndrew Moore #define FD_720in5_25 13 131b99f0a4aSAndrew Moore #define FD_360in5_25 14 132b99f0a4aSAndrew Moore 1337ca0641bSAndrey A. Chernov 1346f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] = 1355b81b6b3SRodney W. Grimes { 136126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ 137126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ 138126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ 139126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ 140126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ 141126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ 142126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ 143b0568305SAndrey A. Chernov { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ 144ed2fa05eSAndrey A. Chernov 145126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ 146126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ 147126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ 148126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ 149126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ 150126518a1SAndrey A. Chernov { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ 1515b81b6b3SRodney W. Grimes }; 1525b81b6b3SRodney W. Grimes 153b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2 /* 2 floppies */ 154dc16046fSJoerg Wunsch 1555b81b6b3SRodney W. Grimes /***********************************************************************\ 1565b81b6b3SRodney W. Grimes * Per controller structure. * 1575b81b6b3SRodney W. Grimes \***********************************************************************/ 1586182fdbdSPeter Wemm static devclass_t fdc_devclass; 1595b81b6b3SRodney W. Grimes 1605b81b6b3SRodney W. Grimes /***********************************************************************\ 1615b81b6b3SRodney W. Grimes * Per drive structure. * 162b99f0a4aSAndrew Moore * N per controller (DRVS_PER_CTLR) * 1635b81b6b3SRodney W. Grimes \***********************************************************************/ 1646182fdbdSPeter Wemm struct fd_data { 165b99f0a4aSAndrew Moore struct fdc_data *fdc; /* pointer to controller structure */ 1665b81b6b3SRodney W. Grimes int fdsu; /* this units number on this controller */ 1673a2f7427SDavid Greenman int type; /* Drive type (FD_1440...) */ 1685b81b6b3SRodney W. Grimes struct fd_type *ft; /* pointer to the type descriptor */ 1695b81b6b3SRodney W. Grimes int flags; 1705b81b6b3SRodney W. Grimes #define FD_OPEN 0x01 /* it's open */ 1715b81b6b3SRodney W. Grimes #define FD_ACTIVE 0x02 /* it's active */ 1725b81b6b3SRodney W. Grimes #define FD_MOTOR 0x04 /* motor should be on */ 1735b81b6b3SRodney W. Grimes #define FD_MOTOR_WAIT 0x08 /* motor coming up */ 1745b81b6b3SRodney W. Grimes int skip; 1755b81b6b3SRodney W. Grimes int hddrv; 176dc5df763SJoerg Wunsch #define FD_NO_TRACK -2 1775b81b6b3SRodney W. Grimes int track; /* where we think the head is */ 1783a2f7427SDavid Greenman int options; /* user configurable options, see ioctl_fd.h */ 17902a19910SJustin T. Gibbs struct callout_handle toffhandle; 18002a19910SJustin T. Gibbs struct callout_handle tohandle; 181b2dfb1f9SJustin T. Gibbs struct devstat device_stats; 1826182fdbdSPeter Wemm device_t dev; 1836182fdbdSPeter Wemm fdu_t fdu; 1846182fdbdSPeter Wemm }; 1856182fdbdSPeter Wemm static devclass_t fd_devclass; 1865b81b6b3SRodney W. Grimes 1875b81b6b3SRodney W. Grimes /***********************************************************************\ 1885b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used: * 1895b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question * 1905b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller * 1915b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number * 1925b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number * 1935b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit) * 1945b81b6b3SRodney W. Grimes \***********************************************************************/ 195b99f0a4aSAndrew Moore 19669acd21dSWarner Losh #ifdef FDC_YE 19769acd21dSWarner Losh #include "card.h" 19869acd21dSWarner Losh static int yeattach(struct isa_device *); 19969acd21dSWarner Losh #endif 20069acd21dSWarner Losh 2013a2f7427SDavid Greenman /* needed for ft driver, thus exported */ 2026182fdbdSPeter Wemm int in_fdc(struct fdc_data *); 2036182fdbdSPeter Wemm int out_fdc(struct fdc_data *, int); 2043a2f7427SDavid Greenman 2053a2f7427SDavid Greenman /* internal functions */ 2066182fdbdSPeter Wemm static void fdc_add_device(device_t, const char *, int); 2076182fdbdSPeter Wemm static void fdc_intr(void *); 2086182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int); 2093a2f7427SDavid Greenman # define TURNON 1 2103a2f7427SDavid Greenman # define TURNOFF 0 2113a2f7427SDavid Greenman static timeout_t fd_turnoff; 2123a2f7427SDavid Greenman static timeout_t fd_motor_on; 2136182fdbdSPeter Wemm static void fd_turnon(struct fd_data *); 2143a2f7427SDavid Greenman static void fdc_reset(fdc_p); 2156182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *); 2166182fdbdSPeter Wemm static void fdstart(struct fdc_data *); 2175c1a1eaeSBruce Evans static timeout_t fd_iotimeout; 2183a2f7427SDavid Greenman static timeout_t fd_pseudointr; 2196182fdbdSPeter Wemm static int fdstate(struct fdc_data *); 2206182fdbdSPeter Wemm static int retrier(struct fdc_data *); 2213a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *); 2223a2f7427SDavid Greenman 223d66c374fSTor Egge static int enable_fifo(fdc_p fdc); 224d66c374fSTor Egge 225d66c374fSTor Egge static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ 226d66c374fSTor Egge 227aaf08d94SGarrett Wollman 2285b81b6b3SRodney W. Grimes #define DEVIDLE 0 2295b81b6b3SRodney W. Grimes #define FINDWORK 1 2305b81b6b3SRodney W. Grimes #define DOSEEK 2 2315b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 3 2325b81b6b3SRodney W. Grimes #define IOCOMPLETE 4 2335b81b6b3SRodney W. Grimes #define RECALCOMPLETE 5 2345b81b6b3SRodney W. Grimes #define STARTRECAL 6 2355b81b6b3SRodney W. Grimes #define RESETCTLR 7 2365b81b6b3SRodney W. Grimes #define SEEKWAIT 8 2375b81b6b3SRodney W. Grimes #define RECALWAIT 9 2385b81b6b3SRodney W. Grimes #define MOTORWAIT 10 2395b81b6b3SRodney W. Grimes #define IOTIMEDOUT 11 2405c1a1eaeSBruce Evans #define RESETCOMPLETE 12 24169acd21dSWarner Losh #ifdef FDC_YE 24269acd21dSWarner Losh #define PIOREAD 13 24369acd21dSWarner Losh #endif 2445b81b6b3SRodney W. Grimes 245d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 246cba2a7c6SBruce Evans static char const * const fdstates[] = 2475b81b6b3SRodney W. Grimes { 2485b81b6b3SRodney W. Grimes "DEVIDLE", 2495b81b6b3SRodney W. Grimes "FINDWORK", 2505b81b6b3SRodney W. Grimes "DOSEEK", 2515b81b6b3SRodney W. Grimes "SEEKCOMPLETE", 2525b81b6b3SRodney W. Grimes "IOCOMPLETE", 2535b81b6b3SRodney W. Grimes "RECALCOMPLETE", 2545b81b6b3SRodney W. Grimes "STARTRECAL", 2555b81b6b3SRodney W. Grimes "RESETCTLR", 2565b81b6b3SRodney W. Grimes "SEEKWAIT", 2575b81b6b3SRodney W. Grimes "RECALWAIT", 2585b81b6b3SRodney W. Grimes "MOTORWAIT", 2595c1a1eaeSBruce Evans "IOTIMEDOUT", 2605c1a1eaeSBruce Evans "RESETCOMPLETE", 26169acd21dSWarner Losh #ifdef FDC_YE 262ff9607b0SBruce Evans "PIOREAD", 26369acd21dSWarner Losh #endif 2645b81b6b3SRodney W. Grimes }; 2655b81b6b3SRodney W. Grimes 2663a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */ 267cba2a7c6SBruce Evans static int volatile fd_debug = 0; 2685b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg) 2695b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) 270d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */ 2715b81b6b3SRodney W. Grimes #define TRACE0(arg) 2725b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) 273d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 2745b81b6b3SRodney W. Grimes 27569acd21dSWarner Losh #ifdef FDC_YE 27669acd21dSWarner Losh #if NCARD > 0 27769acd21dSWarner Losh #include <sys/select.h> 278d17e4ee6SPeter Wemm #include <sys/module.h> 27969acd21dSWarner Losh #include <pccard/cardinfo.h> 28069acd21dSWarner Losh #include <pccard/driver.h> 28169acd21dSWarner Losh #include <pccard/slot.h> 28269acd21dSWarner Losh 28369acd21dSWarner Losh /* 28469acd21dSWarner Losh * PC-Card (PCMCIA) specific code. 28569acd21dSWarner Losh */ 28669acd21dSWarner Losh static int yeinit(struct pccard_devinfo *); /* init device */ 28769acd21dSWarner Losh static void yeunload(struct pccard_devinfo *); /* Disable driver */ 28869acd21dSWarner Losh static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ 28969acd21dSWarner Losh 290d17e4ee6SPeter Wemm PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); 29169acd21dSWarner Losh 29269acd21dSWarner Losh /* 29369acd21dSWarner Losh * this is the secret PIO data port (offset from base) 29469acd21dSWarner Losh */ 29569acd21dSWarner Losh #define FDC_YE_DATAPORT 6 29669acd21dSWarner Losh 29769acd21dSWarner Losh /* 29869acd21dSWarner Losh * Initialize the device - called from Slot manager. 29969acd21dSWarner Losh */ 30069acd21dSWarner Losh static int yeinit(struct pccard_devinfo *devi) 30169acd21dSWarner Losh { 30269acd21dSWarner Losh fdc_p fdc = &fdc_data[devi->isahd.id_unit]; 30369acd21dSWarner Losh 30469acd21dSWarner Losh /* validate unit number. */ 30569acd21dSWarner Losh if (devi->isahd.id_unit >= NFDC) 30669acd21dSWarner Losh return(ENODEV); 30769acd21dSWarner Losh fdc->baseport = devi->isahd.id_iobase; 30869acd21dSWarner Losh /* 30969acd21dSWarner Losh * reset controller 31069acd21dSWarner Losh */ 31169acd21dSWarner Losh outb(fdc->baseport+FDOUT, 0); 31269acd21dSWarner Losh DELAY(100); 31369acd21dSWarner Losh outb(fdc->baseport+FDOUT, FDO_FRST); 31469acd21dSWarner Losh 31569acd21dSWarner Losh /* 31669acd21dSWarner Losh * wire into system 31769acd21dSWarner Losh */ 31869acd21dSWarner Losh if (yeattach(&devi->isahd) == 0) 31969acd21dSWarner Losh return(ENXIO); 32069acd21dSWarner Losh 32169acd21dSWarner Losh return(0); 32269acd21dSWarner Losh } 32369acd21dSWarner Losh 32469acd21dSWarner Losh /* 32569acd21dSWarner Losh * yeunload - unload the driver and clear the table. 32669acd21dSWarner Losh * XXX TODO: 32769acd21dSWarner Losh * This is usually called when the card is ejected, but 32869acd21dSWarner Losh * can be caused by a modunload of a controller driver. 32969acd21dSWarner Losh * The idea is to reset the driver's view of the device 33069acd21dSWarner Losh * and ensure that any driver entry points such as 33169acd21dSWarner Losh * read and write do not hang. 33269acd21dSWarner Losh */ 33369acd21dSWarner Losh static void yeunload(struct pccard_devinfo *devi) 33469acd21dSWarner Losh { 33569acd21dSWarner Losh if (fd_data[devi->isahd.id_unit].type == NO_TYPE) 33669acd21dSWarner Losh return; 33769acd21dSWarner Losh 33869acd21dSWarner Losh /* 33969acd21dSWarner Losh * this prevents Fdopen() and fdstrategy() from attempting 34069acd21dSWarner Losh * to access unloaded controller 34169acd21dSWarner Losh */ 34269acd21dSWarner Losh fd_data[devi->isahd.id_unit].type = NO_TYPE; 34369acd21dSWarner Losh 34469acd21dSWarner Losh printf("fdc%d: unload\n", devi->isahd.id_unit); 34569acd21dSWarner Losh } 34669acd21dSWarner Losh 34769acd21dSWarner Losh /* 34869acd21dSWarner Losh * yeintr - Shared interrupt called from 34969acd21dSWarner Losh * front end of PC-Card handler. 35069acd21dSWarner Losh */ 35169acd21dSWarner Losh static int yeintr(struct pccard_devinfo *devi) 35269acd21dSWarner Losh { 35369acd21dSWarner Losh fdintr((fdcu_t)devi->isahd.id_unit); 35469acd21dSWarner Losh return(1); 35569acd21dSWarner Losh } 35669acd21dSWarner Losh #endif /* NCARD > 0 */ 35769acd21dSWarner Losh #endif /* FDC_YE */ 35869acd21dSWarner Losh 35987f6c662SJulian Elischer static d_open_t Fdopen; /* NOTE, not fdopen */ 36087f6c662SJulian Elischer static d_close_t fdclose; 36187f6c662SJulian Elischer static d_ioctl_t fdioctl; 36287f6c662SJulian Elischer static d_strategy_t fdstrategy; 36387f6c662SJulian Elischer 36487f6c662SJulian Elischer #define CDEV_MAJOR 9 36587f6c662SJulian Elischer #define BDEV_MAJOR 2 366f7ea2f55SJulian Elischer 3674e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = { 3684e2f199eSPoul-Henning Kamp /* open */ Fdopen, 3694e2f199eSPoul-Henning Kamp /* close */ fdclose, 3704e2f199eSPoul-Henning Kamp /* read */ physread, 3714e2f199eSPoul-Henning Kamp /* write */ physwrite, 3724e2f199eSPoul-Henning Kamp /* ioctl */ fdioctl, 3734e2f199eSPoul-Henning Kamp /* stop */ nostop, 3744e2f199eSPoul-Henning Kamp /* reset */ noreset, 3754e2f199eSPoul-Henning Kamp /* devtotty */ nodevtotty, 3764e2f199eSPoul-Henning Kamp /* poll */ nopoll, 3774e2f199eSPoul-Henning Kamp /* mmap */ nommap, 3784e2f199eSPoul-Henning Kamp /* strategy */ fdstrategy, 3794e2f199eSPoul-Henning Kamp /* name */ "fd", 3804e2f199eSPoul-Henning Kamp /* parms */ noparms, 3814e2f199eSPoul-Henning Kamp /* maj */ CDEV_MAJOR, 3824e2f199eSPoul-Henning Kamp /* dump */ nodump, 3834e2f199eSPoul-Henning Kamp /* psize */ nopsize, 3844e2f199eSPoul-Henning Kamp /* flags */ D_DISK, 3854e2f199eSPoul-Henning Kamp /* maxio */ 0, 3864e2f199eSPoul-Henning Kamp /* bmaj */ BDEV_MAJOR 3874e2f199eSPoul-Henning Kamp }; 3884e2f199eSPoul-Henning Kamp 389dc5df763SJoerg Wunsch static int 3906182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s) 391dc5df763SJoerg Wunsch { 3926182fdbdSPeter Wemm fdc->fdc_errs++; 39316b04b6aSJoerg Wunsch if (s) { 3946182fdbdSPeter Wemm if (fdc->fdc_errs < FDC_ERRMAX) { 3956182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 3966182fdbdSPeter Wemm printf("%s", s); 3976182fdbdSPeter Wemm } else if (fdc->fdc_errs == FDC_ERRMAX) { 3986182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 3996182fdbdSPeter Wemm printf("too many errors, not logging any more\n"); 4006182fdbdSPeter Wemm } 40116b04b6aSJoerg Wunsch } 402dc5df763SJoerg Wunsch 403dc5df763SJoerg Wunsch return FD_FAILED; 404dc5df763SJoerg Wunsch } 405dc5df763SJoerg Wunsch 406dc5df763SJoerg Wunsch /* 407dc5df763SJoerg Wunsch * fd_cmd: Send a command to the chip. Takes a varargs with this structure: 408dc5df763SJoerg Wunsch * Unit number, 409dc5df763SJoerg Wunsch * # of output bytes, output bytes as ints ..., 410dc5df763SJoerg Wunsch * # of input bytes, input bytes as ints ... 411dc5df763SJoerg Wunsch */ 4126f4e0bebSPoul-Henning Kamp static int 4136182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...) 414dc5df763SJoerg Wunsch { 415dc5df763SJoerg Wunsch u_char cmd; 416dc5df763SJoerg Wunsch int n_in; 417dc5df763SJoerg Wunsch int n; 418dc5df763SJoerg Wunsch va_list ap; 419dc5df763SJoerg Wunsch 420dc5df763SJoerg Wunsch va_start(ap, n_out); 421dc5df763SJoerg Wunsch cmd = (u_char)(va_arg(ap, int)); 422dc5df763SJoerg Wunsch va_end(ap); 423dc5df763SJoerg Wunsch va_start(ap, n_out); 424dc5df763SJoerg Wunsch for (n = 0; n < n_out; n++) 425dc5df763SJoerg Wunsch { 4266182fdbdSPeter Wemm if (out_fdc(fdc, va_arg(ap, int)) < 0) 427dc5df763SJoerg Wunsch { 428dc5df763SJoerg Wunsch char msg[50]; 4292127f260SArchie Cobbs snprintf(msg, sizeof(msg), 430dc5df763SJoerg Wunsch "cmd %x failed at out byte %d of %d\n", 431dc5df763SJoerg Wunsch cmd, n + 1, n_out); 4326182fdbdSPeter Wemm return fdc_err(fdc, msg); 433dc5df763SJoerg Wunsch } 434dc5df763SJoerg Wunsch } 435dc5df763SJoerg Wunsch n_in = va_arg(ap, int); 436dc5df763SJoerg Wunsch for (n = 0; n < n_in; n++) 437dc5df763SJoerg Wunsch { 438dc5df763SJoerg Wunsch int *ptr = va_arg(ap, int *); 4396182fdbdSPeter Wemm if (fd_in(fdc, ptr) < 0) 440dc5df763SJoerg Wunsch { 441dc5df763SJoerg Wunsch char msg[50]; 4422127f260SArchie Cobbs snprintf(msg, sizeof(msg), 443dc5df763SJoerg Wunsch "cmd %02x failed at in byte %d of %d\n", 444dc5df763SJoerg Wunsch cmd, n + 1, n_in); 4456182fdbdSPeter Wemm return fdc_err(fdc, msg); 446dc5df763SJoerg Wunsch } 447dc5df763SJoerg Wunsch } 448dc5df763SJoerg Wunsch 449dc5df763SJoerg Wunsch return 0; 450dc5df763SJoerg Wunsch } 451dc5df763SJoerg Wunsch 4526f4e0bebSPoul-Henning Kamp static int 453d66c374fSTor Egge enable_fifo(fdc_p fdc) 454d66c374fSTor Egge { 455d66c374fSTor Egge int i, j; 456d66c374fSTor Egge 457d66c374fSTor Egge if ((fdc->flags & FDC_HAS_FIFO) == 0) { 458d66c374fSTor Egge 459d66c374fSTor Egge /* 460d66c374fSTor Egge * XXX: 461d66c374fSTor Egge * Cannot use fd_cmd the normal way here, since 462d66c374fSTor Egge * this might be an invalid command. Thus we send the 463d66c374fSTor Egge * first byte, and check for an early turn of data directon. 464d66c374fSTor Egge */ 465d66c374fSTor Egge 4666182fdbdSPeter Wemm if (out_fdc(fdc, I8207X_CONFIGURE) < 0) 4676182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 468d66c374fSTor Egge 469d66c374fSTor Egge /* If command is invalid, return */ 470d66c374fSTor Egge j = 100000; 471d66c374fSTor Egge while ((i = inb(fdc->baseport + FDSTS) & (NE7_DIO | NE7_RQM)) 472d66c374fSTor Egge != NE7_RQM && j-- > 0) 473d66c374fSTor Egge if (i == (NE7_DIO | NE7_RQM)) { 474d66c374fSTor Egge fdc_reset(fdc); 475d66c374fSTor Egge return FD_FAILED; 476d66c374fSTor Egge } 477d66c374fSTor Egge if (j<0 || 4786182fdbdSPeter Wemm fd_cmd(fdc, 3, 479d66c374fSTor Egge 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { 480d66c374fSTor Egge fdc_reset(fdc); 4816182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 482d66c374fSTor Egge } 483d66c374fSTor Egge fdc->flags |= FDC_HAS_FIFO; 484d66c374fSTor Egge return 0; 485d66c374fSTor Egge } 4866182fdbdSPeter Wemm if (fd_cmd(fdc, 4, 487d66c374fSTor Egge I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) 4886182fdbdSPeter Wemm return fdc_err(fdc, "Re-enable FIFO failed\n"); 489d66c374fSTor Egge return 0; 490d66c374fSTor Egge } 491d66c374fSTor Egge 492d66c374fSTor Egge static int 493dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p) 494dc5df763SJoerg Wunsch { 495dc5df763SJoerg Wunsch int st3; 496dc5df763SJoerg Wunsch 4976182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) 498dc5df763SJoerg Wunsch { 4996182fdbdSPeter Wemm return fdc_err(fdc, "Sense Drive Status failed\n"); 500dc5df763SJoerg Wunsch } 501dc5df763SJoerg Wunsch if (st3p) 502dc5df763SJoerg Wunsch *st3p = st3; 503dc5df763SJoerg Wunsch 504dc5df763SJoerg Wunsch return 0; 505dc5df763SJoerg Wunsch } 506dc5df763SJoerg Wunsch 5076f4e0bebSPoul-Henning Kamp static int 508dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp) 509dc5df763SJoerg Wunsch { 5106182fdbdSPeter Wemm int cyl, st0, ret; 511dc5df763SJoerg Wunsch 5126182fdbdSPeter Wemm ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); 5136182fdbdSPeter Wemm if (ret) { 5146182fdbdSPeter Wemm (void)fdc_err(fdc, 515dc5df763SJoerg Wunsch "sense intr err reading stat reg 0\n"); 516dc5df763SJoerg Wunsch return ret; 517dc5df763SJoerg Wunsch } 518dc5df763SJoerg Wunsch 519dc5df763SJoerg Wunsch if (st0p) 520dc5df763SJoerg Wunsch *st0p = st0; 521dc5df763SJoerg Wunsch 5226182fdbdSPeter Wemm if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { 523dc5df763SJoerg Wunsch /* 524dc5df763SJoerg Wunsch * There doesn't seem to have been an interrupt. 525dc5df763SJoerg Wunsch */ 526dc5df763SJoerg Wunsch return FD_NOT_VALID; 527dc5df763SJoerg Wunsch } 528dc5df763SJoerg Wunsch 5296182fdbdSPeter Wemm if (fd_in(fdc, &cyl) < 0) { 5306182fdbdSPeter Wemm return fdc_err(fdc, "can't get cyl num\n"); 531dc5df763SJoerg Wunsch } 532dc5df763SJoerg Wunsch 533dc5df763SJoerg Wunsch if (cylp) 534dc5df763SJoerg Wunsch *cylp = cyl; 535dc5df763SJoerg Wunsch 536dc5df763SJoerg Wunsch return 0; 537dc5df763SJoerg Wunsch } 538dc5df763SJoerg Wunsch 539dc5df763SJoerg Wunsch 5406f4e0bebSPoul-Henning Kamp static int 541dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu) 542dc5df763SJoerg Wunsch { 543dc5df763SJoerg Wunsch int i, ret; 544b5e8ce9fSBruce Evans 5456182fdbdSPeter Wemm for (i = 0; i < 7; i++) { 546b5e8ce9fSBruce Evans /* 547b5e8ce9fSBruce Evans * XXX types are poorly chosen. Only bytes can by read 548a838d83dSBruce Evans * from the hardware, but fdc->status[] wants u_ints and 549b5e8ce9fSBruce Evans * fd_in() gives ints. 550b5e8ce9fSBruce Evans */ 551b5e8ce9fSBruce Evans int status; 552b5e8ce9fSBruce Evans 5536182fdbdSPeter Wemm ret = fd_in(fdc, &status); 554b5e8ce9fSBruce Evans fdc->status[i] = status; 555b5e8ce9fSBruce Evans if (ret != 0) 556dc5df763SJoerg Wunsch break; 557dc5df763SJoerg Wunsch } 558dc5df763SJoerg Wunsch 559dc5df763SJoerg Wunsch if (ret == 0) 560dc5df763SJoerg Wunsch fdc->flags |= FDC_STAT_VALID; 561dc5df763SJoerg Wunsch else 562dc5df763SJoerg Wunsch fdc->flags &= ~FDC_STAT_VALID; 563dc5df763SJoerg Wunsch 564dc5df763SJoerg Wunsch return ret; 565dc5df763SJoerg Wunsch } 566dc5df763SJoerg Wunsch 5675b81b6b3SRodney W. Grimes /****************************************************************************/ 5685b81b6b3SRodney W. Grimes /* autoconfiguration stuff */ 5695b81b6b3SRodney W. Grimes /****************************************************************************/ 570dc5df763SJoerg Wunsch 57116e68fc6SPeter Wemm /* 57216e68fc6SPeter Wemm * fdc controller section. 57316e68fc6SPeter Wemm */ 5743a2f7427SDavid Greenman static int 5756182fdbdSPeter Wemm fdc_probe(device_t dev) 5765b81b6b3SRodney W. Grimes { 5776182fdbdSPeter Wemm int error, i, ic_type; 5786182fdbdSPeter Wemm struct fdc_data *fdc; 5796182fdbdSPeter Wemm char myname[8]; /* better be long enough */ 5805b81b6b3SRodney W. Grimes 581a97c75b7SDoug Rabson /* No pnp support */ 582a97c75b7SDoug Rabson if (isa_get_vendorid(dev)) 583a97c75b7SDoug Rabson return (ENXIO); 584a97c75b7SDoug Rabson 5856182fdbdSPeter Wemm fdc = device_get_softc(dev); 5866182fdbdSPeter Wemm bzero(fdc, sizeof *fdc); 5876182fdbdSPeter Wemm fdc->fdc_dev = dev; 5886182fdbdSPeter Wemm fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; 5896182fdbdSPeter Wemm fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; 5906182fdbdSPeter Wemm 5916182fdbdSPeter Wemm fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, 5926182fdbdSPeter Wemm &fdc->rid_ioport, 0ul, ~0ul, 5936182fdbdSPeter Wemm IO_FDCSIZE, RF_ACTIVE); 5946182fdbdSPeter Wemm if (fdc->res_ioport == 0) { 5956182fdbdSPeter Wemm device_print_prettyname(dev); 5966182fdbdSPeter Wemm printf("cannot reserve I/O port range\n"); 5976182fdbdSPeter Wemm error = ENXIO; 5986182fdbdSPeter Wemm goto out; 5996182fdbdSPeter Wemm } 6006182fdbdSPeter Wemm fdc->baseport = fdc->res_ioport->r_start; 6016182fdbdSPeter Wemm 6026182fdbdSPeter Wemm fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, 6036182fdbdSPeter Wemm &fdc->rid_irq, 0ul, ~0ul, 1, 6046182fdbdSPeter Wemm RF_ACTIVE); 6056182fdbdSPeter Wemm if (fdc->res_irq == 0) { 6066182fdbdSPeter Wemm device_print_prettyname(dev); 6076182fdbdSPeter Wemm printf("cannot reserve interrupt line\n"); 6086182fdbdSPeter Wemm error = ENXIO; 6096182fdbdSPeter Wemm goto out; 6106182fdbdSPeter Wemm } 6116182fdbdSPeter Wemm fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, 6126182fdbdSPeter Wemm &fdc->rid_drq, 0ul, ~0ul, 1, 6136182fdbdSPeter Wemm RF_ACTIVE); 6146182fdbdSPeter Wemm if (fdc->res_drq == 0) { 6156182fdbdSPeter Wemm device_print_prettyname(dev); 6166182fdbdSPeter Wemm printf("cannot reserve DMA request line\n"); 6176182fdbdSPeter Wemm error = ENXIO; 6186182fdbdSPeter Wemm goto out; 6196182fdbdSPeter Wemm } 6206182fdbdSPeter Wemm fdc->dmachan = fdc->res_drq->r_start; 621566643e3SDoug Rabson error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, 622566643e3SDoug Rabson INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr); 6235b81b6b3SRodney W. Grimes 62416111cedSAndrew Moore /* First - lets reset the floppy controller */ 6256182fdbdSPeter Wemm outb(fdc->baseport + FDOUT, 0); 62616111cedSAndrew Moore DELAY(100); 6276182fdbdSPeter Wemm outb(fdc->baseport + FDOUT, FDO_FRST); 62816111cedSAndrew Moore 6295b81b6b3SRodney W. Grimes /* see if it can handle a command */ 6306182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), 6316182fdbdSPeter Wemm NE7_SPEC_2(2, 0), 0)) { 6326182fdbdSPeter Wemm error = ENXIO; 6336182fdbdSPeter Wemm goto out; 6345b81b6b3SRodney W. Grimes } 6356182fdbdSPeter Wemm 6366182fdbdSPeter Wemm if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { 6376182fdbdSPeter Wemm ic_type = (u_char)ic_type; 6386182fdbdSPeter Wemm switch (ic_type) { 6396182fdbdSPeter Wemm case 0x80: 6406182fdbdSPeter Wemm device_set_desc(dev, "NEC 765 or clone"); 6416182fdbdSPeter Wemm fdc->fdct = FDC_NE765; 6426182fdbdSPeter Wemm break; 6436182fdbdSPeter Wemm case 0x81: 6446182fdbdSPeter Wemm device_set_desc(dev, "Intel 82077 or clone"); 6456182fdbdSPeter Wemm fdc->fdct = FDC_I82077; 6466182fdbdSPeter Wemm break; 6476182fdbdSPeter Wemm case 0x90: 6486182fdbdSPeter Wemm device_set_desc(dev, "NEC 72065B or clone"); 6496182fdbdSPeter Wemm fdc->fdct = FDC_NE72065; 6506182fdbdSPeter Wemm break; 6516182fdbdSPeter Wemm default: 6526182fdbdSPeter Wemm device_set_desc(dev, "generic floppy controller"); 6536182fdbdSPeter Wemm fdc->fdct = FDC_UNKNOWN; 6546182fdbdSPeter Wemm break; 6556182fdbdSPeter Wemm } 6566182fdbdSPeter Wemm } 6576182fdbdSPeter Wemm 6586182fdbdSPeter Wemm snprintf(myname, sizeof(myname), "%s%d", device_get_name(dev), 6596182fdbdSPeter Wemm device_get_unit(dev)); 6606182fdbdSPeter Wemm for (i = resource_query_string(-1, "at", myname); i != -1; 6616182fdbdSPeter Wemm i = resource_query_string(i, "at", myname)) 6626182fdbdSPeter Wemm fdc_add_device(dev, resource_query_name(i), 6636182fdbdSPeter Wemm resource_query_unit(i)); 66469acd21dSWarner Losh #ifdef FDC_YE 66569acd21dSWarner Losh /* 66669acd21dSWarner Losh * don't succeed on probe; wait 66769acd21dSWarner Losh * for PCCARD subsystem to do it 66869acd21dSWarner Losh */ 66969acd21dSWarner Losh if (dev->id_flags & FDC_IS_PCMCIA) 67069acd21dSWarner Losh return(0); 67169acd21dSWarner Losh #endif 6726182fdbdSPeter Wemm return (0); 6736182fdbdSPeter Wemm 6746182fdbdSPeter Wemm out: 6756182fdbdSPeter Wemm if (fdc->fdc_intr) 6766182fdbdSPeter Wemm BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, 6776182fdbdSPeter Wemm fdc->fdc_intr); 6786182fdbdSPeter Wemm if (fdc->res_irq != 0) { 6796182fdbdSPeter Wemm bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 6806182fdbdSPeter Wemm fdc->res_irq); 6816182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 6826182fdbdSPeter Wemm fdc->res_irq); 6836182fdbdSPeter Wemm } 6846182fdbdSPeter Wemm if (fdc->res_ioport != 0) { 6856182fdbdSPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 6866182fdbdSPeter Wemm fdc->res_ioport); 6876182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 6886182fdbdSPeter Wemm fdc->res_ioport); 6896182fdbdSPeter Wemm } 6906182fdbdSPeter Wemm if (fdc->res_drq != 0) { 6916182fdbdSPeter Wemm bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 6926182fdbdSPeter Wemm fdc->res_drq); 6936182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 6946182fdbdSPeter Wemm fdc->res_drq); 6956182fdbdSPeter Wemm } 6966182fdbdSPeter Wemm return (error); 6975b81b6b3SRodney W. Grimes } 6985b81b6b3SRodney W. Grimes 6995b81b6b3SRodney W. Grimes /* 7006182fdbdSPeter Wemm * Aped dfr@freebsd.org's isa_add_device(). 7015b81b6b3SRodney W. Grimes */ 7026182fdbdSPeter Wemm static void 7036182fdbdSPeter Wemm fdc_add_device(device_t dev, const char *name, int unit) 7045b81b6b3SRodney W. Grimes { 7056182fdbdSPeter Wemm int disabled, *ivar; 7066182fdbdSPeter Wemm device_t child; 70792200632SGarrett Wollman 7086182fdbdSPeter Wemm ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); 7096182fdbdSPeter Wemm if (ivar == 0) 7106182fdbdSPeter Wemm return; 711a97c75b7SDoug Rabson if (resource_int_value(name, unit, "drive", ivar) != 0) 7126182fdbdSPeter Wemm *ivar = 0; 7136182fdbdSPeter Wemm child = device_add_child(dev, name, unit, ivar); 7146182fdbdSPeter Wemm if (child == 0) 7156182fdbdSPeter Wemm return; 716a97c75b7SDoug Rabson if (resource_int_value(name, unit, "disabled", &disabled) == 0 717a97c75b7SDoug Rabson && disabled != 0) 7186182fdbdSPeter Wemm device_disable(child); 7196182fdbdSPeter Wemm } 7206182fdbdSPeter Wemm 7216182fdbdSPeter Wemm static int 7226182fdbdSPeter Wemm fdc_attach(device_t dev) 7236182fdbdSPeter Wemm { 7246182fdbdSPeter Wemm struct fdc_data *fdc = device_get_softc(dev); 7256182fdbdSPeter Wemm fdcu_t fdcu = device_get_unit(dev); 7266182fdbdSPeter Wemm 7275b81b6b3SRodney W. Grimes fdc->fdcu = fdcu; 7285b81b6b3SRodney W. Grimes fdc->flags |= FDC_ATTACHED; 7296182fdbdSPeter Wemm 730100f78bbSSujal Patel /* Acquire the DMA channel forever, The driver will do the rest */ 7316182fdbdSPeter Wemm /* XXX should integrate with rman */ 732100f78bbSSujal Patel isa_dma_acquire(fdc->dmachan); 733dd87702aSBruce Evans isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); 7345b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 7356182fdbdSPeter Wemm 7363a2f7427SDavid Greenman /* reset controller, turn motor off, clear fdout mirror reg */ 7373a2f7427SDavid Greenman outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); 73802a19910SJustin T. Gibbs bufq_init(&fdc->head); 7395b81b6b3SRodney W. Grimes 7406182fdbdSPeter Wemm #ifdef FIFO_BEFORE_MOTORON 7416182fdbdSPeter Wemm /* Hmm, this doesn't work here - is set_motor() magic? -Peter */ 7426182fdbdSPeter Wemm if (fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN 7436182fdbdSPeter Wemm && enable_fifo(fdc) == 0) { 7446182fdbdSPeter Wemm device_print_prettyname(dev); 7456182fdbdSPeter Wemm printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); 7466182fdbdSPeter Wemm } 7476182fdbdSPeter Wemm #endif 7486182fdbdSPeter Wemm /* 7496182fdbdSPeter Wemm * Probe and attach any children as were configured above. 7506182fdbdSPeter Wemm */ 7516182fdbdSPeter Wemm return (bus_generic_attach(dev)); 7526182fdbdSPeter Wemm } 7536182fdbdSPeter Wemm 75415317dd8SMatthew N. Dodd static int 7556182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child) 7566182fdbdSPeter Wemm { 75715317dd8SMatthew N. Dodd int retval = 0; 75815317dd8SMatthew N. Dodd 75915317dd8SMatthew N. Dodd retval += bus_print_child_header(me, child); 76015317dd8SMatthew N. Dodd retval += printf(" on %s drive %d\n", device_get_nameunit(me), 7616182fdbdSPeter Wemm *(int *)device_get_ivars(child)); 76215317dd8SMatthew N. Dodd 76315317dd8SMatthew N. Dodd return (retval); 7646182fdbdSPeter Wemm } 7656182fdbdSPeter Wemm 76616e68fc6SPeter Wemm static device_method_t fdc_methods[] = { 76716e68fc6SPeter Wemm /* Device interface */ 76816e68fc6SPeter Wemm DEVMETHOD(device_probe, fdc_probe), 76916e68fc6SPeter Wemm DEVMETHOD(device_attach, fdc_attach), 77016e68fc6SPeter Wemm DEVMETHOD(device_detach, bus_generic_detach), 77116e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 77216e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), 77316e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), 77416e68fc6SPeter Wemm 77516e68fc6SPeter Wemm /* Bus interface */ 77616e68fc6SPeter Wemm DEVMETHOD(bus_print_child, fdc_print_child), 77716e68fc6SPeter Wemm /* Our children never use any other bus interface methods. */ 77816e68fc6SPeter Wemm 77916e68fc6SPeter Wemm { 0, 0 } 78016e68fc6SPeter Wemm }; 78116e68fc6SPeter Wemm 78216e68fc6SPeter Wemm static driver_t fdc_driver = { 78316e68fc6SPeter Wemm "fdc", 78416e68fc6SPeter Wemm fdc_methods, 78516e68fc6SPeter Wemm sizeof(struct fdc_data) 78616e68fc6SPeter Wemm }; 78716e68fc6SPeter Wemm 78816e68fc6SPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); 78916e68fc6SPeter Wemm 79016e68fc6SPeter Wemm /******************************************************************/ 79116e68fc6SPeter Wemm /* 79216e68fc6SPeter Wemm * devices attached to the controller section. 79316e68fc6SPeter Wemm */ 7946182fdbdSPeter Wemm static int 7956182fdbdSPeter Wemm fd_probe(device_t dev) 7966182fdbdSPeter Wemm { 7976182fdbdSPeter Wemm int i; 7986182fdbdSPeter Wemm u_int fdt, st0, st3; 7996182fdbdSPeter Wemm struct fd_data *fd; 8006182fdbdSPeter Wemm struct fdc_data *fdc; 8016182fdbdSPeter Wemm fdsu_t fdsu; 8026182fdbdSPeter Wemm #ifndef FIFO_BEFORE_MOTORON 8036182fdbdSPeter Wemm static int fd_fifo = 0; 8046182fdbdSPeter Wemm #endif 8056182fdbdSPeter Wemm 8066182fdbdSPeter Wemm fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ 8076182fdbdSPeter Wemm fd = device_get_softc(dev); 8086182fdbdSPeter Wemm fdc = device_get_softc(device_get_parent(dev)); 8096182fdbdSPeter Wemm 8106182fdbdSPeter Wemm bzero(fd, sizeof *fd); 8116182fdbdSPeter Wemm fd->dev = dev; 8126182fdbdSPeter Wemm fd->fdc = fdc; 8136182fdbdSPeter Wemm fd->fdsu = fdsu; 8146182fdbdSPeter Wemm fd->fdu = device_get_unit(dev); 8156182fdbdSPeter Wemm 816a97c75b7SDoug Rabson #ifdef __i386__ 817b99f0a4aSAndrew Moore /* look up what bios thinks we have */ 8186182fdbdSPeter Wemm switch (fd->fdu) { 8196182fdbdSPeter Wemm case 0: 820062acdb7SDoug Rabson if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) 8210722d6abSJoerg Wunsch fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; 8220722d6abSJoerg Wunsch else 8230722d6abSJoerg Wunsch fdt = (rtcin(RTC_FDISKETTE) & 0xf0); 824b99f0a4aSAndrew Moore break; 8256182fdbdSPeter Wemm case 1: 8266182fdbdSPeter Wemm fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); 827dc5df763SJoerg Wunsch break; 828dc5df763SJoerg Wunsch default: 8296182fdbdSPeter Wemm fdt = RTCFDT_NONE; 830dc5df763SJoerg Wunsch break; 8316b7bd95bSJoerg Wunsch } 832a97c75b7SDoug Rabson #else 833a97c75b7SDoug Rabson fdt = RTCFDT_144M; /* XXX probably */ 834a97c75b7SDoug Rabson #endif 8356182fdbdSPeter Wemm 8366182fdbdSPeter Wemm /* is there a unit? */ 8376182fdbdSPeter Wemm if (fdt == RTCFDT_NONE) 8386182fdbdSPeter Wemm return (ENXIO); 8396182fdbdSPeter Wemm 8406182fdbdSPeter Wemm /* select it */ 8416182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNON); 8426182fdbdSPeter Wemm DELAY(1000000); /* 1 sec */ 8436182fdbdSPeter Wemm 8446182fdbdSPeter Wemm #ifndef FIFO_BEFORE_MOTORON 8456182fdbdSPeter Wemm if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN 8466182fdbdSPeter Wemm && enable_fifo(fdc) == 0) { 8476182fdbdSPeter Wemm device_print_prettyname(device_get_parent(dev)); 8486182fdbdSPeter Wemm printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); 849d66c374fSTor Egge } 8506182fdbdSPeter Wemm fd_fifo = 1; 8516182fdbdSPeter Wemm #endif 8526182fdbdSPeter Wemm 8536182fdbdSPeter Wemm if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) 8546182fdbdSPeter Wemm && (st3 & NE7_ST3_T0)) { 855dc5df763SJoerg Wunsch /* if at track 0, first seek inwards */ 856dc5df763SJoerg Wunsch /* seek some steps: */ 8576182fdbdSPeter Wemm fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); 858dc5df763SJoerg Wunsch DELAY(300000); /* ...wait a moment... */ 8596182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ 860dc5df763SJoerg Wunsch } 861dc5df763SJoerg Wunsch 862dc5df763SJoerg Wunsch /* If we're at track 0 first seek inwards. */ 8636182fdbdSPeter Wemm if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { 864dc5df763SJoerg Wunsch /* Seek some steps... */ 8656182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 866dc5df763SJoerg Wunsch /* ...wait a moment... */ 867dc5df763SJoerg Wunsch DELAY(300000); 868dc5df763SJoerg Wunsch /* make ctrlr happy: */ 8696182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); 870dc5df763SJoerg Wunsch } 871dc5df763SJoerg Wunsch } 872dc5df763SJoerg Wunsch 8736b7bd95bSJoerg Wunsch for (i = 0; i < 2; i++) { 8746b7bd95bSJoerg Wunsch /* 8756b7bd95bSJoerg Wunsch * we must recalibrate twice, just in case the 8766b7bd95bSJoerg Wunsch * heads have been beyond cylinder 76, since most 8776b7bd95bSJoerg Wunsch * FDCs still barf when attempting to recalibrate 8786b7bd95bSJoerg Wunsch * more than 77 steps 8796b7bd95bSJoerg Wunsch */ 880dc5df763SJoerg Wunsch /* go back to 0: */ 8816182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 8826b7bd95bSJoerg Wunsch /* a second being enough for full stroke seek*/ 8836b7bd95bSJoerg Wunsch DELAY(i == 0 ? 1000000 : 300000); 8845b81b6b3SRodney W. Grimes 8856b7bd95bSJoerg Wunsch /* anything responding? */ 886dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, 0) == 0 && 887dc5df763SJoerg Wunsch (st0 & NE7_ST0_EC) == 0) 8886b7bd95bSJoerg Wunsch break; /* already probed succesfully */ 8896b7bd95bSJoerg Wunsch } 890dc5df763SJoerg Wunsch } 8916b7bd95bSJoerg Wunsch 8926182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNOFF); 8933a2f7427SDavid Greenman 8943a2f7427SDavid Greenman if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ 8956182fdbdSPeter Wemm return (ENXIO); 8965b81b6b3SRodney W. Grimes 897dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 898b99f0a4aSAndrew Moore fd->fdc = fdc; 899b99f0a4aSAndrew Moore fd->fdsu = fdsu; 9003a2f7427SDavid Greenman fd->options = 0; 90102a19910SJustin T. Gibbs callout_handle_init(&fd->toffhandle); 90202a19910SJustin T. Gibbs callout_handle_init(&fd->tohandle); 9035b81b6b3SRodney W. Grimes 904b99f0a4aSAndrew Moore switch (fdt) { 9057ca0641bSAndrey A. Chernov case RTCFDT_12M: 9066182fdbdSPeter Wemm device_set_desc(dev, "1200-KB 5.25\" drive"); 907b99f0a4aSAndrew Moore fd->type = FD_1200; 9087ca0641bSAndrey A. Chernov break; 9090722d6abSJoerg Wunsch case RTCFDT_144M | RTCFDT_144M_PRETENDED: 9106182fdbdSPeter Wemm device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); 9110722d6abSJoerg Wunsch fdt = RTCFDT_144M; 9126182fdbdSPeter Wemm fd->type = FD_1440; 9137ca0641bSAndrey A. Chernov case RTCFDT_144M: 9146182fdbdSPeter Wemm device_set_desc(dev, "1440-KB 3.5\" drive"); 915b99f0a4aSAndrew Moore fd->type = FD_1440; 9167ca0641bSAndrey A. Chernov break; 917290dd077SJoerg Wunsch case RTCFDT_288M: 91886a727d9SJoerg Wunsch case RTCFDT_288M_1: 9196182fdbdSPeter Wemm device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); 920290dd077SJoerg Wunsch fd->type = FD_1440; 921290dd077SJoerg Wunsch break; 9227ca0641bSAndrey A. Chernov case RTCFDT_360K: 9236182fdbdSPeter Wemm device_set_desc(dev, "360-KB 5.25\" drive"); 924b99f0a4aSAndrew Moore fd->type = FD_360; 9257ca0641bSAndrey A. Chernov break; 926ed2fa05eSAndrey A. Chernov case RTCFDT_720K: 9276182fdbdSPeter Wemm printf("720-KB 3.5\" drive"); 928b99f0a4aSAndrew Moore fd->type = FD_720; 929ed2fa05eSAndrey A. Chernov break; 9307ca0641bSAndrey A. Chernov default: 9316182fdbdSPeter Wemm return (ENXIO); 9325b81b6b3SRodney W. Grimes } 9336182fdbdSPeter Wemm return (0); 9346182fdbdSPeter Wemm } 9356182fdbdSPeter Wemm 9366182fdbdSPeter Wemm static int 9376182fdbdSPeter Wemm fd_attach(device_t dev) 9386182fdbdSPeter Wemm { 9396182fdbdSPeter Wemm struct fd_data *fd; 940ada9bd8cSJulian Elischer #if 0 941ada9bd8cSJulian Elischer int i; 942ada9bd8cSJulian Elischer int mynor; 943ada9bd8cSJulian Elischer int typemynor; 944ada9bd8cSJulian Elischer int typesize; 945ada9bd8cSJulian Elischer #endif 9466182fdbdSPeter Wemm 9476182fdbdSPeter Wemm fd = device_get_softc(dev); 9486182fdbdSPeter Wemm 949ada9bd8cSJulian Elischer make_dev(&fd_cdevsw, (fd->fdu << 6), 950ada9bd8cSJulian Elischer UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fd->fdu); 951ada9bd8cSJulian Elischer 952ada9bd8cSJulian Elischer #if 0 953ada9bd8cSJulian Elischer /* Other make_dev() go here. */ 954ada9bd8cSJulian Elischer #endif 955ada9bd8cSJulian Elischer 956671e2ceeSBruce Evans /* 957b2dfb1f9SJustin T. Gibbs * Export the drive to the devstat interface. 958671e2ceeSBruce Evans */ 9596182fdbdSPeter Wemm devstat_add_entry(&fd->device_stats, device_get_name(dev), 9606182fdbdSPeter Wemm device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, 9612a888f93SKenneth D. Merry DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, 9622a888f93SKenneth D. Merry DEVSTAT_PRIORITY_FD); 9636182fdbdSPeter Wemm return (0); 9645b81b6b3SRodney W. Grimes } 9655b81b6b3SRodney W. Grimes 96616e68fc6SPeter Wemm static device_method_t fd_methods[] = { 96716e68fc6SPeter Wemm /* Device interface */ 96816e68fc6SPeter Wemm DEVMETHOD(device_probe, fd_probe), 96916e68fc6SPeter Wemm DEVMETHOD(device_attach, fd_attach), 97016e68fc6SPeter Wemm DEVMETHOD(device_detach, bus_generic_detach), 97116e68fc6SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 97216e68fc6SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ 97316e68fc6SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ 97416e68fc6SPeter Wemm 97516e68fc6SPeter Wemm { 0, 0 } 97616e68fc6SPeter Wemm }; 97716e68fc6SPeter Wemm 97816e68fc6SPeter Wemm static driver_t fd_driver = { 97916e68fc6SPeter Wemm "fd", 98016e68fc6SPeter Wemm fd_methods, 98116e68fc6SPeter Wemm sizeof(struct fd_data) 98216e68fc6SPeter Wemm }; 98316e68fc6SPeter Wemm 98416e68fc6SPeter Wemm DEV_DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, fd_cdevsw, 0, 0); 98516e68fc6SPeter Wemm 98616e68fc6SPeter Wemm /******************************************************************/ 98716e68fc6SPeter Wemm 98869acd21dSWarner Losh #ifdef FDC_YE 98969acd21dSWarner Losh /* 99069acd21dSWarner Losh * this is a subset of fdattach() optimized for the Y-E Data 99169acd21dSWarner Losh * PCMCIA floppy drive. 99269acd21dSWarner Losh */ 99369acd21dSWarner Losh static int yeattach(struct isa_device *dev) 99469acd21dSWarner Losh { 99569acd21dSWarner Losh fdcu_t fdcu = dev->id_unit; 99669acd21dSWarner Losh fdc_p fdc = fdc_data + fdcu; 99769acd21dSWarner Losh fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ 99869acd21dSWarner Losh fdu_t fdu; 99969acd21dSWarner Losh fd_p fd; 100069acd21dSWarner Losh int st0, st3, i; 100169acd21dSWarner Losh fdc->fdcu = fdcu; 100269acd21dSWarner Losh /* 100369acd21dSWarner Losh * the FDC_PCMCIA flag is used to to indicate special PIO is used 100469acd21dSWarner Losh * instead of DMA 100569acd21dSWarner Losh */ 100669acd21dSWarner Losh fdc->flags = FDC_ATTACHED|FDC_PCMCIA; 100769acd21dSWarner Losh fdc->state = DEVIDLE; 100869acd21dSWarner Losh /* reset controller, turn motor off, clear fdout mirror reg */ 100969acd21dSWarner Losh outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); 101069acd21dSWarner Losh bufq_init(&fdc->head); 101169acd21dSWarner Losh /* 101269acd21dSWarner Losh * assume 2 drives/ "normal" controller 101369acd21dSWarner Losh */ 101469acd21dSWarner Losh fdu = fdcu * 2; 101569acd21dSWarner Losh if (fdu >= NFD) { 101669acd21dSWarner Losh printf("fdu %d >= NFD\n",fdu); 101769acd21dSWarner Losh return(0); 101869acd21dSWarner Losh }; 101969acd21dSWarner Losh fd = &fd_data[fdu]; 102069acd21dSWarner Losh 102169acd21dSWarner Losh set_motor(fdcu, fdsu, TURNON); 102269acd21dSWarner Losh DELAY(1000000); /* 1 sec */ 102369acd21dSWarner Losh fdc->fdct = FDC_NE765; 102469acd21dSWarner Losh 102569acd21dSWarner Losh if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && 102669acd21dSWarner Losh (st3 & NE7_ST3_T0)) { 102769acd21dSWarner Losh /* if at track 0, first seek inwards */ 102869acd21dSWarner Losh /* seek some steps: */ 102969acd21dSWarner Losh (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); 103069acd21dSWarner Losh DELAY(300000); /* ...wait a moment... */ 103169acd21dSWarner Losh (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ 103269acd21dSWarner Losh } 103369acd21dSWarner Losh 103469acd21dSWarner Losh /* If we're at track 0 first seek inwards. */ 103569acd21dSWarner Losh if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { 103669acd21dSWarner Losh /* Seek some steps... */ 103769acd21dSWarner Losh if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 103869acd21dSWarner Losh /* ...wait a moment... */ 103969acd21dSWarner Losh DELAY(300000); 104069acd21dSWarner Losh /* make ctrlr happy: */ 104169acd21dSWarner Losh (void)fd_sense_int(fdc, 0, 0); 104269acd21dSWarner Losh } 104369acd21dSWarner Losh } 104469acd21dSWarner Losh 104569acd21dSWarner Losh for(i = 0; i < 2; i++) { 104669acd21dSWarner Losh /* 104769acd21dSWarner Losh * we must recalibrate twice, just in case the 104869acd21dSWarner Losh * heads have been beyond cylinder 76, since most 104969acd21dSWarner Losh * FDCs still barf when attempting to recalibrate 105069acd21dSWarner Losh * more than 77 steps 105169acd21dSWarner Losh */ 105269acd21dSWarner Losh /* go back to 0: */ 105369acd21dSWarner Losh if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 105469acd21dSWarner Losh /* a second being enough for full stroke seek*/ 105569acd21dSWarner Losh DELAY(i == 0? 1000000: 300000); 105669acd21dSWarner Losh 105769acd21dSWarner Losh /* anything responding? */ 105869acd21dSWarner Losh if (fd_sense_int(fdc, &st0, 0) == 0 && 105969acd21dSWarner Losh (st0 & NE7_ST0_EC) == 0) 106069acd21dSWarner Losh break; /* already probed succesfully */ 106169acd21dSWarner Losh } 106269acd21dSWarner Losh } 106369acd21dSWarner Losh 106469acd21dSWarner Losh set_motor(fdcu, fdsu, TURNOFF); 106569acd21dSWarner Losh 106669acd21dSWarner Losh if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ 106769acd21dSWarner Losh return(0); 106869acd21dSWarner Losh 106969acd21dSWarner Losh fd->track = FD_NO_TRACK; 107069acd21dSWarner Losh fd->fdc = fdc; 107169acd21dSWarner Losh fd->fdsu = fdsu; 107269acd21dSWarner Losh fd->options = 0; 107369acd21dSWarner Losh printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); 107469acd21dSWarner Losh fd->type = FD_1440; 107569acd21dSWarner Losh 107669acd21dSWarner Losh return (1); 107769acd21dSWarner Losh } 107869acd21dSWarner Losh #endif 107969acd21dSWarner Losh 10805b81b6b3SRodney W. Grimes /****************************************************************************/ 10815b81b6b3SRodney W. Grimes /* motor control stuff */ 10825e235068SJordan K. Hubbard /* remember to not deselect the drive we're working on */ 10835b81b6b3SRodney W. Grimes /****************************************************************************/ 10843a2f7427SDavid Greenman static void 10856182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon) 10865b81b6b3SRodney W. Grimes { 10876182fdbdSPeter Wemm int fdout = fdc->fdout; 10883a2f7427SDavid Greenman int needspecify = 0; 10893a2f7427SDavid Greenman 10903a2f7427SDavid Greenman if(turnon) { 10913a2f7427SDavid Greenman fdout &= ~FDO_FDSEL; 10923a2f7427SDavid Greenman fdout |= (FDO_MOEN0 << fdsu) + fdsu; 10933a2f7427SDavid Greenman } else 10943a2f7427SDavid Greenman fdout &= ~(FDO_MOEN0 << fdsu); 10953a2f7427SDavid Greenman 10965e235068SJordan K. Hubbard if(!turnon 10975e235068SJordan K. Hubbard && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) 10985e235068SJordan K. Hubbard /* gonna turn off the last drive, put FDC to bed */ 10995e235068SJordan K. Hubbard fdout &= ~ (FDO_FRST|FDO_FDMAEN); 11005e235068SJordan K. Hubbard else { 11013a2f7427SDavid Greenman /* make sure controller is selected and specified */ 11023a2f7427SDavid Greenman if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) 11033a2f7427SDavid Greenman needspecify = 1; 11043a2f7427SDavid Greenman fdout |= (FDO_FRST|FDO_FDMAEN); 11055b81b6b3SRodney W. Grimes } 11065b81b6b3SRodney W. Grimes 11076182fdbdSPeter Wemm outb(fdc->baseport+FDOUT, fdout); 11086182fdbdSPeter Wemm fdc->fdout = fdout; 11093a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdout); 11103a2f7427SDavid Greenman 11113a2f7427SDavid Greenman if (needspecify) { 1112dc8603e3SJoerg Wunsch /* 1113dc5df763SJoerg Wunsch * XXX 1114dc8603e3SJoerg Wunsch * special case: since we have just woken up the FDC 1115dc8603e3SJoerg Wunsch * from its sleep, we silently assume the command will 1116dc8603e3SJoerg Wunsch * be accepted, and do not test for a timeout 1117dc8603e3SJoerg Wunsch */ 11186182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1119dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1120dc5df763SJoerg Wunsch 0); 11216182fdbdSPeter Wemm if (fdc->flags & FDC_HAS_FIFO) 11226182fdbdSPeter Wemm (void) enable_fifo(fdc); 11233a2f7427SDavid Greenman } 11243a2f7427SDavid Greenman } 11253a2f7427SDavid Greenman 1126381fe1aaSGarrett Wollman static void 11276182fdbdSPeter Wemm fd_turnoff(void *xfd) 11285b81b6b3SRodney W. Grimes { 1129f5f7ba03SJordan K. Hubbard int s; 11306182fdbdSPeter Wemm fd_p fd = xfd; 1131dc16046fSJoerg Wunsch 11326182fdbdSPeter Wemm TRACE1("[fd%d: turnoff]", fd->fdu); 11338335c1b8SBruce Evans 11348335c1b8SBruce Evans /* 11358335c1b8SBruce Evans * Don't turn off the motor yet if the drive is active. 11368335c1b8SBruce Evans * XXX shouldn't even schedule turnoff until drive is inactive 11378335c1b8SBruce Evans * and nothing is queued on it. 11388335c1b8SBruce Evans */ 11396182fdbdSPeter Wemm if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { 11406182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 11418335c1b8SBruce Evans return; 11428335c1b8SBruce Evans } 11438335c1b8SBruce Evans 1144f5f7ba03SJordan K. Hubbard s = splbio(); 11455b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR; 11466182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNOFF); 1147f5f7ba03SJordan K. Hubbard splx(s); 11485b81b6b3SRodney W. Grimes } 11495b81b6b3SRodney W. Grimes 11503a2f7427SDavid Greenman static void 11516182fdbdSPeter Wemm fd_motor_on(void *xfd) 11525b81b6b3SRodney W. Grimes { 1153f5f7ba03SJordan K. Hubbard int s; 11546182fdbdSPeter Wemm fd_p fd = xfd; 1155f5f7ba03SJordan K. Hubbard 1156f5f7ba03SJordan K. Hubbard s = splbio(); 11575b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR_WAIT; 11585b81b6b3SRodney W. Grimes if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) 11595b81b6b3SRodney W. Grimes { 11606182fdbdSPeter Wemm fdc_intr(fd->fdc); 11615b81b6b3SRodney W. Grimes } 1162f5f7ba03SJordan K. Hubbard splx(s); 11635b81b6b3SRodney W. Grimes } 11645b81b6b3SRodney W. Grimes 11653a2f7427SDavid Greenman static void 11666182fdbdSPeter Wemm fd_turnon(fd_p fd) 11675b81b6b3SRodney W. Grimes { 11685b81b6b3SRodney W. Grimes if(!(fd->flags & FD_MOTOR)) 11695b81b6b3SRodney W. Grimes { 11703a2f7427SDavid Greenman fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); 11716182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNON); 11726182fdbdSPeter Wemm timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ 11735b81b6b3SRodney W. Grimes } 11745b81b6b3SRodney W. Grimes } 11755b81b6b3SRodney W. Grimes 1176381fe1aaSGarrett Wollman static void 1177dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc) 11785b81b6b3SRodney W. Grimes { 11793a2f7427SDavid Greenman /* Try a reset, keep motor on */ 11803a2f7427SDavid Greenman outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 11813a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 11823a2f7427SDavid Greenman DELAY(100); 11833a2f7427SDavid Greenman /* enable FDC, but defer interrupts a moment */ 11843a2f7427SDavid Greenman outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); 11853a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); 11863a2f7427SDavid Greenman DELAY(100); 11873a2f7427SDavid Greenman outb(fdc->baseport + FDOUT, fdc->fdout); 11883a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout); 11893a2f7427SDavid Greenman 1190dc5df763SJoerg Wunsch /* XXX after a reset, silently believe the FDC will accept commands */ 11916182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1192dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1193dc5df763SJoerg Wunsch 0); 1194d66c374fSTor Egge if (fdc->flags & FDC_HAS_FIFO) 1195d66c374fSTor Egge (void) enable_fifo(fdc); 11965b81b6b3SRodney W. Grimes } 11975b81b6b3SRodney W. Grimes 11985b81b6b3SRodney W. Grimes /****************************************************************************/ 11995b81b6b3SRodney W. Grimes /* fdc in/out */ 12005b81b6b3SRodney W. Grimes /****************************************************************************/ 12015b81b6b3SRodney W. Grimes int 12026182fdbdSPeter Wemm in_fdc(struct fdc_data *fdc) 12035b81b6b3SRodney W. Grimes { 12046182fdbdSPeter Wemm int baseport = fdc->baseport; 12055b81b6b3SRodney W. Grimes int i, j = 100000; 12063a2f7427SDavid Greenman while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) 12075b81b6b3SRodney W. Grimes != (NE7_DIO|NE7_RQM) && j-- > 0) 1208dc5df763SJoerg Wunsch if (i == NE7_RQM) 12096182fdbdSPeter Wemm return fdc_err(fdc, "ready for output in input\n"); 12105b81b6b3SRodney W. Grimes if (j <= 0) 12116182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); 1212d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 12133a2f7427SDavid Greenman i = inb(baseport+FDDATA); 12143a2f7427SDavid Greenman TRACE1("[FDDATA->0x%x]", (unsigned char)i); 12155b81b6b3SRodney W. Grimes return(i); 1216d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 12173a2f7427SDavid Greenman return inb(baseport+FDDATA); 1218d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 12195b81b6b3SRodney W. Grimes } 12205b81b6b3SRodney W. Grimes 1221dc5df763SJoerg Wunsch /* 1222dc5df763SJoerg Wunsch * fd_in: Like in_fdc, but allows you to see if it worked. 1223dc5df763SJoerg Wunsch */ 1224b5e8ce9fSBruce Evans static int 12256182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr) 1226dc5df763SJoerg Wunsch { 12276182fdbdSPeter Wemm int baseport = fdc->baseport; 1228dc5df763SJoerg Wunsch int i, j = 100000; 1229dc5df763SJoerg Wunsch while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) 1230dc5df763SJoerg Wunsch != (NE7_DIO|NE7_RQM) && j-- > 0) 1231dc5df763SJoerg Wunsch if (i == NE7_RQM) 12326182fdbdSPeter Wemm return fdc_err(fdc, "ready for output in input\n"); 1233dc5df763SJoerg Wunsch if (j <= 0) 12346182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); 1235d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 1236dc5df763SJoerg Wunsch i = inb(baseport+FDDATA); 1237dc5df763SJoerg Wunsch TRACE1("[FDDATA->0x%x]", (unsigned char)i); 1238dc5df763SJoerg Wunsch *ptr = i; 1239dc5df763SJoerg Wunsch return 0; 1240d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 1241dc5df763SJoerg Wunsch i = inb(baseport+FDDATA); 1242dc5df763SJoerg Wunsch if (ptr) 1243dc5df763SJoerg Wunsch *ptr = i; 1244dc5df763SJoerg Wunsch return 0; 1245d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 1246dc5df763SJoerg Wunsch } 1247dc5df763SJoerg Wunsch 1248dc5df763SJoerg Wunsch int 12496182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x) 12505b81b6b3SRodney W. Grimes { 12516182fdbdSPeter Wemm int baseport = fdc->baseport; 12523b3837dbSRodney W. Grimes int i; 12535b81b6b3SRodney W. Grimes 12543b3837dbSRodney W. Grimes /* Check that the direction bit is set */ 12553b3837dbSRodney W. Grimes i = 100000; 12563a2f7427SDavid Greenman while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0); 12576182fdbdSPeter Wemm if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); 12583b3837dbSRodney W. Grimes 12593b3837dbSRodney W. Grimes /* Check that the floppy controller is ready for a command */ 12603b3837dbSRodney W. Grimes i = 100000; 12613a2f7427SDavid Greenman while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0); 126216b04b6aSJoerg Wunsch if (i <= 0) 12636182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); 12643b3837dbSRodney W. Grimes 12653b3837dbSRodney W. Grimes /* Send the command and return */ 12663a2f7427SDavid Greenman outb(baseport+FDDATA, x); 12673a2f7427SDavid Greenman TRACE1("[0x%x->FDDATA]", x); 12685b81b6b3SRodney W. Grimes return (0); 12695b81b6b3SRodney W. Grimes } 12705b81b6b3SRodney W. Grimes 12715b81b6b3SRodney W. Grimes /****************************************************************************/ 12725b81b6b3SRodney W. Grimes /* fdopen/fdclose */ 12735b81b6b3SRodney W. Grimes /****************************************************************************/ 1274381fe1aaSGarrett Wollman int 1275671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p) 12765b81b6b3SRodney W. Grimes { 12775b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 127820a29168SAndrey A. Chernov int type = FDTYPE(minor(dev)); 12796182fdbdSPeter Wemm fd_p fd; 1280b99f0a4aSAndrew Moore fdc_p fdc; 12815b81b6b3SRodney W. Grimes 12825b81b6b3SRodney W. Grimes /* check bounds */ 12836182fdbdSPeter Wemm if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) 1284b99f0a4aSAndrew Moore return (ENXIO); 12856182fdbdSPeter Wemm fdc = fd->fdc; 12866182fdbdSPeter Wemm if ((fdc == NULL) || (fd->type == NO_TYPE)) 1287b99f0a4aSAndrew Moore return (ENXIO); 1288b99f0a4aSAndrew Moore if (type > NUMDENS) 1289b99f0a4aSAndrew Moore return (ENXIO); 12907ca0641bSAndrey A. Chernov if (type == 0) 12916182fdbdSPeter Wemm type = fd->type; 12927ca0641bSAndrey A. Chernov else { 12933e425b96SJulian Elischer /* 12943e425b96SJulian Elischer * For each type of basic drive, make sure we are trying 12953e425b96SJulian Elischer * to open a type it can do, 12963e425b96SJulian Elischer */ 12976182fdbdSPeter Wemm if (type != fd->type) { 12986182fdbdSPeter Wemm switch (fd->type) { 12997ca0641bSAndrey A. Chernov case FD_360: 13007ca0641bSAndrey A. Chernov return (ENXIO); 1301ed2fa05eSAndrey A. Chernov case FD_720: 1302b39c878eSAndrey A. Chernov if ( type != FD_820 1303b39c878eSAndrey A. Chernov && type != FD_800 1304ed2fa05eSAndrey A. Chernov ) 1305ed2fa05eSAndrey A. Chernov return (ENXIO); 1306ed2fa05eSAndrey A. Chernov break; 13077ca0641bSAndrey A. Chernov case FD_1200: 1308b39c878eSAndrey A. Chernov switch (type) { 1309b39c878eSAndrey A. Chernov case FD_1480: 1310b39c878eSAndrey A. Chernov type = FD_1480in5_25; 1311fa4700b4SAndrey A. Chernov break; 13127ca0641bSAndrey A. Chernov case FD_1440: 1313b39c878eSAndrey A. Chernov type = FD_1440in5_25; 1314b39c878eSAndrey A. Chernov break; 1315b39c878eSAndrey A. Chernov case FD_820: 1316b39c878eSAndrey A. Chernov type = FD_820in5_25; 1317b39c878eSAndrey A. Chernov break; 1318b39c878eSAndrey A. Chernov case FD_800: 1319b39c878eSAndrey A. Chernov type = FD_800in5_25; 1320b39c878eSAndrey A. Chernov break; 1321b39c878eSAndrey A. Chernov case FD_720: 1322b39c878eSAndrey A. Chernov type = FD_720in5_25; 1323b39c878eSAndrey A. Chernov break; 1324b39c878eSAndrey A. Chernov case FD_360: 1325b39c878eSAndrey A. Chernov type = FD_360in5_25; 1326b39c878eSAndrey A. Chernov break; 1327b39c878eSAndrey A. Chernov default: 1328b39c878eSAndrey A. Chernov return(ENXIO); 1329b39c878eSAndrey A. Chernov } 1330b39c878eSAndrey A. Chernov break; 1331b39c878eSAndrey A. Chernov case FD_1440: 1332b39c878eSAndrey A. Chernov if ( type != FD_1720 1333b39c878eSAndrey A. Chernov && type != FD_1480 1334ed2fa05eSAndrey A. Chernov && type != FD_1200 1335b39c878eSAndrey A. Chernov && type != FD_820 1336b39c878eSAndrey A. Chernov && type != FD_800 1337b39c878eSAndrey A. Chernov && type != FD_720 13387ca0641bSAndrey A. Chernov ) 1339dffff499SAndrey A. Chernov return(ENXIO); 1340fa4700b4SAndrey A. Chernov break; 13417ca0641bSAndrey A. Chernov } 13427ca0641bSAndrey A. Chernov } 1343fa4700b4SAndrey A. Chernov } 13446182fdbdSPeter Wemm fd->ft = fd_types + type - 1; 13456182fdbdSPeter Wemm fd->flags |= FD_OPEN; 13466182fdbdSPeter Wemm device_busy(fd->dev); 13476182fdbdSPeter Wemm device_busy(fd->fdc->fdc_dev); 13485b81b6b3SRodney W. Grimes return 0; 13495b81b6b3SRodney W. Grimes } 13505b81b6b3SRodney W. Grimes 1351381fe1aaSGarrett Wollman int 1352671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p) 13535b81b6b3SRodney W. Grimes { 13545b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 13556182fdbdSPeter Wemm struct fd_data *fd; 1356b99f0a4aSAndrew Moore 13576182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 13586182fdbdSPeter Wemm fd->flags &= ~FD_OPEN; 13596182fdbdSPeter Wemm fd->options &= ~FDOPT_NORETRY; 1360dc16046fSJoerg Wunsch 13615b81b6b3SRodney W. Grimes return (0); 13625b81b6b3SRodney W. Grimes } 13635b81b6b3SRodney W. Grimes 13643a2f7427SDavid Greenman /****************************************************************************/ 13653a2f7427SDavid Greenman /* fdstrategy */ 13663a2f7427SDavid Greenman /****************************************************************************/ 13673a2f7427SDavid Greenman void 13683a2f7427SDavid Greenman fdstrategy(struct buf *bp) 13693a2f7427SDavid Greenman { 1370bb6382faSJoerg Wunsch unsigned nblocks, blknum, cando; 13713a2f7427SDavid Greenman int s; 13723a2f7427SDavid Greenman fdu_t fdu; 13733a2f7427SDavid Greenman fdc_p fdc; 13743a2f7427SDavid Greenman fd_p fd; 13753a2f7427SDavid Greenman size_t fdblk; 13763a2f7427SDavid Greenman 13773a2f7427SDavid Greenman fdu = FDUNIT(minor(bp->b_dev)); 13786182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 13796182fdbdSPeter Wemm if (fd == 0) 13806182fdbdSPeter Wemm panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", 13816182fdbdSPeter Wemm (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); 13823a2f7427SDavid Greenman fdc = fd->fdc; 138369acd21dSWarner Losh #ifdef FDC_YE 138469acd21dSWarner Losh if (fd->type == NO_TYPE) { 138569acd21dSWarner Losh bp->b_error = ENXIO; 138669acd21dSWarner Losh bp->b_flags |= B_ERROR; 138769acd21dSWarner Losh /* 138869acd21dSWarner Losh * I _refuse_ to use a goto 138969acd21dSWarner Losh */ 139069acd21dSWarner Losh biodone(bp); 139169acd21dSWarner Losh return; 139269acd21dSWarner Losh }; 139369acd21dSWarner Losh #endif 13943a2f7427SDavid Greenman 1395d3628763SRodney W. Grimes fdblk = 128 << (fd->ft->secsize); 13963a2f7427SDavid Greenman if (!(bp->b_flags & B_FORMAT)) { 13976182fdbdSPeter Wemm if (bp->b_blkno < 0) { 1398dc5df763SJoerg Wunsch printf( 13996a0e6f42SRodney W. Grimes "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", 1400702c623aSPoul-Henning Kamp fdu, (u_long)bp->b_blkno, bp->b_bcount); 14013a2f7427SDavid Greenman bp->b_error = EINVAL; 14023a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 14033a2f7427SDavid Greenman goto bad; 14043a2f7427SDavid Greenman } 14053a2f7427SDavid Greenman if ((bp->b_bcount % fdblk) != 0) { 14063a2f7427SDavid Greenman bp->b_error = EINVAL; 14073a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 14083a2f7427SDavid Greenman goto bad; 14093a2f7427SDavid Greenman } 14103a2f7427SDavid Greenman } 14113a2f7427SDavid Greenman 14123a2f7427SDavid Greenman /* 14133a2f7427SDavid Greenman * Set up block calculations. 14143a2f7427SDavid Greenman */ 1415bb6382faSJoerg Wunsch if (bp->b_blkno > 20000000) { 1416bb6382faSJoerg Wunsch /* 1417bb6382faSJoerg Wunsch * Reject unreasonably high block number, prevent the 1418bb6382faSJoerg Wunsch * multiplication below from overflowing. 1419bb6382faSJoerg Wunsch */ 1420bb6382faSJoerg Wunsch bp->b_error = EINVAL; 14213a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 14223a2f7427SDavid Greenman goto bad; 14233a2f7427SDavid Greenman } 1424bb6382faSJoerg Wunsch blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; 1425bb6382faSJoerg Wunsch nblocks = fd->ft->size; 1426bb6382faSJoerg Wunsch bp->b_resid = 0; 1427bb6382faSJoerg Wunsch if (blknum + (bp->b_bcount / fdblk) > nblocks) { 1428bb6382faSJoerg Wunsch if (blknum <= nblocks) { 1429bb6382faSJoerg Wunsch cando = (nblocks - blknum) * fdblk; 1430bb6382faSJoerg Wunsch bp->b_resid = bp->b_bcount - cando; 1431bb6382faSJoerg Wunsch if (cando == 0) 1432bb6382faSJoerg Wunsch goto bad; /* not actually bad but EOF */ 1433bb6382faSJoerg Wunsch } else { 1434bb6382faSJoerg Wunsch bp->b_error = EINVAL; 1435bb6382faSJoerg Wunsch bp->b_flags |= B_ERROR; 1436bb6382faSJoerg Wunsch goto bad; 1437bb6382faSJoerg Wunsch } 1438bb6382faSJoerg Wunsch } 143927513ca7SBruce Evans bp->b_pblkno = bp->b_blkno; 14403a2f7427SDavid Greenman s = splbio(); 144102a19910SJustin T. Gibbs bufqdisksort(&fdc->head, bp); 14426182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ 1443b2dfb1f9SJustin T. Gibbs 1444b2dfb1f9SJustin T. Gibbs /* Tell devstat we are starting on the transaction */ 1445b2dfb1f9SJustin T. Gibbs devstat_start_transaction(&fd->device_stats); 1446b2dfb1f9SJustin T. Gibbs 14476182fdbdSPeter Wemm fdstart(fdc); 14483a2f7427SDavid Greenman splx(s); 14493a2f7427SDavid Greenman return; 14503a2f7427SDavid Greenman 14513a2f7427SDavid Greenman bad: 14523a2f7427SDavid Greenman biodone(bp); 14533a2f7427SDavid Greenman } 14543a2f7427SDavid Greenman 14555b81b6b3SRodney W. Grimes /***************************************************************\ 14565b81b6b3SRodney W. Grimes * fdstart * 14575b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy * 14585b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command * 14595b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more* 14605b81b6b3SRodney W. Grimes * work to do and picks up what we just added. * 14615b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it * 14625b81b6b3SRodney W. Grimes * will pick up our work when the present work completes * 14635b81b6b3SRodney W. Grimes \***************************************************************/ 1464381fe1aaSGarrett Wollman static void 14656182fdbdSPeter Wemm fdstart(struct fdc_data *fdc) 14665b81b6b3SRodney W. Grimes { 14675b81b6b3SRodney W. Grimes int s; 14685b81b6b3SRodney W. Grimes 14695b81b6b3SRodney W. Grimes s = splbio(); 14706182fdbdSPeter Wemm if(fdc->state == DEVIDLE) 14715b81b6b3SRodney W. Grimes { 14726182fdbdSPeter Wemm fdc_intr(fdc); 14735b81b6b3SRodney W. Grimes } 14745b81b6b3SRodney W. Grimes splx(s); 14755b81b6b3SRodney W. Grimes } 14765b81b6b3SRodney W. Grimes 1477381fe1aaSGarrett Wollman static void 14786182fdbdSPeter Wemm fd_iotimeout(void *xfdc) 14795b81b6b3SRodney W. Grimes { 14805c1a1eaeSBruce Evans fdc_p fdc; 1481f5f7ba03SJordan K. Hubbard int s; 14825b81b6b3SRodney W. Grimes 14836182fdbdSPeter Wemm fdc = xfdc; 14845c1a1eaeSBruce Evans TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); 14855b81b6b3SRodney W. Grimes 14863a2f7427SDavid Greenman /* 14873a2f7427SDavid Greenman * Due to IBM's brain-dead design, the FDC has a faked ready 14883a2f7427SDavid Greenman * signal, hardwired to ready == true. Thus, any command 14893a2f7427SDavid Greenman * issued if there's no diskette in the drive will _never_ 14903a2f7427SDavid Greenman * complete, and must be aborted by resetting the FDC. 14913a2f7427SDavid Greenman * Many thanks, Big Blue! 14925c1a1eaeSBruce Evans * The FDC must not be reset directly, since that would 14935c1a1eaeSBruce Evans * interfere with the state machine. Instead, pretend that 14945c1a1eaeSBruce Evans * the command completed but was invalid. The state machine 14955c1a1eaeSBruce Evans * will reset the FDC and retry once. 14963a2f7427SDavid Greenman */ 14973a2f7427SDavid Greenman s = splbio(); 14985c1a1eaeSBruce Evans fdc->status[0] = NE7_ST0_IC_IV; 14995c1a1eaeSBruce Evans fdc->flags &= ~FDC_STAT_VALID; 15005c1a1eaeSBruce Evans fdc->state = IOTIMEDOUT; 15016182fdbdSPeter Wemm fdc_intr(fdc); 1502f5f7ba03SJordan K. Hubbard splx(s); 15035b81b6b3SRodney W. Grimes } 15045b81b6b3SRodney W. Grimes 15055b81b6b3SRodney W. Grimes /* just ensure it has the right spl */ 1506381fe1aaSGarrett Wollman static void 15076182fdbdSPeter Wemm fd_pseudointr(void *xfdc) 15085b81b6b3SRodney W. Grimes { 15095b81b6b3SRodney W. Grimes int s; 15103a2f7427SDavid Greenman 15115b81b6b3SRodney W. Grimes s = splbio(); 15126182fdbdSPeter Wemm fdc_intr(xfdc); 15135b81b6b3SRodney W. Grimes splx(s); 15145b81b6b3SRodney W. Grimes } 15155b81b6b3SRodney W. Grimes 15165b81b6b3SRodney W. Grimes /***********************************************************************\ 15175b81b6b3SRodney W. Grimes * fdintr * 15185b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0 * 15195b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO * 15205b81b6b3SRodney W. Grimes \***********************************************************************/ 1521fe310de8SBruce Evans static void 15226182fdbdSPeter Wemm fdc_intr(void *xfdc) 15235b81b6b3SRodney W. Grimes { 15246182fdbdSPeter Wemm fdc_p fdc = xfdc; 15256182fdbdSPeter Wemm while(fdstate(fdc)) 1526381fe1aaSGarrett Wollman ; 15275b81b6b3SRodney W. Grimes } 15285b81b6b3SRodney W. Grimes 152969acd21dSWarner Losh #ifdef FDC_YE 153069acd21dSWarner Losh /* 153169acd21dSWarner Losh * magic pseudo-DMA initialization for YE FDC. Sets count and 153269acd21dSWarner Losh * direction 153369acd21dSWarner Losh */ 153469acd21dSWarner Losh #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ 153569acd21dSWarner Losh outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) 153669acd21dSWarner Losh 153769acd21dSWarner Losh /* 153869acd21dSWarner Losh * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy 153969acd21dSWarner Losh */ 154069acd21dSWarner Losh static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) 154169acd21dSWarner Losh { 154269acd21dSWarner Losh u_char *cptr = (u_char *)addr; 154369acd21dSWarner Losh fdc_p fdc = &fdc_data[fdcu]; 154469acd21dSWarner Losh int io = fdc->baseport; 154569acd21dSWarner Losh 154669acd21dSWarner Losh if (flags & B_READ) { 154769acd21dSWarner Losh if (fdc->state != PIOREAD) { 154869acd21dSWarner Losh fdc->state = PIOREAD; 154969acd21dSWarner Losh return(0); 155069acd21dSWarner Losh }; 155169acd21dSWarner Losh SET_BCDR(0,count,io); 155269acd21dSWarner Losh insb(io+FDC_YE_DATAPORT,cptr,count); 155369acd21dSWarner Losh } else { 155469acd21dSWarner Losh outsb(io+FDC_YE_DATAPORT,cptr,count); 155569acd21dSWarner Losh SET_BCDR(0,count,io); 155669acd21dSWarner Losh }; 155769acd21dSWarner Losh return(1); 155869acd21dSWarner Losh } 155969acd21dSWarner Losh #endif /* FDC_YE */ 156069acd21dSWarner Losh 15615b81b6b3SRodney W. Grimes /***********************************************************************\ 15625b81b6b3SRodney W. Grimes * The controller state machine. * 15635b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly * 15645b81b6b3SRodney W. Grimes \***********************************************************************/ 15653a2f7427SDavid Greenman static int 15666182fdbdSPeter Wemm fdstate(fdc_p fdc) 15675b81b6b3SRodney W. Grimes { 15685c1a1eaeSBruce Evans int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; 1569bb6382faSJoerg Wunsch unsigned blknum = 0, b_cylinder = 0; 15705b81b6b3SRodney W. Grimes fdu_t fdu = fdc->fdu; 15715b81b6b3SRodney W. Grimes fd_p fd; 157217542807SPoul-Henning Kamp register struct buf *bp; 1573b39c878eSAndrey A. Chernov struct fd_formb *finfo = NULL; 15743a2f7427SDavid Greenman size_t fdblk; 15755b81b6b3SRodney W. Grimes 1576e93e63cbSBruce Evans bp = fdc->bp; 1577e93e63cbSBruce Evans if (bp == NULL) { 157802a19910SJustin T. Gibbs bp = bufq_first(&fdc->head); 1579e93e63cbSBruce Evans if (bp != NULL) { 1580e93e63cbSBruce Evans bufq_remove(&fdc->head, bp); 1581e93e63cbSBruce Evans fdc->bp = bp; 1582e93e63cbSBruce Evans } 1583e93e63cbSBruce Evans } 1584e93e63cbSBruce Evans if (bp == NULL) { 15855b81b6b3SRodney W. Grimes /***********************************************\ 15865b81b6b3SRodney W. Grimes * nothing left for this controller to do * 15875b81b6b3SRodney W. Grimes * Force into the IDLE state, * 15885b81b6b3SRodney W. Grimes \***********************************************/ 15895b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 15906182fdbdSPeter Wemm if (fdc->fd) { 15916182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 15926182fdbdSPeter Wemm printf("unexpected valid fd pointer\n"); 15935b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 15945b81b6b3SRodney W. Grimes fdc->fdu = -1; 15955b81b6b3SRodney W. Grimes } 15966182fdbdSPeter Wemm TRACE1("[fdc%d IDLE]", fdc->fdcu); 15975b81b6b3SRodney W. Grimes return (0); 15985b81b6b3SRodney W. Grimes } 15995b81b6b3SRodney W. Grimes fdu = FDUNIT(minor(bp->b_dev)); 16006182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 16013a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 16026182fdbdSPeter Wemm if (fdc->fd && (fd != fdc->fd)) { 16036182fdbdSPeter Wemm device_print_prettyname(fd->dev); 16046182fdbdSPeter Wemm printf("confused fd pointers\n"); 16055b81b6b3SRodney W. Grimes } 16065b81b6b3SRodney W. Grimes read = bp->b_flags & B_READ; 1607b39c878eSAndrey A. Chernov format = bp->b_flags & B_FORMAT; 1608bb6382faSJoerg Wunsch if (format) { 1609ab3f7469SPoul-Henning Kamp finfo = (struct fd_formb *)bp->b_data; 1610bb6382faSJoerg Wunsch fd->skip = (char *)&(finfo->fd_formb_cylno(0)) 1611bb6382faSJoerg Wunsch - (char *)finfo; 1612bb6382faSJoerg Wunsch } 1613bb6382faSJoerg Wunsch if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { 16143e425b96SJulian Elischer blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + 1615bb6382faSJoerg Wunsch fd->skip/fdblk; 1616bb6382faSJoerg Wunsch b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1617bb6382faSJoerg Wunsch } 16185b81b6b3SRodney W. Grimes TRACE1("fd%d", fdu); 16195b81b6b3SRodney W. Grimes TRACE1("[%s]", fdstates[fdc->state]); 16205b81b6b3SRodney W. Grimes TRACE1("(0x%x)", fd->flags); 16216182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); 16226182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 16235b81b6b3SRodney W. Grimes switch (fdc->state) 16245b81b6b3SRodney W. Grimes { 16255b81b6b3SRodney W. Grimes case DEVIDLE: 16265b81b6b3SRodney W. Grimes case FINDWORK: /* we have found new work */ 16275b81b6b3SRodney W. Grimes fdc->retry = 0; 16285b81b6b3SRodney W. Grimes fd->skip = 0; 16295b81b6b3SRodney W. Grimes fdc->fd = fd; 16305b81b6b3SRodney W. Grimes fdc->fdu = fdu; 16313a2f7427SDavid Greenman outb(fdc->baseport+FDCTL, fd->ft->trans); 16323a2f7427SDavid Greenman TRACE1("[0x%x->FDCTL]", fd->ft->trans); 16335b81b6b3SRodney W. Grimes /*******************************************************\ 16345b81b6b3SRodney W. Grimes * If the next drive has a motor startup pending, then * 1635dc733423SDag-Erling Smørgrav * it will start up in its own good time * 16365b81b6b3SRodney W. Grimes \*******************************************************/ 16376182fdbdSPeter Wemm if(fd->flags & FD_MOTOR_WAIT) { 16385b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 16395b81b6b3SRodney W. Grimes return (0); /* come back later */ 16405b81b6b3SRodney W. Grimes } 16415b81b6b3SRodney W. Grimes /*******************************************************\ 16425b81b6b3SRodney W. Grimes * Maybe if it's not starting, it SHOULD be starting * 16435b81b6b3SRodney W. Grimes \*******************************************************/ 16445b81b6b3SRodney W. Grimes if (!(fd->flags & FD_MOTOR)) 16455b81b6b3SRodney W. Grimes { 16465b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 16476182fdbdSPeter Wemm fd_turnon(fd); 16485b81b6b3SRodney W. Grimes return (0); 16495b81b6b3SRodney W. Grimes } 16505b81b6b3SRodney W. Grimes else /* at least make sure we are selected */ 16515b81b6b3SRodney W. Grimes { 16526182fdbdSPeter Wemm set_motor(fdc, fd->fdsu, TURNON); 16535b81b6b3SRodney W. Grimes } 16545c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 16555c1a1eaeSBruce Evans fdc->state = RESETCTLR; 16565c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 16575c1a1eaeSBruce Evans } else 16585e235068SJordan K. Hubbard fdc->state = DOSEEK; 16595b81b6b3SRodney W. Grimes break; 16605b81b6b3SRodney W. Grimes case DOSEEK: 1661bb6382faSJoerg Wunsch if (b_cylinder == (unsigned)fd->track) 16625b81b6b3SRodney W. Grimes { 16635b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 16645b81b6b3SRodney W. Grimes break; 16655b81b6b3SRodney W. Grimes } 16666182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, 1667bb6382faSJoerg Wunsch fd->fdsu, b_cylinder * fd->ft->steptrac, 1668dc5df763SJoerg Wunsch 0)) 1669dc8603e3SJoerg Wunsch { 1670dc8603e3SJoerg Wunsch /* 1671dc8603e3SJoerg Wunsch * seek command not accepted, looks like 1672dc8603e3SJoerg Wunsch * the FDC went off to the Saints... 1673dc8603e3SJoerg Wunsch */ 1674dc8603e3SJoerg Wunsch fdc->retry = 6; /* try a reset */ 16756182fdbdSPeter Wemm return(retrier(fdc)); 1676dc8603e3SJoerg Wunsch } 1677dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 16785b81b6b3SRodney W. Grimes fdc->state = SEEKWAIT; 16795b81b6b3SRodney W. Grimes return(0); /* will return later */ 16805b81b6b3SRodney W. Grimes case SEEKWAIT: 16815b81b6b3SRodney W. Grimes /* allow heads to settle */ 16826182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 16); 16835b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 16845b81b6b3SRodney W. Grimes return(0); /* will return later */ 16855b81b6b3SRodney W. Grimes case SEEKCOMPLETE : /* SEEK DONE, START DMA */ 16865b81b6b3SRodney W. Grimes /* Make sure seek really happened*/ 16876182fdbdSPeter Wemm if(fd->track == FD_NO_TRACK) { 1688bb6382faSJoerg Wunsch int descyl = b_cylinder * fd->ft->steptrac; 16893a2f7427SDavid Greenman do { 16903a2f7427SDavid Greenman /* 1691dc5df763SJoerg Wunsch * This might be a "ready changed" interrupt, 1692dc5df763SJoerg Wunsch * which cannot really happen since the 1693dc5df763SJoerg Wunsch * RDY pin is hardwired to + 5 volts. This 1694dc5df763SJoerg Wunsch * generally indicates a "bouncing" intr 1695dc5df763SJoerg Wunsch * line, so do one of the following: 1696dc5df763SJoerg Wunsch * 1697dc5df763SJoerg Wunsch * When running on an enhanced FDC that is 1698dc5df763SJoerg Wunsch * known to not go stuck after responding 1699dc5df763SJoerg Wunsch * with INVALID, fetch all interrupt states 1700dc5df763SJoerg Wunsch * until seeing either an INVALID or a 1701dc5df763SJoerg Wunsch * real interrupt condition. 1702dc5df763SJoerg Wunsch * 1703dc5df763SJoerg Wunsch * When running on a dumb old NE765, give 1704dc5df763SJoerg Wunsch * up immediately. The controller will 1705dc5df763SJoerg Wunsch * provide up to four dummy RC interrupt 1706dc5df763SJoerg Wunsch * conditions right after reset (for the 1707dc5df763SJoerg Wunsch * corresponding four drives), so this is 1708dc5df763SJoerg Wunsch * our only chance to get notice that it 1709dc5df763SJoerg Wunsch * was not the FDC that caused the interrupt. 17103a2f7427SDavid Greenman */ 1711dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) 1712dc5df763SJoerg Wunsch == FD_NOT_VALID) 1713dc5df763SJoerg Wunsch return 0; 1714dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 1715dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 1716dc5df763SJoerg Wunsch return 0; /* hope for a real intr */ 17173a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 1718dc5df763SJoerg Wunsch 17196182fdbdSPeter Wemm if (0 == descyl) { 1720dc5df763SJoerg Wunsch int failed = 0; 17213a2f7427SDavid Greenman /* 17223a2f7427SDavid Greenman * seek to cyl 0 requested; make sure we are 17233a2f7427SDavid Greenman * really there 17243a2f7427SDavid Greenman */ 1725dc5df763SJoerg Wunsch if (fd_sense_drive_status(fdc, &st3)) 1726dc5df763SJoerg Wunsch failed = 1; 17273a2f7427SDavid Greenman if ((st3 & NE7_ST3_T0) == 0) { 17283a2f7427SDavid Greenman printf( 17293a2f7427SDavid Greenman "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", 17303a2f7427SDavid Greenman fdu, st3, NE7_ST3BITS); 1731dc5df763SJoerg Wunsch failed = 1; 1732dc5df763SJoerg Wunsch } 1733dc5df763SJoerg Wunsch 17346182fdbdSPeter Wemm if (failed) { 17353a2f7427SDavid Greenman if(fdc->retry < 3) 17363a2f7427SDavid Greenman fdc->retry = 3; 17376182fdbdSPeter Wemm return (retrier(fdc)); 17383a2f7427SDavid Greenman } 17393a2f7427SDavid Greenman } 1740dc5df763SJoerg Wunsch 17416182fdbdSPeter Wemm if (cyl != descyl) { 17423a2f7427SDavid Greenman printf( 17433a2f7427SDavid Greenman "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", 17442d9d0204SRodney W. Grimes fdu, descyl, cyl, st0); 1745e5d7d243SBruce Evans if (fdc->retry < 3) 1746e5d7d243SBruce Evans fdc->retry = 3; 17476182fdbdSPeter Wemm return (retrier(fdc)); 17485b81b6b3SRodney W. Grimes } 17495b81b6b3SRodney W. Grimes } 17505b81b6b3SRodney W. Grimes 1751bb6382faSJoerg Wunsch fd->track = b_cylinder; 175269acd21dSWarner Losh #ifdef FDC_YE 175369acd21dSWarner Losh if (!(fdc->flags & FDC_PCMCIA)) 175469acd21dSWarner Losh #endif 1755ab3f7469SPoul-Henning Kamp isa_dmastart(bp->b_flags, bp->b_data+fd->skip, 17563a2f7427SDavid Greenman format ? bp->b_bcount : fdblk, fdc->dmachan); 17575b81b6b3SRodney W. Grimes sectrac = fd->ft->sectrac; 17585b81b6b3SRodney W. Grimes sec = blknum % (sectrac * fd->ft->heads); 17595b81b6b3SRodney W. Grimes head = sec / sectrac; 17605b81b6b3SRodney W. Grimes sec = sec % sectrac + 1; 17613a2f7427SDavid Greenman fd->hddrv = ((head&1)<<2)+fdu; 17623a2f7427SDavid Greenman 17633a2f7427SDavid Greenman if(format || !read) 17643a2f7427SDavid Greenman { 17653a2f7427SDavid Greenman /* make sure the drive is writable */ 1766dc5df763SJoerg Wunsch if(fd_sense_drive_status(fdc, &st3) != 0) 1767dc8603e3SJoerg Wunsch { 1768dc8603e3SJoerg Wunsch /* stuck controller? */ 17695c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 17705c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 17715c1a1eaeSBruce Evans fdc->dmachan); 1772dc8603e3SJoerg Wunsch fdc->retry = 6; /* reset the beast */ 17736182fdbdSPeter Wemm return (retrier(fdc)); 1774dc8603e3SJoerg Wunsch } 17753a2f7427SDavid Greenman if(st3 & NE7_ST3_WP) 17763a2f7427SDavid Greenman { 17773a2f7427SDavid Greenman /* 17783a2f7427SDavid Greenman * XXX YES! this is ugly. 17793a2f7427SDavid Greenman * in order to force the current operation 17803a2f7427SDavid Greenman * to fail, we will have to fake an FDC 17813a2f7427SDavid Greenman * error - all error handling is done 17823a2f7427SDavid Greenman * by the retrier() 17833a2f7427SDavid Greenman */ 17843a2f7427SDavid Greenman fdc->status[0] = NE7_ST0_IC_AT; 17853a2f7427SDavid Greenman fdc->status[1] = NE7_ST1_NW; 17863a2f7427SDavid Greenman fdc->status[2] = 0; 17873a2f7427SDavid Greenman fdc->status[3] = fd->track; 17883a2f7427SDavid Greenman fdc->status[4] = head; 17893a2f7427SDavid Greenman fdc->status[5] = sec; 17903a2f7427SDavid Greenman fdc->retry = 8; /* break out immediately */ 17913a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; /* not really... */ 17923a2f7427SDavid Greenman return (1); 17933a2f7427SDavid Greenman } 17943a2f7427SDavid Greenman } 17955b81b6b3SRodney W. Grimes 17966182fdbdSPeter Wemm if (format) { 179769acd21dSWarner Losh #ifdef FDC_YE 179869acd21dSWarner Losh if (fdc->flags & FDC_PCMCIA) 179969acd21dSWarner Losh (void)fdcpio(fdcu,bp->b_flags, 180069acd21dSWarner Losh bp->b_data+fd->skip, 180169acd21dSWarner Losh bp->b_bcount); 180269acd21dSWarner Losh #endif 1803b39c878eSAndrey A. Chernov /* formatting */ 18046182fdbdSPeter Wemm if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, 1805dc5df763SJoerg Wunsch finfo->fd_formb_secshift, 1806dc5df763SJoerg Wunsch finfo->fd_formb_nsecs, 1807dc5df763SJoerg Wunsch finfo->fd_formb_gaplen, 18086182fdbdSPeter Wemm finfo->fd_formb_fillbyte, 0)) { 1809dc8603e3SJoerg Wunsch /* controller fell over */ 18105c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 18115c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 18125c1a1eaeSBruce Evans fdc->dmachan); 1813dc8603e3SJoerg Wunsch fdc->retry = 6; 18146182fdbdSPeter Wemm return (retrier(fdc)); 1815dc8603e3SJoerg Wunsch } 18166182fdbdSPeter Wemm } else { 181769acd21dSWarner Losh #ifdef FDC_YE 181869acd21dSWarner Losh if (fdc->flags & FDC_PCMCIA) { 181969acd21dSWarner Losh /* 182069acd21dSWarner Losh * this seems to be necessary even when 182169acd21dSWarner Losh * reading data 182269acd21dSWarner Losh */ 182369acd21dSWarner Losh SET_BCDR(1,fdblk,fdc->baseport); 182469acd21dSWarner Losh 182569acd21dSWarner Losh /* 182669acd21dSWarner Losh * perform the write pseudo-DMA before 182769acd21dSWarner Losh * the WRITE command is sent 182869acd21dSWarner Losh */ 182969acd21dSWarner Losh if (!read) 183069acd21dSWarner Losh (void)fdcpio(fdcu,bp->b_flags, 183169acd21dSWarner Losh bp->b_data+fd->skip, 183269acd21dSWarner Losh fdblk); 183369acd21dSWarner Losh } 183469acd21dSWarner Losh #endif 18356182fdbdSPeter Wemm if (fd_cmd(fdc, 9, 1836dc5df763SJoerg Wunsch (read ? NE7CMD_READ : NE7CMD_WRITE), 1837dc5df763SJoerg Wunsch head << 2 | fdu, /* head & unit */ 1838dc5df763SJoerg Wunsch fd->track, /* track */ 1839dc5df763SJoerg Wunsch head, 1840dc5df763SJoerg Wunsch sec, /* sector + 1 */ 1841dc5df763SJoerg Wunsch fd->ft->secsize, /* sector size */ 1842dc5df763SJoerg Wunsch sectrac, /* sectors/track */ 1843dc5df763SJoerg Wunsch fd->ft->gap, /* gap size */ 1844dc5df763SJoerg Wunsch fd->ft->datalen, /* data length */ 18456182fdbdSPeter Wemm 0)) { 1846dc8603e3SJoerg Wunsch /* the beast is sleeping again */ 18475c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 18485c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 18495c1a1eaeSBruce Evans fdc->dmachan); 1850dc8603e3SJoerg Wunsch fdc->retry = 6; 18516182fdbdSPeter Wemm return (retrier(fdc)); 18525b81b6b3SRodney W. Grimes } 1853b39c878eSAndrey A. Chernov } 185469acd21dSWarner Losh #ifdef FDC_YE 185569acd21dSWarner Losh if (fdc->flags & FDC_PCMCIA) 185669acd21dSWarner Losh /* 185769acd21dSWarner Losh * if this is a read, then simply await interrupt 185869acd21dSWarner Losh * before performing PIO 185969acd21dSWarner Losh */ 186069acd21dSWarner Losh if (read && !fdcpio(fdcu,bp->b_flags, 186169acd21dSWarner Losh bp->b_data+fd->skip,fdblk)) { 186269acd21dSWarner Losh fd->tohandle = timeout(fd_iotimeout, 186369acd21dSWarner Losh (caddr_t)fdcu, hz); 186469acd21dSWarner Losh return(0); /* will return later */ 186569acd21dSWarner Losh }; 186669acd21dSWarner Losh 186769acd21dSWarner Losh /* 186869acd21dSWarner Losh * write (or format) operation will fall through and 186969acd21dSWarner Losh * await completion interrupt 187069acd21dSWarner Losh */ 187169acd21dSWarner Losh #endif 18725b81b6b3SRodney W. Grimes fdc->state = IOCOMPLETE; 18736182fdbdSPeter Wemm fd->tohandle = timeout(fd_iotimeout, fdc, hz); 18745b81b6b3SRodney W. Grimes return (0); /* will return later */ 187569acd21dSWarner Losh #ifdef FDC_YE 187669acd21dSWarner Losh case PIOREAD: 187769acd21dSWarner Losh /* 187869acd21dSWarner Losh * actually perform the PIO read. The IOCOMPLETE case 187969acd21dSWarner Losh * removes the timeout for us. 188069acd21dSWarner Losh */ 188169acd21dSWarner Losh (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); 188269acd21dSWarner Losh fdc->state = IOCOMPLETE; 188369acd21dSWarner Losh /* FALLTHROUGH */ 188469acd21dSWarner Losh #endif 18855b81b6b3SRodney W. Grimes case IOCOMPLETE: /* IO DONE, post-analyze */ 18866182fdbdSPeter Wemm untimeout(fd_iotimeout, fdc, fd->tohandle); 1887dc5df763SJoerg Wunsch 18886182fdbdSPeter Wemm if (fd_read_status(fdc, fd->fdsu)) { 18895c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 18905c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 18915c1a1eaeSBruce Evans fdc->dmachan); 1892dc5df763SJoerg Wunsch if (fdc->retry < 6) 1893dc5df763SJoerg Wunsch fdc->retry = 6; /* force a reset */ 18946182fdbdSPeter Wemm return (retrier(fdc)); 18955b81b6b3SRodney W. Grimes } 1896dc5df763SJoerg Wunsch 18973a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; 1898dc5df763SJoerg Wunsch 18993a2f7427SDavid Greenman /* FALLTHROUGH */ 1900dc5df763SJoerg Wunsch 19013a2f7427SDavid Greenman case IOTIMEDOUT: 190269acd21dSWarner Losh #ifdef FDC_YE 190369acd21dSWarner Losh if (!(fdc->flags & FDC_PCMCIA)) 190469acd21dSWarner Losh #endif 1905ab3f7469SPoul-Henning Kamp isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 19063a2f7427SDavid Greenman format ? bp->b_bcount : fdblk, fdc->dmachan); 19076182fdbdSPeter Wemm if (fdc->status[0] & NE7_ST0_IC) { 19083a2f7427SDavid Greenman if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 19093a2f7427SDavid Greenman && fdc->status[1] & NE7_ST1_OR) { 1910b39c878eSAndrey A. Chernov /* 19113a2f7427SDavid Greenman * DMA overrun. Someone hogged the bus 19123a2f7427SDavid Greenman * and didn't release it in time for the 19133a2f7427SDavid Greenman * next FDC transfer. 19143a2f7427SDavid Greenman * Just restart it, don't increment retry 19153a2f7427SDavid Greenman * count. (vak) 1916b39c878eSAndrey A. Chernov */ 1917b39c878eSAndrey A. Chernov fdc->state = SEEKCOMPLETE; 1918b39c878eSAndrey A. Chernov return (1); 1919b39c878eSAndrey A. Chernov } 19203a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV 19213a2f7427SDavid Greenman && fdc->retry < 6) 19223a2f7427SDavid Greenman fdc->retry = 6; /* force a reset */ 19233a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 19243a2f7427SDavid Greenman && fdc->status[2] & NE7_ST2_WC 19253a2f7427SDavid Greenman && fdc->retry < 3) 19263a2f7427SDavid Greenman fdc->retry = 3; /* force recalibrate */ 19276182fdbdSPeter Wemm return (retrier(fdc)); 19285b81b6b3SRodney W. Grimes } 19295b81b6b3SRodney W. Grimes /* All OK */ 19303a2f7427SDavid Greenman fd->skip += fdblk; 19316182fdbdSPeter Wemm if (!format && fd->skip < bp->b_bcount - bp->b_resid) { 19325b81b6b3SRodney W. Grimes /* set up next transfer */ 19335b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 19346182fdbdSPeter Wemm } else { 19355b81b6b3SRodney W. Grimes /* ALL DONE */ 19365b81b6b3SRodney W. Grimes fd->skip = 0; 1937e93e63cbSBruce Evans fdc->bp = NULL; 19382186cd9eSPoul-Henning Kamp devstat_end_transaction_buf(&fd->device_stats, bp); 19395b81b6b3SRodney W. Grimes biodone(bp); 19405b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 19415b81b6b3SRodney W. Grimes fdc->fdu = -1; 19425b81b6b3SRodney W. Grimes fdc->state = FINDWORK; 19435b81b6b3SRodney W. Grimes } 19445b81b6b3SRodney W. Grimes return (1); 19455b81b6b3SRodney W. Grimes case RESETCTLR: 19463a2f7427SDavid Greenman fdc_reset(fdc); 19475b81b6b3SRodney W. Grimes fdc->retry++; 19485c1a1eaeSBruce Evans fdc->state = RESETCOMPLETE; 19495c1a1eaeSBruce Evans return (0); 19505c1a1eaeSBruce Evans case RESETCOMPLETE: 19515c1a1eaeSBruce Evans /* 19525c1a1eaeSBruce Evans * Discard all the results from the reset so that they 19535c1a1eaeSBruce Evans * can't cause an unexpected interrupt later. 19545c1a1eaeSBruce Evans */ 19550e317d05SJoerg Wunsch for (i = 0; i < 4; i++) 19560e317d05SJoerg Wunsch (void)fd_sense_int(fdc, &st0, &cyl); 19575c1a1eaeSBruce Evans fdc->state = STARTRECAL; 19585c1a1eaeSBruce Evans /* Fall through. */ 19595c1a1eaeSBruce Evans case STARTRECAL: 19606182fdbdSPeter Wemm if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { 1961dc8603e3SJoerg Wunsch /* arrgl */ 1962dc8603e3SJoerg Wunsch fdc->retry = 6; 19636182fdbdSPeter Wemm return (retrier(fdc)); 1964dc8603e3SJoerg Wunsch } 19655b81b6b3SRodney W. Grimes fdc->state = RECALWAIT; 19665b81b6b3SRodney W. Grimes return (0); /* will return later */ 19675b81b6b3SRodney W. Grimes case RECALWAIT: 19685b81b6b3SRodney W. Grimes /* allow heads to settle */ 19696182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 8); 19705b81b6b3SRodney W. Grimes fdc->state = RECALCOMPLETE; 19715b81b6b3SRodney W. Grimes return (0); /* will return later */ 19725b81b6b3SRodney W. Grimes case RECALCOMPLETE: 19733a2f7427SDavid Greenman do { 19743a2f7427SDavid Greenman /* 1975dc5df763SJoerg Wunsch * See SEEKCOMPLETE for a comment on this: 19763a2f7427SDavid Greenman */ 1977dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) 1978dc5df763SJoerg Wunsch return 0; 1979dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 1980dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 1981dc5df763SJoerg Wunsch return 0; /* hope for a real intr */ 19823a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 19833a2f7427SDavid Greenman if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) 19845b81b6b3SRodney W. Grimes { 1985dc8603e3SJoerg Wunsch if(fdc->retry > 3) 1986dc8603e3SJoerg Wunsch /* 1987dc8603e3SJoerg Wunsch * a recalibrate from beyond cylinder 77 1988dc8603e3SJoerg Wunsch * will "fail" due to the FDC limitations; 1989dc8603e3SJoerg Wunsch * since people used to complain much about 1990dc8603e3SJoerg Wunsch * the failure message, try not logging 1991dc8603e3SJoerg Wunsch * this one if it seems to be the first 1992dc8603e3SJoerg Wunsch * time in a line 1993dc8603e3SJoerg Wunsch */ 1994dc8603e3SJoerg Wunsch printf("fd%d: recal failed ST0 %b cyl %d\n", 1995dc8603e3SJoerg Wunsch fdu, st0, NE7_ST0BITS, cyl); 19963a2f7427SDavid Greenman if(fdc->retry < 3) fdc->retry = 3; 19976182fdbdSPeter Wemm return (retrier(fdc)); 19985b81b6b3SRodney W. Grimes } 19995b81b6b3SRodney W. Grimes fd->track = 0; 20005b81b6b3SRodney W. Grimes /* Seek (probably) necessary */ 20015b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 20025b81b6b3SRodney W. Grimes return (1); /* will return immediatly */ 20035b81b6b3SRodney W. Grimes case MOTORWAIT: 20045b81b6b3SRodney W. Grimes if(fd->flags & FD_MOTOR_WAIT) 20055b81b6b3SRodney W. Grimes { 20065b81b6b3SRodney W. Grimes return (0); /* time's not up yet */ 20075b81b6b3SRodney W. Grimes } 20085c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 20095c1a1eaeSBruce Evans fdc->state = RESETCTLR; 20105c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 20115c1a1eaeSBruce Evans } else { 20125e235068SJordan K. Hubbard /* 20135c1a1eaeSBruce Evans * If all motors were off, then the controller was 20145c1a1eaeSBruce Evans * reset, so it has lost track of the current 20155c1a1eaeSBruce Evans * cylinder. Recalibrate to handle this case. 2016f86e4077SBruce Evans * But first, discard the results of the reset. 20175e235068SJordan K. Hubbard */ 2018f86e4077SBruce Evans fdc->state = RESETCOMPLETE; 20195c1a1eaeSBruce Evans } 20205b81b6b3SRodney W. Grimes return (1); /* will return immediatly */ 20215b81b6b3SRodney W. Grimes default: 20226182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 20236182fdbdSPeter Wemm printf("unexpected FD int->"); 2024dc5df763SJoerg Wunsch if (fd_read_status(fdc, fd->fdsu) == 0) 2025a838d83dSBruce Evans printf("FDC status :%x %x %x %x %x %x %x ", 20265b81b6b3SRodney W. Grimes fdc->status[0], 20275b81b6b3SRodney W. Grimes fdc->status[1], 20285b81b6b3SRodney W. Grimes fdc->status[2], 20295b81b6b3SRodney W. Grimes fdc->status[3], 20305b81b6b3SRodney W. Grimes fdc->status[4], 20315b81b6b3SRodney W. Grimes fdc->status[5], 20325b81b6b3SRodney W. Grimes fdc->status[6] ); 20333a2f7427SDavid Greenman else 2034dac0f2dbSJoerg Wunsch printf("No status available "); 2035dac0f2dbSJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) != 0) 2036dac0f2dbSJoerg Wunsch { 2037dac0f2dbSJoerg Wunsch printf("[controller is dead now]\n"); 20385b81b6b3SRodney W. Grimes return (0); 20395b81b6b3SRodney W. Grimes } 2040dac0f2dbSJoerg Wunsch printf("ST0 = %x, PCN = %x\n", st0, cyl); 2041dac0f2dbSJoerg Wunsch return (0); 2042dac0f2dbSJoerg Wunsch } 2043dac0f2dbSJoerg Wunsch /*XXX confusing: some branches return immediately, others end up here*/ 20445b81b6b3SRodney W. Grimes return (1); /* Come back immediatly to new state */ 20455b81b6b3SRodney W. Grimes } 20465b81b6b3SRodney W. Grimes 2047aaf08d94SGarrett Wollman static int 20486182fdbdSPeter Wemm retrier(struct fdc_data *fdc) 20495b81b6b3SRodney W. Grimes { 205017542807SPoul-Henning Kamp register struct buf *bp; 20516182fdbdSPeter Wemm struct fd_data *fd; 20526182fdbdSPeter Wemm int fdu; 20535b81b6b3SRodney W. Grimes 2054e93e63cbSBruce Evans bp = fdc->bp; 20555b81b6b3SRodney W. Grimes 20566182fdbdSPeter Wemm /* XXX shouldn't this be cached somewhere? */ 20576182fdbdSPeter Wemm fdu = FDUNIT(minor(bp->b_dev)); 20586182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 20596182fdbdSPeter Wemm if (fd->options & FDOPT_NORETRY) 20603a2f7427SDavid Greenman goto fail; 20616182fdbdSPeter Wemm 20626182fdbdSPeter Wemm switch (fdc->retry) { 20635b81b6b3SRodney W. Grimes case 0: case 1: case 2: 20645b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 20655b81b6b3SRodney W. Grimes break; 20665b81b6b3SRodney W. Grimes case 3: case 4: case 5: 20675b81b6b3SRodney W. Grimes fdc->state = STARTRECAL; 20685b81b6b3SRodney W. Grimes break; 20695b81b6b3SRodney W. Grimes case 6: 20705b81b6b3SRodney W. Grimes fdc->state = RESETCTLR; 20715b81b6b3SRodney W. Grimes break; 20725b81b6b3SRodney W. Grimes case 7: 20735b81b6b3SRodney W. Grimes break; 20745b81b6b3SRodney W. Grimes default: 20753a2f7427SDavid Greenman fail: 20765b81b6b3SRodney W. Grimes { 20777ca0641bSAndrey A. Chernov dev_t sav_b_dev = bp->b_dev; 20787ca0641bSAndrey A. Chernov /* Trick diskerr */ 20793a2f7427SDavid Greenman bp->b_dev = makedev(major(bp->b_dev), 20803a2f7427SDavid Greenman (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); 2081887ba12fSBruce Evans diskerr(bp, "hard error", LOG_PRINTF, 20823a2f7427SDavid Greenman fdc->fd->skip / DEV_BSIZE, 20833a2f7427SDavid Greenman (struct disklabel *)NULL); 20847ca0641bSAndrey A. Chernov bp->b_dev = sav_b_dev; 2085dc5df763SJoerg Wunsch if (fdc->flags & FDC_STAT_VALID) 2086dc5df763SJoerg Wunsch { 2087dc5df763SJoerg Wunsch printf( 2088a838d83dSBruce Evans " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", 2089dc5df763SJoerg Wunsch fdc->status[0], NE7_ST0BITS, 2090dc5df763SJoerg Wunsch fdc->status[1], NE7_ST1BITS, 2091dc5df763SJoerg Wunsch fdc->status[2], NE7_ST2BITS, 2092dc5df763SJoerg Wunsch fdc->status[3], fdc->status[4], 2093dc5df763SJoerg Wunsch fdc->status[5]); 2094dc5df763SJoerg Wunsch } 2095dc5df763SJoerg Wunsch else 2096dc5df763SJoerg Wunsch printf(" (No status)\n"); 20975b81b6b3SRodney W. Grimes } 20985b81b6b3SRodney W. Grimes bp->b_flags |= B_ERROR; 20995b81b6b3SRodney W. Grimes bp->b_error = EIO; 2100bb6382faSJoerg Wunsch bp->b_resid += bp->b_bcount - fdc->fd->skip; 2101e93e63cbSBruce Evans fdc->bp = NULL; 21025b81b6b3SRodney W. Grimes fdc->fd->skip = 0; 210311a0be87SPoul-Henning Kamp devstat_end_transaction_buf(&fdc->fd->device_stats, bp); 21045b81b6b3SRodney W. Grimes biodone(bp); 210592ed385aSRodney W. Grimes fdc->state = FINDWORK; 21065c1a1eaeSBruce Evans fdc->flags |= FDC_NEEDS_RESET; 21075b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 21085b81b6b3SRodney W. Grimes fdc->fdu = -1; 210992ed385aSRodney W. Grimes return (1); 21105b81b6b3SRodney W. Grimes } 21115b81b6b3SRodney W. Grimes fdc->retry++; 21125b81b6b3SRodney W. Grimes return (1); 21135b81b6b3SRodney W. Grimes } 21145b81b6b3SRodney W. Grimes 2115b39c878eSAndrey A. Chernov static int 2116b39c878eSAndrey A. Chernov fdformat(dev, finfo, p) 2117b39c878eSAndrey A. Chernov dev_t dev; 2118b39c878eSAndrey A. Chernov struct fd_formb *finfo; 2119b39c878eSAndrey A. Chernov struct proc *p; 2120b39c878eSAndrey A. Chernov { 2121b39c878eSAndrey A. Chernov fdu_t fdu; 2122b39c878eSAndrey A. Chernov fd_p fd; 2123b39c878eSAndrey A. Chernov 2124b39c878eSAndrey A. Chernov struct buf *bp; 2125b39c878eSAndrey A. Chernov int rv = 0, s; 21263a2f7427SDavid Greenman size_t fdblk; 2127b39c878eSAndrey A. Chernov 2128b39c878eSAndrey A. Chernov fdu = FDUNIT(minor(dev)); 21296182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 21303a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2131b39c878eSAndrey A. Chernov 2132b39c878eSAndrey A. Chernov /* set up a buffer header for fdstrategy() */ 2133b39c878eSAndrey A. Chernov bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); 2134b39c878eSAndrey A. Chernov if(bp == 0) 2135b39c878eSAndrey A. Chernov return ENOBUFS; 213682f5379bSJoerg Wunsch /* 213782f5379bSJoerg Wunsch * keep the process from being swapped 213882f5379bSJoerg Wunsch */ 21392ada239cSPeter Wemm PHOLD(p); 2140b39c878eSAndrey A. Chernov bzero((void *)bp, sizeof(struct buf)); 214167812eacSKirk McKusick BUF_LOCKINIT(bp); 214267812eacSKirk McKusick BUF_LOCK(bp, LK_EXCLUSIVE); 214367812eacSKirk McKusick bp->b_flags = B_PHYS | B_FORMAT; 2144b39c878eSAndrey A. Chernov 2145b39c878eSAndrey A. Chernov /* 2146b39c878eSAndrey A. Chernov * calculate a fake blkno, so fdstrategy() would initiate a 2147b39c878eSAndrey A. Chernov * seek to the requested cylinder 2148b39c878eSAndrey A. Chernov */ 2149b39c878eSAndrey A. Chernov bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) 21503a2f7427SDavid Greenman + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; 2151b39c878eSAndrey A. Chernov 2152b39c878eSAndrey A. Chernov bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; 2153ab3f7469SPoul-Henning Kamp bp->b_data = (caddr_t)finfo; 2154b39c878eSAndrey A. Chernov 2155b39c878eSAndrey A. Chernov /* now do the format */ 21563e425b96SJulian Elischer bp->b_dev = dev; 215749ff4debSPoul-Henning Kamp BUF_STRATEGY(bp, 0); 2158b39c878eSAndrey A. Chernov 2159b39c878eSAndrey A. Chernov /* ...and wait for it to complete */ 2160b39c878eSAndrey A. Chernov s = splbio(); 21616182fdbdSPeter Wemm while(!(bp->b_flags & B_DONE)) { 21625e235068SJordan K. Hubbard rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); 2163b39c878eSAndrey A. Chernov if (rv == EWOULDBLOCK) 2164b39c878eSAndrey A. Chernov break; 2165b39c878eSAndrey A. Chernov } 2166b39c878eSAndrey A. Chernov splx(s); 2167b39c878eSAndrey A. Chernov 216882f5379bSJoerg Wunsch if (rv == EWOULDBLOCK) { 2169b39c878eSAndrey A. Chernov /* timed out */ 2170b39c878eSAndrey A. Chernov rv = EIO; 217182f5379bSJoerg Wunsch biodone(bp); 217282f5379bSJoerg Wunsch } 21733a2f7427SDavid Greenman if (bp->b_flags & B_ERROR) 21743a2f7427SDavid Greenman rv = bp->b_error; 217582f5379bSJoerg Wunsch /* 217682f5379bSJoerg Wunsch * allow the process to be swapped 217782f5379bSJoerg Wunsch */ 21782ada239cSPeter Wemm PRELE(p); 217967812eacSKirk McKusick BUF_UNLOCK(bp); 218067812eacSKirk McKusick BUF_LOCKFREE(bp); 2181b39c878eSAndrey A. Chernov free(bp, M_TEMP); 2182b39c878eSAndrey A. Chernov return rv; 2183b39c878eSAndrey A. Chernov } 2184b39c878eSAndrey A. Chernov 2185f5f7ba03SJordan K. Hubbard /* 2186671e2ceeSBruce Evans * TODO: don't allocate buffer on stack. 2187f5f7ba03SJordan K. Hubbard */ 21885b81b6b3SRodney W. Grimes 21893e425b96SJulian Elischer static int 2190b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p) 2191f5f7ba03SJordan K. Hubbard dev_t dev; 2192ecbb00a2SDoug Rabson u_long cmd; 2193f5f7ba03SJordan K. Hubbard caddr_t addr; 2194f5f7ba03SJordan K. Hubbard int flag; 2195b39c878eSAndrey A. Chernov struct proc *p; 2196f5f7ba03SJordan K. Hubbard { 21973a2f7427SDavid Greenman fdu_t fdu = FDUNIT(minor(dev)); 21986182fdbdSPeter Wemm fd_p fd = devclass_get_softc(fd_devclass, fdu); 21993a2f7427SDavid Greenman size_t fdblk; 22003a2f7427SDavid Greenman 2201f5f7ba03SJordan K. Hubbard struct fd_type *fdt; 2202f5f7ba03SJordan K. Hubbard struct disklabel *dl; 2203f5f7ba03SJordan K. Hubbard char buffer[DEV_BSIZE]; 22043a2f7427SDavid Greenman int error = 0; 2205f5f7ba03SJordan K. Hubbard 22063a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2207f5f7ba03SJordan K. Hubbard 22086182fdbdSPeter Wemm switch (cmd) { 2209f5f7ba03SJordan K. Hubbard case DIOCGDINFO: 2210f5f7ba03SJordan K. Hubbard bzero(buffer, sizeof (buffer)); 2211f5f7ba03SJordan K. Hubbard dl = (struct disklabel *)buffer; 22123a2f7427SDavid Greenman dl->d_secsize = fdblk; 22136182fdbdSPeter Wemm fdt = fd->ft; 2214f5f7ba03SJordan K. Hubbard dl->d_secpercyl = fdt->size / fdt->tracks; 2215f5f7ba03SJordan K. Hubbard dl->d_type = DTYPE_FLOPPY; 2216f5f7ba03SJordan K. Hubbard 221749ff4debSPoul-Henning Kamp if (readdisklabel(dkmodpart(dev, RAW_PART), dl) 2218191e1a59SBruce Evans == NULL) 2219f5f7ba03SJordan K. Hubbard error = 0; 2220f5f7ba03SJordan K. Hubbard else 2221f5f7ba03SJordan K. Hubbard error = EINVAL; 2222f5f7ba03SJordan K. Hubbard 2223f5f7ba03SJordan K. Hubbard *(struct disklabel *)addr = *dl; 2224f5f7ba03SJordan K. Hubbard break; 2225f5f7ba03SJordan K. Hubbard 2226f5f7ba03SJordan K. Hubbard case DIOCSDINFO: 2227f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2228f5f7ba03SJordan K. Hubbard error = EBADF; 2229f5f7ba03SJordan K. Hubbard break; 2230f5f7ba03SJordan K. Hubbard 2231f5f7ba03SJordan K. Hubbard case DIOCWLABEL: 2232f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2233f5f7ba03SJordan K. Hubbard error = EBADF; 2234f5f7ba03SJordan K. Hubbard break; 2235f5f7ba03SJordan K. Hubbard 2236f5f7ba03SJordan K. Hubbard case DIOCWDINFO: 22376182fdbdSPeter Wemm if ((flag & FWRITE) == 0) { 2238f5f7ba03SJordan K. Hubbard error = EBADF; 2239f5f7ba03SJordan K. Hubbard break; 2240f5f7ba03SJordan K. Hubbard } 2241f5f7ba03SJordan K. Hubbard 2242f5f7ba03SJordan K. Hubbard dl = (struct disklabel *)addr; 2243f5f7ba03SJordan K. Hubbard 2244191e1a59SBruce Evans if ((error = setdisklabel((struct disklabel *)buffer, dl, 2245191e1a59SBruce Evans (u_long)0)) != 0) 2246f5f7ba03SJordan K. Hubbard break; 2247f5f7ba03SJordan K. Hubbard 224849ff4debSPoul-Henning Kamp error = writedisklabel(dev, (struct disklabel *)buffer); 2249b39c878eSAndrey A. Chernov break; 2250b39c878eSAndrey A. Chernov case FD_FORM: 2251b39c878eSAndrey A. Chernov if ((flag & FWRITE) == 0) 2252b39c878eSAndrey A. Chernov error = EBADF; /* must be opened for writing */ 2253b39c878eSAndrey A. Chernov else if (((struct fd_formb *)addr)->format_version != 2254b39c878eSAndrey A. Chernov FD_FORMAT_VERSION) 2255b39c878eSAndrey A. Chernov error = EINVAL; /* wrong version of formatting prog */ 2256b39c878eSAndrey A. Chernov else 2257b39c878eSAndrey A. Chernov error = fdformat(dev, (struct fd_formb *)addr, p); 2258b39c878eSAndrey A. Chernov break; 2259b39c878eSAndrey A. Chernov 2260b39c878eSAndrey A. Chernov case FD_GTYPE: /* get drive type */ 22613e425b96SJulian Elischer *(struct fd_type *)addr = *fd->ft; 2262f5f7ba03SJordan K. Hubbard break; 2263f5f7ba03SJordan K. Hubbard 22643a2f7427SDavid Greenman case FD_STYPE: /* set drive type */ 22653a2f7427SDavid Greenman /* this is considered harmful; only allow for superuser */ 2266f711d546SPoul-Henning Kamp if (suser(p) != 0) 22673a2f7427SDavid Greenman return EPERM; 22683e425b96SJulian Elischer *fd->ft = *(struct fd_type *)addr; 22693a2f7427SDavid Greenman break; 22703a2f7427SDavid Greenman 22713a2f7427SDavid Greenman case FD_GOPTS: /* get drive options */ 22723e425b96SJulian Elischer *(int *)addr = fd->options; 22733a2f7427SDavid Greenman break; 22743a2f7427SDavid Greenman 22753a2f7427SDavid Greenman case FD_SOPTS: /* set drive options */ 22763e425b96SJulian Elischer fd->options = *(int *)addr; 22773a2f7427SDavid Greenman break; 22783a2f7427SDavid Greenman 2279f5f7ba03SJordan K. Hubbard default: 22803a2f7427SDavid Greenman error = ENOTTY; 2281f5f7ba03SJordan K. Hubbard break; 2282f5f7ba03SJordan K. Hubbard } 2283f5f7ba03SJordan K. Hubbard return (error); 2284f5f7ba03SJordan K. Hubbard } 2285f5f7ba03SJordan K. Hubbard 22866182fdbdSPeter Wemm #endif /* NFDC > 0 */ 22873e425b96SJulian Elischer 22883a2f7427SDavid Greenman /* 22893a2f7427SDavid Greenman * Hello emacs, these are the 22903a2f7427SDavid Greenman * Local Variables: 22913a2f7427SDavid Greenman * c-indent-level: 8 22923a2f7427SDavid Greenman * c-continued-statement-offset: 8 22933a2f7427SDavid Greenman * c-continued-brace-offset: 0 22943a2f7427SDavid Greenman * c-brace-offset: -8 22953a2f7427SDavid Greenman * c-brace-imaginary-offset: 0 22963a2f7427SDavid Greenman * c-argdecl-indent: 8 22973a2f7427SDavid Greenman * c-label-offset: -8 22983a2f7427SDavid Greenman * c++-hanging-braces: 1 22993a2f7427SDavid Greenman * c++-access-specifier-offset: -8 23003a2f7427SDavid Greenman * c++-empty-arglist-indent: 8 23013a2f7427SDavid Greenman * c++-friend-offset: 0 23023a2f7427SDavid Greenman * End: 23033a2f7427SDavid Greenman */ 2304