📄 fd1772.c
字号:
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 (QUEUE_EMPTY) 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 TRACKBUFFERrepeat: 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); clf(); 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 { paddr = (unsigned long) PhysDMABuffer;#ifdef TRACKBUFFER if (read_track) paddr = (unsigned long)PhysTrackBuffer;#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); clf(); 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 */ /* Hmm - should do something DAG */ old_motoron = MotorOn; MotorOn = 1; NeedSeek = 1; /* wait for interrupt */#ifdef TRACKBUFFER if (read_track) { /* * If reading a whole track, wait about one disk rotation and * then check if all sectors are read. The FDC will even * search for the first non-existant sector and need 1 sec to * recognise that it isn't present :-( */ /* 1 rot. + 5 rot.s if motor was off */ mod_timer(&readtrack_timer, jiffies + HZ/5 + (old_motoron ? 0 : HZ)); DPRINT(("Setting readtrack_timer to %d @ %d\n", readtrack_timer.expires,jiffies)); MultReadInProgress = 1; }#endif /*DPRINT(("fd_rwsec() before START_TIMEOUT \n")); */ START_TIMEOUT(); /*DPRINT(("fd_rwsec() after START_TIMEOUT \n")); */}#ifdef TRACKBUFFERstatic void fd_readtrack_check(unsigned long dummy){ unsigned long flags, addr; extern unsigned char *fdc1772_dataaddr; DPRINT(("fd_readtrack_check @ %d\n",jiffies)); save_flags(flags); clf(); del_timer( &readtrack_timer ); if (!MultReadInProgress) { /* This prevents a race condition that could arise if the * interrupt is triggered while the calling of this timer * callback function takes place. The IRQ function then has * already cleared 'MultReadInProgress' when control flow * gets here. */ restore_flags(flags); return; } /* get the current DMA address */ addr=(unsigned long)fdc1772_dataaddr; /* DAG - ? */ DPRINT(("fd_readtrack_check: addr=%x PhysTrackBuffer=%x\n",addr,PhysTrackBuffer)); if (addr >= (unsigned int)PhysTrackBuffer + unit[SelectedDrive].disktype->spt*512) { /* already read enough data, force an FDC interrupt to stop * the read operation */ SET_IRQ_HANDLER( NULL ); restore_flags(flags); DPRINT(("fd_readtrack_check(): done\n")); FDC1772_WRITE( FDC1772REG_CMD, FDC1772CMD_FORCI ); udelay(25); /* No error until now -- the FDC would have interrupted * otherwise! */ fd_rwsec_done( 0 ); } else { /* not yet finished, wait another tenth rotation */ restore_flags(flags); DPRINT(("fd_readtrack_check(): not yet finished\n")); readtrack_timer.expires = jiffies + HZ/5/10; add_timer( &readtrack_timer ); }}#endifstatic void fd_rwsec_done(int status){ unsigned int track; DPRINT(("fd_rwsec_done() status=%d @ %d\n", status,jiffies));#ifdef TRACKBUFFER if (read_track && !MultReadInProgress) return; MultReadInProgress = 0; STOP_TIMEOUT(); if (read_track) del_timer( &readtrack_timer );#endif /* Correct the track if stretch != 0 */ if (unit[SelectedDrive].disktype->stretch) { track = FDC1772_READ(FDC1772REG_TRACK); FDC1772_WRITE(FDC1772REG_TRACK, track << unit[SelectedDrive].disktype->stretch); } if (ReqCmd == WRITE && (status & FDC1772STAT_WPROT)) { printk("fd%d: is write protected\n", SelectedDrive); goto err_end; } if ((status & FDC1772STAT_RECNF)#ifdef TRACKBUFFER /* RECNF is no error after a multiple read when the FDC * searched for a non-existant sector! */ && !(read_track && FDC1772_READ(FDC1772REG_SECTOR) > unit[SelectedDrive].disktype->spt)#endif ) { if (Probing) { if (unit[SelectedDrive].disktype > disk_type) { /* try another disk type */ unit[SelectedDrive].disktype--; floppy_sizes[SelectedDrive] = unit[SelectedDrive].disktype->blocks >> 1; } else Probing = 0; } else { /* record not found, but not probing. Maybe stretch wrong ? Restart probing */ if (unit[SelectedDrive].autoprobe) { unit[SelectedDrive].disktype = disk_type + NUM_DISK_TYPES - 1; floppy_sizes[SelectedDrive] = unit[SelectedDrive].disktype->blocks >> 1; Probing = 1; } } if (Probing) { setup_req_params(SelectedDrive);#ifdef TRACKBUFFER BufferDrive = -1;#endif do_fd_action(SelectedDrive); return; } printk("fd%d: sector %d not found (side %d, track %d)\n", SelectedDrive, FDC1772_READ(FDC1772REG_SECTOR), ReqSide, ReqTrack); goto err_end; } if (status & FDC1772STAT_CRC) { printk("fd%d: CRC error (side %d, track %d, sector %d)\n", SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR)); goto err_end; } if (status & FDC1772STAT_LOST) { printk("fd%d: lost data (side %d, track %d, sector %d)\n", SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR)); goto err_end; } Probing = 0; if (ReqCmd == READ) {#ifdef TRACKBUFFER if (!read_track) { /*cache_clear (PhysDMABuffer, 512);*/ copy_buffer (DMABuffer, ReqData); } else { /*cache_clear (PhysTrackBuffer, FD1772_MAX_SECTORS * 512);*/ BufferDrive = SelectedDrive; BufferSide = ReqSide; BufferTrack = ReqTrack; copy_buffer (SECTOR_BUFFER (ReqSector), ReqData); }#else /*cache_clear( PhysDMABuffer, 512 ); */ copy_buffer(DMABuffer, ReqData);#endif } if (++ReqCnt < CURRENT->current_nr_sectors) { /* read next sector */ setup_req_params(SelectedDrive); do_fd_action(SelectedDrive); } else { /* all sectors finished */ CURRENT->nr_sectors -= CURRENT->current_nr_sectors; CURRENT->sector += CURRENT->current_nr_sectors; end_request(1); redo_fd_request(); } return;err_end:#ifdef TRACKBUFFER BufferDrive = -1;#endif fd_error();}static void fd_times_out(unsigned long dummy){ SET_IRQ_HANDLER(NULL); /* If the timeout occurred while the readtrack_check timer was * active, we need to cancel it, else bad things will happen */ del_timer( &readtrack_timer ); FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI); udelay(25); printk("floppy timeout\n"); STOP_TIMEOUT(); /* hmm - should we do this ? */ fd_error();}/* The (noop) seek operation here is needed to make the WP bit in the * FDC1772 status register accessible for check_change. If the last disk * operation would have been a RDSEC, this bit would always read as 0 * no matter what :-( To save time, the seek goes to the track we're * already on. */static void finish_fdc(void){ /* DAG - just try without this dummy seek! */ finish_fdc_done(0); return; if (!NeedSeek) { finish_fdc_done(0); } else { DPRINT(("finish_fdc: dummy seek started\n")); FDC1772_WRITE(FDC1772REG_DATA, unit[SelectedDrive].track); SET_IRQ_HANDLER(finish_fdc_done); FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK); MotorOn = 1; START_TIMEOUT(); /* we must wait for the IRQ here, because the ST-DMA is * released immediatly afterwards and the interrupt may be * delivered to the wrong driver. */ }}static void finish_fdc_done(int dummy){ unsigned long flags; DPRINT(("finish_fdc_done entered\n"));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -