📄 fdc-io.c
字号:
outb_p(data, fdc.dor2); } else { outb_p(data, fdc.dor); } ftape_sleep(10 * FT_MILLISECOND); TRACE_EXIT;}static void fdc_update_dsr(void){ TRACE_FUN(ft_t_any); TRACE(ft_t_flow, "rate = %d Kbps, precomp = %d ns", fdc_data_rate, fdc_precomp); if (fdc.type >= i82077) { outb_p((fdc_rate_code & 0x03) | fdc_prec_code, fdc.dsr); } else { outb_p(fdc_rate_code & 0x03, fdc.ccr); } TRACE_EXIT;}void fdc_set_write_precomp(int precomp){ TRACE_FUN(ft_t_any); TRACE(ft_t_noise, "New precomp: %d nsec", precomp); fdc_precomp = precomp; /* write precompensation can be set in multiples of 41.67 nsec. * round the parameter to the nearest multiple and convert it * into a fdc setting. Note that 0 means default to the fdc, * 7 is used instead of that. */ fdc_prec_code = ((fdc_precomp + 21) / 42) << 2; if (fdc_prec_code == 0 || fdc_prec_code > (6 << 2)) { fdc_prec_code = 7 << 2; } fdc_update_dsr(); TRACE_EXIT;}/* Reprogram the 82078 registers to use Data Rate Table 1 on all drives. */static void fdc_set_drive_specs(void){ __u8 cmd[] = { FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0}; int result; TRACE_FUN(ft_t_any); TRACE(ft_t_flow, "Setting of drive specs called"); if (fdc.type >= i82078_1) { cmd[1] = (0 << 5) | (2 << 2); cmd[2] = (1 << 5) | (2 << 2); cmd[3] = (2 << 5) | (2 << 2); cmd[4] = (3 << 5) | (2 << 2); result = fdc_command(cmd, NR_ITEMS(cmd)); if (result < 0) { TRACE(ft_t_err, "Setting of drive specs failed"); } } TRACE_EXIT;}/* Select clock for fdc, must correspond with tape drive setting ! * This also influences the fdc timing so we must adjust some values. */int fdc_set_data_rate(int rate){ int bad_rate = 0; TRACE_FUN(ft_t_any); /* Select clock for fdc, must correspond with tape drive setting ! * This also influences the fdc timing so we must adjust some values. */ TRACE(ft_t_fdc_dma, "new rate = %d", rate); switch (rate) { case 250: fdc_rate_code = fdc_data_rate_250; break; case 500: fdc_rate_code = fdc_data_rate_500; break; case 1000: if (fdc.type < i82077) { bad_rate = 1; } else { fdc_rate_code = fdc_data_rate_1000; } break; case 2000: if (fdc.type < i82078_1) { bad_rate = 1; } else { fdc_rate_code = fdc_data_rate_2000; } break; default: bad_rate = 1; } if (bad_rate) { TRACE_ABORT(-EIO, ft_t_fdc_dma, "%d is not a valid data rate", rate); } fdc_data_rate = rate; fdc_update_dsr(); fdc_set_seek_rate(fdc_seek_rate); /* clock changed! */ ftape_udelay(1000); TRACE_EXIT 0;}/* keep the unit select if keep_select is != 0, */static void fdc_dor_reset(int keep_select){ __u8 fdc_ctl = ft_drive_sel; if (keep_select != 0) { fdc_ctl |= FDC_DMA_MODE; if (ftape_motor) { fdc_ctl |= FDC_MOTOR_0 << ft_drive_sel; } } ftape_udelay(10); /* ??? but seems to be necessary */ if (ft_mach2) { outb_p(fdc_ctl & 0x0f, fdc.dor); outb_p(fdc_ctl, fdc.dor2); } else { outb_p(fdc_ctl, fdc.dor); } fdc_usec_wait(10); /* delay >= 14 fdc clocks */ if (keep_select == 0) { fdc_ctl = 0; } fdc_ctl |= FDC_RESET_NOT; if (ft_mach2) { outb_p(fdc_ctl & 0x0f, fdc.dor); outb_p(fdc_ctl, fdc.dor2); } else { outb_p(fdc_ctl, fdc.dor); }}/* Reset the floppy disk controller. Leave the ftape_unit selected. */void fdc_reset(void){ int st0; int i; int dummy; unsigned long flags; TRACE_FUN(ft_t_any); spin_lock_irqsave(&fdc_io_lock, flags); fdc_dor_reset(1); /* keep unit selected */ fdc_mode = fdc_idle; /* maybe the cli()/sti() pair is not necessary, BUT: * the following line MUST be here. Otherwise fdc_interrupt_wait() * won't wait. Note that fdc_reset() is called from * ftape_dumb_stop() when the fdc is busy transferring data. In this * case fdc_isr() MOST PROBABLY sets ft_interrupt_seen, and tries * to get the result bytes from the fdc etc. CLASH. */ ft_interrupt_seen = 0; /* Program data rate */ fdc_update_dsr(); /* restore data rate and precomp */ spin_unlock_irqrestore(&fdc_io_lock, flags); /* * Wait for first polling cycle to complete */ if (fdc_interrupt_wait(1 * FT_SECOND) < 0) { TRACE(ft_t_err, "no drive polling interrupt!"); } else { /* clear all disk-changed statuses */ for (i = 0; i < 4; ++i) { if(fdc_sense_interrupt_status(&st0, &dummy) != 0) { TRACE(ft_t_err, "sense failed for %d", i); } if (i == ft_drive_sel) { ftape_current_cylinder = dummy; } } TRACE(ft_t_noise, "drive polling completed"); } /* * SPECIFY COMMAND */ fdc_set_seek_rate(fdc_seek_rate); /* * DRIVE SPECIFICATION COMMAND (if fdc type known) */ if (fdc.type >= i82078_1) { fdc_set_drive_specs(); } TRACE_EXIT;}#if !defined(CLK_48MHZ)# define CLK_48MHZ 1#endif/* When we're done, put the fdc into reset mode so that the regular * floppy disk driver will figure out that something is wrong and * initialize the controller the way it wants. */void fdc_disable(void){ __u8 cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00}; __u8 cmd2[] = {FDC_LOCK}; __u8 cmd3[] = {FDC_UNLOCK}; __u8 stat[1]; TRACE_FUN(ft_t_flow); if (!fdc_fifo_locked) { fdc_reset(); TRACE_EXIT; } if (fdc_issue_command(cmd3, 1, stat, 1) < 0 || stat[0] != 0x00) { fdc_dor_reset(0); TRACE_ABORT(/**/, ft_t_bug, "couldn't unlock fifo, configuration remains changed"); } fdc_fifo_locked = 0; if (CLK_48MHZ && fdc.type >= i82078) { cmd1[0] |= FDC_CLK48_BIT; } cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1); if (fdc_command(cmd1, NR_ITEMS(cmd1)) < 0) { fdc_dor_reset(0); TRACE_ABORT(/**/, ft_t_bug, "couldn't reconfigure fifo to old state"); } if (fdc_lock_state && fdc_issue_command(cmd2, 1, stat, 1) < 0) { fdc_dor_reset(0); TRACE_ABORT(/**/, ft_t_bug, "couldn't lock old state again"); } TRACE(ft_t_noise, "fifo restored: %sabled, thr. %d, %slocked", fdc_fifo_state ? "en" : "dis", fdc_fifo_thr, (fdc_lock_state) ? "" : "not "); fdc_dor_reset(0); TRACE_EXIT;}/* Specify FDC seek-rate (milliseconds) */static int fdc_set_seek_rate(int seek_rate){ /* set step rate, dma mode, and minimal head load and unload times */ __u8 in[3] = { FDC_SPECIFY, 1, (1 << 1)}; fdc_seek_rate = seek_rate; in[1] |= (16 - (fdc_data_rate * fdc_seek_rate) / 500) << 4; return fdc_command(in, 3);}/* Sense drive status: get unit's drive status (ST3) */int fdc_sense_drive_status(int *st3){ __u8 out[2]; __u8 in[1]; TRACE_FUN(ft_t_any); out[0] = FDC_SENSED; out[1] = ft_drive_sel; TRACE_CATCH(fdc_issue_command(out, 2, in, 1),); *st3 = in[0]; TRACE_EXIT 0;}/* Sense Interrupt Status command: * should be issued at the end of each seek. * get ST0 and current cylinder. */int fdc_sense_interrupt_status(int *st0, int *current_cylinder){ __u8 out[1]; __u8 in[2]; TRACE_FUN(ft_t_any); out[0] = FDC_SENSEI; TRACE_CATCH(fdc_issue_command(out, 1, in, 2),); *st0 = in[0]; *current_cylinder = in[1]; TRACE_EXIT 0;}/* step to track */int fdc_seek(int track){ __u8 out[3]; int st0, pcn;#ifdef TESTING unsigned int time;#endif TRACE_FUN(ft_t_any); out[0] = FDC_SEEK; out[1] = ft_drive_sel; out[2] = track;#ifdef TESTING time = ftape_timestamp();#endif /* We really need this command to work ! */ ft_seek_completed = 0; TRACE_CATCH(fdc_command(out, 3), fdc_reset(); TRACE(ft_t_noise, "destination was: %d, resetting FDC...", track)); /* Handle interrupts until ft_seek_completed or timeout. */ for (;;) { TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),); if (ft_seek_completed) { TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),); if ((st0 & ST0_SEEK_END) == 0) { TRACE_ABORT(-EIO, ft_t_err, "no seek-end after seek completion !??"); } break; } }#ifdef TESTING time = ftape_timediff(time, ftape_timestamp()) / abs(track - ftape_current_cylinder); if ((time < 900 || time > 3100) && abs(track - ftape_current_cylinder) > 5) { TRACE(ft_t_warn, "Wrong FDC STEP interval: %d usecs (%d)", time, track - ftape_current_cylinder); }#endif /* Verify whether we issued the right tape command. */ /* Verify that we seek to the proper track. */ if (pcn != track) { TRACE_ABORT(-EIO, ft_t_err, "bad seek.."); } ftape_current_cylinder = track; TRACE_EXIT 0;}static int perpend_mode; /* set if fdc is in perpendicular mode */static int perpend_off(void){ __u8 perpend[] = {FDC_PERPEND, 0x00}; TRACE_FUN(ft_t_any); if (perpend_mode) { /* Turn off perpendicular mode */ perpend[1] = 0x80; TRACE_CATCH(fdc_command(perpend, 2), TRACE(ft_t_err,"Perpendicular mode exit failed!")); perpend_mode = 0; } TRACE_EXIT 0;}static int handle_perpend(int segment_id){ __u8 perpend[] = {FDC_PERPEND, 0x00}; TRACE_FUN(ft_t_any); /* When writing QIC-3020 tapes, turn on perpendicular mode * if tape is moving in forward direction (even tracks). */ if (ft_qic_std == QIC_TAPE_QIC3020 && ((segment_id / ft_segments_per_track) & 1) == 0) {/* FIXME: some i82077 seem to support perpendicular mode as * well. */#if 0 if (fdc.type < i82077AA) {}#else if (fdc.type < i82077 && ft_data_rate < 1000) {#endif /* fdc does not support perpendicular mode: complain */ TRACE_ABORT(-EIO, ft_t_err, "Your FDC does not support QIC-3020."); } perpend[1] = 0x03 /* 0x83 + (0x4 << ft_drive_sel) */ ; TRACE_CATCH(fdc_command(perpend, 2), TRACE(ft_t_err,"Perpendicular mode entry failed!")); TRACE(ft_t_flow, "Perpendicular mode set"); perpend_mode = 1; TRACE_EXIT 0; } TRACE_EXIT perpend_off();}static inline void fdc_setup_dma(char mode, volatile void *addr, unsigned int count){ /* Program the DMA controller. */ disable_dma(fdc.dma); clear_dma_ff(fdc.dma); set_dma_mode(fdc.dma, mode); set_dma_addr(fdc.dma, virt_to_bus((void*)addr)); set_dma_count(fdc.dma, count); enable_dma(fdc.dma);}/* Setup fdc and dma for formatting the next segment */int fdc_setup_formatting(buffer_struct * buff){ unsigned long flags; __u8 out[6] = { FDC_FORMAT, 0x00, 3, 4 * FT_SECTORS_PER_SEGMENT, 0x00, 0x6b }; TRACE_FUN(ft_t_any); TRACE_CATCH(handle_perpend(buff->segment_id),); /* Program the DMA controller. */ TRACE(ft_t_fdc_dma, "phys. addr. = %lx", virt_to_bus((void*) buff->ptr)); spin_lock_irqsave(&fdc_io_lock, flags); fdc_setup_dma(DMA_MODE_WRITE, buff->ptr, FT_SECTORS_PER_SEGMENT * 4); /* Issue FDC command to start reading/writing. */ out[1] = ft_drive_sel; out[4] = buff->gap3; TRACE_CATCH(fdc_setup_error = fdc_command(out, sizeof(out)), restore_flags(flags); fdc_mode = fdc_idle); spin_unlock_irqrestore(&fdc_io_lock, flags); TRACE_EXIT 0;}/* Setup Floppy Disk Controller and DMA to read or write the next cluster * of good sectors from or to the current segment. */int fdc_setup_read_write(buffer_struct * buff, __u8 operation){ unsigned long flags; __u8 out[9]; int dma_mode; TRACE_FUN(ft_t_any); switch(operation) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -