?? ide-pmac.c
字號:
static __inline__ unsigned charudma_bits_to_command(unsigned char bits, int high_speed){ if (high_speed) { if(bits & 0x10) return XFER_UDMA_4; if(bits & 0x08) return XFER_UDMA_3; } if(bits & 0x04) return XFER_UDMA_2; if(bits & 0x02) return XFER_UDMA_1; if(bits & 0x01) return XFER_UDMA_0; return 0;}/* Calculate MultiWord DMA timings */static int __pmacpmac_ide_mdma_enable(ide_drive_t *drive, int idx){ byte bits = drive->id->dma_mword & 0x07; byte feature = dma_bits_to_command(bits); u32 *timings; int drive_cycle_time; struct hd_driveid *id = drive->id; int ret; /* Set feature on drive */ printk(KERN_INFO "%s: Enabling MultiWord DMA %d\n", drive->name, feature & 0xf); ret = pmac_ide_do_setfeature(drive, feature); if (ret) { printk(KERN_WARNING "%s: Failed !\n", drive->name); return 0; } if (!drive->init_speed) drive->init_speed = feature; /* which drive is it ? */ if (drive->select.b.unit & 0x01) timings = &pmac_ide[idx].timings[1]; else timings = &pmac_ide[idx].timings[0]; /* Check if drive provide explicit cycle time */ if ((id->field_valid & 2) && (id->eide_dma_time)) drive_cycle_time = id->eide_dma_time; else drive_cycle_time = 0; /* Calculate controller timings */ set_timings_mdma(pmac_ide[idx].kind, timings, feature, drive_cycle_time); drive->current_speed = feature; return 1;}/* Calculate Ultra DMA timings */static int __pmacpmac_ide_udma_enable(ide_drive_t *drive, int idx, int high_speed){ byte bits = drive->id->dma_ultra & 0x1f; byte feature = udma_bits_to_command(bits, high_speed); u32 *timings; int ret; /* Set feature on drive */ printk(KERN_INFO "%s: Enabling Ultra DMA %d\n", drive->name, feature & 0xf); ret = pmac_ide_do_setfeature(drive, feature); if (ret) { printk(KERN_WARNING "%s: Failed !\n", drive->name); return 0; } if (!drive->init_speed) drive->init_speed = feature; /* which drive is it ? */ if (drive->select.b.unit & 0x01) timings = &pmac_ide[idx].timings[1]; else timings = &pmac_ide[idx].timings[0]; set_timings_udma(timings, feature); drive->current_speed = feature; return 1;}static int __pmacpmac_ide_check_dma(ide_drive_t *drive){ int ata4, udma, idx; struct hd_driveid *id = drive->id; int enable = 1; drive->using_dma = 0; idx = pmac_ide_find(drive); if (idx < 0) return 0; if (drive->media == ide_floppy) enable = 0; if (((id->capability & 1) == 0) && !check_drive_lists(drive, GOOD_DMA_DRIVE)) enable = 0; if (check_drive_lists(drive, BAD_DMA_DRIVE)) enable = 0; udma = 0; ata4 = (pmac_ide[idx].kind == controller_kl_ata4 || pmac_ide[idx].kind == controller_kl_ata4_80); if(enable) { if (ata4 && (drive->media == ide_disk) && (id->field_valid & 0x0004) && (id->dma_ultra & 0x1f)) { /* UltraDMA modes. */ drive->using_dma = pmac_ide_udma_enable(drive, idx, pmac_ide[idx].kind == controller_kl_ata4_80); } if (!drive->using_dma && (id->dma_mword & 0x0007)) { /* Normal MultiWord DMA modes. */ drive->using_dma = pmac_ide_mdma_enable(drive, idx); } OUT_BYTE(0, IDE_CONTROL_REG); /* Apply settings to controller */ pmac_ide_selectproc(drive); } return 0;}static int __pmacpmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive){ int ix, dstat; volatile struct dbdma_regs *dma; byte unit = (drive->select.b.unit & 0x01); byte ata4; /* Can we stuff a pointer to our intf structure in config_data * or select_data in hwif ? */ ix = pmac_ide_find(drive); if (ix < 0) return 0; dma = pmac_ide[ix].dma_regs; ata4 = (pmac_ide[ix].kind == controller_kl_ata4 || pmac_ide[ix].kind == controller_kl_ata4_80); switch (func) { case ide_dma_off: printk(KERN_INFO "%s: DMA disabled\n", drive->name); case ide_dma_off_quietly: drive->using_dma = 0; break; case ide_dma_on: case ide_dma_check: pmac_ide_check_dma(drive); break; case ide_dma_read: case ide_dma_write: if (!pmac_ide_build_dmatable(drive, ix, func==ide_dma_write)) return 1; /* Apple adds 60ns to wrDataSetup on reads */ if (ata4 && (pmac_ide[ix].timings[unit] & TR_66_UDMA_EN)) { out_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE), pmac_ide[ix].timings[unit] + ((func == ide_dma_read) ? 0x00800000UL : 0)); (void)in_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE)); } drive->waiting_for_dma = 1; if (drive->media != ide_disk) return 0; ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); OUT_BYTE(func==ide_dma_write? WIN_WRITEDMA: WIN_READDMA, IDE_COMMAND_REG); case ide_dma_begin: out_le32(&dma->control, (RUN << 16) | RUN); /* Make sure it gets to the controller right now */ (void)in_le32(&dma->control); break; case ide_dma_end: /* returns 1 on error, 0 otherwise */ drive->waiting_for_dma = 0; dstat = in_le32(&dma->status); out_le32(&dma->control, ((RUN|WAKE|DEAD) << 16)); /* verify good dma status */ return (dstat & (RUN|DEAD|ACTIVE)) != RUN; case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */ /* We have to things to deal with here: * * - The dbdma won't stop if the command was started * but completed with an error without transfering all * datas. This happens when bad blocks are met during * a multi-block transfer. * * - The dbdma fifo hasn't yet finished flushing to * to system memory when the disk interrupt occurs. * * The trick here is to increment drive->waiting_for_dma, * and return as if no interrupt occured. If the counter * reach a certain timeout value, we then return 1. If * we really got the interrupt, it will happen right away * again. * Apple's solution here may be more elegant. They issue * a DMA channel interrupt (a separate irq line) via a DBDMA * NOP command just before the STOP, and wait for both the * disk and DBDMA interrupts to have completed. */ /* If ACTIVE is cleared, the STOP command have passed and * transfer is complete. */ if (!(in_le32(&dma->status) & ACTIVE)) return 1; if (!drive->waiting_for_dma) printk(KERN_WARNING "ide%d, ide_dma_test_irq \ called while not waiting\n", ix); /* If dbdma didn't execute the STOP command yet, the * active bit is still set */ drive->waiting_for_dma++; if (drive->waiting_for_dma >= DMA_WAIT_TIMEOUT) { printk(KERN_WARNING "ide%d, timeout waiting \ for dbdma command stop\n", ix); return 1; } udelay(1); return 0; /* Let's implement tose just in case someone wants them */ case ide_dma_bad_drive: case ide_dma_good_drive: return check_drive_lists(drive, (func == ide_dma_good_drive)); case ide_dma_verbose: return report_drive_dmaing(drive); case ide_dma_retune: case ide_dma_lostirq: case ide_dma_timeout: printk(KERN_WARNING "ide_pmac_dmaproc: chipset supported %s func only: %d\n", ide_dmafunc_verbose(func), func); return 1; default: printk(KERN_WARNING "ide_pmac_dmaproc: unsupported %s func: %d\n", ide_dmafunc_verbose(func), func); return 1; } return 0;}#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */static void __pmacidepmac_sleep_device(ide_drive_t *drive, int i, unsigned base){ int j; /* FIXME: We only handle the master IDE disk, we shoud * try to fix CD-ROMs here */ switch (drive->media) { case ide_disk: /* Spin down the drive */ outb(drive->select.all, base+0x60); (void)inb(base+0x60); udelay(100); outb(0x0, base+0x30); outb(0x0, base+0x20); outb(0x0, base+0x40); outb(0x0, base+0x50); outb(0xe0, base+0x70); outb(0x2, base+0x160); for (j = 0; j < 10; j++) { int status; mdelay(100); status = inb(base+0x70); if (!(status & BUSY_STAT) && (status & DRQ_STAT)) break; } break; case ide_cdrom: // todo break; case ide_floppy: // todo break; }}#ifdef CONFIG_PMAC_PBOOKstatic void __pmacidepmac_wake_device(ide_drive_t *drive, int used_dma){ /* We force the IDE subdriver to check for a media change * This must be done first or we may lost the condition * * Problem: This can schedule. I moved the block device * wakeup almost late by priority because of that. */ if (DRIVER(drive) && DRIVER(drive)->media_change) DRIVER(drive)->media_change(drive); /* We kick the VFS too (see fix in ide.c revalidate) */ check_disk_change(MKDEV(HWIF(drive)->major, (drive->select.b.unit) << PARTN_BITS)); #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC /* We re-enable DMA on the drive if it was active. */ /* This doesn't work with the CD-ROM in the media-bay, probably * because of a pending unit attention. The problem if that if I * clear the error, the filesystem dies. */ if (used_dma && !ide_spin_wait_hwgroup(drive)) { /* Lock HW group */ HWGROUP(drive)->busy = 1; pmac_ide_check_dma(drive); HWGROUP(drive)->busy = 0; if (!list_empty(&drive->queue.queue_head)) ide_do_request(HWGROUP(drive), 0); spin_unlock_irq(&io_request_lock); }#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */}static void __pmacidepmac_sleep_interface(int i, unsigned base, int mediabay){ struct device_node* np = pmac_ide[i].node; /* We clear the timings */ pmac_ide[i].timings[0] = 0; pmac_ide[i].timings[1] = 0; /* The media bay will handle itself just fine */ if (mediabay) return; /* Disable the bus */ ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmac_ide[i].aapl_bus_id, 0);}static void __pmacidepmac_wake_interface(int i, unsigned long base, int mediabay){ struct device_node* np = pmac_ide[i].node; if (!mediabay) { /* Revive IDE disk and controller */ ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmac_ide[i].aapl_bus_id, 1); ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmac_ide[i].aapl_bus_id, 1); mdelay(10); ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmac_ide[i].aapl_bus_id, 0); }}static voididepmac_sleep_drive(ide_drive_t *drive, int idx, unsigned long base){ int unlock = 0; /* Wait for HW group to complete operations */ if (ide_spin_wait_hwgroup(drive)) { // What can we do here ? Wake drive we had already // put to sleep and return an error ? } else { unlock = 1; /* Lock HW group */ HWGROUP(drive)->busy = 1; /* Stop the device */ idepmac_sleep_device(drive, idx, base); } if (unlock) spin_unlock_irq(&io_request_lock);}static voididepmac_wake_drive(ide_drive_t *drive, unsigned long base){ unsigned long flags; int j; /* Reset timings */ pmac_ide_selectproc(drive); mdelay(10); /* Wait up to 20 seconds for the drive to be ready */ for (j = 0; j < 200; j++) { int status; mdelay(100); outb(drive->select.all, base + 0x60); if (inb(base + 0x60) != drive->select.all) continue; status = inb(base + 0x70); if (!(status & BUSY_STAT)) break; } /* We resume processing on the HW group */ spin_lock_irqsave(&io_request_lock, flags); HWGROUP(drive)->busy = 0; if (!list_empty(&drive->queue.queue_head)) ide_do_request(HWGROUP(drive), 0); spin_unlock_irqrestore(&io_request_lock, flags); }/* Note: We support only master drives for now. This will have to be * improved if we want to handle sleep on the iMacDV where the CD-ROM * is a slave */static int __pmacidepmac_notify_sleep(struct pmu_sleep_notifier *self, int when){ int i, ret; unsigned long base; int big_delay; switch (when) { case PBOOK_SLEEP_REQUEST: break; case PBOOK_SLEEP_REJECT: break; case PBOOK_SLEEP_NOW: for (i = 0; i < pmac_ide_count; ++i) { ide_hwif_t *hwif; int dn; if ((base = pmac_ide[i].regbase) == 0) continue; hwif = &ide_hwifs[i]; for (dn=0; dn<MAX_DRIVES; dn++) { if (!hwif->drives[dn].present) continue; idepmac_sleep_drive(&hwif->drives[dn], i, base); } /* Disable irq during sleep */ disable_irq(pmac_ide[i].irq); /* Check if this is a media bay with an IDE device or not * a media bay. */ ret = check_media_bay_by_base(base, MB_CD); if ((ret == 0) || (ret == -ENODEV)) idepmac_sleep_interface(i, base, (ret == 0)); } break; case PBOOK_WAKE: big_delay = 0; for (i = 0; i < pmac_ide_count; ++i) { if ((base = pmac_ide[i].regbase) == 0) continue; /* Make sure we have sane timings */ sanitize_timings(i); /* Check if this is a media bay with an IDE device or not * a media bay */ ret = check_media_bay_by_base(base, MB_CD); if ((ret == 0) || (ret == -ENODEV)) { idepmac_wake_interface(i, base, (ret == 0)); big_delay = 1; } } /* Let hardware get up to speed */ if (big_delay) mdelay(IDE_WAKEUP_DELAY_MS); for (i = 0; i < pmac_ide_count; ++i) { ide_hwif_t *hwif; int used_dma, dn; int irq_on = 0; if ((base = pmac_ide[i].regbase) == 0) continue; hwif = &ide_hwifs[i]; for (dn=0; dn<MAX_DRIVES; dn++) { ide_drive_t *drive = &hwif->drives[dn]; if (!drive->present) continue; /* We don't have re-configured DMA yet */ used_dma = drive->using_dma; drive->using_dma = 0; idepmac_wake_drive(drive, base); if (!irq_on) { enable_irq(pmac_ide[i].irq); irq_on = 1; } idepmac_wake_device(drive, used_dma); } if (!irq_on) enable_irq(pmac_ide[i].irq); } break; } return PBOOK_SLEEP_OK;}#endif /* CONFIG_PMAC_PBOOK */static int __pmacpmac_ide_notify_reboot(struct notifier_block *this, unsigned long code, void *x){ int i, gotone; unsigned long base; if (code != SYS_HALT && code != SYS_POWER_OFF) return 0; gotone = 0; for (i = 0; i < pmac_ide_count; ++i) { ide_hwif_t *hwif; ide_drive_t *drive; int unlock = 0; int dn; if ((base = pmac_ide[i].regbase) == 0) continue; hwif = &ide_hwifs[i]; for (dn=0; dn<MAX_DRIVES; dn++) { drive = &hwif->drives[dn]; if (drive->present) { gotone = 1; /* Wait for HW group to complete operations */ if (ide_spin_wait_hwgroup(drive)) { // What can we do here ? Wake drive we had already // put to sleep and return an error ? } else { unlock = 1; /* Lock HW group */ HWGROUP(drive)->busy = 1; /* Stop the device */ idepmac_sleep_device(drive, i, base); } } if (unlock) spin_unlock_irq(&io_request_lock); } } if (gotone) mdelay(1000); return NOTIFY_DONE;}
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -