aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/fdc/fdc.c
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>1998-12-12 08:16:01 +0000
committerWarner Losh <imp@FreeBSD.org>1998-12-12 08:16:01 +0000
commit69acd21dfd4d320496e1345b821009802be59c6f (patch)
treea8290ec68e76afb7c3d552f4ce76fe6f01772133 /sys/dev/fdc/fdc.c
parent01e85cb38da437bcb1b7c464e7bb7272c884d5e1 (diff)
downloadsrc-69acd21dfd4d320496e1345b821009802be59c6f.tar.gz
src-69acd21dfd4d320496e1345b821009802be59c6f.zip
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)
Notes
Notes: svn path=/head/; revision=41693
Diffstat (limited to 'sys/dev/fdc/fdc.c')
-rw-r--r--sys/dev/fdc/fdc.c367
1 files changed, 360 insertions, 7 deletions
diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c
index 4d78f2eeebb5..e139cd769a8d 100644
--- a/sys/dev/fdc/fdc.c
+++ b/sys/dev/fdc/fdc.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