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 5049ff4debSPoul-Henning Kamp * $Id: fd.c,v 1.151 1999/07/29 11:27:33 joerg Exp $ 515b81b6b3SRodney W. Grimes * 525b81b6b3SRodney W. Grimes */ 535b81b6b3SRodney W. Grimes 545b81b6b3SRodney W. Grimes #include "fd.h" 557b778b5eSEivind Eklund #include "opt_devfs.h" 56d2fb4892SJoerg Wunsch #include "opt_fdc.h" 575b81b6b3SRodney W. Grimes 58b99f0a4aSAndrew Moore #if NFDC > 0 59b99f0a4aSAndrew Moore 60b99f0a4aSAndrew Moore #include <sys/param.h> 61b99f0a4aSAndrew Moore #include <sys/systm.h> 62b99f0a4aSAndrew Moore #include <sys/kernel.h> 63b99f0a4aSAndrew Moore #include <sys/buf.h> 646182fdbdSPeter Wemm #include <sys/bus.h> 656182fdbdSPeter Wemm #include <sys/conf.h> 666182fdbdSPeter Wemm #include <sys/disklabel.h> 67b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h> 686182fdbdSPeter Wemm #include <sys/fcntl.h> 69b99f0a4aSAndrew Moore #include <sys/malloc.h> 706182fdbdSPeter Wemm #include <sys/module.h> 713a2f7427SDavid Greenman #include <sys/proc.h> 72b99f0a4aSAndrew Moore #include <sys/syslog.h> 736182fdbdSPeter Wemm 746182fdbdSPeter Wemm #include <sys/bus.h> 756182fdbdSPeter Wemm #include <machine/bus.h> 766182fdbdSPeter Wemm #include <sys/rman.h> 776182fdbdSPeter Wemm 786182fdbdSPeter Wemm #include <machine/clock.h> 796182fdbdSPeter Wemm #include <machine/ioctl_fd.h> 806182fdbdSPeter Wemm #include <machine/resource.h> 81dc5df763SJoerg Wunsch #include <machine/stdarg.h> 826182fdbdSPeter Wemm 838af5d536SJulian Elischer #ifdef DEVFS 848af5d536SJulian Elischer #include <sys/devfsext.h> 853e425b96SJulian Elischer #endif /* DEVFS */ 865b81b6b3SRodney W. Grimes 876182fdbdSPeter Wemm #include <isa/isavar.h> 88a97c75b7SDoug Rabson #include <isa/isareg.h> 89a97c75b7SDoug Rabson #include <isa/fdreg.h> 90a97c75b7SDoug Rabson #include <isa/fdc.h> 91a97c75b7SDoug Rabson #include <isa/rtc.h> 926182fdbdSPeter Wemm 93edfdec19SPeter Wemm #ifdef FDC_YE 94edfdec19SPeter Wemm #undef FDC_YE 95edfdec19SPeter Wemm #warning "fix FDC_YE! - newbus casualty" 96edfdec19SPeter Wemm #endif 97edfdec19SPeter Wemm 98b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */ 99b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX 1005b81b6b3SRodney W. Grimes 1010722d6abSJoerg Wunsch /* configuration flags */ 1020722d6abSJoerg Wunsch #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ 10369acd21dSWarner Losh #ifdef FDC_YE 10469acd21dSWarner Losh #define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's 10569acd21dSWarner Losh a PCMCIA device */ 10669acd21dSWarner Losh #endif 1070722d6abSJoerg Wunsch 1080722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */ 1090722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED 0x1000 1100722d6abSJoerg Wunsch 111dc5df763SJoerg Wunsch /* error returns for fd_cmd() */ 112dc5df763SJoerg Wunsch #define FD_FAILED -1 113dc5df763SJoerg Wunsch #define FD_NOT_VALID -2 114dc5df763SJoerg Wunsch #define FDC_ERRMAX 100 /* do not log more */ 115dc5df763SJoerg Wunsch 116b39c878eSAndrey A. Chernov #define NUMTYPES 14 117b39c878eSAndrey A. Chernov #define NUMDENS (NUMTYPES - 6) 1187ca0641bSAndrey A. Chernov 1193a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */ 120b99f0a4aSAndrew Moore #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ 121b99f0a4aSAndrew Moore #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ 122b99f0a4aSAndrew Moore #define FD_1720 1 123b99f0a4aSAndrew Moore #define FD_1480 2 124b99f0a4aSAndrew Moore #define FD_1440 3 125b99f0a4aSAndrew Moore #define FD_1200 4 126b99f0a4aSAndrew Moore #define FD_820 5 127b99f0a4aSAndrew Moore #define FD_800 6 128b99f0a4aSAndrew Moore #define FD_720 7 129b99f0a4aSAndrew Moore #define FD_360 8 130ed2fa05eSAndrey A. Chernov 131b99f0a4aSAndrew Moore #define FD_1480in5_25 9 132b99f0a4aSAndrew Moore #define FD_1440in5_25 10 133b99f0a4aSAndrew Moore #define FD_820in5_25 11 134b99f0a4aSAndrew Moore #define FD_800in5_25 12 135b99f0a4aSAndrew Moore #define FD_720in5_25 13 136b99f0a4aSAndrew Moore #define FD_360in5_25 14 137b99f0a4aSAndrew Moore 1387ca0641bSAndrey A. Chernov 1396f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] = 1405b81b6b3SRodney W. Grimes { 141126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ 142126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ 143126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ 144126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ 145126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ 146126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ 147126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ 148b0568305SAndrey A. Chernov { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ 149ed2fa05eSAndrey A. Chernov 150126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ 151126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ 152126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ 153126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ 154126518a1SAndrey A. Chernov { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ 155126518a1SAndrey A. Chernov { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ 1565b81b6b3SRodney W. Grimes }; 1575b81b6b3SRodney W. Grimes 158b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2 /* 2 floppies */ 159dc16046fSJoerg Wunsch 1605b81b6b3SRodney W. Grimes /***********************************************************************\ 1615b81b6b3SRodney W. Grimes * Per controller structure. * 1625b81b6b3SRodney W. Grimes \***********************************************************************/ 1636182fdbdSPeter Wemm static devclass_t fdc_devclass; 1645b81b6b3SRodney W. Grimes 1655b81b6b3SRodney W. Grimes /***********************************************************************\ 1665b81b6b3SRodney W. Grimes * Per drive structure. * 167b99f0a4aSAndrew Moore * N per controller (DRVS_PER_CTLR) * 1685b81b6b3SRodney W. Grimes \***********************************************************************/ 1696182fdbdSPeter Wemm struct fd_data { 170b99f0a4aSAndrew Moore struct fdc_data *fdc; /* pointer to controller structure */ 1715b81b6b3SRodney W. Grimes int fdsu; /* this units number on this controller */ 1723a2f7427SDavid Greenman int type; /* Drive type (FD_1440...) */ 1735b81b6b3SRodney W. Grimes struct fd_type *ft; /* pointer to the type descriptor */ 1745b81b6b3SRodney W. Grimes int flags; 1755b81b6b3SRodney W. Grimes #define FD_OPEN 0x01 /* it's open */ 1765b81b6b3SRodney W. Grimes #define FD_ACTIVE 0x02 /* it's active */ 1775b81b6b3SRodney W. Grimes #define FD_MOTOR 0x04 /* motor should be on */ 1785b81b6b3SRodney W. Grimes #define FD_MOTOR_WAIT 0x08 /* motor coming up */ 1795b81b6b3SRodney W. Grimes int skip; 1805b81b6b3SRodney W. Grimes int hddrv; 181dc5df763SJoerg Wunsch #define FD_NO_TRACK -2 1825b81b6b3SRodney W. Grimes int track; /* where we think the head is */ 1833a2f7427SDavid Greenman int options; /* user configurable options, see ioctl_fd.h */ 18402a19910SJustin T. Gibbs struct callout_handle toffhandle; 18502a19910SJustin T. Gibbs struct callout_handle tohandle; 186b2dfb1f9SJustin T. Gibbs struct devstat device_stats; 18787f6c662SJulian Elischer #ifdef DEVFS 18821519754SBruce Evans void *bdevs[1 + NUMDENS + MAXPARTITIONS]; 18921519754SBruce Evans void *cdevs[1 + NUMDENS + MAXPARTITIONS]; 19087f6c662SJulian Elischer #endif 1916182fdbdSPeter Wemm device_t dev; 1926182fdbdSPeter Wemm fdu_t fdu; 1936182fdbdSPeter Wemm }; 1946182fdbdSPeter Wemm static devclass_t fd_devclass; 1955b81b6b3SRodney W. Grimes 1965b81b6b3SRodney W. Grimes /***********************************************************************\ 1975b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used: * 1985b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question * 1995b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller * 2005b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number * 2015b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number * 2025b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit) * 2035b81b6b3SRodney W. Grimes \***********************************************************************/ 204b99f0a4aSAndrew Moore 20569acd21dSWarner Losh #ifdef FDC_YE 20669acd21dSWarner Losh #include "card.h" 20769acd21dSWarner Losh static int yeattach(struct isa_device *); 20869acd21dSWarner Losh #endif 20969acd21dSWarner Losh 2103a2f7427SDavid Greenman /* needed for ft driver, thus exported */ 2116182fdbdSPeter Wemm int in_fdc(struct fdc_data *); 2126182fdbdSPeter Wemm int out_fdc(struct fdc_data *, int); 2133a2f7427SDavid Greenman 2143a2f7427SDavid Greenman /* internal functions */ 2156182fdbdSPeter Wemm static void fdc_add_device(device_t, const char *, int); 2166182fdbdSPeter Wemm static void fdc_intr(void *); 2176182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int); 2183a2f7427SDavid Greenman # define TURNON 1 2193a2f7427SDavid Greenman # define TURNOFF 0 2203a2f7427SDavid Greenman static timeout_t fd_turnoff; 2213a2f7427SDavid Greenman static timeout_t fd_motor_on; 2226182fdbdSPeter Wemm static void fd_turnon(struct fd_data *); 2233a2f7427SDavid Greenman static void fdc_reset(fdc_p); 2246182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *); 2256182fdbdSPeter Wemm static void fdstart(struct fdc_data *); 2265c1a1eaeSBruce Evans static timeout_t fd_iotimeout; 2273a2f7427SDavid Greenman static timeout_t fd_pseudointr; 2286182fdbdSPeter Wemm static int fdstate(struct fdc_data *); 2296182fdbdSPeter Wemm static int retrier(struct fdc_data *); 2303a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *); 2313a2f7427SDavid Greenman 232d66c374fSTor Egge static int enable_fifo(fdc_p fdc); 233d66c374fSTor Egge 234d66c374fSTor Egge static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ 235d66c374fSTor Egge 236aaf08d94SGarrett Wollman 2375b81b6b3SRodney W. Grimes #define DEVIDLE 0 2385b81b6b3SRodney W. Grimes #define FINDWORK 1 2395b81b6b3SRodney W. Grimes #define DOSEEK 2 2405b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 3 2415b81b6b3SRodney W. Grimes #define IOCOMPLETE 4 2425b81b6b3SRodney W. Grimes #define RECALCOMPLETE 5 2435b81b6b3SRodney W. Grimes #define STARTRECAL 6 2445b81b6b3SRodney W. Grimes #define RESETCTLR 7 2455b81b6b3SRodney W. Grimes #define SEEKWAIT 8 2465b81b6b3SRodney W. Grimes #define RECALWAIT 9 2475b81b6b3SRodney W. Grimes #define MOTORWAIT 10 2485b81b6b3SRodney W. Grimes #define IOTIMEDOUT 11 2495c1a1eaeSBruce Evans #define RESETCOMPLETE 12 25069acd21dSWarner Losh #ifdef FDC_YE 25169acd21dSWarner Losh #define PIOREAD 13 25269acd21dSWarner Losh #endif 2535b81b6b3SRodney W. Grimes 254d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 255cba2a7c6SBruce Evans static char const * const fdstates[] = 2565b81b6b3SRodney W. Grimes { 2575b81b6b3SRodney W. Grimes "DEVIDLE", 2585b81b6b3SRodney W. Grimes "FINDWORK", 2595b81b6b3SRodney W. Grimes "DOSEEK", 2605b81b6b3SRodney W. Grimes "SEEKCOMPLETE", 2615b81b6b3SRodney W. Grimes "IOCOMPLETE", 2625b81b6b3SRodney W. Grimes "RECALCOMPLETE", 2635b81b6b3SRodney W. Grimes "STARTRECAL", 2645b81b6b3SRodney W. Grimes "RESETCTLR", 2655b81b6b3SRodney W. Grimes "SEEKWAIT", 2665b81b6b3SRodney W. Grimes "RECALWAIT", 2675b81b6b3SRodney W. Grimes "MOTORWAIT", 2685c1a1eaeSBruce Evans "IOTIMEDOUT", 2695c1a1eaeSBruce Evans "RESETCOMPLETE", 27069acd21dSWarner Losh #ifdef FDC_YE 271ff9607b0SBruce Evans "PIOREAD", 27269acd21dSWarner Losh #endif 2735b81b6b3SRodney W. Grimes }; 2745b81b6b3SRodney W. Grimes 2753a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */ 276cba2a7c6SBruce Evans static int volatile fd_debug = 0; 2775b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg) 2785b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) 279d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */ 2805b81b6b3SRodney W. Grimes #define TRACE0(arg) 2815b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) 282d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 2835b81b6b3SRodney W. Grimes 28469acd21dSWarner Losh #ifdef FDC_YE 28569acd21dSWarner Losh #if NCARD > 0 28669acd21dSWarner Losh #include <sys/select.h> 287d17e4ee6SPeter Wemm #include <sys/module.h> 28869acd21dSWarner Losh #include <pccard/cardinfo.h> 28969acd21dSWarner Losh #include <pccard/driver.h> 29069acd21dSWarner Losh #include <pccard/slot.h> 29169acd21dSWarner Losh 29269acd21dSWarner Losh /* 29369acd21dSWarner Losh * PC-Card (PCMCIA) specific code. 29469acd21dSWarner Losh */ 29569acd21dSWarner Losh static int yeinit(struct pccard_devinfo *); /* init device */ 29669acd21dSWarner Losh static void yeunload(struct pccard_devinfo *); /* Disable driver */ 29769acd21dSWarner Losh static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ 29869acd21dSWarner Losh 299d17e4ee6SPeter Wemm PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); 30069acd21dSWarner Losh 30169acd21dSWarner Losh /* 30269acd21dSWarner Losh * this is the secret PIO data port (offset from base) 30369acd21dSWarner Losh */ 30469acd21dSWarner Losh #define FDC_YE_DATAPORT 6 30569acd21dSWarner Losh 30669acd21dSWarner Losh /* 30769acd21dSWarner Losh * Initialize the device - called from Slot manager. 30869acd21dSWarner Losh */ 30969acd21dSWarner Losh static int yeinit(struct pccard_devinfo *devi) 31069acd21dSWarner Losh { 31169acd21dSWarner Losh fdc_p fdc = &fdc_data[devi->isahd.id_unit]; 31269acd21dSWarner Losh 31369acd21dSWarner Losh /* validate unit number. */ 31469acd21dSWarner Losh if (devi->isahd.id_unit >= NFDC) 31569acd21dSWarner Losh return(ENODEV); 31669acd21dSWarner Losh fdc->baseport = devi->isahd.id_iobase; 31769acd21dSWarner Losh /* 31869acd21dSWarner Losh * reset controller 31969acd21dSWarner Losh */ 32069acd21dSWarner Losh outb(fdc->baseport+FDOUT, 0); 32169acd21dSWarner Losh DELAY(100); 32269acd21dSWarner Losh outb(fdc->baseport+FDOUT, FDO_FRST); 32369acd21dSWarner Losh 32469acd21dSWarner Losh /* 32569acd21dSWarner Losh * wire into system 32669acd21dSWarner Losh */ 32769acd21dSWarner Losh if (yeattach(&devi->isahd) == 0) 32869acd21dSWarner Losh return(ENXIO); 32969acd21dSWarner Losh 33069acd21dSWarner Losh return(0); 33169acd21dSWarner Losh } 33269acd21dSWarner Losh 33369acd21dSWarner Losh /* 33469acd21dSWarner Losh * yeunload - unload the driver and clear the table. 33569acd21dSWarner Losh * XXX TODO: 33669acd21dSWarner Losh * This is usually called when the card is ejected, but 33769acd21dSWarner Losh * can be caused by a modunload of a controller driver. 33869acd21dSWarner Losh * The idea is to reset the driver's view of the device 33969acd21dSWarner Losh * and ensure that any driver entry points such as 34069acd21dSWarner Losh * read and write do not hang. 34169acd21dSWarner Losh */ 34269acd21dSWarner Losh static void yeunload(struct pccard_devinfo *devi) 34369acd21dSWarner Losh { 34469acd21dSWarner Losh if (fd_data[devi->isahd.id_unit].type == NO_TYPE) 34569acd21dSWarner Losh return; 34669acd21dSWarner Losh 34769acd21dSWarner Losh /* 34869acd21dSWarner Losh * this prevents Fdopen() and fdstrategy() from attempting 34969acd21dSWarner Losh * to access unloaded controller 35069acd21dSWarner Losh */ 35169acd21dSWarner Losh fd_data[devi->isahd.id_unit].type = NO_TYPE; 35269acd21dSWarner Losh 35369acd21dSWarner Losh printf("fdc%d: unload\n", devi->isahd.id_unit); 35469acd21dSWarner Losh } 35569acd21dSWarner Losh 35669acd21dSWarner Losh /* 35769acd21dSWarner Losh * yeintr - Shared interrupt called from 35869acd21dSWarner Losh * front end of PC-Card handler. 35969acd21dSWarner Losh */ 36069acd21dSWarner Losh static int yeintr(struct pccard_devinfo *devi) 36169acd21dSWarner Losh { 36269acd21dSWarner Losh fdintr((fdcu_t)devi->isahd.id_unit); 36369acd21dSWarner Losh return(1); 36469acd21dSWarner Losh } 36569acd21dSWarner Losh #endif /* NCARD > 0 */ 36669acd21dSWarner Losh #endif /* FDC_YE */ 36769acd21dSWarner Losh 36887f6c662SJulian Elischer static d_open_t Fdopen; /* NOTE, not fdopen */ 36987f6c662SJulian Elischer static d_close_t fdclose; 37087f6c662SJulian Elischer static d_ioctl_t fdioctl; 37187f6c662SJulian Elischer static d_strategy_t fdstrategy; 37287f6c662SJulian Elischer 37387f6c662SJulian Elischer #define CDEV_MAJOR 9 37487f6c662SJulian Elischer #define BDEV_MAJOR 2 375f7ea2f55SJulian Elischer 3764e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = { 3774e2f199eSPoul-Henning Kamp /* open */ Fdopen, 3784e2f199eSPoul-Henning Kamp /* close */ fdclose, 3794e2f199eSPoul-Henning Kamp /* read */ physread, 3804e2f199eSPoul-Henning Kamp /* write */ physwrite, 3814e2f199eSPoul-Henning Kamp /* ioctl */ fdioctl, 3824e2f199eSPoul-Henning Kamp /* stop */ nostop, 3834e2f199eSPoul-Henning Kamp /* reset */ noreset, 3844e2f199eSPoul-Henning Kamp /* devtotty */ nodevtotty, 3854e2f199eSPoul-Henning Kamp /* poll */ nopoll, 3864e2f199eSPoul-Henning Kamp /* mmap */ nommap, 3874e2f199eSPoul-Henning Kamp /* strategy */ fdstrategy, 3884e2f199eSPoul-Henning Kamp /* name */ "fd", 3894e2f199eSPoul-Henning Kamp /* parms */ noparms, 3904e2f199eSPoul-Henning Kamp /* maj */ CDEV_MAJOR, 3914e2f199eSPoul-Henning Kamp /* dump */ nodump, 3924e2f199eSPoul-Henning Kamp /* psize */ nopsize, 3934e2f199eSPoul-Henning Kamp /* flags */ D_DISK, 3944e2f199eSPoul-Henning Kamp /* maxio */ 0, 3954e2f199eSPoul-Henning Kamp /* bmaj */ BDEV_MAJOR 3964e2f199eSPoul-Henning Kamp }; 3974e2f199eSPoul-Henning Kamp 398dc5df763SJoerg Wunsch static int 3996182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s) 400dc5df763SJoerg Wunsch { 4016182fdbdSPeter Wemm fdc->fdc_errs++; 40216b04b6aSJoerg Wunsch if (s) { 4036182fdbdSPeter Wemm if (fdc->fdc_errs < FDC_ERRMAX) { 4046182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 4056182fdbdSPeter Wemm printf("%s", s); 4066182fdbdSPeter Wemm } else if (fdc->fdc_errs == FDC_ERRMAX) { 4076182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 4086182fdbdSPeter Wemm printf("too many errors, not logging any more\n"); 4096182fdbdSPeter Wemm } 41016b04b6aSJoerg Wunsch } 411dc5df763SJoerg Wunsch 412dc5df763SJoerg Wunsch return FD_FAILED; 413dc5df763SJoerg Wunsch } 414dc5df763SJoerg Wunsch 415dc5df763SJoerg Wunsch /* 416dc5df763SJoerg Wunsch * fd_cmd: Send a command to the chip. Takes a varargs with this structure: 417dc5df763SJoerg Wunsch * Unit number, 418dc5df763SJoerg Wunsch * # of output bytes, output bytes as ints ..., 419dc5df763SJoerg Wunsch * # of input bytes, input bytes as ints ... 420dc5df763SJoerg Wunsch */ 4216f4e0bebSPoul-Henning Kamp static int 4226182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...) 423dc5df763SJoerg Wunsch { 424dc5df763SJoerg Wunsch u_char cmd; 425dc5df763SJoerg Wunsch int n_in; 426dc5df763SJoerg Wunsch int n; 427dc5df763SJoerg Wunsch va_list ap; 428dc5df763SJoerg Wunsch 429dc5df763SJoerg Wunsch va_start(ap, n_out); 430dc5df763SJoerg Wunsch cmd = (u_char)(va_arg(ap, int)); 431dc5df763SJoerg Wunsch va_end(ap); 432dc5df763SJoerg Wunsch va_start(ap, n_out); 433dc5df763SJoerg Wunsch for (n = 0; n < n_out; n++) 434dc5df763SJoerg Wunsch { 4356182fdbdSPeter Wemm if (out_fdc(fdc, va_arg(ap, int)) < 0) 436dc5df763SJoerg Wunsch { 437dc5df763SJoerg Wunsch char msg[50]; 4382127f260SArchie Cobbs snprintf(msg, sizeof(msg), 439dc5df763SJoerg Wunsch "cmd %x failed at out byte %d of %d\n", 440dc5df763SJoerg Wunsch cmd, n + 1, n_out); 4416182fdbdSPeter Wemm return fdc_err(fdc, msg); 442dc5df763SJoerg Wunsch } 443dc5df763SJoerg Wunsch } 444dc5df763SJoerg Wunsch n_in = va_arg(ap, int); 445dc5df763SJoerg Wunsch for (n = 0; n < n_in; n++) 446dc5df763SJoerg Wunsch { 447dc5df763SJoerg Wunsch int *ptr = va_arg(ap, int *); 4486182fdbdSPeter Wemm if (fd_in(fdc, ptr) < 0) 449dc5df763SJoerg Wunsch { 450dc5df763SJoerg Wunsch char msg[50]; 4512127f260SArchie Cobbs snprintf(msg, sizeof(msg), 452dc5df763SJoerg Wunsch "cmd %02x failed at in byte %d of %d\n", 453dc5df763SJoerg Wunsch cmd, n + 1, n_in); 4546182fdbdSPeter Wemm return fdc_err(fdc, msg); 455dc5df763SJoerg Wunsch } 456dc5df763SJoerg Wunsch } 457dc5df763SJoerg Wunsch 458dc5df763SJoerg Wunsch return 0; 459dc5df763SJoerg Wunsch } 460dc5df763SJoerg Wunsch 4616f4e0bebSPoul-Henning Kamp static int 462d66c374fSTor Egge enable_fifo(fdc_p fdc) 463d66c374fSTor Egge { 464d66c374fSTor Egge int i, j; 465d66c374fSTor Egge 466d66c374fSTor Egge if ((fdc->flags & FDC_HAS_FIFO) == 0) { 467d66c374fSTor Egge 468d66c374fSTor Egge /* 469d66c374fSTor Egge * XXX: 470d66c374fSTor Egge * Cannot use fd_cmd the normal way here, since 471d66c374fSTor Egge * this might be an invalid command. Thus we send the 472d66c374fSTor Egge * first byte, and check for an early turn of data directon. 473d66c374fSTor Egge */ 474d66c374fSTor Egge 4756182fdbdSPeter Wemm if (out_fdc(fdc, I8207X_CONFIGURE) < 0) 4766182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 477d66c374fSTor Egge 478d66c374fSTor Egge /* If command is invalid, return */ 479d66c374fSTor Egge j = 100000; 480d66c374fSTor Egge while ((i = inb(fdc->baseport + FDSTS) & (NE7_DIO | NE7_RQM)) 481d66c374fSTor Egge != NE7_RQM && j-- > 0) 482d66c374fSTor Egge if (i == (NE7_DIO | NE7_RQM)) { 483d66c374fSTor Egge fdc_reset(fdc); 484d66c374fSTor Egge return FD_FAILED; 485d66c374fSTor Egge } 486d66c374fSTor Egge if (j<0 || 4876182fdbdSPeter Wemm fd_cmd(fdc, 3, 488d66c374fSTor Egge 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { 489d66c374fSTor Egge fdc_reset(fdc); 4906182fdbdSPeter Wemm return fdc_err(fdc, "Enable FIFO failed\n"); 491d66c374fSTor Egge } 492d66c374fSTor Egge fdc->flags |= FDC_HAS_FIFO; 493d66c374fSTor Egge return 0; 494d66c374fSTor Egge } 4956182fdbdSPeter Wemm if (fd_cmd(fdc, 4, 496d66c374fSTor Egge I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) 4976182fdbdSPeter Wemm return fdc_err(fdc, "Re-enable FIFO failed\n"); 498d66c374fSTor Egge return 0; 499d66c374fSTor Egge } 500d66c374fSTor Egge 501d66c374fSTor Egge static int 502dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p) 503dc5df763SJoerg Wunsch { 504dc5df763SJoerg Wunsch int st3; 505dc5df763SJoerg Wunsch 5066182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) 507dc5df763SJoerg Wunsch { 5086182fdbdSPeter Wemm return fdc_err(fdc, "Sense Drive Status failed\n"); 509dc5df763SJoerg Wunsch } 510dc5df763SJoerg Wunsch if (st3p) 511dc5df763SJoerg Wunsch *st3p = st3; 512dc5df763SJoerg Wunsch 513dc5df763SJoerg Wunsch return 0; 514dc5df763SJoerg Wunsch } 515dc5df763SJoerg Wunsch 5166f4e0bebSPoul-Henning Kamp static int 517dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp) 518dc5df763SJoerg Wunsch { 5196182fdbdSPeter Wemm int cyl, st0, ret; 520dc5df763SJoerg Wunsch 5216182fdbdSPeter Wemm ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); 5226182fdbdSPeter Wemm if (ret) { 5236182fdbdSPeter Wemm (void)fdc_err(fdc, 524dc5df763SJoerg Wunsch "sense intr err reading stat reg 0\n"); 525dc5df763SJoerg Wunsch return ret; 526dc5df763SJoerg Wunsch } 527dc5df763SJoerg Wunsch 528dc5df763SJoerg Wunsch if (st0p) 529dc5df763SJoerg Wunsch *st0p = st0; 530dc5df763SJoerg Wunsch 5316182fdbdSPeter Wemm if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { 532dc5df763SJoerg Wunsch /* 533dc5df763SJoerg Wunsch * There doesn't seem to have been an interrupt. 534dc5df763SJoerg Wunsch */ 535dc5df763SJoerg Wunsch return FD_NOT_VALID; 536dc5df763SJoerg Wunsch } 537dc5df763SJoerg Wunsch 5386182fdbdSPeter Wemm if (fd_in(fdc, &cyl) < 0) { 5396182fdbdSPeter Wemm return fdc_err(fdc, "can't get cyl num\n"); 540dc5df763SJoerg Wunsch } 541dc5df763SJoerg Wunsch 542dc5df763SJoerg Wunsch if (cylp) 543dc5df763SJoerg Wunsch *cylp = cyl; 544dc5df763SJoerg Wunsch 545dc5df763SJoerg Wunsch return 0; 546dc5df763SJoerg Wunsch } 547dc5df763SJoerg Wunsch 548dc5df763SJoerg Wunsch 5496f4e0bebSPoul-Henning Kamp static int 550dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu) 551dc5df763SJoerg Wunsch { 552dc5df763SJoerg Wunsch int i, ret; 553b5e8ce9fSBruce Evans 5546182fdbdSPeter Wemm for (i = 0; i < 7; i++) { 555b5e8ce9fSBruce Evans /* 556b5e8ce9fSBruce Evans * XXX types are poorly chosen. Only bytes can by read 557a838d83dSBruce Evans * from the hardware, but fdc->status[] wants u_ints and 558b5e8ce9fSBruce Evans * fd_in() gives ints. 559b5e8ce9fSBruce Evans */ 560b5e8ce9fSBruce Evans int status; 561b5e8ce9fSBruce Evans 5626182fdbdSPeter Wemm ret = fd_in(fdc, &status); 563b5e8ce9fSBruce Evans fdc->status[i] = status; 564b5e8ce9fSBruce Evans if (ret != 0) 565dc5df763SJoerg Wunsch break; 566dc5df763SJoerg Wunsch } 567dc5df763SJoerg Wunsch 568dc5df763SJoerg Wunsch if (ret == 0) 569dc5df763SJoerg Wunsch fdc->flags |= FDC_STAT_VALID; 570dc5df763SJoerg Wunsch else 571dc5df763SJoerg Wunsch fdc->flags &= ~FDC_STAT_VALID; 572dc5df763SJoerg Wunsch 573dc5df763SJoerg Wunsch return ret; 574dc5df763SJoerg Wunsch } 575dc5df763SJoerg Wunsch 5765b81b6b3SRodney W. Grimes /****************************************************************************/ 5775b81b6b3SRodney W. Grimes /* autoconfiguration stuff */ 5785b81b6b3SRodney W. Grimes /****************************************************************************/ 579dc5df763SJoerg Wunsch 5803a2f7427SDavid Greenman static int 5816182fdbdSPeter Wemm fdc_probe(device_t dev) 5825b81b6b3SRodney W. Grimes { 5836182fdbdSPeter Wemm int error, i, ic_type; 5846182fdbdSPeter Wemm struct fdc_data *fdc; 5856182fdbdSPeter Wemm char myname[8]; /* better be long enough */ 5865b81b6b3SRodney W. Grimes 587a97c75b7SDoug Rabson /* No pnp support */ 588a97c75b7SDoug Rabson if (isa_get_vendorid(dev)) 589a97c75b7SDoug Rabson return (ENXIO); 590a97c75b7SDoug Rabson 5916182fdbdSPeter Wemm fdc = device_get_softc(dev); 5926182fdbdSPeter Wemm bzero(fdc, sizeof *fdc); 5936182fdbdSPeter Wemm fdc->fdc_dev = dev; 5946182fdbdSPeter Wemm fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; 5956182fdbdSPeter Wemm fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; 5966182fdbdSPeter Wemm 5976182fdbdSPeter Wemm fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, 5986182fdbdSPeter Wemm &fdc->rid_ioport, 0ul, ~0ul, 5996182fdbdSPeter Wemm IO_FDCSIZE, RF_ACTIVE); 6006182fdbdSPeter Wemm if (fdc->res_ioport == 0) { 6016182fdbdSPeter Wemm device_print_prettyname(dev); 6026182fdbdSPeter Wemm printf("cannot reserve I/O port range\n"); 6036182fdbdSPeter Wemm error = ENXIO; 6046182fdbdSPeter Wemm goto out; 6056182fdbdSPeter Wemm } 6066182fdbdSPeter Wemm fdc->baseport = fdc->res_ioport->r_start; 6076182fdbdSPeter Wemm 6086182fdbdSPeter Wemm fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, 6096182fdbdSPeter Wemm &fdc->rid_irq, 0ul, ~0ul, 1, 6106182fdbdSPeter Wemm RF_ACTIVE); 6116182fdbdSPeter Wemm if (fdc->res_irq == 0) { 6126182fdbdSPeter Wemm device_print_prettyname(dev); 6136182fdbdSPeter Wemm printf("cannot reserve interrupt line\n"); 6146182fdbdSPeter Wemm error = ENXIO; 6156182fdbdSPeter Wemm goto out; 6166182fdbdSPeter Wemm } 6176182fdbdSPeter Wemm fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, 6186182fdbdSPeter Wemm &fdc->rid_drq, 0ul, ~0ul, 1, 6196182fdbdSPeter Wemm RF_ACTIVE); 6206182fdbdSPeter Wemm if (fdc->res_drq == 0) { 6216182fdbdSPeter Wemm device_print_prettyname(dev); 6226182fdbdSPeter Wemm printf("cannot reserve DMA request line\n"); 6236182fdbdSPeter Wemm error = ENXIO; 6246182fdbdSPeter Wemm goto out; 6256182fdbdSPeter Wemm } 6266182fdbdSPeter Wemm fdc->dmachan = fdc->res_drq->r_start; 627566643e3SDoug Rabson error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, 628566643e3SDoug Rabson INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr); 6295b81b6b3SRodney W. Grimes 63016111cedSAndrew Moore /* First - lets reset the floppy controller */ 6316182fdbdSPeter Wemm outb(fdc->baseport + FDOUT, 0); 63216111cedSAndrew Moore DELAY(100); 6336182fdbdSPeter Wemm outb(fdc->baseport + FDOUT, FDO_FRST); 63416111cedSAndrew Moore 6355b81b6b3SRodney W. Grimes /* see if it can handle a command */ 6366182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), 6376182fdbdSPeter Wemm NE7_SPEC_2(2, 0), 0)) { 6386182fdbdSPeter Wemm error = ENXIO; 6396182fdbdSPeter Wemm goto out; 6405b81b6b3SRodney W. Grimes } 6416182fdbdSPeter Wemm 6426182fdbdSPeter Wemm if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { 6436182fdbdSPeter Wemm ic_type = (u_char)ic_type; 6446182fdbdSPeter Wemm switch (ic_type) { 6456182fdbdSPeter Wemm case 0x80: 6466182fdbdSPeter Wemm device_set_desc(dev, "NEC 765 or clone"); 6476182fdbdSPeter Wemm fdc->fdct = FDC_NE765; 6486182fdbdSPeter Wemm break; 6496182fdbdSPeter Wemm case 0x81: 6506182fdbdSPeter Wemm device_set_desc(dev, "Intel 82077 or clone"); 6516182fdbdSPeter Wemm fdc->fdct = FDC_I82077; 6526182fdbdSPeter Wemm break; 6536182fdbdSPeter Wemm case 0x90: 6546182fdbdSPeter Wemm device_set_desc(dev, "NEC 72065B or clone"); 6556182fdbdSPeter Wemm fdc->fdct = FDC_NE72065; 6566182fdbdSPeter Wemm break; 6576182fdbdSPeter Wemm default: 6586182fdbdSPeter Wemm device_set_desc(dev, "generic floppy controller"); 6596182fdbdSPeter Wemm fdc->fdct = FDC_UNKNOWN; 6606182fdbdSPeter Wemm break; 6616182fdbdSPeter Wemm } 6626182fdbdSPeter Wemm } 6636182fdbdSPeter Wemm 6646182fdbdSPeter Wemm snprintf(myname, sizeof(myname), "%s%d", device_get_name(dev), 6656182fdbdSPeter Wemm device_get_unit(dev)); 6666182fdbdSPeter Wemm for (i = resource_query_string(-1, "at", myname); i != -1; 6676182fdbdSPeter Wemm i = resource_query_string(i, "at", myname)) 6686182fdbdSPeter Wemm fdc_add_device(dev, resource_query_name(i), 6696182fdbdSPeter Wemm resource_query_unit(i)); 67069acd21dSWarner Losh #ifdef FDC_YE 67169acd21dSWarner Losh /* 67269acd21dSWarner Losh * don't succeed on probe; wait 67369acd21dSWarner Losh * for PCCARD subsystem to do it 67469acd21dSWarner Losh */ 67569acd21dSWarner Losh if (dev->id_flags & FDC_IS_PCMCIA) 67669acd21dSWarner Losh return(0); 67769acd21dSWarner Losh #endif 6786182fdbdSPeter Wemm return (0); 6796182fdbdSPeter Wemm 6806182fdbdSPeter Wemm out: 6816182fdbdSPeter Wemm if (fdc->fdc_intr) 6826182fdbdSPeter Wemm BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, 6836182fdbdSPeter Wemm fdc->fdc_intr); 6846182fdbdSPeter Wemm if (fdc->res_irq != 0) { 6856182fdbdSPeter Wemm bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 6866182fdbdSPeter Wemm fdc->res_irq); 6876182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, 6886182fdbdSPeter Wemm fdc->res_irq); 6896182fdbdSPeter Wemm } 6906182fdbdSPeter Wemm if (fdc->res_ioport != 0) { 6916182fdbdSPeter Wemm bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 6926182fdbdSPeter Wemm fdc->res_ioport); 6936182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, 6946182fdbdSPeter Wemm fdc->res_ioport); 6956182fdbdSPeter Wemm } 6966182fdbdSPeter Wemm if (fdc->res_drq != 0) { 6976182fdbdSPeter Wemm bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 6986182fdbdSPeter Wemm fdc->res_drq); 6996182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, 7006182fdbdSPeter Wemm fdc->res_drq); 7016182fdbdSPeter Wemm } 7026182fdbdSPeter Wemm return (error); 7035b81b6b3SRodney W. Grimes } 7045b81b6b3SRodney W. Grimes 7055b81b6b3SRodney W. Grimes /* 7066182fdbdSPeter Wemm * Aped dfr@freebsd.org's isa_add_device(). 7075b81b6b3SRodney W. Grimes */ 7086182fdbdSPeter Wemm static void 7096182fdbdSPeter Wemm fdc_add_device(device_t dev, const char *name, int unit) 7105b81b6b3SRodney W. Grimes { 7116182fdbdSPeter Wemm int disabled, *ivar; 7126182fdbdSPeter Wemm device_t child; 71392200632SGarrett Wollman 7146182fdbdSPeter Wemm ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); 7156182fdbdSPeter Wemm if (ivar == 0) 7166182fdbdSPeter Wemm return; 717a97c75b7SDoug Rabson if (resource_int_value(name, unit, "drive", ivar) != 0) 7186182fdbdSPeter Wemm *ivar = 0; 7196182fdbdSPeter Wemm child = device_add_child(dev, name, unit, ivar); 7206182fdbdSPeter Wemm if (child == 0) 7216182fdbdSPeter Wemm return; 722a97c75b7SDoug Rabson if (resource_int_value(name, unit, "disabled", &disabled) == 0 723a97c75b7SDoug Rabson && disabled != 0) 7246182fdbdSPeter Wemm device_disable(child); 7256182fdbdSPeter Wemm } 7266182fdbdSPeter Wemm 7276182fdbdSPeter Wemm static int 7286182fdbdSPeter Wemm fdc_attach(device_t dev) 7296182fdbdSPeter Wemm { 7306182fdbdSPeter Wemm struct fdc_data *fdc = device_get_softc(dev); 7316182fdbdSPeter Wemm fdcu_t fdcu = device_get_unit(dev); 7326182fdbdSPeter Wemm 7335b81b6b3SRodney W. Grimes fdc->fdcu = fdcu; 7345b81b6b3SRodney W. Grimes fdc->flags |= FDC_ATTACHED; 7356182fdbdSPeter Wemm 736100f78bbSSujal Patel /* Acquire the DMA channel forever, The driver will do the rest */ 7376182fdbdSPeter Wemm /* XXX should integrate with rman */ 738100f78bbSSujal Patel isa_dma_acquire(fdc->dmachan); 739dd87702aSBruce Evans isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); 7405b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 7416182fdbdSPeter Wemm 7423a2f7427SDavid Greenman /* reset controller, turn motor off, clear fdout mirror reg */ 7433a2f7427SDavid Greenman outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); 74402a19910SJustin T. Gibbs bufq_init(&fdc->head); 7455b81b6b3SRodney W. Grimes 7466182fdbdSPeter Wemm #ifdef FIFO_BEFORE_MOTORON 7476182fdbdSPeter Wemm /* Hmm, this doesn't work here - is set_motor() magic? -Peter */ 7486182fdbdSPeter Wemm if (fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN 7496182fdbdSPeter Wemm && enable_fifo(fdc) == 0) { 7506182fdbdSPeter Wemm device_print_prettyname(dev); 7516182fdbdSPeter Wemm printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); 7526182fdbdSPeter Wemm } 7536182fdbdSPeter Wemm #endif 7546182fdbdSPeter Wemm /* 7556182fdbdSPeter Wemm * Probe and attach any children as were configured above. 7566182fdbdSPeter Wemm */ 7576182fdbdSPeter Wemm return (bus_generic_attach(dev)); 7586182fdbdSPeter Wemm } 7596182fdbdSPeter Wemm 76015317dd8SMatthew N. Dodd static int 7616182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child) 7626182fdbdSPeter Wemm { 76315317dd8SMatthew N. Dodd int retval = 0; 76415317dd8SMatthew N. Dodd 76515317dd8SMatthew N. Dodd retval += bus_print_child_header(me, child); 76615317dd8SMatthew N. Dodd retval += printf(" on %s drive %d\n", device_get_nameunit(me), 7676182fdbdSPeter Wemm *(int *)device_get_ivars(child)); 76815317dd8SMatthew N. Dodd 76915317dd8SMatthew N. Dodd return (retval); 7706182fdbdSPeter Wemm } 7716182fdbdSPeter Wemm 7726182fdbdSPeter Wemm static int 7736182fdbdSPeter Wemm fd_probe(device_t dev) 7746182fdbdSPeter Wemm { 7756182fdbdSPeter Wemm int i; 7766182fdbdSPeter Wemm u_int fdt, st0, st3; 7776182fdbdSPeter Wemm struct fd_data *fd; 7786182fdbdSPeter Wemm struct fdc_data *fdc; 7796182fdbdSPeter Wemm fdsu_t fdsu; 7806182fdbdSPeter Wemm #ifndef FIFO_BEFORE_MOTORON 7816182fdbdSPeter Wemm static int fd_fifo = 0; 7826182fdbdSPeter Wemm #endif 7836182fdbdSPeter Wemm 7846182fdbdSPeter Wemm fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ 7856182fdbdSPeter Wemm fd = device_get_softc(dev); 7866182fdbdSPeter Wemm fdc = device_get_softc(device_get_parent(dev)); 7876182fdbdSPeter Wemm 7886182fdbdSPeter Wemm bzero(fd, sizeof *fd); 7896182fdbdSPeter Wemm fd->dev = dev; 7906182fdbdSPeter Wemm fd->fdc = fdc; 7916182fdbdSPeter Wemm fd->fdsu = fdsu; 7926182fdbdSPeter Wemm fd->fdu = device_get_unit(dev); 7936182fdbdSPeter Wemm 794a97c75b7SDoug Rabson #ifdef __i386__ 795b99f0a4aSAndrew Moore /* look up what bios thinks we have */ 7966182fdbdSPeter Wemm switch (fd->fdu) { 7976182fdbdSPeter Wemm case 0: 7986182fdbdSPeter Wemm if (isa_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) 7990722d6abSJoerg Wunsch fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; 8000722d6abSJoerg Wunsch else 8010722d6abSJoerg Wunsch fdt = (rtcin(RTC_FDISKETTE) & 0xf0); 802b99f0a4aSAndrew Moore break; 8036182fdbdSPeter Wemm case 1: 8046182fdbdSPeter Wemm fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); 805dc5df763SJoerg Wunsch break; 806dc5df763SJoerg Wunsch default: 8076182fdbdSPeter Wemm fdt = RTCFDT_NONE; 808dc5df763SJoerg Wunsch break; 8096b7bd95bSJoerg Wunsch } 810a97c75b7SDoug Rabson #else 811a97c75b7SDoug Rabson fdt = RTCFDT_144M; /* XXX probably */ 812a97c75b7SDoug Rabson #endif 8136182fdbdSPeter Wemm 8146182fdbdSPeter Wemm /* is there a unit? */ 8156182fdbdSPeter Wemm if (fdt == RTCFDT_NONE) 8166182fdbdSPeter Wemm return (ENXIO); 8176182fdbdSPeter Wemm 8186182fdbdSPeter Wemm /* select it */ 8196182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNON); 8206182fdbdSPeter Wemm DELAY(1000000); /* 1 sec */ 8216182fdbdSPeter Wemm 8226182fdbdSPeter Wemm #ifndef FIFO_BEFORE_MOTORON 8236182fdbdSPeter Wemm if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN 8246182fdbdSPeter Wemm && enable_fifo(fdc) == 0) { 8256182fdbdSPeter Wemm device_print_prettyname(device_get_parent(dev)); 8266182fdbdSPeter Wemm printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); 827d66c374fSTor Egge } 8286182fdbdSPeter Wemm fd_fifo = 1; 8296182fdbdSPeter Wemm #endif 8306182fdbdSPeter Wemm 8316182fdbdSPeter Wemm if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) 8326182fdbdSPeter Wemm && (st3 & NE7_ST3_T0)) { 833dc5df763SJoerg Wunsch /* if at track 0, first seek inwards */ 834dc5df763SJoerg Wunsch /* seek some steps: */ 8356182fdbdSPeter Wemm fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); 836dc5df763SJoerg Wunsch DELAY(300000); /* ...wait a moment... */ 8376182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ 838dc5df763SJoerg Wunsch } 839dc5df763SJoerg Wunsch 840dc5df763SJoerg Wunsch /* If we're at track 0 first seek inwards. */ 8416182fdbdSPeter Wemm if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { 842dc5df763SJoerg Wunsch /* Seek some steps... */ 8436182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 844dc5df763SJoerg Wunsch /* ...wait a moment... */ 845dc5df763SJoerg Wunsch DELAY(300000); 846dc5df763SJoerg Wunsch /* make ctrlr happy: */ 8476182fdbdSPeter Wemm fd_sense_int(fdc, 0, 0); 848dc5df763SJoerg Wunsch } 849dc5df763SJoerg Wunsch } 850dc5df763SJoerg Wunsch 8516b7bd95bSJoerg Wunsch for (i = 0; i < 2; i++) { 8526b7bd95bSJoerg Wunsch /* 8536b7bd95bSJoerg Wunsch * we must recalibrate twice, just in case the 8546b7bd95bSJoerg Wunsch * heads have been beyond cylinder 76, since most 8556b7bd95bSJoerg Wunsch * FDCs still barf when attempting to recalibrate 8566b7bd95bSJoerg Wunsch * more than 77 steps 8576b7bd95bSJoerg Wunsch */ 858dc5df763SJoerg Wunsch /* go back to 0: */ 8596182fdbdSPeter Wemm if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 8606b7bd95bSJoerg Wunsch /* a second being enough for full stroke seek*/ 8616b7bd95bSJoerg Wunsch DELAY(i == 0 ? 1000000 : 300000); 8625b81b6b3SRodney W. Grimes 8636b7bd95bSJoerg Wunsch /* anything responding? */ 864dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, 0) == 0 && 865dc5df763SJoerg Wunsch (st0 & NE7_ST0_EC) == 0) 8666b7bd95bSJoerg Wunsch break; /* already probed succesfully */ 8676b7bd95bSJoerg Wunsch } 868dc5df763SJoerg Wunsch } 8696b7bd95bSJoerg Wunsch 8706182fdbdSPeter Wemm set_motor(fdc, fdsu, TURNOFF); 8713a2f7427SDavid Greenman 8723a2f7427SDavid Greenman if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ 8736182fdbdSPeter Wemm return (ENXIO); 8745b81b6b3SRodney W. Grimes 875dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 876b99f0a4aSAndrew Moore fd->fdc = fdc; 877b99f0a4aSAndrew Moore fd->fdsu = fdsu; 8783a2f7427SDavid Greenman fd->options = 0; 87902a19910SJustin T. Gibbs callout_handle_init(&fd->toffhandle); 88002a19910SJustin T. Gibbs callout_handle_init(&fd->tohandle); 8815b81b6b3SRodney W. Grimes 882b99f0a4aSAndrew Moore switch (fdt) { 8837ca0641bSAndrey A. Chernov case RTCFDT_12M: 8846182fdbdSPeter Wemm device_set_desc(dev, "1200-KB 5.25\" drive"); 885b99f0a4aSAndrew Moore fd->type = FD_1200; 8867ca0641bSAndrey A. Chernov break; 8870722d6abSJoerg Wunsch case RTCFDT_144M | RTCFDT_144M_PRETENDED: 8886182fdbdSPeter Wemm device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); 8890722d6abSJoerg Wunsch fdt = RTCFDT_144M; 8906182fdbdSPeter Wemm fd->type = FD_1440; 8917ca0641bSAndrey A. Chernov case RTCFDT_144M: 8926182fdbdSPeter Wemm device_set_desc(dev, "1440-KB 3.5\" drive"); 893b99f0a4aSAndrew Moore fd->type = FD_1440; 8947ca0641bSAndrey A. Chernov break; 895290dd077SJoerg Wunsch case RTCFDT_288M: 89686a727d9SJoerg Wunsch case RTCFDT_288M_1: 8976182fdbdSPeter Wemm device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); 898290dd077SJoerg Wunsch fd->type = FD_1440; 899290dd077SJoerg Wunsch break; 9007ca0641bSAndrey A. Chernov case RTCFDT_360K: 9016182fdbdSPeter Wemm device_set_desc(dev, "360-KB 5.25\" drive"); 902b99f0a4aSAndrew Moore fd->type = FD_360; 9037ca0641bSAndrey A. Chernov break; 904ed2fa05eSAndrey A. Chernov case RTCFDT_720K: 9056182fdbdSPeter Wemm printf("720-KB 3.5\" drive"); 906b99f0a4aSAndrew Moore fd->type = FD_720; 907ed2fa05eSAndrey A. Chernov break; 9087ca0641bSAndrey A. Chernov default: 9096182fdbdSPeter Wemm return (ENXIO); 9105b81b6b3SRodney W. Grimes } 9116182fdbdSPeter Wemm return (0); 9126182fdbdSPeter Wemm } 9136182fdbdSPeter Wemm 9146182fdbdSPeter Wemm static int 9156182fdbdSPeter Wemm fd_attach(device_t dev) 9166182fdbdSPeter Wemm { 9176182fdbdSPeter Wemm struct fd_data *fd; 918edfdec19SPeter Wemm #ifdef DEVFS 919edfdec19SPeter Wemm int i; 920edfdec19SPeter Wemm int mynor; 921edfdec19SPeter Wemm int typemynor; 922edfdec19SPeter Wemm int typesize; 923edfdec19SPeter Wemm #endif 9246182fdbdSPeter Wemm 9256182fdbdSPeter Wemm fd = device_get_softc(dev); 9266182fdbdSPeter Wemm 9276182fdbdSPeter Wemm #ifdef DEVFS /* XXX bitrot */ 928edfdec19SPeter Wemm mynor = fd->fdu << 6; 929f7ea2f55SJulian Elischer fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, 930f85120acSBruce Evans UID_ROOT, GID_OPERATOR, 0640, 931edfdec19SPeter Wemm "fd%d", fd->fdu); 93221519754SBruce Evans fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, 933f85120acSBruce Evans UID_ROOT, GID_OPERATOR, 0640, 934edfdec19SPeter Wemm "rfd%d", fd->fdu); 93521519754SBruce Evans for (i = 1; i < 1 + NUMDENS; i++) { 93621519754SBruce Evans /* 93721519754SBruce Evans * XXX this and the lookup in Fdopen() should be 93821519754SBruce Evans * data driven. 93921519754SBruce Evans */ 94021519754SBruce Evans switch (fd->type) { 94121519754SBruce Evans case FD_360: 94221519754SBruce Evans if (i != FD_360) 94321519754SBruce Evans continue; 94421519754SBruce Evans break; 94521519754SBruce Evans case FD_720: 94621519754SBruce Evans if (i != FD_720 && i != FD_800 && i != FD_820) 94721519754SBruce Evans continue; 94821519754SBruce Evans break; 94921519754SBruce Evans case FD_1200: 95021519754SBruce Evans if (i != FD_360 && i != FD_720 && i != FD_800 95121519754SBruce Evans && i != FD_820 && i != FD_1200 95221519754SBruce Evans && i != FD_1440 && i != FD_1480) 95321519754SBruce Evans continue; 95421519754SBruce Evans break; 95521519754SBruce Evans case FD_1440: 95621519754SBruce Evans if (i != FD_720 && i != FD_800 && i != FD_820 95721519754SBruce Evans && i != FD_1200 && i != FD_1440 95821519754SBruce Evans && i != FD_1480 && i != FD_1720) 95921519754SBruce Evans continue; 96021519754SBruce Evans break; 96121519754SBruce Evans } 96221519754SBruce Evans typesize = fd_types[i - 1].size / 2; 96321519754SBruce Evans /* 96421519754SBruce Evans * XXX all these conversions give bloated code and 96521519754SBruce Evans * confusing names. 96621519754SBruce Evans */ 96721519754SBruce Evans if (typesize == 1476) 96821519754SBruce Evans typesize = 1480; 96921519754SBruce Evans if (typesize == 1722) 97021519754SBruce Evans typesize = 1720; 9713e425b96SJulian Elischer typemynor = mynor | i; 97221519754SBruce Evans fd->bdevs[i] = 973f7ea2f55SJulian Elischer devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK, 97421519754SBruce Evans UID_ROOT, GID_OPERATOR, 0640, 975edfdec19SPeter Wemm "fd%d.%d", fd->fdu, typesize); 97621519754SBruce Evans fd->cdevs[i] = 97721519754SBruce Evans devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, 97821519754SBruce Evans UID_ROOT, GID_OPERATOR, 0640, 979edfdec19SPeter Wemm "rfd%d.%d", fd->fdu, typesize); 98021519754SBruce Evans } 9813e425b96SJulian Elischer 98221519754SBruce Evans for (i = 0; i < MAXPARTITIONS; i++) { 9832ae353f9SEivind Eklund fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], 984edfdec19SPeter Wemm "fd%d%c", fd->fdu, 'a' + i); 98521519754SBruce Evans fd->cdevs[1 + NUMDENS + i] = 9862ae353f9SEivind Eklund devfs_makelink(fd->cdevs[0], 987edfdec19SPeter Wemm "rfd%d%c", fd->fdu, 'a' + i); 98821519754SBruce Evans } 989999422d7SJulian Elischer #endif /* DEVFS */ 990671e2ceeSBruce Evans /* 991b2dfb1f9SJustin T. Gibbs * Export the drive to the devstat interface. 992671e2ceeSBruce Evans */ 9936182fdbdSPeter Wemm devstat_add_entry(&fd->device_stats, device_get_name(dev), 9946182fdbdSPeter Wemm device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, 9952a888f93SKenneth D. Merry DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, 9962a888f93SKenneth D. Merry DEVSTAT_PRIORITY_FD); 9976182fdbdSPeter Wemm return (0); 9985b81b6b3SRodney W. Grimes } 9995b81b6b3SRodney W. Grimes 100069acd21dSWarner Losh #ifdef FDC_YE 100169acd21dSWarner Losh /* 100269acd21dSWarner Losh * this is a subset of fdattach() optimized for the Y-E Data 100369acd21dSWarner Losh * PCMCIA floppy drive. 100469acd21dSWarner Losh */ 100569acd21dSWarner Losh static int yeattach(struct isa_device *dev) 100669acd21dSWarner Losh { 100769acd21dSWarner Losh fdcu_t fdcu = dev->id_unit; 100869acd21dSWarner Losh fdc_p fdc = fdc_data + fdcu; 100969acd21dSWarner Losh fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ 101069acd21dSWarner Losh fdu_t fdu; 101169acd21dSWarner Losh fd_p fd; 101269acd21dSWarner Losh int st0, st3, i; 101369acd21dSWarner Losh #ifdef DEVFS 101469acd21dSWarner Losh int mynor; 101569acd21dSWarner Losh int typemynor; 101669acd21dSWarner Losh int typesize; 101769acd21dSWarner Losh #endif 101869acd21dSWarner Losh fdc->fdcu = fdcu; 101969acd21dSWarner Losh /* 102069acd21dSWarner Losh * the FDC_PCMCIA flag is used to to indicate special PIO is used 102169acd21dSWarner Losh * instead of DMA 102269acd21dSWarner Losh */ 102369acd21dSWarner Losh fdc->flags = FDC_ATTACHED|FDC_PCMCIA; 102469acd21dSWarner Losh fdc->state = DEVIDLE; 102569acd21dSWarner Losh /* reset controller, turn motor off, clear fdout mirror reg */ 102669acd21dSWarner Losh outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); 102769acd21dSWarner Losh bufq_init(&fdc->head); 102869acd21dSWarner Losh /* 102969acd21dSWarner Losh * assume 2 drives/ "normal" controller 103069acd21dSWarner Losh */ 103169acd21dSWarner Losh fdu = fdcu * 2; 103269acd21dSWarner Losh if (fdu >= NFD) { 103369acd21dSWarner Losh printf("fdu %d >= NFD\n",fdu); 103469acd21dSWarner Losh return(0); 103569acd21dSWarner Losh }; 103669acd21dSWarner Losh fd = &fd_data[fdu]; 103769acd21dSWarner Losh 103869acd21dSWarner Losh set_motor(fdcu, fdsu, TURNON); 103969acd21dSWarner Losh DELAY(1000000); /* 1 sec */ 104069acd21dSWarner Losh fdc->fdct = FDC_NE765; 104169acd21dSWarner Losh 104269acd21dSWarner Losh if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && 104369acd21dSWarner Losh (st3 & NE7_ST3_T0)) { 104469acd21dSWarner Losh /* if at track 0, first seek inwards */ 104569acd21dSWarner Losh /* seek some steps: */ 104669acd21dSWarner Losh (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); 104769acd21dSWarner Losh DELAY(300000); /* ...wait a moment... */ 104869acd21dSWarner Losh (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ 104969acd21dSWarner Losh } 105069acd21dSWarner Losh 105169acd21dSWarner Losh /* If we're at track 0 first seek inwards. */ 105269acd21dSWarner Losh if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { 105369acd21dSWarner Losh /* Seek some steps... */ 105469acd21dSWarner Losh if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { 105569acd21dSWarner Losh /* ...wait a moment... */ 105669acd21dSWarner Losh DELAY(300000); 105769acd21dSWarner Losh /* make ctrlr happy: */ 105869acd21dSWarner Losh (void)fd_sense_int(fdc, 0, 0); 105969acd21dSWarner Losh } 106069acd21dSWarner Losh } 106169acd21dSWarner Losh 106269acd21dSWarner Losh for(i = 0; i < 2; i++) { 106369acd21dSWarner Losh /* 106469acd21dSWarner Losh * we must recalibrate twice, just in case the 106569acd21dSWarner Losh * heads have been beyond cylinder 76, since most 106669acd21dSWarner Losh * FDCs still barf when attempting to recalibrate 106769acd21dSWarner Losh * more than 77 steps 106869acd21dSWarner Losh */ 106969acd21dSWarner Losh /* go back to 0: */ 107069acd21dSWarner Losh if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { 107169acd21dSWarner Losh /* a second being enough for full stroke seek*/ 107269acd21dSWarner Losh DELAY(i == 0? 1000000: 300000); 107369acd21dSWarner Losh 107469acd21dSWarner Losh /* anything responding? */ 107569acd21dSWarner Losh if (fd_sense_int(fdc, &st0, 0) == 0 && 107669acd21dSWarner Losh (st0 & NE7_ST0_EC) == 0) 107769acd21dSWarner Losh break; /* already probed succesfully */ 107869acd21dSWarner Losh } 107969acd21dSWarner Losh } 108069acd21dSWarner Losh 108169acd21dSWarner Losh set_motor(fdcu, fdsu, TURNOFF); 108269acd21dSWarner Losh 108369acd21dSWarner Losh if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ 108469acd21dSWarner Losh return(0); 108569acd21dSWarner Losh 108669acd21dSWarner Losh fd->track = FD_NO_TRACK; 108769acd21dSWarner Losh fd->fdc = fdc; 108869acd21dSWarner Losh fd->fdsu = fdsu; 108969acd21dSWarner Losh fd->options = 0; 109069acd21dSWarner Losh printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); 109169acd21dSWarner Losh fd->type = FD_1440; 109269acd21dSWarner Losh 109369acd21dSWarner Losh #ifdef DEVFS 109469acd21dSWarner Losh mynor = fdcu << 6; 1095ff9607b0SBruce Evans fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, 109669acd21dSWarner Losh UID_ROOT, GID_OPERATOR, 0640, 109769acd21dSWarner Losh "fd%d", fdu); 109869acd21dSWarner Losh fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, 109969acd21dSWarner Losh UID_ROOT, GID_OPERATOR, 0640, 110069acd21dSWarner Losh "rfd%d", fdu); 110169acd21dSWarner Losh /* 110269acd21dSWarner Losh * XXX this and the lookup in Fdopen() should be 110369acd21dSWarner Losh * data driven. 110469acd21dSWarner Losh */ 110569acd21dSWarner Losh typemynor = mynor | FD_1440; 110669acd21dSWarner Losh typesize = fd_types[FD_1440 - 1].size / 2; 110769acd21dSWarner Losh /* 110869acd21dSWarner Losh * XXX all these conversions give bloated code and 110969acd21dSWarner Losh * confusing names. 111069acd21dSWarner Losh */ 111169acd21dSWarner Losh if (typesize == 1476) 111269acd21dSWarner Losh typesize = 1480; 111369acd21dSWarner Losh if (typesize == 1722) 111469acd21dSWarner Losh typesize = 1720; 1115ff9607b0SBruce Evans fd->bdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor, 111669acd21dSWarner Losh DV_BLK, UID_ROOT, GID_OPERATOR, 111769acd21dSWarner Losh 0640, "fd%d.%d", fdu, typesize); 111869acd21dSWarner Losh fd->cdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor, 111969acd21dSWarner Losh DV_CHR, UID_ROOT, GID_OPERATOR, 112069acd21dSWarner Losh 0640,"rfd%d.%d", fdu, typesize); 112169acd21dSWarner Losh for (i = 0; i < MAXPARTITIONS; i++) { 1122ff9607b0SBruce Evans fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], 112369acd21dSWarner Losh "fd%d%c", fdu, 'a' + i); 1124ff9607b0SBruce Evans fd->cdevs[1 + NUMDENS + i] = devfs_makelink(fd->cdevs[0], 112569acd21dSWarner Losh "rfd%d%c", fdu, 'a' + i); 112669acd21dSWarner Losh } 112769acd21dSWarner Losh #endif /* DEVFS */ 112869acd21dSWarner Losh return (1); 112969acd21dSWarner Losh } 113069acd21dSWarner Losh #endif 113169acd21dSWarner Losh 11325b81b6b3SRodney W. Grimes /****************************************************************************/ 11335b81b6b3SRodney W. Grimes /* motor control stuff */ 11345e235068SJordan K. Hubbard /* remember to not deselect the drive we're working on */ 11355b81b6b3SRodney W. Grimes /****************************************************************************/ 11363a2f7427SDavid Greenman static void 11376182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon) 11385b81b6b3SRodney W. Grimes { 11396182fdbdSPeter Wemm int fdout = fdc->fdout; 11403a2f7427SDavid Greenman int needspecify = 0; 11413a2f7427SDavid Greenman 11423a2f7427SDavid Greenman if(turnon) { 11433a2f7427SDavid Greenman fdout &= ~FDO_FDSEL; 11443a2f7427SDavid Greenman fdout |= (FDO_MOEN0 << fdsu) + fdsu; 11453a2f7427SDavid Greenman } else 11463a2f7427SDavid Greenman fdout &= ~(FDO_MOEN0 << fdsu); 11473a2f7427SDavid Greenman 11485e235068SJordan K. Hubbard if(!turnon 11495e235068SJordan K. Hubbard && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) 11505e235068SJordan K. Hubbard /* gonna turn off the last drive, put FDC to bed */ 11515e235068SJordan K. Hubbard fdout &= ~ (FDO_FRST|FDO_FDMAEN); 11525e235068SJordan K. Hubbard else { 11533a2f7427SDavid Greenman /* make sure controller is selected and specified */ 11543a2f7427SDavid Greenman if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) 11553a2f7427SDavid Greenman needspecify = 1; 11563a2f7427SDavid Greenman fdout |= (FDO_FRST|FDO_FDMAEN); 11575b81b6b3SRodney W. Grimes } 11585b81b6b3SRodney W. Grimes 11596182fdbdSPeter Wemm outb(fdc->baseport+FDOUT, fdout); 11606182fdbdSPeter Wemm fdc->fdout = fdout; 11613a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdout); 11623a2f7427SDavid Greenman 11633a2f7427SDavid Greenman if (needspecify) { 1164dc8603e3SJoerg Wunsch /* 1165dc5df763SJoerg Wunsch * XXX 1166dc8603e3SJoerg Wunsch * special case: since we have just woken up the FDC 1167dc8603e3SJoerg Wunsch * from its sleep, we silently assume the command will 1168dc8603e3SJoerg Wunsch * be accepted, and do not test for a timeout 1169dc8603e3SJoerg Wunsch */ 11706182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1171dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1172dc5df763SJoerg Wunsch 0); 11736182fdbdSPeter Wemm if (fdc->flags & FDC_HAS_FIFO) 11746182fdbdSPeter Wemm (void) enable_fifo(fdc); 11753a2f7427SDavid Greenman } 11763a2f7427SDavid Greenman } 11773a2f7427SDavid Greenman 1178381fe1aaSGarrett Wollman static void 11796182fdbdSPeter Wemm fd_turnoff(void *xfd) 11805b81b6b3SRodney W. Grimes { 1181f5f7ba03SJordan K. Hubbard int s; 11826182fdbdSPeter Wemm fd_p fd = xfd; 1183dc16046fSJoerg Wunsch 11846182fdbdSPeter Wemm TRACE1("[fd%d: turnoff]", fd->fdu); 11858335c1b8SBruce Evans 11868335c1b8SBruce Evans /* 11878335c1b8SBruce Evans * Don't turn off the motor yet if the drive is active. 11888335c1b8SBruce Evans * XXX shouldn't even schedule turnoff until drive is inactive 11898335c1b8SBruce Evans * and nothing is queued on it. 11908335c1b8SBruce Evans */ 11916182fdbdSPeter Wemm if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { 11926182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 11938335c1b8SBruce Evans return; 11948335c1b8SBruce Evans } 11958335c1b8SBruce Evans 1196f5f7ba03SJordan K. Hubbard s = splbio(); 11975b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR; 11986182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNOFF); 1199f5f7ba03SJordan K. Hubbard splx(s); 12005b81b6b3SRodney W. Grimes } 12015b81b6b3SRodney W. Grimes 12023a2f7427SDavid Greenman static void 12036182fdbdSPeter Wemm fd_motor_on(void *xfd) 12045b81b6b3SRodney W. Grimes { 1205f5f7ba03SJordan K. Hubbard int s; 12066182fdbdSPeter Wemm fd_p fd = xfd; 1207f5f7ba03SJordan K. Hubbard 1208f5f7ba03SJordan K. Hubbard s = splbio(); 12095b81b6b3SRodney W. Grimes fd->flags &= ~FD_MOTOR_WAIT; 12105b81b6b3SRodney W. Grimes if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) 12115b81b6b3SRodney W. Grimes { 12126182fdbdSPeter Wemm fdc_intr(fd->fdc); 12135b81b6b3SRodney W. Grimes } 1214f5f7ba03SJordan K. Hubbard splx(s); 12155b81b6b3SRodney W. Grimes } 12165b81b6b3SRodney W. Grimes 12173a2f7427SDavid Greenman static void 12186182fdbdSPeter Wemm fd_turnon(fd_p fd) 12195b81b6b3SRodney W. Grimes { 12205b81b6b3SRodney W. Grimes if(!(fd->flags & FD_MOTOR)) 12215b81b6b3SRodney W. Grimes { 12223a2f7427SDavid Greenman fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); 12236182fdbdSPeter Wemm set_motor(fd->fdc, fd->fdsu, TURNON); 12246182fdbdSPeter Wemm timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ 12255b81b6b3SRodney W. Grimes } 12265b81b6b3SRodney W. Grimes } 12275b81b6b3SRodney W. Grimes 1228381fe1aaSGarrett Wollman static void 1229dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc) 12305b81b6b3SRodney W. Grimes { 12313a2f7427SDavid Greenman /* Try a reset, keep motor on */ 12323a2f7427SDavid Greenman outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 12333a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); 12343a2f7427SDavid Greenman DELAY(100); 12353a2f7427SDavid Greenman /* enable FDC, but defer interrupts a moment */ 12363a2f7427SDavid Greenman outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); 12373a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); 12383a2f7427SDavid Greenman DELAY(100); 12393a2f7427SDavid Greenman outb(fdc->baseport + FDOUT, fdc->fdout); 12403a2f7427SDavid Greenman TRACE1("[0x%x->FDOUT]", fdc->fdout); 12413a2f7427SDavid Greenman 1242dc5df763SJoerg Wunsch /* XXX after a reset, silently believe the FDC will accept commands */ 12436182fdbdSPeter Wemm (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, 1244dc5df763SJoerg Wunsch NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 1245dc5df763SJoerg Wunsch 0); 1246d66c374fSTor Egge if (fdc->flags & FDC_HAS_FIFO) 1247d66c374fSTor Egge (void) enable_fifo(fdc); 12485b81b6b3SRodney W. Grimes } 12495b81b6b3SRodney W. Grimes 12505b81b6b3SRodney W. Grimes /****************************************************************************/ 12515b81b6b3SRodney W. Grimes /* fdc in/out */ 12525b81b6b3SRodney W. Grimes /****************************************************************************/ 12535b81b6b3SRodney W. Grimes int 12546182fdbdSPeter Wemm in_fdc(struct fdc_data *fdc) 12555b81b6b3SRodney W. Grimes { 12566182fdbdSPeter Wemm int baseport = fdc->baseport; 12575b81b6b3SRodney W. Grimes int i, j = 100000; 12583a2f7427SDavid Greenman while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) 12595b81b6b3SRodney W. Grimes != (NE7_DIO|NE7_RQM) && j-- > 0) 1260dc5df763SJoerg Wunsch if (i == NE7_RQM) 12616182fdbdSPeter Wemm return fdc_err(fdc, "ready for output in input\n"); 12625b81b6b3SRodney W. Grimes if (j <= 0) 12636182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); 1264d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 12653a2f7427SDavid Greenman i = inb(baseport+FDDATA); 12663a2f7427SDavid Greenman TRACE1("[FDDATA->0x%x]", (unsigned char)i); 12675b81b6b3SRodney W. Grimes return(i); 1268d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 12693a2f7427SDavid Greenman return inb(baseport+FDDATA); 1270d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 12715b81b6b3SRodney W. Grimes } 12725b81b6b3SRodney W. Grimes 1273dc5df763SJoerg Wunsch /* 1274dc5df763SJoerg Wunsch * fd_in: Like in_fdc, but allows you to see if it worked. 1275dc5df763SJoerg Wunsch */ 1276b5e8ce9fSBruce Evans static int 12776182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr) 1278dc5df763SJoerg Wunsch { 12796182fdbdSPeter Wemm int baseport = fdc->baseport; 1280dc5df763SJoerg Wunsch int i, j = 100000; 1281dc5df763SJoerg Wunsch while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) 1282dc5df763SJoerg Wunsch != (NE7_DIO|NE7_RQM) && j-- > 0) 1283dc5df763SJoerg Wunsch if (i == NE7_RQM) 12846182fdbdSPeter Wemm return fdc_err(fdc, "ready for output in input\n"); 1285dc5df763SJoerg Wunsch if (j <= 0) 12866182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); 1287d2fb4892SJoerg Wunsch #ifdef FDC_DEBUG 1288dc5df763SJoerg Wunsch i = inb(baseport+FDDATA); 1289dc5df763SJoerg Wunsch TRACE1("[FDDATA->0x%x]", (unsigned char)i); 1290dc5df763SJoerg Wunsch *ptr = i; 1291dc5df763SJoerg Wunsch return 0; 1292d2fb4892SJoerg Wunsch #else /* !FDC_DEBUG */ 1293dc5df763SJoerg Wunsch i = inb(baseport+FDDATA); 1294dc5df763SJoerg Wunsch if (ptr) 1295dc5df763SJoerg Wunsch *ptr = i; 1296dc5df763SJoerg Wunsch return 0; 1297d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */ 1298dc5df763SJoerg Wunsch } 1299dc5df763SJoerg Wunsch 1300dc5df763SJoerg Wunsch int 13016182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x) 13025b81b6b3SRodney W. Grimes { 13036182fdbdSPeter Wemm int baseport = fdc->baseport; 13043b3837dbSRodney W. Grimes int i; 13055b81b6b3SRodney W. Grimes 13063b3837dbSRodney W. Grimes /* Check that the direction bit is set */ 13073b3837dbSRodney W. Grimes i = 100000; 13083a2f7427SDavid Greenman while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0); 13096182fdbdSPeter Wemm if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); 13103b3837dbSRodney W. Grimes 13113b3837dbSRodney W. Grimes /* Check that the floppy controller is ready for a command */ 13123b3837dbSRodney W. Grimes i = 100000; 13133a2f7427SDavid Greenman while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0); 131416b04b6aSJoerg Wunsch if (i <= 0) 13156182fdbdSPeter Wemm return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); 13163b3837dbSRodney W. Grimes 13173b3837dbSRodney W. Grimes /* Send the command and return */ 13183a2f7427SDavid Greenman outb(baseport+FDDATA, x); 13193a2f7427SDavid Greenman TRACE1("[0x%x->FDDATA]", x); 13205b81b6b3SRodney W. Grimes return (0); 13215b81b6b3SRodney W. Grimes } 13225b81b6b3SRodney W. Grimes 13235b81b6b3SRodney W. Grimes /****************************************************************************/ 13245b81b6b3SRodney W. Grimes /* fdopen/fdclose */ 13255b81b6b3SRodney W. Grimes /****************************************************************************/ 1326381fe1aaSGarrett Wollman int 1327671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p) 13285b81b6b3SRodney W. Grimes { 13295b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 133020a29168SAndrey A. Chernov int type = FDTYPE(minor(dev)); 13316182fdbdSPeter Wemm fd_p fd; 1332b99f0a4aSAndrew Moore fdc_p fdc; 13335b81b6b3SRodney W. Grimes 13345b81b6b3SRodney W. Grimes /* check bounds */ 13356182fdbdSPeter Wemm if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) 1336b99f0a4aSAndrew Moore return (ENXIO); 13376182fdbdSPeter Wemm fdc = fd->fdc; 13386182fdbdSPeter Wemm if ((fdc == NULL) || (fd->type == NO_TYPE)) 1339b99f0a4aSAndrew Moore return (ENXIO); 1340b99f0a4aSAndrew Moore if (type > NUMDENS) 1341b99f0a4aSAndrew Moore return (ENXIO); 13427ca0641bSAndrey A. Chernov if (type == 0) 13436182fdbdSPeter Wemm type = fd->type; 13447ca0641bSAndrey A. Chernov else { 13453e425b96SJulian Elischer /* 13463e425b96SJulian Elischer * For each type of basic drive, make sure we are trying 13473e425b96SJulian Elischer * to open a type it can do, 13483e425b96SJulian Elischer */ 13496182fdbdSPeter Wemm if (type != fd->type) { 13506182fdbdSPeter Wemm switch (fd->type) { 13517ca0641bSAndrey A. Chernov case FD_360: 13527ca0641bSAndrey A. Chernov return (ENXIO); 1353ed2fa05eSAndrey A. Chernov case FD_720: 1354b39c878eSAndrey A. Chernov if ( type != FD_820 1355b39c878eSAndrey A. Chernov && type != FD_800 1356ed2fa05eSAndrey A. Chernov ) 1357ed2fa05eSAndrey A. Chernov return (ENXIO); 1358ed2fa05eSAndrey A. Chernov break; 13597ca0641bSAndrey A. Chernov case FD_1200: 1360b39c878eSAndrey A. Chernov switch (type) { 1361b39c878eSAndrey A. Chernov case FD_1480: 1362b39c878eSAndrey A. Chernov type = FD_1480in5_25; 1363fa4700b4SAndrey A. Chernov break; 13647ca0641bSAndrey A. Chernov case FD_1440: 1365b39c878eSAndrey A. Chernov type = FD_1440in5_25; 1366b39c878eSAndrey A. Chernov break; 1367b39c878eSAndrey A. Chernov case FD_820: 1368b39c878eSAndrey A. Chernov type = FD_820in5_25; 1369b39c878eSAndrey A. Chernov break; 1370b39c878eSAndrey A. Chernov case FD_800: 1371b39c878eSAndrey A. Chernov type = FD_800in5_25; 1372b39c878eSAndrey A. Chernov break; 1373b39c878eSAndrey A. Chernov case FD_720: 1374b39c878eSAndrey A. Chernov type = FD_720in5_25; 1375b39c878eSAndrey A. Chernov break; 1376b39c878eSAndrey A. Chernov case FD_360: 1377b39c878eSAndrey A. Chernov type = FD_360in5_25; 1378b39c878eSAndrey A. Chernov break; 1379b39c878eSAndrey A. Chernov default: 1380b39c878eSAndrey A. Chernov return(ENXIO); 1381b39c878eSAndrey A. Chernov } 1382b39c878eSAndrey A. Chernov break; 1383b39c878eSAndrey A. Chernov case FD_1440: 1384b39c878eSAndrey A. Chernov if ( type != FD_1720 1385b39c878eSAndrey A. Chernov && type != FD_1480 1386ed2fa05eSAndrey A. Chernov && type != FD_1200 1387b39c878eSAndrey A. Chernov && type != FD_820 1388b39c878eSAndrey A. Chernov && type != FD_800 1389b39c878eSAndrey A. Chernov && type != FD_720 13907ca0641bSAndrey A. Chernov ) 1391dffff499SAndrey A. Chernov return(ENXIO); 1392fa4700b4SAndrey A. Chernov break; 13937ca0641bSAndrey A. Chernov } 13947ca0641bSAndrey A. Chernov } 1395fa4700b4SAndrey A. Chernov } 13966182fdbdSPeter Wemm fd->ft = fd_types + type - 1; 13976182fdbdSPeter Wemm fd->flags |= FD_OPEN; 13986182fdbdSPeter Wemm device_busy(fd->dev); 13996182fdbdSPeter Wemm device_busy(fd->fdc->fdc_dev); 14005b81b6b3SRodney W. Grimes return 0; 14015b81b6b3SRodney W. Grimes } 14025b81b6b3SRodney W. Grimes 1403381fe1aaSGarrett Wollman int 1404671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p) 14055b81b6b3SRodney W. Grimes { 14065b81b6b3SRodney W. Grimes fdu_t fdu = FDUNIT(minor(dev)); 14076182fdbdSPeter Wemm struct fd_data *fd; 1408b99f0a4aSAndrew Moore 14096182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 14106182fdbdSPeter Wemm fd->flags &= ~FD_OPEN; 14116182fdbdSPeter Wemm fd->options &= ~FDOPT_NORETRY; 1412dc16046fSJoerg Wunsch 14135b81b6b3SRodney W. Grimes return (0); 14145b81b6b3SRodney W. Grimes } 14155b81b6b3SRodney W. Grimes 14163a2f7427SDavid Greenman /****************************************************************************/ 14173a2f7427SDavid Greenman /* fdstrategy */ 14183a2f7427SDavid Greenman /****************************************************************************/ 14193a2f7427SDavid Greenman void 14203a2f7427SDavid Greenman fdstrategy(struct buf *bp) 14213a2f7427SDavid Greenman { 1422bb6382faSJoerg Wunsch unsigned nblocks, blknum, cando; 14233a2f7427SDavid Greenman int s; 14243a2f7427SDavid Greenman fdu_t fdu; 14253a2f7427SDavid Greenman fdc_p fdc; 14263a2f7427SDavid Greenman fd_p fd; 14273a2f7427SDavid Greenman size_t fdblk; 14283a2f7427SDavid Greenman 14293a2f7427SDavid Greenman fdu = FDUNIT(minor(bp->b_dev)); 14306182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 14316182fdbdSPeter Wemm if (fd == 0) 14326182fdbdSPeter Wemm panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", 14336182fdbdSPeter Wemm (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); 14343a2f7427SDavid Greenman fdc = fd->fdc; 143569acd21dSWarner Losh #ifdef FDC_YE 143669acd21dSWarner Losh if (fd->type == NO_TYPE) { 143769acd21dSWarner Losh bp->b_error = ENXIO; 143869acd21dSWarner Losh bp->b_flags |= B_ERROR; 143969acd21dSWarner Losh /* 144069acd21dSWarner Losh * I _refuse_ to use a goto 144169acd21dSWarner Losh */ 144269acd21dSWarner Losh biodone(bp); 144369acd21dSWarner Losh return; 144469acd21dSWarner Losh }; 144569acd21dSWarner Losh #endif 14463a2f7427SDavid Greenman 1447d3628763SRodney W. Grimes fdblk = 128 << (fd->ft->secsize); 14483a2f7427SDavid Greenman if (!(bp->b_flags & B_FORMAT)) { 14496182fdbdSPeter Wemm if (bp->b_blkno < 0) { 1450dc5df763SJoerg Wunsch printf( 14516a0e6f42SRodney W. Grimes "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", 1452702c623aSPoul-Henning Kamp fdu, (u_long)bp->b_blkno, bp->b_bcount); 14533a2f7427SDavid Greenman bp->b_error = EINVAL; 14543a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 14553a2f7427SDavid Greenman goto bad; 14563a2f7427SDavid Greenman } 14573a2f7427SDavid Greenman if ((bp->b_bcount % fdblk) != 0) { 14583a2f7427SDavid Greenman bp->b_error = EINVAL; 14593a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 14603a2f7427SDavid Greenman goto bad; 14613a2f7427SDavid Greenman } 14623a2f7427SDavid Greenman } 14633a2f7427SDavid Greenman 14643a2f7427SDavid Greenman /* 14653a2f7427SDavid Greenman * Set up block calculations. 14663a2f7427SDavid Greenman */ 1467bb6382faSJoerg Wunsch if (bp->b_blkno > 20000000) { 1468bb6382faSJoerg Wunsch /* 1469bb6382faSJoerg Wunsch * Reject unreasonably high block number, prevent the 1470bb6382faSJoerg Wunsch * multiplication below from overflowing. 1471bb6382faSJoerg Wunsch */ 1472bb6382faSJoerg Wunsch bp->b_error = EINVAL; 14733a2f7427SDavid Greenman bp->b_flags |= B_ERROR; 14743a2f7427SDavid Greenman goto bad; 14753a2f7427SDavid Greenman } 1476bb6382faSJoerg Wunsch blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; 1477bb6382faSJoerg Wunsch nblocks = fd->ft->size; 1478bb6382faSJoerg Wunsch bp->b_resid = 0; 1479bb6382faSJoerg Wunsch if (blknum + (bp->b_bcount / fdblk) > nblocks) { 1480bb6382faSJoerg Wunsch if (blknum <= nblocks) { 1481bb6382faSJoerg Wunsch cando = (nblocks - blknum) * fdblk; 1482bb6382faSJoerg Wunsch bp->b_resid = bp->b_bcount - cando; 1483bb6382faSJoerg Wunsch if (cando == 0) 1484bb6382faSJoerg Wunsch goto bad; /* not actually bad but EOF */ 1485bb6382faSJoerg Wunsch } else { 1486bb6382faSJoerg Wunsch bp->b_error = EINVAL; 1487bb6382faSJoerg Wunsch bp->b_flags |= B_ERROR; 1488bb6382faSJoerg Wunsch goto bad; 1489bb6382faSJoerg Wunsch } 1490bb6382faSJoerg Wunsch } 149127513ca7SBruce Evans bp->b_pblkno = bp->b_blkno; 14923a2f7427SDavid Greenman s = splbio(); 149302a19910SJustin T. Gibbs bufqdisksort(&fdc->head, bp); 14946182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ 1495b2dfb1f9SJustin T. Gibbs 1496b2dfb1f9SJustin T. Gibbs /* Tell devstat we are starting on the transaction */ 1497b2dfb1f9SJustin T. Gibbs devstat_start_transaction(&fd->device_stats); 1498b2dfb1f9SJustin T. Gibbs 14996182fdbdSPeter Wemm fdstart(fdc); 15003a2f7427SDavid Greenman splx(s); 15013a2f7427SDavid Greenman return; 15023a2f7427SDavid Greenman 15033a2f7427SDavid Greenman bad: 15043a2f7427SDavid Greenman biodone(bp); 15053a2f7427SDavid Greenman } 15063a2f7427SDavid Greenman 15075b81b6b3SRodney W. Grimes /***************************************************************\ 15085b81b6b3SRodney W. Grimes * fdstart * 15095b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy * 15105b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command * 15115b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more* 15125b81b6b3SRodney W. Grimes * work to do and picks up what we just added. * 15135b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it * 15145b81b6b3SRodney W. Grimes * will pick up our work when the present work completes * 15155b81b6b3SRodney W. Grimes \***************************************************************/ 1516381fe1aaSGarrett Wollman static void 15176182fdbdSPeter Wemm fdstart(struct fdc_data *fdc) 15185b81b6b3SRodney W. Grimes { 15195b81b6b3SRodney W. Grimes int s; 15205b81b6b3SRodney W. Grimes 15215b81b6b3SRodney W. Grimes s = splbio(); 15226182fdbdSPeter Wemm if(fdc->state == DEVIDLE) 15235b81b6b3SRodney W. Grimes { 15246182fdbdSPeter Wemm fdc_intr(fdc); 15255b81b6b3SRodney W. Grimes } 15265b81b6b3SRodney W. Grimes splx(s); 15275b81b6b3SRodney W. Grimes } 15285b81b6b3SRodney W. Grimes 1529381fe1aaSGarrett Wollman static void 15306182fdbdSPeter Wemm fd_iotimeout(void *xfdc) 15315b81b6b3SRodney W. Grimes { 15325c1a1eaeSBruce Evans fdc_p fdc; 1533f5f7ba03SJordan K. Hubbard int s; 15345b81b6b3SRodney W. Grimes 15356182fdbdSPeter Wemm fdc = xfdc; 15365c1a1eaeSBruce Evans TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); 15375b81b6b3SRodney W. Grimes 15383a2f7427SDavid Greenman /* 15393a2f7427SDavid Greenman * Due to IBM's brain-dead design, the FDC has a faked ready 15403a2f7427SDavid Greenman * signal, hardwired to ready == true. Thus, any command 15413a2f7427SDavid Greenman * issued if there's no diskette in the drive will _never_ 15423a2f7427SDavid Greenman * complete, and must be aborted by resetting the FDC. 15433a2f7427SDavid Greenman * Many thanks, Big Blue! 15445c1a1eaeSBruce Evans * The FDC must not be reset directly, since that would 15455c1a1eaeSBruce Evans * interfere with the state machine. Instead, pretend that 15465c1a1eaeSBruce Evans * the command completed but was invalid. The state machine 15475c1a1eaeSBruce Evans * will reset the FDC and retry once. 15483a2f7427SDavid Greenman */ 15493a2f7427SDavid Greenman s = splbio(); 15505c1a1eaeSBruce Evans fdc->status[0] = NE7_ST0_IC_IV; 15515c1a1eaeSBruce Evans fdc->flags &= ~FDC_STAT_VALID; 15525c1a1eaeSBruce Evans fdc->state = IOTIMEDOUT; 15536182fdbdSPeter Wemm fdc_intr(fdc); 1554f5f7ba03SJordan K. Hubbard splx(s); 15555b81b6b3SRodney W. Grimes } 15565b81b6b3SRodney W. Grimes 15575b81b6b3SRodney W. Grimes /* just ensure it has the right spl */ 1558381fe1aaSGarrett Wollman static void 15596182fdbdSPeter Wemm fd_pseudointr(void *xfdc) 15605b81b6b3SRodney W. Grimes { 15615b81b6b3SRodney W. Grimes int s; 15623a2f7427SDavid Greenman 15635b81b6b3SRodney W. Grimes s = splbio(); 15646182fdbdSPeter Wemm fdc_intr(xfdc); 15655b81b6b3SRodney W. Grimes splx(s); 15665b81b6b3SRodney W. Grimes } 15675b81b6b3SRodney W. Grimes 15685b81b6b3SRodney W. Grimes /***********************************************************************\ 15695b81b6b3SRodney W. Grimes * fdintr * 15705b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0 * 15715b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO * 15725b81b6b3SRodney W. Grimes \***********************************************************************/ 1573fe310de8SBruce Evans static void 15746182fdbdSPeter Wemm fdc_intr(void *xfdc) 15755b81b6b3SRodney W. Grimes { 15766182fdbdSPeter Wemm fdc_p fdc = xfdc; 15776182fdbdSPeter Wemm while(fdstate(fdc)) 1578381fe1aaSGarrett Wollman ; 15795b81b6b3SRodney W. Grimes } 15805b81b6b3SRodney W. Grimes 158169acd21dSWarner Losh #ifdef FDC_YE 158269acd21dSWarner Losh /* 158369acd21dSWarner Losh * magic pseudo-DMA initialization for YE FDC. Sets count and 158469acd21dSWarner Losh * direction 158569acd21dSWarner Losh */ 158669acd21dSWarner Losh #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ 158769acd21dSWarner Losh outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) 158869acd21dSWarner Losh 158969acd21dSWarner Losh /* 159069acd21dSWarner Losh * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy 159169acd21dSWarner Losh */ 159269acd21dSWarner Losh static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) 159369acd21dSWarner Losh { 159469acd21dSWarner Losh u_char *cptr = (u_char *)addr; 159569acd21dSWarner Losh fdc_p fdc = &fdc_data[fdcu]; 159669acd21dSWarner Losh int io = fdc->baseport; 159769acd21dSWarner Losh 159869acd21dSWarner Losh if (flags & B_READ) { 159969acd21dSWarner Losh if (fdc->state != PIOREAD) { 160069acd21dSWarner Losh fdc->state = PIOREAD; 160169acd21dSWarner Losh return(0); 160269acd21dSWarner Losh }; 160369acd21dSWarner Losh SET_BCDR(0,count,io); 160469acd21dSWarner Losh insb(io+FDC_YE_DATAPORT,cptr,count); 160569acd21dSWarner Losh } else { 160669acd21dSWarner Losh outsb(io+FDC_YE_DATAPORT,cptr,count); 160769acd21dSWarner Losh SET_BCDR(0,count,io); 160869acd21dSWarner Losh }; 160969acd21dSWarner Losh return(1); 161069acd21dSWarner Losh } 161169acd21dSWarner Losh #endif /* FDC_YE */ 161269acd21dSWarner Losh 16135b81b6b3SRodney W. Grimes /***********************************************************************\ 16145b81b6b3SRodney W. Grimes * The controller state machine. * 16155b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly * 16165b81b6b3SRodney W. Grimes \***********************************************************************/ 16173a2f7427SDavid Greenman static int 16186182fdbdSPeter Wemm fdstate(fdc_p fdc) 16195b81b6b3SRodney W. Grimes { 16205c1a1eaeSBruce Evans int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; 1621bb6382faSJoerg Wunsch unsigned blknum = 0, b_cylinder = 0; 16225b81b6b3SRodney W. Grimes fdu_t fdu = fdc->fdu; 16235b81b6b3SRodney W. Grimes fd_p fd; 162417542807SPoul-Henning Kamp register struct buf *bp; 1625b39c878eSAndrey A. Chernov struct fd_formb *finfo = NULL; 16263a2f7427SDavid Greenman size_t fdblk; 16275b81b6b3SRodney W. Grimes 1628e93e63cbSBruce Evans bp = fdc->bp; 1629e93e63cbSBruce Evans if (bp == NULL) { 163002a19910SJustin T. Gibbs bp = bufq_first(&fdc->head); 1631e93e63cbSBruce Evans if (bp != NULL) { 1632e93e63cbSBruce Evans bufq_remove(&fdc->head, bp); 1633e93e63cbSBruce Evans fdc->bp = bp; 1634e93e63cbSBruce Evans } 1635e93e63cbSBruce Evans } 1636e93e63cbSBruce Evans if (bp == NULL) { 16375b81b6b3SRodney W. Grimes /***********************************************\ 16385b81b6b3SRodney W. Grimes * nothing left for this controller to do * 16395b81b6b3SRodney W. Grimes * Force into the IDLE state, * 16405b81b6b3SRodney W. Grimes \***********************************************/ 16415b81b6b3SRodney W. Grimes fdc->state = DEVIDLE; 16426182fdbdSPeter Wemm if (fdc->fd) { 16436182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 16446182fdbdSPeter Wemm printf("unexpected valid fd pointer\n"); 16455b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 16465b81b6b3SRodney W. Grimes fdc->fdu = -1; 16475b81b6b3SRodney W. Grimes } 16486182fdbdSPeter Wemm TRACE1("[fdc%d IDLE]", fdc->fdcu); 16495b81b6b3SRodney W. Grimes return (0); 16505b81b6b3SRodney W. Grimes } 16515b81b6b3SRodney W. Grimes fdu = FDUNIT(minor(bp->b_dev)); 16526182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 16533a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 16546182fdbdSPeter Wemm if (fdc->fd && (fd != fdc->fd)) { 16556182fdbdSPeter Wemm device_print_prettyname(fd->dev); 16566182fdbdSPeter Wemm printf("confused fd pointers\n"); 16575b81b6b3SRodney W. Grimes } 16585b81b6b3SRodney W. Grimes read = bp->b_flags & B_READ; 1659b39c878eSAndrey A. Chernov format = bp->b_flags & B_FORMAT; 1660bb6382faSJoerg Wunsch if (format) { 1661ab3f7469SPoul-Henning Kamp finfo = (struct fd_formb *)bp->b_data; 1662bb6382faSJoerg Wunsch fd->skip = (char *)&(finfo->fd_formb_cylno(0)) 1663bb6382faSJoerg Wunsch - (char *)finfo; 1664bb6382faSJoerg Wunsch } 1665bb6382faSJoerg Wunsch if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { 16663e425b96SJulian Elischer blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + 1667bb6382faSJoerg Wunsch fd->skip/fdblk; 1668bb6382faSJoerg Wunsch b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); 1669bb6382faSJoerg Wunsch } 16705b81b6b3SRodney W. Grimes TRACE1("fd%d", fdu); 16715b81b6b3SRodney W. Grimes TRACE1("[%s]", fdstates[fdc->state]); 16725b81b6b3SRodney W. Grimes TRACE1("(0x%x)", fd->flags); 16736182fdbdSPeter Wemm untimeout(fd_turnoff, fd, fd->toffhandle); 16746182fdbdSPeter Wemm fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); 16755b81b6b3SRodney W. Grimes switch (fdc->state) 16765b81b6b3SRodney W. Grimes { 16775b81b6b3SRodney W. Grimes case DEVIDLE: 16785b81b6b3SRodney W. Grimes case FINDWORK: /* we have found new work */ 16795b81b6b3SRodney W. Grimes fdc->retry = 0; 16805b81b6b3SRodney W. Grimes fd->skip = 0; 16815b81b6b3SRodney W. Grimes fdc->fd = fd; 16825b81b6b3SRodney W. Grimes fdc->fdu = fdu; 16833a2f7427SDavid Greenman outb(fdc->baseport+FDCTL, fd->ft->trans); 16843a2f7427SDavid Greenman TRACE1("[0x%x->FDCTL]", fd->ft->trans); 16855b81b6b3SRodney W. Grimes /*******************************************************\ 16865b81b6b3SRodney W. Grimes * If the next drive has a motor startup pending, then * 1687dc733423SDag-Erling Smørgrav * it will start up in its own good time * 16885b81b6b3SRodney W. Grimes \*******************************************************/ 16896182fdbdSPeter Wemm if(fd->flags & FD_MOTOR_WAIT) { 16905b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 16915b81b6b3SRodney W. Grimes return (0); /* come back later */ 16925b81b6b3SRodney W. Grimes } 16935b81b6b3SRodney W. Grimes /*******************************************************\ 16945b81b6b3SRodney W. Grimes * Maybe if it's not starting, it SHOULD be starting * 16955b81b6b3SRodney W. Grimes \*******************************************************/ 16965b81b6b3SRodney W. Grimes if (!(fd->flags & FD_MOTOR)) 16975b81b6b3SRodney W. Grimes { 16985b81b6b3SRodney W. Grimes fdc->state = MOTORWAIT; 16996182fdbdSPeter Wemm fd_turnon(fd); 17005b81b6b3SRodney W. Grimes return (0); 17015b81b6b3SRodney W. Grimes } 17025b81b6b3SRodney W. Grimes else /* at least make sure we are selected */ 17035b81b6b3SRodney W. Grimes { 17046182fdbdSPeter Wemm set_motor(fdc, fd->fdsu, TURNON); 17055b81b6b3SRodney W. Grimes } 17065c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 17075c1a1eaeSBruce Evans fdc->state = RESETCTLR; 17085c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 17095c1a1eaeSBruce Evans } else 17105e235068SJordan K. Hubbard fdc->state = DOSEEK; 17115b81b6b3SRodney W. Grimes break; 17125b81b6b3SRodney W. Grimes case DOSEEK: 1713bb6382faSJoerg Wunsch if (b_cylinder == (unsigned)fd->track) 17145b81b6b3SRodney W. Grimes { 17155b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 17165b81b6b3SRodney W. Grimes break; 17175b81b6b3SRodney W. Grimes } 17186182fdbdSPeter Wemm if (fd_cmd(fdc, 3, NE7CMD_SEEK, 1719bb6382faSJoerg Wunsch fd->fdsu, b_cylinder * fd->ft->steptrac, 1720dc5df763SJoerg Wunsch 0)) 1721dc8603e3SJoerg Wunsch { 1722dc8603e3SJoerg Wunsch /* 1723dc8603e3SJoerg Wunsch * seek command not accepted, looks like 1724dc8603e3SJoerg Wunsch * the FDC went off to the Saints... 1725dc8603e3SJoerg Wunsch */ 1726dc8603e3SJoerg Wunsch fdc->retry = 6; /* try a reset */ 17276182fdbdSPeter Wemm return(retrier(fdc)); 1728dc8603e3SJoerg Wunsch } 1729dc5df763SJoerg Wunsch fd->track = FD_NO_TRACK; 17305b81b6b3SRodney W. Grimes fdc->state = SEEKWAIT; 17315b81b6b3SRodney W. Grimes return(0); /* will return later */ 17325b81b6b3SRodney W. Grimes case SEEKWAIT: 17335b81b6b3SRodney W. Grimes /* allow heads to settle */ 17346182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 16); 17355b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 17365b81b6b3SRodney W. Grimes return(0); /* will return later */ 17375b81b6b3SRodney W. Grimes case SEEKCOMPLETE : /* SEEK DONE, START DMA */ 17385b81b6b3SRodney W. Grimes /* Make sure seek really happened*/ 17396182fdbdSPeter Wemm if(fd->track == FD_NO_TRACK) { 1740bb6382faSJoerg Wunsch int descyl = b_cylinder * fd->ft->steptrac; 17413a2f7427SDavid Greenman do { 17423a2f7427SDavid Greenman /* 1743dc5df763SJoerg Wunsch * This might be a "ready changed" interrupt, 1744dc5df763SJoerg Wunsch * which cannot really happen since the 1745dc5df763SJoerg Wunsch * RDY pin is hardwired to + 5 volts. This 1746dc5df763SJoerg Wunsch * generally indicates a "bouncing" intr 1747dc5df763SJoerg Wunsch * line, so do one of the following: 1748dc5df763SJoerg Wunsch * 1749dc5df763SJoerg Wunsch * When running on an enhanced FDC that is 1750dc5df763SJoerg Wunsch * known to not go stuck after responding 1751dc5df763SJoerg Wunsch * with INVALID, fetch all interrupt states 1752dc5df763SJoerg Wunsch * until seeing either an INVALID or a 1753dc5df763SJoerg Wunsch * real interrupt condition. 1754dc5df763SJoerg Wunsch * 1755dc5df763SJoerg Wunsch * When running on a dumb old NE765, give 1756dc5df763SJoerg Wunsch * up immediately. The controller will 1757dc5df763SJoerg Wunsch * provide up to four dummy RC interrupt 1758dc5df763SJoerg Wunsch * conditions right after reset (for the 1759dc5df763SJoerg Wunsch * corresponding four drives), so this is 1760dc5df763SJoerg Wunsch * our only chance to get notice that it 1761dc5df763SJoerg Wunsch * was not the FDC that caused the interrupt. 17623a2f7427SDavid Greenman */ 1763dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) 1764dc5df763SJoerg Wunsch == FD_NOT_VALID) 1765dc5df763SJoerg Wunsch return 0; 1766dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 1767dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 1768dc5df763SJoerg Wunsch return 0; /* hope for a real intr */ 17693a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 1770dc5df763SJoerg Wunsch 17716182fdbdSPeter Wemm if (0 == descyl) { 1772dc5df763SJoerg Wunsch int failed = 0; 17733a2f7427SDavid Greenman /* 17743a2f7427SDavid Greenman * seek to cyl 0 requested; make sure we are 17753a2f7427SDavid Greenman * really there 17763a2f7427SDavid Greenman */ 1777dc5df763SJoerg Wunsch if (fd_sense_drive_status(fdc, &st3)) 1778dc5df763SJoerg Wunsch failed = 1; 17793a2f7427SDavid Greenman if ((st3 & NE7_ST3_T0) == 0) { 17803a2f7427SDavid Greenman printf( 17813a2f7427SDavid Greenman "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", 17823a2f7427SDavid Greenman fdu, st3, NE7_ST3BITS); 1783dc5df763SJoerg Wunsch failed = 1; 1784dc5df763SJoerg Wunsch } 1785dc5df763SJoerg Wunsch 17866182fdbdSPeter Wemm if (failed) { 17873a2f7427SDavid Greenman if(fdc->retry < 3) 17883a2f7427SDavid Greenman fdc->retry = 3; 17896182fdbdSPeter Wemm return (retrier(fdc)); 17903a2f7427SDavid Greenman } 17913a2f7427SDavid Greenman } 1792dc5df763SJoerg Wunsch 17936182fdbdSPeter Wemm if (cyl != descyl) { 17943a2f7427SDavid Greenman printf( 17953a2f7427SDavid Greenman "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", 17962d9d0204SRodney W. Grimes fdu, descyl, cyl, st0); 1797e5d7d243SBruce Evans if (fdc->retry < 3) 1798e5d7d243SBruce Evans fdc->retry = 3; 17996182fdbdSPeter Wemm return (retrier(fdc)); 18005b81b6b3SRodney W. Grimes } 18015b81b6b3SRodney W. Grimes } 18025b81b6b3SRodney W. Grimes 1803bb6382faSJoerg Wunsch fd->track = b_cylinder; 180469acd21dSWarner Losh #ifdef FDC_YE 180569acd21dSWarner Losh if (!(fdc->flags & FDC_PCMCIA)) 180669acd21dSWarner Losh #endif 1807ab3f7469SPoul-Henning Kamp isa_dmastart(bp->b_flags, bp->b_data+fd->skip, 18083a2f7427SDavid Greenman format ? bp->b_bcount : fdblk, fdc->dmachan); 18095b81b6b3SRodney W. Grimes sectrac = fd->ft->sectrac; 18105b81b6b3SRodney W. Grimes sec = blknum % (sectrac * fd->ft->heads); 18115b81b6b3SRodney W. Grimes head = sec / sectrac; 18125b81b6b3SRodney W. Grimes sec = sec % sectrac + 1; 18133a2f7427SDavid Greenman fd->hddrv = ((head&1)<<2)+fdu; 18143a2f7427SDavid Greenman 18153a2f7427SDavid Greenman if(format || !read) 18163a2f7427SDavid Greenman { 18173a2f7427SDavid Greenman /* make sure the drive is writable */ 1818dc5df763SJoerg Wunsch if(fd_sense_drive_status(fdc, &st3) != 0) 1819dc8603e3SJoerg Wunsch { 1820dc8603e3SJoerg Wunsch /* stuck controller? */ 18215c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 18225c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 18235c1a1eaeSBruce Evans fdc->dmachan); 1824dc8603e3SJoerg Wunsch fdc->retry = 6; /* reset the beast */ 18256182fdbdSPeter Wemm return (retrier(fdc)); 1826dc8603e3SJoerg Wunsch } 18273a2f7427SDavid Greenman if(st3 & NE7_ST3_WP) 18283a2f7427SDavid Greenman { 18293a2f7427SDavid Greenman /* 18303a2f7427SDavid Greenman * XXX YES! this is ugly. 18313a2f7427SDavid Greenman * in order to force the current operation 18323a2f7427SDavid Greenman * to fail, we will have to fake an FDC 18333a2f7427SDavid Greenman * error - all error handling is done 18343a2f7427SDavid Greenman * by the retrier() 18353a2f7427SDavid Greenman */ 18363a2f7427SDavid Greenman fdc->status[0] = NE7_ST0_IC_AT; 18373a2f7427SDavid Greenman fdc->status[1] = NE7_ST1_NW; 18383a2f7427SDavid Greenman fdc->status[2] = 0; 18393a2f7427SDavid Greenman fdc->status[3] = fd->track; 18403a2f7427SDavid Greenman fdc->status[4] = head; 18413a2f7427SDavid Greenman fdc->status[5] = sec; 18423a2f7427SDavid Greenman fdc->retry = 8; /* break out immediately */ 18433a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; /* not really... */ 18443a2f7427SDavid Greenman return (1); 18453a2f7427SDavid Greenman } 18463a2f7427SDavid Greenman } 18475b81b6b3SRodney W. Grimes 18486182fdbdSPeter Wemm if (format) { 184969acd21dSWarner Losh #ifdef FDC_YE 185069acd21dSWarner Losh if (fdc->flags & FDC_PCMCIA) 185169acd21dSWarner Losh (void)fdcpio(fdcu,bp->b_flags, 185269acd21dSWarner Losh bp->b_data+fd->skip, 185369acd21dSWarner Losh bp->b_bcount); 185469acd21dSWarner Losh #endif 1855b39c878eSAndrey A. Chernov /* formatting */ 18566182fdbdSPeter Wemm if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, 1857dc5df763SJoerg Wunsch finfo->fd_formb_secshift, 1858dc5df763SJoerg Wunsch finfo->fd_formb_nsecs, 1859dc5df763SJoerg Wunsch finfo->fd_formb_gaplen, 18606182fdbdSPeter Wemm finfo->fd_formb_fillbyte, 0)) { 1861dc8603e3SJoerg Wunsch /* controller fell over */ 18625c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 18635c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 18645c1a1eaeSBruce Evans fdc->dmachan); 1865dc8603e3SJoerg Wunsch fdc->retry = 6; 18666182fdbdSPeter Wemm return (retrier(fdc)); 1867dc8603e3SJoerg Wunsch } 18686182fdbdSPeter Wemm } else { 186969acd21dSWarner Losh #ifdef FDC_YE 187069acd21dSWarner Losh if (fdc->flags & FDC_PCMCIA) { 187169acd21dSWarner Losh /* 187269acd21dSWarner Losh * this seems to be necessary even when 187369acd21dSWarner Losh * reading data 187469acd21dSWarner Losh */ 187569acd21dSWarner Losh SET_BCDR(1,fdblk,fdc->baseport); 187669acd21dSWarner Losh 187769acd21dSWarner Losh /* 187869acd21dSWarner Losh * perform the write pseudo-DMA before 187969acd21dSWarner Losh * the WRITE command is sent 188069acd21dSWarner Losh */ 188169acd21dSWarner Losh if (!read) 188269acd21dSWarner Losh (void)fdcpio(fdcu,bp->b_flags, 188369acd21dSWarner Losh bp->b_data+fd->skip, 188469acd21dSWarner Losh fdblk); 188569acd21dSWarner Losh } 188669acd21dSWarner Losh #endif 18876182fdbdSPeter Wemm if (fd_cmd(fdc, 9, 1888dc5df763SJoerg Wunsch (read ? NE7CMD_READ : NE7CMD_WRITE), 1889dc5df763SJoerg Wunsch head << 2 | fdu, /* head & unit */ 1890dc5df763SJoerg Wunsch fd->track, /* track */ 1891dc5df763SJoerg Wunsch head, 1892dc5df763SJoerg Wunsch sec, /* sector + 1 */ 1893dc5df763SJoerg Wunsch fd->ft->secsize, /* sector size */ 1894dc5df763SJoerg Wunsch sectrac, /* sectors/track */ 1895dc5df763SJoerg Wunsch fd->ft->gap, /* gap size */ 1896dc5df763SJoerg Wunsch fd->ft->datalen, /* data length */ 18976182fdbdSPeter Wemm 0)) { 1898dc8603e3SJoerg Wunsch /* the beast is sleeping again */ 18995c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 19005c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 19015c1a1eaeSBruce Evans fdc->dmachan); 1902dc8603e3SJoerg Wunsch fdc->retry = 6; 19036182fdbdSPeter Wemm return (retrier(fdc)); 19045b81b6b3SRodney W. Grimes } 1905b39c878eSAndrey A. Chernov } 190669acd21dSWarner Losh #ifdef FDC_YE 190769acd21dSWarner Losh if (fdc->flags & FDC_PCMCIA) 190869acd21dSWarner Losh /* 190969acd21dSWarner Losh * if this is a read, then simply await interrupt 191069acd21dSWarner Losh * before performing PIO 191169acd21dSWarner Losh */ 191269acd21dSWarner Losh if (read && !fdcpio(fdcu,bp->b_flags, 191369acd21dSWarner Losh bp->b_data+fd->skip,fdblk)) { 191469acd21dSWarner Losh fd->tohandle = timeout(fd_iotimeout, 191569acd21dSWarner Losh (caddr_t)fdcu, hz); 191669acd21dSWarner Losh return(0); /* will return later */ 191769acd21dSWarner Losh }; 191869acd21dSWarner Losh 191969acd21dSWarner Losh /* 192069acd21dSWarner Losh * write (or format) operation will fall through and 192169acd21dSWarner Losh * await completion interrupt 192269acd21dSWarner Losh */ 192369acd21dSWarner Losh #endif 19245b81b6b3SRodney W. Grimes fdc->state = IOCOMPLETE; 19256182fdbdSPeter Wemm fd->tohandle = timeout(fd_iotimeout, fdc, hz); 19265b81b6b3SRodney W. Grimes return (0); /* will return later */ 192769acd21dSWarner Losh #ifdef FDC_YE 192869acd21dSWarner Losh case PIOREAD: 192969acd21dSWarner Losh /* 193069acd21dSWarner Losh * actually perform the PIO read. The IOCOMPLETE case 193169acd21dSWarner Losh * removes the timeout for us. 193269acd21dSWarner Losh */ 193369acd21dSWarner Losh (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); 193469acd21dSWarner Losh fdc->state = IOCOMPLETE; 193569acd21dSWarner Losh /* FALLTHROUGH */ 193669acd21dSWarner Losh #endif 19375b81b6b3SRodney W. Grimes case IOCOMPLETE: /* IO DONE, post-analyze */ 19386182fdbdSPeter Wemm untimeout(fd_iotimeout, fdc, fd->tohandle); 1939dc5df763SJoerg Wunsch 19406182fdbdSPeter Wemm if (fd_read_status(fdc, fd->fdsu)) { 19415c1a1eaeSBruce Evans isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 19425c1a1eaeSBruce Evans format ? bp->b_bcount : fdblk, 19435c1a1eaeSBruce Evans fdc->dmachan); 1944dc5df763SJoerg Wunsch if (fdc->retry < 6) 1945dc5df763SJoerg Wunsch fdc->retry = 6; /* force a reset */ 19466182fdbdSPeter Wemm return (retrier(fdc)); 19475b81b6b3SRodney W. Grimes } 1948dc5df763SJoerg Wunsch 19493a2f7427SDavid Greenman fdc->state = IOTIMEDOUT; 1950dc5df763SJoerg Wunsch 19513a2f7427SDavid Greenman /* FALLTHROUGH */ 1952dc5df763SJoerg Wunsch 19533a2f7427SDavid Greenman case IOTIMEDOUT: 195469acd21dSWarner Losh #ifdef FDC_YE 195569acd21dSWarner Losh if (!(fdc->flags & FDC_PCMCIA)) 195669acd21dSWarner Losh #endif 1957ab3f7469SPoul-Henning Kamp isa_dmadone(bp->b_flags, bp->b_data + fd->skip, 19583a2f7427SDavid Greenman format ? bp->b_bcount : fdblk, fdc->dmachan); 19596182fdbdSPeter Wemm if (fdc->status[0] & NE7_ST0_IC) { 19603a2f7427SDavid Greenman if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 19613a2f7427SDavid Greenman && fdc->status[1] & NE7_ST1_OR) { 1962b39c878eSAndrey A. Chernov /* 19633a2f7427SDavid Greenman * DMA overrun. Someone hogged the bus 19643a2f7427SDavid Greenman * and didn't release it in time for the 19653a2f7427SDavid Greenman * next FDC transfer. 19663a2f7427SDavid Greenman * Just restart it, don't increment retry 19673a2f7427SDavid Greenman * count. (vak) 1968b39c878eSAndrey A. Chernov */ 1969b39c878eSAndrey A. Chernov fdc->state = SEEKCOMPLETE; 1970b39c878eSAndrey A. Chernov return (1); 1971b39c878eSAndrey A. Chernov } 19723a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV 19733a2f7427SDavid Greenman && fdc->retry < 6) 19743a2f7427SDavid Greenman fdc->retry = 6; /* force a reset */ 19753a2f7427SDavid Greenman else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT 19763a2f7427SDavid Greenman && fdc->status[2] & NE7_ST2_WC 19773a2f7427SDavid Greenman && fdc->retry < 3) 19783a2f7427SDavid Greenman fdc->retry = 3; /* force recalibrate */ 19796182fdbdSPeter Wemm return (retrier(fdc)); 19805b81b6b3SRodney W. Grimes } 19815b81b6b3SRodney W. Grimes /* All OK */ 19823a2f7427SDavid Greenman fd->skip += fdblk; 19836182fdbdSPeter Wemm if (!format && fd->skip < bp->b_bcount - bp->b_resid) { 19845b81b6b3SRodney W. Grimes /* set up next transfer */ 19855b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 19866182fdbdSPeter Wemm } else { 19875b81b6b3SRodney W. Grimes /* ALL DONE */ 19885b81b6b3SRodney W. Grimes fd->skip = 0; 1989e93e63cbSBruce Evans fdc->bp = NULL; 1990b2dfb1f9SJustin T. Gibbs /* Tell devstat we have finished with the transaction */ 1991b2dfb1f9SJustin T. Gibbs devstat_end_transaction(&fd->device_stats, 1992b2dfb1f9SJustin T. Gibbs bp->b_bcount - bp->b_resid, 1993b2dfb1f9SJustin T. Gibbs DEVSTAT_TAG_NONE, 1994b2dfb1f9SJustin T. Gibbs (bp->b_flags & B_READ) ? 1995b2dfb1f9SJustin T. Gibbs DEVSTAT_READ : DEVSTAT_WRITE); 19965b81b6b3SRodney W. Grimes biodone(bp); 19975b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 19985b81b6b3SRodney W. Grimes fdc->fdu = -1; 19995b81b6b3SRodney W. Grimes fdc->state = FINDWORK; 20005b81b6b3SRodney W. Grimes } 20015b81b6b3SRodney W. Grimes return (1); 20025b81b6b3SRodney W. Grimes case RESETCTLR: 20033a2f7427SDavid Greenman fdc_reset(fdc); 20045b81b6b3SRodney W. Grimes fdc->retry++; 20055c1a1eaeSBruce Evans fdc->state = RESETCOMPLETE; 20065c1a1eaeSBruce Evans return (0); 20075c1a1eaeSBruce Evans case RESETCOMPLETE: 20085c1a1eaeSBruce Evans /* 20095c1a1eaeSBruce Evans * Discard all the results from the reset so that they 20105c1a1eaeSBruce Evans * can't cause an unexpected interrupt later. 20115c1a1eaeSBruce Evans */ 20120e317d05SJoerg Wunsch for (i = 0; i < 4; i++) 20130e317d05SJoerg Wunsch (void)fd_sense_int(fdc, &st0, &cyl); 20145c1a1eaeSBruce Evans fdc->state = STARTRECAL; 20155c1a1eaeSBruce Evans /* Fall through. */ 20165c1a1eaeSBruce Evans case STARTRECAL: 20176182fdbdSPeter Wemm if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { 2018dc8603e3SJoerg Wunsch /* arrgl */ 2019dc8603e3SJoerg Wunsch fdc->retry = 6; 20206182fdbdSPeter Wemm return (retrier(fdc)); 2021dc8603e3SJoerg Wunsch } 20225b81b6b3SRodney W. Grimes fdc->state = RECALWAIT; 20235b81b6b3SRodney W. Grimes return (0); /* will return later */ 20245b81b6b3SRodney W. Grimes case RECALWAIT: 20255b81b6b3SRodney W. Grimes /* allow heads to settle */ 20266182fdbdSPeter Wemm timeout(fd_pseudointr, fdc, hz / 8); 20275b81b6b3SRodney W. Grimes fdc->state = RECALCOMPLETE; 20285b81b6b3SRodney W. Grimes return (0); /* will return later */ 20295b81b6b3SRodney W. Grimes case RECALCOMPLETE: 20303a2f7427SDavid Greenman do { 20313a2f7427SDavid Greenman /* 2032dc5df763SJoerg Wunsch * See SEEKCOMPLETE for a comment on this: 20333a2f7427SDavid Greenman */ 2034dc5df763SJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) 2035dc5df763SJoerg Wunsch return 0; 2036dc5df763SJoerg Wunsch if(fdc->fdct == FDC_NE765 2037dc5df763SJoerg Wunsch && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) 2038dc5df763SJoerg Wunsch return 0; /* hope for a real intr */ 20393a2f7427SDavid Greenman } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); 20403a2f7427SDavid Greenman if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) 20415b81b6b3SRodney W. Grimes { 2042dc8603e3SJoerg Wunsch if(fdc->retry > 3) 2043dc8603e3SJoerg Wunsch /* 2044dc8603e3SJoerg Wunsch * a recalibrate from beyond cylinder 77 2045dc8603e3SJoerg Wunsch * will "fail" due to the FDC limitations; 2046dc8603e3SJoerg Wunsch * since people used to complain much about 2047dc8603e3SJoerg Wunsch * the failure message, try not logging 2048dc8603e3SJoerg Wunsch * this one if it seems to be the first 2049dc8603e3SJoerg Wunsch * time in a line 2050dc8603e3SJoerg Wunsch */ 2051dc8603e3SJoerg Wunsch printf("fd%d: recal failed ST0 %b cyl %d\n", 2052dc8603e3SJoerg Wunsch fdu, st0, NE7_ST0BITS, cyl); 20533a2f7427SDavid Greenman if(fdc->retry < 3) fdc->retry = 3; 20546182fdbdSPeter Wemm return (retrier(fdc)); 20555b81b6b3SRodney W. Grimes } 20565b81b6b3SRodney W. Grimes fd->track = 0; 20575b81b6b3SRodney W. Grimes /* Seek (probably) necessary */ 20585b81b6b3SRodney W. Grimes fdc->state = DOSEEK; 20595b81b6b3SRodney W. Grimes return (1); /* will return immediatly */ 20605b81b6b3SRodney W. Grimes case MOTORWAIT: 20615b81b6b3SRodney W. Grimes if(fd->flags & FD_MOTOR_WAIT) 20625b81b6b3SRodney W. Grimes { 20635b81b6b3SRodney W. Grimes return (0); /* time's not up yet */ 20645b81b6b3SRodney W. Grimes } 20655c1a1eaeSBruce Evans if (fdc->flags & FDC_NEEDS_RESET) { 20665c1a1eaeSBruce Evans fdc->state = RESETCTLR; 20675c1a1eaeSBruce Evans fdc->flags &= ~FDC_NEEDS_RESET; 20685c1a1eaeSBruce Evans } else { 20695e235068SJordan K. Hubbard /* 20705c1a1eaeSBruce Evans * If all motors were off, then the controller was 20715c1a1eaeSBruce Evans * reset, so it has lost track of the current 20725c1a1eaeSBruce Evans * cylinder. Recalibrate to handle this case. 2073f86e4077SBruce Evans * But first, discard the results of the reset. 20745e235068SJordan K. Hubbard */ 2075f86e4077SBruce Evans fdc->state = RESETCOMPLETE; 20765c1a1eaeSBruce Evans } 20775b81b6b3SRodney W. Grimes return (1); /* will return immediatly */ 20785b81b6b3SRodney W. Grimes default: 20796182fdbdSPeter Wemm device_print_prettyname(fdc->fdc_dev); 20806182fdbdSPeter Wemm printf("unexpected FD int->"); 2081dc5df763SJoerg Wunsch if (fd_read_status(fdc, fd->fdsu) == 0) 2082a838d83dSBruce Evans printf("FDC status :%x %x %x %x %x %x %x ", 20835b81b6b3SRodney W. Grimes fdc->status[0], 20845b81b6b3SRodney W. Grimes fdc->status[1], 20855b81b6b3SRodney W. Grimes fdc->status[2], 20865b81b6b3SRodney W. Grimes fdc->status[3], 20875b81b6b3SRodney W. Grimes fdc->status[4], 20885b81b6b3SRodney W. Grimes fdc->status[5], 20895b81b6b3SRodney W. Grimes fdc->status[6] ); 20903a2f7427SDavid Greenman else 2091dac0f2dbSJoerg Wunsch printf("No status available "); 2092dac0f2dbSJoerg Wunsch if (fd_sense_int(fdc, &st0, &cyl) != 0) 2093dac0f2dbSJoerg Wunsch { 2094dac0f2dbSJoerg Wunsch printf("[controller is dead now]\n"); 20955b81b6b3SRodney W. Grimes return (0); 20965b81b6b3SRodney W. Grimes } 2097dac0f2dbSJoerg Wunsch printf("ST0 = %x, PCN = %x\n", st0, cyl); 2098dac0f2dbSJoerg Wunsch return (0); 2099dac0f2dbSJoerg Wunsch } 2100dac0f2dbSJoerg Wunsch /*XXX confusing: some branches return immediately, others end up here*/ 21015b81b6b3SRodney W. Grimes return (1); /* Come back immediatly to new state */ 21025b81b6b3SRodney W. Grimes } 21035b81b6b3SRodney W. Grimes 2104aaf08d94SGarrett Wollman static int 21056182fdbdSPeter Wemm retrier(struct fdc_data *fdc) 21065b81b6b3SRodney W. Grimes { 210717542807SPoul-Henning Kamp register struct buf *bp; 21086182fdbdSPeter Wemm struct fd_data *fd; 21096182fdbdSPeter Wemm int fdu; 21105b81b6b3SRodney W. Grimes 2111e93e63cbSBruce Evans bp = fdc->bp; 21125b81b6b3SRodney W. Grimes 21136182fdbdSPeter Wemm /* XXX shouldn't this be cached somewhere? */ 21146182fdbdSPeter Wemm fdu = FDUNIT(minor(bp->b_dev)); 21156182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 21166182fdbdSPeter Wemm if (fd->options & FDOPT_NORETRY) 21173a2f7427SDavid Greenman goto fail; 21186182fdbdSPeter Wemm 21196182fdbdSPeter Wemm switch (fdc->retry) { 21205b81b6b3SRodney W. Grimes case 0: case 1: case 2: 21215b81b6b3SRodney W. Grimes fdc->state = SEEKCOMPLETE; 21225b81b6b3SRodney W. Grimes break; 21235b81b6b3SRodney W. Grimes case 3: case 4: case 5: 21245b81b6b3SRodney W. Grimes fdc->state = STARTRECAL; 21255b81b6b3SRodney W. Grimes break; 21265b81b6b3SRodney W. Grimes case 6: 21275b81b6b3SRodney W. Grimes fdc->state = RESETCTLR; 21285b81b6b3SRodney W. Grimes break; 21295b81b6b3SRodney W. Grimes case 7: 21305b81b6b3SRodney W. Grimes break; 21315b81b6b3SRodney W. Grimes default: 21323a2f7427SDavid Greenman fail: 21335b81b6b3SRodney W. Grimes { 21347ca0641bSAndrey A. Chernov dev_t sav_b_dev = bp->b_dev; 21357ca0641bSAndrey A. Chernov /* Trick diskerr */ 21363a2f7427SDavid Greenman bp->b_dev = makedev(major(bp->b_dev), 21373a2f7427SDavid Greenman (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); 213892ed385aSRodney W. Grimes diskerr(bp, "fd", "hard error", LOG_PRINTF, 21393a2f7427SDavid Greenman fdc->fd->skip / DEV_BSIZE, 21403a2f7427SDavid Greenman (struct disklabel *)NULL); 21417ca0641bSAndrey A. Chernov bp->b_dev = sav_b_dev; 2142dc5df763SJoerg Wunsch if (fdc->flags & FDC_STAT_VALID) 2143dc5df763SJoerg Wunsch { 2144dc5df763SJoerg Wunsch printf( 2145a838d83dSBruce Evans " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", 2146dc5df763SJoerg Wunsch fdc->status[0], NE7_ST0BITS, 2147dc5df763SJoerg Wunsch fdc->status[1], NE7_ST1BITS, 2148dc5df763SJoerg Wunsch fdc->status[2], NE7_ST2BITS, 2149dc5df763SJoerg Wunsch fdc->status[3], fdc->status[4], 2150dc5df763SJoerg Wunsch fdc->status[5]); 2151dc5df763SJoerg Wunsch } 2152dc5df763SJoerg Wunsch else 2153dc5df763SJoerg Wunsch printf(" (No status)\n"); 21545b81b6b3SRodney W. Grimes } 21555b81b6b3SRodney W. Grimes bp->b_flags |= B_ERROR; 21565b81b6b3SRodney W. Grimes bp->b_error = EIO; 2157bb6382faSJoerg Wunsch bp->b_resid += bp->b_bcount - fdc->fd->skip; 2158e93e63cbSBruce Evans fdc->bp = NULL; 2159b2dfb1f9SJustin T. Gibbs 2160b2dfb1f9SJustin T. Gibbs /* Tell devstat we have finished with the transaction */ 21619f02ad60SJustin T. Gibbs devstat_end_transaction(&fdc->fd->device_stats, 2162b2dfb1f9SJustin T. Gibbs bp->b_bcount - bp->b_resid, 2163b2dfb1f9SJustin T. Gibbs DEVSTAT_TAG_NONE, 2164b2dfb1f9SJustin T. Gibbs (bp->b_flags & B_READ) ? DEVSTAT_READ : 2165b2dfb1f9SJustin T. Gibbs DEVSTAT_WRITE); 21665b81b6b3SRodney W. Grimes fdc->fd->skip = 0; 21675b81b6b3SRodney W. Grimes biodone(bp); 216892ed385aSRodney W. Grimes fdc->state = FINDWORK; 21695c1a1eaeSBruce Evans fdc->flags |= FDC_NEEDS_RESET; 21705b81b6b3SRodney W. Grimes fdc->fd = (fd_p) 0; 21715b81b6b3SRodney W. Grimes fdc->fdu = -1; 217292ed385aSRodney W. Grimes return (1); 21735b81b6b3SRodney W. Grimes } 21745b81b6b3SRodney W. Grimes fdc->retry++; 21755b81b6b3SRodney W. Grimes return (1); 21765b81b6b3SRodney W. Grimes } 21775b81b6b3SRodney W. Grimes 2178b39c878eSAndrey A. Chernov static int 2179b39c878eSAndrey A. Chernov fdformat(dev, finfo, p) 2180b39c878eSAndrey A. Chernov dev_t dev; 2181b39c878eSAndrey A. Chernov struct fd_formb *finfo; 2182b39c878eSAndrey A. Chernov struct proc *p; 2183b39c878eSAndrey A. Chernov { 2184b39c878eSAndrey A. Chernov fdu_t fdu; 2185b39c878eSAndrey A. Chernov fd_p fd; 2186b39c878eSAndrey A. Chernov 2187b39c878eSAndrey A. Chernov struct buf *bp; 2188b39c878eSAndrey A. Chernov int rv = 0, s; 21893a2f7427SDavid Greenman size_t fdblk; 2190b39c878eSAndrey A. Chernov 2191b39c878eSAndrey A. Chernov fdu = FDUNIT(minor(dev)); 21926182fdbdSPeter Wemm fd = devclass_get_softc(fd_devclass, fdu); 21933a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2194b39c878eSAndrey A. Chernov 2195b39c878eSAndrey A. Chernov /* set up a buffer header for fdstrategy() */ 2196b39c878eSAndrey A. Chernov bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); 2197b39c878eSAndrey A. Chernov if(bp == 0) 2198b39c878eSAndrey A. Chernov return ENOBUFS; 219982f5379bSJoerg Wunsch /* 220082f5379bSJoerg Wunsch * keep the process from being swapped 220182f5379bSJoerg Wunsch */ 22022ada239cSPeter Wemm PHOLD(p); 2203b39c878eSAndrey A. Chernov bzero((void *)bp, sizeof(struct buf)); 220467812eacSKirk McKusick BUF_LOCKINIT(bp); 220567812eacSKirk McKusick BUF_LOCK(bp, LK_EXCLUSIVE); 220667812eacSKirk McKusick bp->b_flags = B_PHYS | B_FORMAT; 2207b39c878eSAndrey A. Chernov 2208b39c878eSAndrey A. Chernov /* 2209b39c878eSAndrey A. Chernov * calculate a fake blkno, so fdstrategy() would initiate a 2210b39c878eSAndrey A. Chernov * seek to the requested cylinder 2211b39c878eSAndrey A. Chernov */ 2212b39c878eSAndrey A. Chernov bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) 22133a2f7427SDavid Greenman + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; 2214b39c878eSAndrey A. Chernov 2215b39c878eSAndrey A. Chernov bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; 2216ab3f7469SPoul-Henning Kamp bp->b_data = (caddr_t)finfo; 2217b39c878eSAndrey A. Chernov 2218b39c878eSAndrey A. Chernov /* now do the format */ 22193e425b96SJulian Elischer bp->b_dev = dev; 222049ff4debSPoul-Henning Kamp BUF_STRATEGY(bp, 0); 2221b39c878eSAndrey A. Chernov 2222b39c878eSAndrey A. Chernov /* ...and wait for it to complete */ 2223b39c878eSAndrey A. Chernov s = splbio(); 22246182fdbdSPeter Wemm while(!(bp->b_flags & B_DONE)) { 22255e235068SJordan K. Hubbard rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); 2226b39c878eSAndrey A. Chernov if (rv == EWOULDBLOCK) 2227b39c878eSAndrey A. Chernov break; 2228b39c878eSAndrey A. Chernov } 2229b39c878eSAndrey A. Chernov splx(s); 2230b39c878eSAndrey A. Chernov 223182f5379bSJoerg Wunsch if (rv == EWOULDBLOCK) { 2232b39c878eSAndrey A. Chernov /* timed out */ 2233b39c878eSAndrey A. Chernov rv = EIO; 223482f5379bSJoerg Wunsch biodone(bp); 223582f5379bSJoerg Wunsch } 22363a2f7427SDavid Greenman if (bp->b_flags & B_ERROR) 22373a2f7427SDavid Greenman rv = bp->b_error; 223882f5379bSJoerg Wunsch /* 223982f5379bSJoerg Wunsch * allow the process to be swapped 224082f5379bSJoerg Wunsch */ 22412ada239cSPeter Wemm PRELE(p); 224267812eacSKirk McKusick BUF_UNLOCK(bp); 224367812eacSKirk McKusick BUF_LOCKFREE(bp); 2244b39c878eSAndrey A. Chernov free(bp, M_TEMP); 2245b39c878eSAndrey A. Chernov return rv; 2246b39c878eSAndrey A. Chernov } 2247b39c878eSAndrey A. Chernov 2248f5f7ba03SJordan K. Hubbard /* 2249671e2ceeSBruce Evans * TODO: don't allocate buffer on stack. 2250f5f7ba03SJordan K. Hubbard */ 22515b81b6b3SRodney W. Grimes 22523e425b96SJulian Elischer static int 2253b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p) 2254f5f7ba03SJordan K. Hubbard dev_t dev; 2255ecbb00a2SDoug Rabson u_long cmd; 2256f5f7ba03SJordan K. Hubbard caddr_t addr; 2257f5f7ba03SJordan K. Hubbard int flag; 2258b39c878eSAndrey A. Chernov struct proc *p; 2259f5f7ba03SJordan K. Hubbard { 22603a2f7427SDavid Greenman fdu_t fdu = FDUNIT(minor(dev)); 22616182fdbdSPeter Wemm fd_p fd = devclass_get_softc(fd_devclass, fdu); 22623a2f7427SDavid Greenman size_t fdblk; 22633a2f7427SDavid Greenman 2264f5f7ba03SJordan K. Hubbard struct fd_type *fdt; 2265f5f7ba03SJordan K. Hubbard struct disklabel *dl; 2266f5f7ba03SJordan K. Hubbard char buffer[DEV_BSIZE]; 22673a2f7427SDavid Greenman int error = 0; 2268f5f7ba03SJordan K. Hubbard 22693a2f7427SDavid Greenman fdblk = 128 << fd->ft->secsize; 2270f5f7ba03SJordan K. Hubbard 22716182fdbdSPeter Wemm switch (cmd) { 2272f5f7ba03SJordan K. Hubbard case DIOCGDINFO: 2273f5f7ba03SJordan K. Hubbard bzero(buffer, sizeof (buffer)); 2274f5f7ba03SJordan K. Hubbard dl = (struct disklabel *)buffer; 22753a2f7427SDavid Greenman dl->d_secsize = fdblk; 22766182fdbdSPeter Wemm fdt = fd->ft; 2277f5f7ba03SJordan K. Hubbard dl->d_secpercyl = fdt->size / fdt->tracks; 2278f5f7ba03SJordan K. Hubbard dl->d_type = DTYPE_FLOPPY; 2279f5f7ba03SJordan K. Hubbard 228049ff4debSPoul-Henning Kamp if (readdisklabel(dkmodpart(dev, RAW_PART), dl) 2281191e1a59SBruce Evans == NULL) 2282f5f7ba03SJordan K. Hubbard error = 0; 2283f5f7ba03SJordan K. Hubbard else 2284f5f7ba03SJordan K. Hubbard error = EINVAL; 2285f5f7ba03SJordan K. Hubbard 2286f5f7ba03SJordan K. Hubbard *(struct disklabel *)addr = *dl; 2287f5f7ba03SJordan K. Hubbard break; 2288f5f7ba03SJordan K. Hubbard 2289f5f7ba03SJordan K. Hubbard case DIOCSDINFO: 2290f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2291f5f7ba03SJordan K. Hubbard error = EBADF; 2292f5f7ba03SJordan K. Hubbard break; 2293f5f7ba03SJordan K. Hubbard 2294f5f7ba03SJordan K. Hubbard case DIOCWLABEL: 2295f5f7ba03SJordan K. Hubbard if ((flag & FWRITE) == 0) 2296f5f7ba03SJordan K. Hubbard error = EBADF; 2297f5f7ba03SJordan K. Hubbard break; 2298f5f7ba03SJordan K. Hubbard 2299f5f7ba03SJordan K. Hubbard case DIOCWDINFO: 23006182fdbdSPeter Wemm if ((flag & FWRITE) == 0) { 2301f5f7ba03SJordan K. Hubbard error = EBADF; 2302f5f7ba03SJordan K. Hubbard break; 2303f5f7ba03SJordan K. Hubbard } 2304f5f7ba03SJordan K. Hubbard 2305f5f7ba03SJordan K. Hubbard dl = (struct disklabel *)addr; 2306f5f7ba03SJordan K. Hubbard 2307191e1a59SBruce Evans if ((error = setdisklabel((struct disklabel *)buffer, dl, 2308191e1a59SBruce Evans (u_long)0)) != 0) 2309f5f7ba03SJordan K. Hubbard break; 2310f5f7ba03SJordan K. Hubbard 231149ff4debSPoul-Henning Kamp error = writedisklabel(dev, (struct disklabel *)buffer); 2312b39c878eSAndrey A. Chernov break; 2313b39c878eSAndrey A. Chernov case FD_FORM: 2314b39c878eSAndrey A. Chernov if ((flag & FWRITE) == 0) 2315b39c878eSAndrey A. Chernov error = EBADF; /* must be opened for writing */ 2316b39c878eSAndrey A. Chernov else if (((struct fd_formb *)addr)->format_version != 2317b39c878eSAndrey A. Chernov FD_FORMAT_VERSION) 2318b39c878eSAndrey A. Chernov error = EINVAL; /* wrong version of formatting prog */ 2319b39c878eSAndrey A. Chernov else 2320b39c878eSAndrey A. Chernov error = fdformat(dev, (struct fd_formb *)addr, p); 2321b39c878eSAndrey A. Chernov break; 2322b39c878eSAndrey A. Chernov 2323b39c878eSAndrey A. Chernov case FD_GTYPE: /* get drive type */ 23243e425b96SJulian Elischer *(struct fd_type *)addr = *fd->ft; 2325f5f7ba03SJordan K. Hubbard break; 2326f5f7ba03SJordan K. Hubbard 23273a2f7427SDavid Greenman case FD_STYPE: /* set drive type */ 23283a2f7427SDavid Greenman /* this is considered harmful; only allow for superuser */ 2329f711d546SPoul-Henning Kamp if (suser(p) != 0) 23303a2f7427SDavid Greenman return EPERM; 23313e425b96SJulian Elischer *fd->ft = *(struct fd_type *)addr; 23323a2f7427SDavid Greenman break; 23333a2f7427SDavid Greenman 23343a2f7427SDavid Greenman case FD_GOPTS: /* get drive options */ 23353e425b96SJulian Elischer *(int *)addr = fd->options; 23363a2f7427SDavid Greenman break; 23373a2f7427SDavid Greenman 23383a2f7427SDavid Greenman case FD_SOPTS: /* set drive options */ 23393e425b96SJulian Elischer fd->options = *(int *)addr; 23403a2f7427SDavid Greenman break; 23413a2f7427SDavid Greenman 2342f5f7ba03SJordan K. Hubbard default: 23433a2f7427SDavid Greenman error = ENOTTY; 2344f5f7ba03SJordan K. Hubbard break; 2345f5f7ba03SJordan K. Hubbard } 2346f5f7ba03SJordan K. Hubbard return (error); 2347f5f7ba03SJordan K. Hubbard } 2348f5f7ba03SJordan K. Hubbard 23496182fdbdSPeter Wemm static device_method_t fdc_methods[] = { 23506182fdbdSPeter Wemm /* Device interface */ 23516182fdbdSPeter Wemm DEVMETHOD(device_probe, fdc_probe), 23526182fdbdSPeter Wemm DEVMETHOD(device_attach, fdc_attach), 23536182fdbdSPeter Wemm DEVMETHOD(device_detach, bus_generic_detach), 23546182fdbdSPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 23556182fdbdSPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), 23566182fdbdSPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), 23577146c13eSJulian Elischer 23586182fdbdSPeter Wemm /* Bus interface */ 23596182fdbdSPeter Wemm DEVMETHOD(bus_print_child, fdc_print_child), 23606182fdbdSPeter Wemm /* Our children never use any other bus interface methods. */ 23617146c13eSJulian Elischer 23626182fdbdSPeter Wemm { 0, 0 } 23636182fdbdSPeter Wemm }; 236487f6c662SJulian Elischer 23656182fdbdSPeter Wemm static driver_t fdc_driver = { 23666182fdbdSPeter Wemm "fdc", 23676182fdbdSPeter Wemm fdc_methods, 23686182fdbdSPeter Wemm sizeof(struct fdc_data) 23696182fdbdSPeter Wemm }; 237087f6c662SJulian Elischer 23716182fdbdSPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); 237287f6c662SJulian Elischer 23736182fdbdSPeter Wemm static device_method_t fd_methods[] = { 23746182fdbdSPeter Wemm /* Device interface */ 23756182fdbdSPeter Wemm DEVMETHOD(device_probe, fd_probe), 23766182fdbdSPeter Wemm DEVMETHOD(device_attach, fd_attach), 23776182fdbdSPeter Wemm DEVMETHOD(device_detach, bus_generic_detach), 23786182fdbdSPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 23796182fdbdSPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ 23806182fdbdSPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ 23813e425b96SJulian Elischer 23826182fdbdSPeter Wemm { 0, 0 } 23836182fdbdSPeter Wemm }; 23846182fdbdSPeter Wemm 23856182fdbdSPeter Wemm static driver_t fd_driver = { 23866182fdbdSPeter Wemm "fd", 23876182fdbdSPeter Wemm fd_methods, 23886182fdbdSPeter Wemm sizeof(struct fd_data) 23896182fdbdSPeter Wemm }; 23906182fdbdSPeter Wemm 239103016f42SPoul-Henning Kamp DEV_DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, fd_cdevsw, 0, 0); 23926182fdbdSPeter Wemm 23936182fdbdSPeter Wemm #endif /* NFDC > 0 */ 23943e425b96SJulian Elischer 23953a2f7427SDavid Greenman /* 23963a2f7427SDavid Greenman * Hello emacs, these are the 23973a2f7427SDavid Greenman * Local Variables: 23983a2f7427SDavid Greenman * c-indent-level: 8 23993a2f7427SDavid Greenman * c-continued-statement-offset: 8 24003a2f7427SDavid Greenman * c-continued-brace-offset: 0 24013a2f7427SDavid Greenman * c-brace-offset: -8 24023a2f7427SDavid Greenman * c-brace-imaginary-offset: 0 24033a2f7427SDavid Greenman * c-argdecl-indent: 8 24043a2f7427SDavid Greenman * c-label-offset: -8 24053a2f7427SDavid Greenman * c++-hanging-braces: 1 24063a2f7427SDavid Greenman * c++-access-specifier-offset: -8 24073a2f7427SDavid Greenman * c++-empty-arglist-indent: 8 24083a2f7427SDavid Greenman * c++-friend-offset: 0 24093a2f7427SDavid Greenman * End: 24103a2f7427SDavid Greenman */ 2411