📄 fd1772.c
字号:
cli(); oldlatch_aupdate(LATCHA_FDSELALL, 0xf - (1 << drive)); restore_flags(flags); /* restore track register to saved value */ FDC1772_WRITE(FDC1772REG_TRACK, unit[drive].track); udelay(25); SelectedDrive = drive;}/* Deselect both drives. */static void fd_deselect(void){ unsigned long flags; DPRINT(("fd_deselect\n")); save_flags(flags); cli(); oldlatch_aupdate(LATCHA_FDSELALL | LATCHA_MOTOR | LATCHA_INUSE, 0xf | LATCHA_MOTOR | LATCHA_INUSE); restore_flags(flags); SelectedDrive = -1;}/* This timer function deselects the drives when the FDC1772 switched the * motor off. The deselection cannot happen earlier because the FDC1772 * counts the index signals, which arrive only if one drive is selected. */static void fd_motor_off_timer(unsigned long dummy){ unsigned long flags; unsigned char status; int delay; del_timer(&motor_off_timer); if (SelectedDrive < 0) /* no drive selected, needn't deselect anyone */ return; save_flags(flags); cli(); if (fdc_busy) /* was stdma_islocked */ goto retry; status = FDC1772_READ(FDC1772REG_STATUS); if (!(status & 0x80)) { /* motor already turned off by FDC1772 -> deselect drives */ /* In actual fact its this deselection which turns the motor off on the Arc, since the motor control is actually on Latch A */ DPRINT(("fdc1772: deselecting in fd_motor_off_timer\n")); fd_deselect(); MotorOn = 0; restore_flags(flags); return; } /* not yet off, try again */ retry: restore_flags(flags); /* Test again later; if tested too often, it seems there is no disk * in the drive and the FDC1772 will leave the motor on forever (or, * at least until a disk is inserted). So we'll test only twice * per second from then on... */ delay = (MotorOffTrys < FD_MOTOR_OFF_MAXTRY) ? (++MotorOffTrys, HZ / 20) : HZ / 2; START_MOTOR_OFF_TIMER(delay);}/* This function is repeatedly called to detect disk changes (as good * as possible) and keep track of the current state of the write protection. */static void check_change(void){ static int drive = 0; unsigned long flags; int stat; if (fdc_busy) return; /* Don't start poking about if the fdc is busy */ return; /* lets just forget it for the mo DAG */ if (++drive > 1 || !unit[drive].connected) drive = 0; save_flags(flags); cli(); if (!stdma_islocked()) { stat = !!(FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_WPROT); /* The idea here is that if the write protect line has changed then the disc must have changed */ if (stat != unit[drive].wpstat) { DPRINT(("wpstat[%d] = %d\n", drive, stat)); unit[drive].wpstat = stat; set_bit(drive, &changed_floppies); } } restore_flags(flags); START_CHECK_CHANGE_TIMER(CHECK_CHANGE_DELAY);}/* Handling of the Head Settling Flag: This flag should be set after each * seek operation, because we dont't use seeks with verify. */static __inline__ void set_head_settle_flag(void){ HeadSettleFlag = FDC1772CMDADD_E;}static __inline__ int get_head_settle_flag(void){ int tmp = HeadSettleFlag; HeadSettleFlag = 0; return (tmp);}/* General Interrupt Handling */static void (*FloppyIRQHandler) (int status) = NULL;static void floppy_irqconsequencehandler(void){ unsigned char status; void (*handler) (int); fdc1772_fdc_int_done = 0; handler = FloppyIRQHandler; FloppyIRQHandler = NULL; if (handler) { nop(); status = (unsigned char) fdc1772_comendstatus; DPRINT(("FDC1772 irq, status = %02x handler = %08lx\n", (unsigned int) status, (unsigned long) handler)); handler(status); } else { DPRINT(("FDC1772 irq, no handler status=%02x\n", fdc1772_comendstatus)); } DPRINT(("FDC1772 irq: end of floppy_irq\n"));}/* Error handling: If some error happened, retry some times, then * recalibrate, then try again, and fail after MAX_ERRORS. */static void fd_error(void){ printk("FDC1772: fd_error\n"); /*panic("fd1772: fd_error"); *//* DAG tmp */ if (!CURRENT) return; CURRENT->errors++; if (CURRENT->errors >= MAX_ERRORS) { printk("fd%d: too many errors.\n", SelectedDrive); end_request(0); } else if (CURRENT->errors == RECALIBRATE_ERRORS) { printk("fd%d: recalibrating\n", SelectedDrive); if (SelectedDrive != -1) unit[SelectedDrive].track = -1; } redo_fd_request();}#define SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0)/* do_fd_action() is the general procedure for a fd request: All * required parameter settings (drive select, side select, track * position) are checked and set if needed. For each of these * parameters and the actual reading or writing exist two functions: * one that starts the setting (or skips it if possible) and one * callback for the "done" interrupt. Each done func calls the next * set function to propagate the request down to fd_rwsec_done(). */static void do_fd_action(int drive){ DPRINT(("do_fd_action unit[drive].track=%d\n", unit[drive].track));#ifdef TRACKBUFFER repeat: if (IS_BUFFERED( drive, ReqSide, ReqTrack )) { if (ReqCmd == READ) { copy_buffer( SECTOR_BUFFER(ReqSector), ReqData ); if (++ReqCnt < CURRENT->current_nr_sectors) { /* read next sector */ setup_req_params( drive ); goto repeat; } else { /* all sectors finished */ CURRENT->nr_sectors -= CURRENT->current_nr_sectors; CURRENT->sector += CURRENT->current_nr_sectors; end_request( 1 ); redo_fd_request(); return; } } else { /* cmd == WRITE, pay attention to track buffer * consistency! */ copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) ); } }#endif if (SelectedDrive != drive) { /*unit[drive].track = -1; DAG */ fd_select_drive(drive); }; if (unit[drive].track == -1) fd_calibrate(); else if (unit[drive].track != ReqTrack << unit[drive].disktype->stretch) fd_seek(); else fd_rwsec();}/* Seek to track 0 if the current track is unknown */static void fd_calibrate(void){ DPRINT(("fd_calibrate\n")); if (unit[SelectedDrive].track >= 0) { fd_calibrate_done(0); return; } DPRINT(("fd_calibrate (after track compare)\n")); SET_IRQ_HANDLER(fd_calibrate_done); /* we can't verify, since the speed may be incorrect */ FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | unit[SelectedDrive].steprate); NeedSeek = 1; MotorOn = 1; START_TIMEOUT(); /* wait for IRQ */}static void fd_calibrate_done(int status){ DPRINT(("fd_calibrate_done()\n")); STOP_TIMEOUT(); /* set the correct speed now */ if (status & FDC1772STAT_RECNF) { printk("fd%d: restore failed\n", SelectedDrive); fd_error(); } else { unit[SelectedDrive].track = 0; fd_seek(); }}/* Seek the drive to the requested track. The drive must have been * calibrated at some point before this. */static void fd_seek(void){ unsigned long flags; DPRINT(("fd_seek() to track %d (unit[SelectedDrive].track=%d)\n", ReqTrack, unit[SelectedDrive].track)); if (unit[SelectedDrive].track == ReqTrack << unit[SelectedDrive].disktype->stretch) { fd_seek_done(0); return; } FDC1772_WRITE(FDC1772REG_DATA, ReqTrack << unit[SelectedDrive].disktype->stretch); udelay(25); save_flags(flags); cliIF(); SET_IRQ_HANDLER(fd_seek_done); FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK | unit[SelectedDrive].steprate | /* DAG */ (MotorOn?FDC1772CMDADD_H:0)); restore_flags(flags); MotorOn = 1; set_head_settle_flag(); START_TIMEOUT(); /* wait for IRQ */}static void fd_seek_done(int status){ DPRINT(("fd_seek_done()\n")); STOP_TIMEOUT(); /* set the correct speed */ if (status & FDC1772STAT_RECNF) { printk("fd%d: seek error (to track %d)\n", SelectedDrive, ReqTrack); /* we don't know exactly which track we are on now! */ unit[SelectedDrive].track = -1; fd_error(); } else { unit[SelectedDrive].track = ReqTrack << unit[SelectedDrive].disktype->stretch; NeedSeek = 0; fd_rwsec(); }}/* This does the actual reading/writing after positioning the head * over the correct track. */#ifdef TRACKBUFFERstatic int MultReadInProgress = 0;#endifstatic void fd_rwsec(void){ unsigned long paddr, flags; unsigned int rwflag, old_motoron; unsigned int track; DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n", ReqSector, ReqCmd == WRITE ? 'w' : 'r')); if (ReqCmd == WRITE) { /*cache_push( (unsigned long)ReqData, 512 ); */ paddr = (unsigned long) ReqData; rwflag = 0x100; } else {#ifdef TRACKBUFFER if (read_track) paddr = (unsigned long)PhysTrackBuffer; else paddr =(unsigned long)PhysDMABuffer;#else paddr = (unsigned long)PhysDMABuffer;#endif rwflag = 0; } DPRINT(("fd_rwsec() before sidesel rwflag=%d sec=%d trk=%d\n", rwflag, ReqSector, FDC1772_READ(FDC1772REG_TRACK))); fd_select_side(ReqSide); /*DPRINT(("fd_rwsec() before start sector \n")); */ /* Start sector of this operation */#ifdef TRACKBUFFER FDC1772_WRITE( FDC1772REG_SECTOR, !read_track ? ReqSector : 1 );#else FDC1772_WRITE( FDC1772REG_SECTOR, ReqSector );#endif /* Cheat for track if stretch != 0 */ if (unit[SelectedDrive].disktype->stretch) { track = FDC1772_READ(FDC1772REG_TRACK); FDC1772_WRITE(FDC1772REG_TRACK, track >> unit[SelectedDrive].disktype->stretch); } udelay(25); DPRINT(("fd_rwsec() before setup DMA \n")); /* Setup DMA - Heavily modified by DAG */ save_flags(flags); cliIF(); disable_dma(FLOPPY_DMA); set_dma_mode(FLOPPY_DMA, rwflag ? DMA_MODE_WRITE : DMA_MODE_READ); set_dma_addr(FLOPPY_DMA, (long) paddr); /* DAG - changed from Atari specific */#ifdef TRACKBUFFER set_dma_count(FLOPPY_DMA,(!read_track ? 1 : unit[SelectedDrive].disktype->spt)*512);#else set_dma_count(FLOPPY_DMA, 512); /* Block/sector size - going to have to change */#endif SET_IRQ_HANDLER(fd_rwsec_done); /* Turn on dma int */ enable_dma(FLOPPY_DMA); /* Now give it something to do */ FDC1772_WRITE(FDC1772REG_CMD, (rwflag ? (FDC1772CMD_WRSEC | FDC1772CMDADD_P) : #ifdef TRACKBUFFER (FDC1772CMD_RDSEC | (read_track ? FDC1772CMDADD_M : 0) | /* Hmm - the idea here is to stop the FDC spinning the disc up when we know that we already still have it spinning */ (MotorOn?FDC1772CMDADD_H:0))#else FDC1772CMD_RDSEC#endif )); restore_flags(flags); DPRINT(("fd_rwsec() after DMA setup flags=0x%08x\n", flags)); /*sti(); *//* DAG - Hmm */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -