/* * 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 #if NFD > 0 #include #include #include #include #include /* ---- */ #include #include #include #include #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<regs->fd_dor &= ~((1<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; }