📄 fdc-io.c
字号:
case FDC_VERIFY: if (fdc.type < i82077) { operation = FDC_READ; } case FDC_READ: case FDC_READ_DELETED: dma_mode = DMA_MODE_READ; TRACE(ft_t_fdc_dma, "xfer %d sectors to 0x%p", buff->sector_count, buff->ptr); TRACE_CATCH(perpend_off(),); break; case FDC_WRITE_DELETED: TRACE(ft_t_noise, "deleting segment %d", buff->segment_id); case FDC_WRITE: dma_mode = DMA_MODE_WRITE; /* When writing QIC-3020 tapes, turn on perpendicular mode * if tape is moving in forward direction (even tracks). */ TRACE_CATCH(handle_perpend(buff->segment_id),); TRACE(ft_t_fdc_dma, "xfer %d sectors from 0x%p", buff->sector_count, buff->ptr); break; default: TRACE_ABORT(-EIO, ft_t_bug, "bug: invalid operation parameter"); } TRACE(ft_t_fdc_dma, "phys. addr. = %lx",virt_to_bus((void*)buff->ptr)); spin_lock_irqsave(&fdc_io_lock, flags); if (operation != FDC_VERIFY) { fdc_setup_dma(dma_mode, buff->ptr, FT_SECTOR_SIZE * buff->sector_count); } /* Issue FDC command to start reading/writing. */ out[0] = operation; out[1] = ft_drive_sel; out[2] = buff->cyl; out[3] = buff->head; out[4] = buff->sect + buff->sector_offset; out[5] = 3; /* Sector size of 1K. */ out[6] = out[4] + buff->sector_count - 1; /* last sector */ out[7] = 109; /* Gap length. */ out[8] = 0xff; /* No limit to transfer size. */ TRACE(ft_t_fdc_dma, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x", out[2], out[3], out[4], out[6] - out[4] + 1); spin_unlock_irqrestore(&fdc_io_lock, flags); TRACE_CATCH(fdc_setup_error = fdc_command(out, 9),fdc_mode = fdc_idle); TRACE_EXIT 0;}int fdc_fifo_threshold(__u8 threshold, int *fifo_state, int *lock_state, int *fifo_thr){ const __u8 cmd0[] = {FDC_DUMPREGS}; __u8 cmd1[] = {FDC_CONFIGURE, 0, (0x0f & (threshold - 1)), 0}; const __u8 cmd2[] = {FDC_LOCK}; const __u8 cmd3[] = {FDC_UNLOCK}; __u8 reg[10]; __u8 stat; int i; int result; TRACE_FUN(ft_t_any); if (CLK_48MHZ && fdc.type >= i82078) { cmd1[0] |= FDC_CLK48_BIT; } /* Dump fdc internal registers for examination */ TRACE_CATCH(fdc_command(cmd0, NR_ITEMS(cmd0)), TRACE(ft_t_warn, "dumpreg cmd failed, fifo unchanged")); /* Now read fdc internal registers from fifo */ for (i = 0; i < (int)NR_ITEMS(reg); ++i) { fdc_read(®[i]); TRACE(ft_t_fdc_dma, "Register %d = 0x%02x", i, reg[i]); } if (fifo_state && lock_state && fifo_thr) { *fifo_state = (reg[8] & 0x20) == 0; *lock_state = reg[7] & 0x80; *fifo_thr = 1 + (reg[8] & 0x0f); } TRACE(ft_t_noise, "original fifo state: %sabled, threshold %d, %slocked", ((reg[8] & 0x20) == 0) ? "en" : "dis", 1 + (reg[8] & 0x0f), (reg[7] & 0x80) ? "" : "not "); /* If fdc is already locked, unlock it first ! */ if (reg[7] & 0x80) { fdc_ready_wait(100); TRACE_CATCH(fdc_issue_command(cmd3, NR_ITEMS(cmd3), &stat, 1), TRACE(ft_t_bug, "FDC unlock command failed, " "configuration unchanged")); } fdc_fifo_locked = 0; /* Enable fifo and set threshold at xx bytes to allow a * reasonably large latency and reduce number of dma bursts. */ fdc_ready_wait(100); if ((result = fdc_command(cmd1, NR_ITEMS(cmd1))) < 0) { TRACE(ft_t_bug, "configure cmd failed, fifo unchanged"); } /* Now lock configuration so reset will not change it */ if(fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1) < 0 || stat != 0x10) { TRACE_ABORT(-EIO, ft_t_bug, "FDC lock command failed, stat = 0x%02x", stat); } fdc_fifo_locked = 1; TRACE_EXIT result;}static int fdc_fifo_enable(void){ TRACE_FUN(ft_t_any); if (fdc_fifo_locked) { TRACE_ABORT(0, ft_t_warn, "Fifo not enabled because locked"); } TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */, &fdc_fifo_state, &fdc_lock_state, &fdc_fifo_thr),); TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */, NULL, NULL, NULL),); TRACE_EXIT 0;}/* Determine fd controller type */static __u8 fdc_save_state[2];static int fdc_probe(void){ __u8 cmd[1]; __u8 stat[16]; /* must be able to hold dumpregs & save results */ int i; TRACE_FUN(ft_t_any); /* Try to find out what kind of fd controller we have to deal with * Scheme borrowed from floppy driver: * first try if FDC_DUMPREGS command works * (this indicates that we have a 82072 or better) * then try the FDC_VERSION command (82072 doesn't support this) * then try the FDC_UNLOCK command (some older 82077's don't support this) * then try the FDC_PARTID command (82078's support this) */ cmd[0] = FDC_DUMPREGS; if (fdc_issue_command(cmd, 1, stat, 1) != 0) { TRACE_ABORT(no_fdc, ft_t_bug, "No FDC found"); } if (stat[0] == 0x80) { /* invalid command: must be pre 82072 */ TRACE_ABORT(i8272, ft_t_warn, "Type 8272A/765A compatible FDC found"); } fdc_result(&stat[1], 9); fdc_save_state[0] = stat[7]; fdc_save_state[1] = stat[8]; cmd[0] = FDC_VERSION; if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) { TRACE_ABORT(i8272, ft_t_warn, "Type 82072 FDC found"); } if (*stat != 0x90) { TRACE_ABORT(i8272, ft_t_warn, "Unknown FDC found"); } cmd[0] = FDC_UNLOCK; if(fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] != 0x00) { TRACE_ABORT(i8272, ft_t_warn, "Type pre-1991 82077 FDC found, " "treating it like a 82072"); } if (fdc_save_state[0] & 0x80) { /* was locked */ cmd[0] = FDC_LOCK; /* restore lock */ (void)fdc_issue_command(cmd, 1, stat, 1); TRACE(ft_t_warn, "FDC is already locked"); } /* Test for a i82078 FDC */ cmd[0] = FDC_PARTID; if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) { /* invalid command: not a i82078xx type FDC */ for (i = 0; i < 4; ++i) { outb_p(i, fdc.tdr); if ((inb_p(fdc.tdr) & 0x03) != i) { TRACE_ABORT(i82077, ft_t_warn, "Type 82077 FDC found"); } } TRACE_ABORT(i82077AA, ft_t_warn, "Type 82077AA FDC found"); } /* FDC_PARTID cmd succeeded */ switch (stat[0] >> 5) { case 0x0: /* i82078SL or i82078-1. The SL part cannot run at * 2Mbps (the SL and -1 dies are identical; they are * speed graded after production, according to Intel). * Some SL's can be detected by doing a SAVE cmd and * look at bit 7 of the first byte (the SEL3V# bit). * If it is 0, the part runs off 3Volts, and hence it * is a SL. */ cmd[0] = FDC_SAVE; if(fdc_issue_command(cmd, 1, stat, 16) < 0) { TRACE(ft_t_err, "FDC_SAVE failed. Dunno why"); /* guess we better claim the fdc to be a i82078 */ TRACE_ABORT(i82078, ft_t_warn, "Type i82078 FDC (i suppose) found"); } if ((stat[0] & FDC_SEL3V_BIT)) { /* fdc running off 5Volts; Pray that it's a i82078-1 */ TRACE_ABORT(i82078_1, ft_t_warn, "Type i82078-1 or 5Volt i82078SL FDC found"); } TRACE_ABORT(i82078, ft_t_warn, "Type 3Volt i82078SL FDC (1Mbps) found"); case 0x1: case 0x2: /* S82078B */ /* The '78B isn't '78 compatible. Detect it as a '77AA */ TRACE_ABORT(i82077AA, ft_t_warn, "Type i82077AA FDC found"); case 0x3: /* NSC PC8744 core; used in several super-IO chips */ TRACE_ABORT(i82077AA, ft_t_warn, "Type 82077AA compatible FDC found"); default: TRACE(ft_t_warn, "A previously undetected FDC found"); TRACE_ABORT(i82077AA, ft_t_warn, "Treating it as a 82077AA. Please report partid= %d", stat[0]); } /* switch(stat[ 0] >> 5) */ TRACE_EXIT no_fdc;}static int fdc_request_regions(void){ TRACE_FUN(ft_t_flow); if (ft_mach2 || ft_probe_fc10) { if (!request_region(fdc.sra, 8, "fdc (ft)")) {#ifndef BROKEN_FLOPPY_DRIVER TRACE_EXIT -EBUSY;#else TRACE(ft_t_warn,"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra);#endif } } else { if (!request_region(fdc.sra, 6, "fdc (ft)")) {#ifndef BROKEN_FLOPPY_DRIVER TRACE_EXIT -EBUSY;#else TRACE(ft_t_warn,"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra);#endif } if (!request_region(fdc.sra + 7, 1, "fdc (ft)")) {#ifndef BROKEN_FLOPPY_DRIVER release_region(fdc.sra, 6); TRACE_EXIT -EBUSY;#else TRACE(ft_t_warn,"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra + 7);#endif } } TRACE_EXIT 0;}void fdc_release_regions(void){ TRACE_FUN(ft_t_flow); if (fdc.sra != 0) { if (fdc.dor2 != 0) { release_region(fdc.sra, 8); } else { release_region(fdc.sra, 6); release_region(fdc.dir, 1); } } TRACE_EXIT;}static int fdc_config_regs(unsigned int fdc_base, unsigned int fdc_irq, unsigned int fdc_dma){ TRACE_FUN(ft_t_flow); fdc.irq = fdc_irq; fdc.dma = fdc_dma; fdc.sra = fdc_base; fdc.srb = fdc_base + 1; fdc.dor = fdc_base + 2; fdc.tdr = fdc_base + 3; fdc.msr = fdc.dsr = fdc_base + 4; fdc.fifo = fdc_base + 5; fdc.dir = fdc.ccr = fdc_base + 7; fdc.dor2 = (ft_mach2 || ft_probe_fc10) ? fdc_base + 6 : 0; TRACE_CATCH(fdc_request_regions(), fdc.sra = 0); TRACE_EXIT 0;}static int fdc_config(void){ static int already_done; TRACE_FUN(ft_t_any); if (already_done) { TRACE_CATCH(fdc_request_regions(),); *(fdc.hook) = fdc_isr; /* hook our handler in */ TRACE_EXIT 0; } if (ft_probe_fc10) { int fc_type; TRACE_CATCH(fdc_config_regs(ft_fdc_base, ft_fdc_irq, ft_fdc_dma),); fc_type = fc10_enable(); if (fc_type != 0) { TRACE(ft_t_warn, "FC-%c0 controller found", '0' + fc_type); fdc.type = fc10; fdc.hook = &do_ftape; *(fdc.hook) = fdc_isr; /* hook our handler in */ already_done = 1; TRACE_EXIT 0; } else { TRACE(ft_t_warn, "FC-10/20 controller not found"); fdc_release_regions(); fdc.type = no_fdc; ft_probe_fc10 = 0; ft_fdc_base = 0x3f0; ft_fdc_irq = 6; ft_fdc_dma = 2; } } TRACE(ft_t_warn, "fdc base: 0x%x, irq: %d, dma: %d", ft_fdc_base, ft_fdc_irq, ft_fdc_dma); TRACE_CATCH(fdc_config_regs(ft_fdc_base, ft_fdc_irq, ft_fdc_dma),); fdc.hook = &do_ftape; *(fdc.hook) = fdc_isr; /* hook our handler in */ already_done = 1; TRACE_EXIT 0;}static irqreturn_t ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs){ void (*handler) (void) = *fdc.hook; int handled = 0; TRACE_FUN(ft_t_any); *fdc.hook = NULL; if (handler) { handled = 1; handler(); } else { TRACE(ft_t_bug, "Unexpected ftape interrupt"); } TRACE_EXIT IRQ_RETVAL(handled);}static int fdc_grab_irq_and_dma(void){ TRACE_FUN(ft_t_any); if (fdc.hook == &do_ftape) { /* Get fast interrupt handler. */ if (request_irq(fdc.irq, ftape_interrupt, SA_INTERRUPT, "ft", ftape_id)) { TRACE_ABORT(-EIO, ft_t_bug, "Unable to grab IRQ%d for ftape driver", fdc.irq); } if (request_dma(fdc.dma, ftape_id)) { free_irq(fdc.irq, ftape_id); TRACE_ABORT(-EIO, ft_t_bug, "Unable to grab DMA%d for ftape driver", fdc.dma); } } if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) { /* Using same dma channel or irq as standard fdc, need * to disable the dma-gate on the std fdc. This * couldn't be done in the floppy driver as some * laptops are using the dma-gate to enter a low power * or even suspended state :-( */ outb_p(FDC_RESET_NOT, 0x3f2); TRACE(ft_t_noise, "DMA-gate on standard fdc disabled"); } TRACE_EXIT 0;}int fdc_release_irq_and_dma(void){ TRACE_FUN(ft_t_any); if (fdc.hook == &do_ftape) { disable_dma(fdc.dma); /* just in case... */ free_dma(fdc.dma); free_irq(fdc.irq, ftape_id); } if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) { /* Using same dma channel as standard fdc, need to * disable the dma-gate on the std fdc. This couldn't * be done in the floppy driver as some laptops are * using the dma-gate to enter a low power or even * suspended state :-( */ outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2); TRACE(ft_t_noise, "DMA-gate on standard fdc enabled again"); } TRACE_EXIT 0;}int fdc_init(void){ TRACE_FUN(ft_t_any); /* find a FDC to use */ TRACE_CATCH(fdc_config(),); TRACE_CATCH(fdc_grab_irq_and_dma(), fdc_release_regions()); ftape_motor = 0; fdc_catch_stray_interrupts(0); /* clear number of awainted * stray interrupte */ fdc_catch_stray_interrupts(1); /* one always comes (?) */ TRACE(ft_t_flow, "resetting fdc"); fdc_set_seek_rate(2); /* use nominal QIC step rate */ fdc_reset(); /* init fdc & clear track counters */ if (fdc.type == no_fdc) { /* no FC-10 or FC-20 found */ fdc.type = fdc_probe(); fdc_reset(); /* update with new knowledge */ } if (fdc.type == no_fdc) { fdc_release_irq_and_dma(); fdc_release_regions(); TRACE_EXIT -ENXIO; } if (fdc.type >= i82077) { if (fdc_fifo_enable() < 0) { TRACE(ft_t_warn, "couldn't enable fdc fifo !"); } else { TRACE(ft_t_flow, "fdc fifo enabled and locked"); } } TRACE_EXIT 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -