diff options
author | Thomas Bushnell <thomas@gnu.org> | 1997-02-25 21:28:37 +0000 |
---|---|---|
committer | Thomas Bushnell <thomas@gnu.org> | 1997-02-25 21:28:37 +0000 |
commit | f07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch) | |
tree | 12b07c7e578fc1a5f53dbfde2632408491ff2a70 /chips/fdc_82077_hdw.c | |
download | gnumach-f07a4c844da9f0ecae5bbee1ab94be56505f26f7.tar.gz gnumach-f07a4c844da9f0ecae5bbee1ab94be56505f26f7.tar.bz2 gnumach-f07a4c844da9f0ecae5bbee1ab94be56505f26f7.zip |
Initial source
Diffstat (limited to 'chips/fdc_82077_hdw.c')
-rw-r--r-- | chips/fdc_82077_hdw.c | 821 |
1 files changed, 821 insertions, 0 deletions
diff --git a/chips/fdc_82077_hdw.c b/chips/fdc_82077_hdw.c new file mode 100644 index 00000000..f52da830 --- /dev/null +++ b/chips/fdc_82077_hdw.c @@ -0,0 +1,821 @@ +/* + * Mach Operating System + * Copyright (c) 1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * File: fdi_82077_hdw.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 1/92 + * + * Driver for the Intel 82077 Floppy Disk Controller. + */ + +#include <fd.h> +#if NFD > 0 + +#include <mach/std_types.h> +#include <machine/machspl.h> +#include <chips/busses.h> + +#include <chips/fdc_82077.h> +#include <platforms.h> + +/* ---- */ +#include <device/param.h> +#include <device/io_req.h> +#include <device/device_types.h> +#include <device/disk_status.h> +#define UNITNO(d) ((d)>>5) +#define SLAVENO(d) (((d)>>3)&0x3) +#define PARAMNO(d) ((d)&0x7) +/* ---- */ + +#ifdef MAXINE + +/* we can only take one */ +#define MAX_DRIVES 1 + +#define my_fdc_type fdc_82077aa +#define the_fdc_type fd->fdc_type +/* later: #define the_fdc_type my_fdc_type */ + +/* Registers are read/written as words, byte 0 */ +/* padding is to x40 boundaries */ +typedef struct { + volatile unsigned int fd_sra; /* r: status register A */ + int pad0[15]; + volatile unsigned int fd_srb; /* r: status register B */ + int pad1[15]; + volatile unsigned int fd_dor; /* rw: digital output reg */ + int pad2[15]; + volatile unsigned int fd_tdr; /* rw: tape drive register */ + int pad3[15]; + volatile unsigned int fd_msr; /* r: main status register */ +/*#define fd_dsr fd_msr; /* w: data rate select reg */ + int pad4[15]; + volatile unsigned int fd_data; /* rw: fifo */ + int pad5[15]; + volatile unsigned int fd_xxx; /* --reserved-- */ + int pad6[15]; + volatile unsigned int fd_dir; /* r: digital input reg */ +/*#define fd_ccr fd_dir; /* w: config control reg */ +} fd_padded_regmap_t; + +#define machdep_reset_8272a(f,r) + +#else /* MAXINE */ + +/* Pick your chip and padding */ +#define my_fdc_type fdc_8272a +#define the_fdc_type my_fdc_type + +#define fd_padded_regmap_t fd_8272a_regmap_t + +#define machdep_reset_8272a(f,r) 1 + +#endif /* MAXINE */ + + +#ifndef MAX_DRIVES +#define MAX_DRIVES DRIVES_PER_FDC +#endif + +/* + * Autoconf info + */ + +static vm_offset_t fd_std[NFD] = { 0 }; +static struct bus_device *fd_info[NFD]; +static struct bus_ctlr *fd_minfo[NFD]; +static int fd_probe(), fd_slave(), fd_go(); +static void fd_attach(); + +struct bus_driver fd_driver = + { fd_probe, fd_slave, fd_attach, fd_go, fd_std, "fd", fd_info, + "fdc", fd_minfo, /*BUS_INTR_B4_PROBE*/}; + +/* + * Externally visible functions + */ +int fd_intr(); /* kernel */ + +/* + * Media table + * + * Cyls,Sec,spc,part,Mtype,RWFpl,FGpl + */ +typedef struct { + unsigned char d_cylperunit; + unsigned char d_secpercyl; + unsigned short d_secperunit; + unsigned char d_secpertrk; + unsigned char d_gpl; + unsigned char d_fgpl; + unsigned char d_xfer_rate; +} fd_params_t; + +fd_params_t fd_params[8] = { + {80, 18, 1440, 9, 0x2a, 0x50, FD_DSR_DD_250}, /* [0] 3.50" 720 Kb */ + {80, 36, 2880, 18, 0x1b, 0x6c, FD_DSR_DD_500}, /* [1] 3.50" 1.44 Meg */ + {40, 18, 720, 9, 0x2a, 0x50, FD_DSR_DD_250}, /* [2] 5.25" 360 Kb */ + {80, 30, 2400, 15, 0x1b, 0x54, FD_DSR_DD_500}, /* [3] 5.25" 1.20 Meg */ +}; + +/* + * Software status of chip + */ +struct fd_softc { + fd_padded_regmap_t *regs; + char fdc_type; + char fdc_mode; + char messed_up; + char slave_active; + struct slave_t { + io_req_t ior; + decl_simple_lock_data(,slave_lock) + + /* status at end of last command */ + unsigned char st0; + unsigned char st1; + unsigned char st2; + unsigned char c; + unsigned char h; + unsigned char r; + unsigned char n; + unsigned char st3; + /* ... */ + unsigned char medium_status; +# define ST_MEDIUM_PRESENT 1 +# define ST_MEDIUM_KNOWN 2 + char last_command; + char bytes_expected; + fd_params_t *params; + + } slave_status[DRIVES_PER_FDC]; +} fd_softc_data[NFD]; + +typedef struct fd_softc *fd_softc_t; + +fd_softc_t fd_softc[NFD]; + +static char *chip_names[4] = { "8272-A", "82072", "82077-AA", 0 }; +static char *mode_names[4] = { "PC AT", "PS/2", "Model 30", 0 }; + +/* + * Probe chip to see if it is there + */ +static fd_probe (reg, ctlr) + vm_offset_t reg; + struct bus_ctlr *ctlr; +{ + int unit = ctlr->unit; + fd_softc_t fd; + fd_padded_regmap_t *regs; + + /* + * See if we are here + */ + if (check_memory(reg, 0)) { + /* no rides today */ + return 0; + } + + fd = &fd_softc_data[unit]; + fd_softc[unit] = fd; + + regs = (fd_padded_regmap_t *)reg; + fd->regs = regs; + fd->fdc_type = my_fdc_type; + + fd_reset(fd); + + if (the_fdc_type == fdc_82077aa) { + /* See if properly functioning */ + unsigned char temp = FD_CMD_VERSION; + if (!fd_go(fd, 0, &temp, 1, 1)) + return 0; /* total brxage */ + if (!fd_get_result(fd, &temp, 1, FALSE)) + return 0; /* partial brxage */ + if (temp != FD_VERSION_82077AA) + printf( "{ %s x%x } ", + "Accepting non-82077aa version id", + temp); + } + + printf("%s%d: %s chip controller", + ctlr->name, ctlr->unit, chip_names[fd->fdc_type]); + if (the_fdc_type == fdc_82077aa) + printf(" in %s mode", mode_names[fd->fdc_mode]); + printf(".\n"); + + return 1; +} + +/* See if we like this slave */ +static fd_slave(ui, reg) + struct bus_device *ui; + vm_offset_t reg; +{ + int slave = ui->slave; + fd_softc_t fd; + unsigned char sns[2]; + + if (slave >= MAX_DRIVES) return 0; + + fd = fd_softc[ui->ctlr]; + + sns[0] = FD_CMD_SENSE_DRIVE_STATUS; + sns[1] = slave & 0x3; + if (the_fdc_type == fdc_82072) + sns[1] |= FD_CMD_SDS_NO_MOT; + if (!fd_go(fd, slave, sns, 2, 1)) return 0; + if (!fd_get_result(fd, sns, 1, FALSE)) return 0; + + fd->slave_status[slave].st3 = sns[0]; + + return 1; +} + +static void +fd_attach (ui) + struct bus_device *ui; +{ + /* Attach a slave */ +} + +static boolean_t +fd_go(fd, slave, cmd, cmdlen, reply_count) + fd_softc_t fd; + unsigned char cmd[]; +{ + + /* XXX check who active, enque ifnot */ + + fd->slave_active = slave; + fd->slave_status[slave].bytes_expected = reply_count; + fd->slave_status[slave].last_command = *cmd; + return fd_command(fd, cmd, cmdlen); +} + +fd_intr (unit, spllevel) +{ + fd_softc_t fd; + fd_padded_regmap_t *regs; + unsigned char msr; + register struct slave_t *slv; + + + splx(spllevel); + + fd = fd_softc[unit]; + regs = fd->regs; + + /* did polling see a media change */ + /* busy bit in msr sez ifasync or not */ + + msr = regs->fd_msr; + if ((msr & (FD_MSR_RQM|FD_MSR_DIO)) == (FD_MSR_RQM|FD_MSR_DIO)) { + + /* result phase */ +*(unsigned int *)0xbc040100 &= ~0x00600000; + + slv = &fd->slave_status[fd->slave_active]; + fd_get_result(fd, &slv->st0, slv->bytes_expected, FALSE); + fd_start(fd, fd->slave_active, TRUE); + return; + } + /* async interrupt, either seek complete or media change */ + while (1) { + unsigned char st[2]; + register int slave, m; + + *st = FD_CMD_SENSE_INT_STATUS; + fd_command(fd, st, 1); + + fd_get_result(fd, st, 2, FALSE); + + slave = *st & FD_ST0_DS; + slv = &fd->slave_status[slave]; + slv->c = st[1]; + + switch (*st & FD_ST0_IC_MASK) { + + case FD_ST0_IC_OK: +/* we get an FD_ST0_SE for RECALIBRATE. Wait for it or discard ? */ + + case FD_ST0_IC_AT: + + case FD_ST0_IC_BAD_CMD: + return; + + case FD_ST0_IC_AT_POLL: + m = slv->medium_status; + if (m & ST_MEDIUM_PRESENT) + m &= ~ST_MEDIUM_PRESENT; + else + m |= ST_MEDIUM_PRESENT; + slv->medium_status = m; + } + } +} + +/* + * Non-interface functions and utilities + */ + +fd_reset(fd) + fd_softc_t fd; +{ + register fd_padded_regmap_t *regs; + + regs = fd->regs; + + /* + * Reset the chip + */ + if (the_fdc_type == fdc_82072) + /* Fix if your box uses an external PLL */ + regs->fd_dsr = FD_DSR_RESET | FD_DSR_EPLL; + else if (the_fdc_type == fdc_82077aa) + regs->fd_dor = 0; + else + machdep_reset_8272a(fd, regs); + + delay(5); /* 4usecs in specs */ + + /* + * Be smart with the smart ones + */ + if (the_fdc_type == fdc_82077aa) { + + /* + * See in which mood we are (it cannot be changed) + */ + int temp; + + /* Take chip out of hw reset */ + regs->fd_dor = FD_DOR_ENABLE | FD_DOR_DMA_GATE; + delay(10); + + /* what do we readback from the DIR register as datarate ? */ + regs->fd_ccr = FD_DSR_SD_125; + delay(10); + + temp = regs->fd_dir; + if ((temp & 0x7) == FD_DSR_SD_125) + fd->fdc_mode = mod30_mode; + else if ((temp & (FD_DIR_ones | FD_DIR_DR_MASK_PS2)) == + ((FD_DSR_SD_125 << FD_DIR_DR_SHIFT_PS2) | FD_DIR_ones)) + fd->fdc_mode = ps2_mode; + else + /* this assumes tri-stated bits 1&2 read the same */ + fd->fdc_mode = at_mode; + + } + + /* + * Send at least 4 sense interrupt cmds, one per slave + */ + { + + unsigned char sns, st[2]; + int i, nloops; + + sns = FD_CMD_SENSE_INT_STATUS; + i = nloops = 0; + + do { + nloops++; + + (void) fd_command(fd, &sns, 1); + + st[0] = 0; /* in case bad status */ + (void) fd_get_result(fd, st, 2, TRUE); + + if ((st[0] & FD_ST0_IC_MASK) == FD_ST0_IC_AT_POLL) { + register int slave; + + slave = st[0] & FD_ST0_DS; + fd->slave_status[slave].st0 = st[0]; + fd->slave_status[slave].c = st[1]; + i++; + } + } while ( (nloops < 30) && + ((i < 4) || (st[0] != FD_ST0_IC_BAD_CMD)) ); + + /* sanity check */ + if (nloops == 30) { + (void) fd_messed_up(fd); + return; + } + } + + /* + * Install current parameters + */ + if (the_fdc_type != fdc_8272a) { + + unsigned char cnf[4]; + + /* send configure command to turn polling off */ + cnf[0] = FD_CMD_CONFIGURE; + cnf[1] = 0x60; /* moff 110 */ + cnf[2] = 0x48; /* eis, poll, thr=8 */ + cnf[3] = 0; + if (!fd_command(fd, cnf, 4)) + return; + /* no status */ + } + + /* + * Send specify to select defaults + */ + { + unsigned char sfy[3]; + + sfy[0] = FD_CMD_SPECIFY; +#if 0 + sfy[1] = (12 << 4) | 7; /* step 4, hut 112us @500 */ + sfy[2] = 2 << 1; /* hlt 29us @500 */ +#else + sfy[1] = (13 << 4) | 15; + sfy[2] = 1 << 1; +#endif + (void) fd_command(fd, sfy, 3); + /* no status */ + } +} + +#define FD_MAX_WAIT 1000 + +boolean_t +fd_command(fd, cmd, cmd_len) + fd_softc_t fd; + char *cmd; +{ + register fd_padded_regmap_t *regs; + + regs = fd->regs; + + while (cmd_len > 0) { + register int i, s; + + /* there might be long delays, so we pay this price */ + s = splhigh(); + for (i = 0; i < FD_MAX_WAIT; i++) + if ((regs->fd_msr & (FD_MSR_RQM|FD_MSR_DIO)) == + FD_MSR_RQM) + break; + else + delay(10); + if (i == FD_MAX_WAIT) { + splx(s); + return fd_messed_up(fd); + } + regs->fd_data = *cmd++; + splx(s); + if (--cmd_len) delay(12); + } + + return TRUE; +} + +boolean_t +fd_get_result(fd, st, st_len, ignore_errors) + fd_softc_t fd; + char *st; +{ + register fd_padded_regmap_t *regs; + + regs = fd->regs; + + while (st_len > 0) { + register int i, s; + + /* there might be long delays, so we pay this price */ + s = splhigh(); + for (i = 0; i < FD_MAX_WAIT; i++) + if ((regs->fd_msr & (FD_MSR_RQM|FD_MSR_DIO)) == + (FD_MSR_RQM|FD_MSR_DIO)) + break; + else + delay(10); + if (i == FD_MAX_WAIT) { + splx(s); + return (ignore_errors) ? FALSE : fd_messed_up(fd); + } + *st++ = regs->fd_data; + splx(s); + st_len--; + } + + return TRUE; +} + + +boolean_t +fd_messed_up(fd) + fd_softc_t fd; +{ + fd->messed_up++; + printf("fd%d: messed up, disabling.\n", fd - fd_softc_data); + /* here code to + ior->error = ..; + restart + */ + return FALSE; +} + +/* + * Debugging aids + */ + +fd_state(unit) +{ + fd_softc_t fd = fd_softc[unit]; + fd_padded_regmap_t *regs; + + if (!fd || !fd->regs) return 0; + regs = fd->regs; + if (the_fdc_type == fdc_8272a) + printf("msr %x\n", regs->fd_msr); + else + printf("sra %x srb %x dor %x tdr %x msr %x dir %x\n", + regs->fd_sra, regs->fd_srb, regs->fd_dor, + regs->fd_tdr, regs->fd_msr, regs->fd_dir); +} + +#endif + +/* to be moved in separate file, or the above modified to live with scsi */ + +fd_open(dev, mode, ior) + int dev; + dev_mode_t mode; + io_req_t ior; +{ + unsigned char cmd[2]; + fd_softc_t fd; + int slave; + + fd = fd_softc[UNITNO(dev)]; + slave = SLAVENO(dev); + + /* XXX find out what medium we have, automagically XXX */ + /* fornow, set params depending on minor */ + fd->slave_status[slave].params = &fd_params[PARAMNO(dev)]; + + /* XXXYYYXXXYYY SEND CONFIGURE if params changed */ + + /* Turn motor on */ + if (the_fdc_type == fdc_82072) { + + cmd[0] = FD_CMD_MOTOR_ON_OFF | FD_CMD_MOT_ON | + ((slave << FD_CMD_MOT_DS_SHIFT) & FD_CMD_MOT_DS); + (void) fd_go(fd, slave, cmd, 1, 0); + /* no status */ + + } else if (the_fdc_type == fdc_82077aa) { + + fd->regs->fd_dor |= ((1<<slave)<<4); + } + + /* recalibrate to track 0 */ + cmd[0] = FD_CMD_RECALIBRATE; + cmd[1] = slave; + if (!fd_go(fd, slave, cmd, 2, 0)) + return D_DEVICE_DOWN; + /* will generate a completion interrupt */ + + /* if not writeable return D_READ_ONLY ? */ + + return D_SUCCESS; +} + +fd_close(dev) + int dev; +{ + fd_softc_t fd; + register int slave; + unsigned char cmd[2]; + + slave = SLAVENO(dev); + fd = fd_softc[UNITNO(dev)]; + + /* do not delete media info, do that iff interrupt sez changed */ + + /* Turn motor off */ + if (the_fdc_type == fdc_82072) { + + cmd[0] = FD_CMD_MOTOR_ON_OFF | + ((slave << FD_CMD_MOT_DS_SHIFT) & FD_CMD_MOT_DS); + (void) fd_go(fd, 0, cmd, 1, 0); + /* no status */ + + } else if (the_fdc_type == fdc_82077aa) { + + fd->regs->fd_dor &= ~((1<<slave)<<4); + } + return D_SUCCESS; +} + +fd_strategy(ior) + io_req_t ior; +{ +#if 0 + if (ior->io_op & IO_READ) + bzero(ior->io_data, ior->io_count); + iodone(ior); +#else + struct slave_t *slv; + fd_softc_t fd; + unsigned int i, rec, max, dev; + fd_params_t *params; + + /* readonly */ + + dev = ior->io_unit; + + /* only one partition */ + fd = fd_softc[UNITNO(dev)]; + slv = &fd->slave_status[SLAVENO(dev)]; + params = slv->params; + max = params->d_secperunit; + rec = ior->io_recnum; + i = btodb(ior->io_count + DEV_BSIZE - 1); + if (((rec + i) > max) || (ior->io_count < 0)) { + ior->io_error = D_INVALID_SIZE; + ior->io_op |= IO_ERROR; + ior->io_residual = ior->io_count; + iodone(ior); + return; + } + + ior->io_residual = rec / params->d_secpercyl; + + /* + * Enqueue operation + */ + i = splbio(); + simple_lock(&slv->slave_lock); + if (slv->ior) { + disksort(slv->ior, ior); + simple_unlock(&slv->slave_lock); + } else { + ior->io_next = 0; + slv->ior = ior; + simple_unlock(&slv->slave_lock); + fd_start(fd, SLAVENO(dev), FALSE); + } + splx(i); +#endif +} + +fd_start(fd, slave, done) + boolean_t done; + fd_softc_t fd; +{ + register io_req_t ior; + struct slave_t *slv; + + slv = &fd->slave_status[slave]; + if ((ior = slv->ior) == 0) + return; + + if (done) { + /* .. errors .. */ + /* .. partial xfers .. */ + + /* dequeue next one */ + { + io_req_t next; + + simple_lock(&slv->target_lock); + next = ior->io_next; + slv->ior = next; + simple_unlock(&slv->target_lock); + + iodone(ior); + if (next == 0) + return; + + ior = next; + } + } + +#ifdef no_eis + if (slv->c != ior->io_residual) SEEK_it; +#endif + +/* setup dma */ +#if 1 + if (ior->io_op & IO_READ) /* like SCSI */ +#else + if ((ior->io_op & IO_READ) == 0) +#endif + { + *(unsigned int *)0xbc040100 |= 0x00200000 | 0x00400000; + } else { + *(unsigned int *)0xbc040100 &= ~0x00400000; + *(unsigned int *)0xbc040100 |= 0x00200000; + } + *(unsigned int *)0xbc040070 = (((unsigned int)kvtophys(ior->io_data))>>2)<<5; + *(unsigned int *)0xbc0401a0 = 13; + +#ifdef no_eis + if (slv->c == ior->io_residual) { +#else + { +#endif + unsigned char cmd[9]; + unsigned char head, sec; + fd_params_t *params; + + params = slv->params; + + fd->regs->fd_dsr = params->d_xfer_rate; + + sec = ior->io_recnum % params->d_secpercyl; + head = sec / params->d_secpertrk; + sec = (sec % params->d_secpertrk); + + cmd[0] = (ior->io_op & IO_READ) ? + FD_CMD_MT | FD_CMD_MFM | FD_CMD_READ_DATA : + FD_CMD_MT | FD_CMD_MFM | FD_CMD_WRITE_DATA; + cmd[1] = (head << 2) | slave; + cmd[2] = ior->io_residual; + cmd[3] = head; + cmd[4] = sec + 1; /* 0 starts at 1 :-) */ + cmd[5] = 0x2; /* 512 byte sectors */ + cmd[6] = params->d_secpertrk; + cmd[7] = params->d_gpl; + cmd[8] = 0xff; + + fd_go( fd, slave, cmd, 9, 7); + + } +} + +extern minphys(); + +fd_read(dev, ior) + int dev; + io_req_t ior; +{ + return block_io(fd_strategy, minphys, ior); +} + +int fdc_write_enable = 1; + +fd_write(dev, ior) + int dev; + io_req_t ior; +{ +/* check if writeable */ + +if (fdc_write_enable) + return block_io(fd_strategy, minphys, ior); +else return D_SUCCESS; +} + +fd_set_status(dev, flavor, status, status_count) + int dev; + int flavor; + dev_status_t status; + unsigned int *status_count; +{ + printf("fdc_set_status(%x, %x, %x, %x)", dev, flavor, status, status_count); + return D_SUCCESS; +} + +fd_get_status(dev, flavor, status, status_count) + int dev; + int flavor; + dev_status_t status; + unsigned int status_count; +{ + printf("fdc_get_status(%x, %x, %x, %x)", dev, flavor, status, status_count); + return D_SUCCESS; +} + |