diff options
author | imp <imp@FreeBSD.org> | 1998-12-12 08:16:01 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 1998-12-12 08:16:01 +0000 |
commit | ca6fd0503c7a84c68a40177a7fae7b198583e14e (patch) | |
tree | d29ffded57ec370abcb723d6271832993789aeee /sys/isa | |
parent | 2da06b289548cde64f579509fa362683b2d0d9d4 (diff) | |
download | FreeBSD-src-ca6fd0503c7a84c68a40177a7fae7b198583e14e.zip FreeBSD-src-ca6fd0503c7a84c68a40177a7fae7b198583e14e.tar.gz |
Add support for the YE-Data external PCMCIA floppy driver. This
floppy is used on the toshiba Libretto line of subnotebook computers.
It differs from a normal floppy in that you must use PIO rather than
DMA to transfer the data.
To enable this, you must add options "FDC_YE" to your kernel. I don't
have a machine that has a floppy and a pcmcia slot to test to make
sure that this doesn't impact normal floppy units, so I've left this as
an option.
I have ported this to -current and made an attempt to ensure that the
indentation conforms to style(9), aka the bruce filter.
Reviewed by: nate, markm
Submitted by: David Horwitt (dhorwitt@ucsd.edu)
Diffstat (limited to 'sys/isa')
-rw-r--r-- | sys/isa/fd.c | 367 | ||||
-rw-r--r-- | sys/isa/fdc.h | 6 |
2 files changed, 365 insertions, 8 deletions
diff --git a/sys/isa/fd.c b/sys/isa/fd.c index 4d78f2e..e139cd7 100644 --- a/sys/isa/fd.c +++ b/sys/isa/fd.c @@ -5,6 +5,10 @@ * This code is derived from software contributed to Berkeley by * Don Ahn. * + * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) + * aided by the Linux floppy driver modifications from David Bateman + * (dbateman@eng.uts.edu.au). + * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) @@ -43,7 +47,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.126 1998/12/07 21:58:20 archie Exp $ + * $Id: fd.c,v 1.127 1998/12/10 19:56:59 eivind Exp $ * */ @@ -89,6 +93,10 @@ /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ +#ifdef FDC_YE +#define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's + a PCMCIA device */ +#endif /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 @@ -201,6 +209,11 @@ int ftsize(dev_t); int ftattach(struct isa_device *, struct isa_device *, int); #endif +#ifdef FDC_YE +#include "card.h" +static int yeattach(struct isa_device *); +#endif + /* autoconfig functions */ static int fdprobe(struct isa_device *); static int fdattach(struct isa_device *); @@ -244,6 +257,9 @@ static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #define MOTORWAIT 10 #define IOTIMEDOUT 11 #define RESETCOMPLETE 12 +#ifdef FDC_YE +#define PIOREAD 13 +#endif #ifdef FDC_DEBUG static char const * const fdstates[] = @@ -261,6 +277,9 @@ static char const * const fdstates[] = "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", +#ifdef FDC_YE +,"PIOREAD" +#endif }; /* CAUTION: fd_debug causes huge amounts of logging output */ @@ -272,6 +291,99 @@ static int volatile fd_debug = 0; #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ +#ifdef FDC_YE +#if NCARD > 0 +#include <sys/select.h> +#include <pccard/cardinfo.h> +#include <pccard/driver.h> +#include <pccard/slot.h> + +/* + * PC-Card (PCMCIA) specific code. + */ +static int yeinit(struct pccard_devinfo *); /* init device */ +static void yeunload(struct pccard_devinfo *); /* Disable driver */ +static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ + +static struct pccard_device ye_info = { + "fdc", + yeinit, + yeunload, + yeintr, + 0, /* Attributes - presently unused */ + &bio_imask /* Interrupt mask for device */ +}; + +DATA_SET(pccarddrv_set, ye_info); + +/* + * this is the secret PIO data port (offset from base) + */ +#define FDC_YE_DATAPORT 6 + +/* + * Initialize the device - called from Slot manager. + */ +static int yeinit(struct pccard_devinfo *devi) +{ + fdc_p fdc = &fdc_data[devi->isahd.id_unit]; + + /* validate unit number. */ + if (devi->isahd.id_unit >= NFDC) + return(ENODEV); + fdc->baseport = devi->isahd.id_iobase; + /* + * reset controller + */ + outb(fdc->baseport+FDOUT, 0); + DELAY(100); + outb(fdc->baseport+FDOUT, FDO_FRST); + + /* + * wire into system + */ + if (yeattach(&devi->isahd) == 0) + return(ENXIO); + + return(0); +} + +/* + * yeunload - unload the driver and clear the table. + * XXX TODO: + * This is usually called when the card is ejected, but + * can be caused by a modunload of a controller driver. + * The idea is to reset the driver's view of the device + * and ensure that any driver entry points such as + * read and write do not hang. + */ +static void yeunload(struct pccard_devinfo *devi) +{ + if (fd_data[devi->isahd.id_unit].type == NO_TYPE) + return; + + /* + * this prevents Fdopen() and fdstrategy() from attempting + * to access unloaded controller + */ + fd_data[devi->isahd.id_unit].type = NO_TYPE; + + printf("fdc%d: unload\n", devi->isahd.id_unit); +} + +/* + * yeintr - Shared interrupt called from + * front end of PC-Card handler. + */ +static int yeintr(struct pccard_devinfo *devi) +{ + fdintr((fdcu_t)devi->isahd.id_unit); + return(1); +} +#endif /* NCARD > 0 */ +#endif /* FDC_YE */ + + /* autoconfig structure */ struct isa_driver fdcdriver = { @@ -310,7 +422,7 @@ fdc_err(fdcu_t fdcu, const char *s) printf("fdc%d: %s", fdcu, s); else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX) printf("fdc%d: too many errors, not logging any more\n", - fdcu); + fdcu); } return FD_FAILED; @@ -515,6 +627,14 @@ fdprobe(struct isa_device *dev) { return(0); } +#ifdef FDC_YE + /* + * don't succeed on probe; wait + * for PCCARD subsystem to do it + */ + if (dev->id_flags & FDC_IS_PCMCIA) + return(0); +#endif return (IO_FDCSIZE); } @@ -641,7 +761,7 @@ fdattach(struct isa_device *dev) enable_fifo(fdc) == 0) { printf("fdc%d: FIFO enabled", fdcu); printf(", %d bytes threshold\n", - fifo_threshold); + fifo_threshold); } } if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && @@ -806,6 +926,138 @@ fdattach(struct isa_device *dev) +#ifdef FDC_YE +/* + * this is a subset of fdattach() optimized for the Y-E Data + * PCMCIA floppy drive. + */ +static int yeattach(struct isa_device *dev) +{ + fdcu_t fdcu = dev->id_unit; + fdc_p fdc = fdc_data + fdcu; + fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ + fdu_t fdu; + fd_p fd; + int st0, st3, i; +#ifdef DEVFS + int mynor; + int typemynor; + int typesize; +#endif + fdc->fdcu = fdcu; + /* + * the FDC_PCMCIA flag is used to to indicate special PIO is used + * instead of DMA + */ + fdc->flags = FDC_ATTACHED|FDC_PCMCIA; + fdc->state = DEVIDLE; + /* reset controller, turn motor off, clear fdout mirror reg */ + outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); + bufq_init(&fdc->head); + /* + * assume 2 drives/ "normal" controller + */ + fdu = fdcu * 2; + if (fdu >= NFD) { + printf("fdu %d >= NFD\n",fdu); + return(0); + }; + fd = &fd_data[fdu]; + + set_motor(fdcu, fdsu, TURNON); + DELAY(1000000); /* 1 sec */ + fdc->fdct = FDC_NE765; + + if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && + (st3 & NE7_ST3_T0)) { + /* if at track 0, first seek inwards */ + /* seek some steps: */ + (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); + DELAY(300000); /* ...wait a moment... */ + (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ + } + + /* If we're at track 0 first seek inwards. */ + if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { + /* Seek some steps... */ + if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { + /* ...wait a moment... */ + DELAY(300000); + /* make ctrlr happy: */ + (void)fd_sense_int(fdc, 0, 0); + } + } + + for(i = 0; i < 2; i++) { + /* + * we must recalibrate twice, just in case the + * heads have been beyond cylinder 76, since most + * FDCs still barf when attempting to recalibrate + * more than 77 steps + */ + /* go back to 0: */ + if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { + /* a second being enough for full stroke seek*/ + DELAY(i == 0? 1000000: 300000); + + /* anything responding? */ + if (fd_sense_int(fdc, &st0, 0) == 0 && + (st0 & NE7_ST0_EC) == 0) + break; /* already probed succesfully */ + } + } + + set_motor(fdcu, fdsu, TURNOFF); + + if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ + return(0); + + fd->track = FD_NO_TRACK; + fd->fdc = fdc; + fd->fdsu = fdsu; + fd->options = 0; + printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); + fd->type = FD_1440; + +#ifdef DEVFS + mynor = fdcu << 6; + fd->bdevs[0] = devfs_add_devswf(&fd_bdevsw, mynor, DV_BLK, + UID_ROOT, GID_OPERATOR, 0640, + "fd%d", fdu); + fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, + UID_ROOT, GID_OPERATOR, 0640, + "rfd%d", fdu); + /* + * XXX this and the lookup in Fdopen() should be + * data driven. + */ + typemynor = mynor | FD_1440; + typesize = fd_types[FD_1440 - 1].size / 2; + /* + * XXX all these conversions give bloated code and + * confusing names. + */ + if (typesize == 1476) + typesize = 1480; + if (typesize == 1722) + typesize = 1720; + fd->bdevs[FD_1440] = devfs_add_devswf(&fd_bdevsw, typemynor, + DV_BLK, UID_ROOT, GID_OPERATOR, + 0640, "fd%d.%d", fdu, typesize); + fd->cdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor, + DV_CHR, UID_ROOT, GID_OPERATOR, + 0640,"rfd%d.%d", fdu, typesize); + for (i = 0; i < MAXPARTITIONS; i++) { + fd->bdevs[1 + NUMDENS + i] = devfs_link(fd->bdevs[0], + "fd%d%c", fdu, 'a' + i); + fd->cdevs[1 + NUMDENS + i] = devfs_link(fd->cdevs[0], + "rfd%d%c", fdu, 'a' + i); + } +#endif /* DEVFS */ + return (1); +} +#endif + /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ @@ -1133,6 +1385,17 @@ fdstrategy(struct buf *bp) fd = &fd_data[fdu]; fdc = fd->fdc; fdcu = fdc->fdcu; +#ifdef FDC_YE + if (fd->type == NO_TYPE) { + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + /* + * I _refuse_ to use a goto + */ + biodone(bp); + return; + }; +#endif #if NFT > 0 if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) { @@ -1292,6 +1555,38 @@ fdintr(fdcu_t fdcu) ; } +#ifdef FDC_YE +/* + * magic pseudo-DMA initialization for YE FDC. Sets count and + * direction + */ +#define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ + outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) + +/* + * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy + */ +static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) +{ + u_char *cptr = (u_char *)addr; + fdc_p fdc = &fdc_data[fdcu]; + int io = fdc->baseport; + + if (flags & B_READ) { + if (fdc->state != PIOREAD) { + fdc->state = PIOREAD; + return(0); + }; + SET_BCDR(0,count,io); + insb(io+FDC_YE_DATAPORT,cptr,count); + } else { + outsb(io+FDC_YE_DATAPORT,cptr,count); + SET_BCDR(0,count,io); + }; + return(1); +} +#endif /* FDC_YE */ + /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * @@ -1482,8 +1777,11 @@ fdstate(fdcu_t fdcu, fdc_p fdc) } fd->track = b_cylinder; - isa_dmastart(bp->b_flags, bp->b_data+fd->skip, - format ? bp->b_bcount : fdblk, fdc->dmachan); +#ifdef FDC_YE + if (!(fdc->flags & FDC_PCMCIA)) +#endif + isa_dmastart(bp->b_flags, bp->b_data+fd->skip, + format ? bp->b_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; @@ -1525,6 +1823,12 @@ fdstate(fdcu_t fdcu, fdc_p fdc) if(format) { +#ifdef FDC_YE + if (fdc->flags & FDC_PCMCIA) + (void)fdcpio(fdcu,bp->b_flags, + bp->b_data+fd->skip, + bp->b_bcount); +#endif /* formatting */ if(fd_cmd(fdcu, 6, NE7CMD_FORMAT, @@ -1545,6 +1849,24 @@ fdstate(fdcu_t fdcu, fdc_p fdc) } else { +#ifdef FDC_YE + if (fdc->flags & FDC_PCMCIA) { + /* + * this seems to be necessary even when + * reading data + */ + SET_BCDR(1,fdblk,fdc->baseport); + + /* + * perform the write pseudo-DMA before + * the WRITE command is sent + */ + if (!read) + (void)fdcpio(fdcu,bp->b_flags, + bp->b_data+fd->skip, + fdblk); + } +#endif if (fd_cmd(fdcu, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ @@ -1565,9 +1887,37 @@ fdstate(fdcu_t fdcu, fdc_p fdc) return(retrier(fdcu)); } } +#ifdef FDC_YE + if (fdc->flags & FDC_PCMCIA) + /* + * if this is a read, then simply await interrupt + * before performing PIO + */ + if (read && !fdcpio(fdcu,bp->b_flags, + bp->b_data+fd->skip,fdblk)) { + fd->tohandle = timeout(fd_iotimeout, + (caddr_t)fdcu, hz); + return(0); /* will return later */ + }; + + /* + * write (or format) operation will fall through and + * await completion interrupt + */ +#endif fdc->state = IOCOMPLETE; fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); return(0); /* will return later */ +#ifdef FDC_YE + case PIOREAD: + /* + * actually perform the PIO read. The IOCOMPLETE case + * removes the timeout for us. + */ + (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); + fdc->state = IOCOMPLETE; + /* FALLTHROUGH */ +#endif case IOCOMPLETE: /* IO DONE, post-analyze */ untimeout(fd_iotimeout, (caddr_t)fdcu, fd->tohandle); @@ -1586,8 +1936,11 @@ fdstate(fdcu_t fdcu, fdc_p fdc) /* FALLTHROUGH */ case IOTIMEDOUT: - isa_dmadone(bp->b_flags, bp->b_data + fd->skip, - format ? bp->b_bcount : fdblk, fdc->dmachan); +#ifdef FDC_YE + if (!(fdc->flags & FDC_PCMCIA)) +#endif + isa_dmadone(bp->b_flags, bp->b_data + fd->skip, + format ? bp->b_bcount : fdblk, fdc->dmachan); if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT diff --git a/sys/isa/fdc.h b/sys/isa/fdc.h index fdaf694..c8c275f 100644 --- a/sys/isa/fdc.h +++ b/sys/isa/fdc.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fdc.h,v 1.10 1998/07/11 06:35:39 bde Exp $ + * $Id: fdc.h,v 1.11 1998/07/29 13:00:42 bde Exp $ * */ @@ -56,6 +56,10 @@ struct fdc_data #define FDC_STAT_VALID 0x08 #define FDC_HAS_FIFO 0x10 #define FDC_NEEDS_RESET 0x20 +#ifdef FDC_YE +#define FDC_PCMCIA 0x40 +#define FDC_UNLOADED 0x80 +#endif struct fd_data *fd; int fdu; /* the active drive */ int state; |