📄 ide-pmac.c
字号:
size = bh->b_size; while ((bh = bh->b_reqnext) != NULL) { if ((addr + size) != virt_to_bus(bh->b_data)) break; size += bh->b_size; } } /* * Fill in the next DBDMA command block. * Note that one DBDMA command can transfer * at most 65535 bytes. */ while (size) { unsigned int tc = (size < 0xfe00)? size: 0xfe00; if (++count >= MAX_DCMDS) { printk("%s: DMA table too small\n", drive->name); return 0; /* revert to PIO for this request */ } st_le16(&table->command, wr? OUTPUT_MORE: INPUT_MORE); st_le16(&table->req_count, tc); st_le32(&table->phy_addr, addr); table->cmd_dep = 0; table->xfer_status = 0; table->res_count = 0; addr += tc; size -= tc; ++table; } } while (bh != NULL); /* convert the last command to an input/output last command */ if (count) st_le16(&table[-1].command, wr? OUTPUT_LAST: INPUT_LAST); else printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name); /* add the stop command to the end of the list */ memset(table, 0, sizeof(struct dbdma_cmd)); out_le16(&table->command, DBDMA_STOP); out_le32(&dma->cmdptr, virt_to_bus(tstart)); return 1;}/* This is fun. -DaveM */#define IDE_SETXFER 0x03#define IDE_SETFEATURE 0xef#define IDE_DMA2_ENABLE 0x22#define IDE_DMA1_ENABLE 0x21#define IDE_DMA0_ENABLE 0x20#define IDE_UDMA4_ENABLE 0x44#define IDE_UDMA3_ENABLE 0x43#define IDE_UDMA2_ENABLE 0x42#define IDE_UDMA1_ENABLE 0x41#define IDE_UDMA0_ENABLE 0x40static __inline__ unsigned chardma_bits_to_command(unsigned char bits){ if(bits & 0x04) return IDE_DMA2_ENABLE; if(bits & 0x02) return IDE_DMA1_ENABLE; return IDE_DMA0_ENABLE;}static __inline__ unsigned charudma_bits_to_command(unsigned char bits){ if(bits & 0x10) return IDE_UDMA4_ENABLE; if(bits & 0x08) return IDE_UDMA3_ENABLE; if(bits & 0x04) return IDE_UDMA2_ENABLE; if(bits & 0x02) return IDE_UDMA1_ENABLE; if(bits & 0x01) return IDE_UDMA0_ENABLE; return 0;}static __inline__ intwait_for_ready(ide_drive_t *drive){ /* Timeout bumped for some powerbooks */ int timeout = 2000; byte stat; while(--timeout) { stat = GET_STAT(); if(!(stat & BUSY_STAT)) { if (drive->ready_stat == 0) break; else if((stat & drive->ready_stat) || (stat & ERR_STAT)) break; } mdelay(1); } if((stat & ERR_STAT) || timeout <= 0) { if (stat & ERR_STAT) { printk("ide_pmace: wait_for_ready, error status: %x\n", stat); } return 1; } return 0;}static intpmac_ide_do_setfeature(ide_drive_t *drive, byte command){ unsigned long flags; byte old_select; int result = 1; save_flags(flags); cli(); old_select = IN_BYTE(IDE_SELECT_REG); OUT_BYTE(drive->select.all, IDE_SELECT_REG); udelay(10); OUT_BYTE(IDE_SETXFER, IDE_FEATURE_REG); OUT_BYTE(command, IDE_NSECTOR_REG); if(wait_for_ready(drive)) { printk("pmac_ide_do_setfeature disk not ready before SET_FEATURE!\n"); goto out; } OUT_BYTE(IDE_SETFEATURE, IDE_COMMAND_REG); result = wait_for_ready(drive); if (result) printk("pmac_ide_do_setfeature disk not ready after SET_FEATURE !\n");out: OUT_BYTE(old_select, IDE_SELECT_REG); restore_flags(flags); return result;}/* Calculate MultiWord DMA timings */static intpmac_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 cycleTime, accessTime; int accessTicks, recTicks; struct hd_driveid *id = drive->id; /* Set feature on drive */ printk("%s: Enabling MultiWord DMA %d\n", drive->name, feature & 0xf); if (pmac_ide_do_setfeature(drive, feature)) { printk("%s: Failed !\n", drive->name); return 0; } if (!drive->init_speed) drive->init_speed = feature; /* which drive is it ? */ if (drive->select.all & 0x10) timings = &pmac_ide[idx].timings[1]; else timings = &pmac_ide[idx].timings[0]; /* Calculate accesstime and cycle time */ cycleTime = mdma_timings[feature & 0xf].cycleTime; accessTime = mdma_timings[feature & 0xf].accessTime; if ((id->field_valid & 2) && (id->eide_dma_time)) cycleTime = id->eide_dma_time; if ((pmac_ide[idx].kind == controller_ohare) && (cycleTime < 150)) cycleTime = 150; /* For ata-4 controller, we don't know the calculation */ if (pmac_ide[idx].kind == controller_kl_ata4) { accessTicks = SYSCLK_TICKS_UDMA(accessTime * 1000); recTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000) - accessTicks; *timings = ((*timings) & 0xffe003ff) | (accessTicks | (recTicks << 5)) << 10; } else { int halfTick = 0; int origAccessTime = accessTime; int origCycleTime = cycleTime; accessTicks = SYSCLK_TICKS(accessTime); if (accessTicks < 1) accessTicks = 1; accessTime = accessTicks * IDE_SYSCLK_NS; recTicks = SYSCLK_TICKS(cycleTime - accessTime) - 1; if (recTicks < 1) recTicks = 1; cycleTime = (recTicks + 1 + accessTicks) * IDE_SYSCLK_NS; /* KeyLargo ata-3 don't support the half-tick stuff */ if ((pmac_ide[idx].kind != controller_kl_ata3) && (accessTicks > 1) && ((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) && ((cycleTime - IDE_SYSCLK_NS) >= origCycleTime)) { halfTick = 1; accessTicks--; } *timings = ((*timings) & 0x7FF) | (accessTicks | (recTicks << 5) | (halfTick << 10)) << 11; }#ifdef IDE_PMAC_DEBUG printk("ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n", feature & 0xf, *timings);#endif drive->current_speed = feature; return 1;}/* Calculate Ultra DMA timings */static intpmac_ide_udma_enable(ide_drive_t *drive, int idx){ byte bits = drive->id->dma_ultra & 0x1f; byte feature = udma_bits_to_command(bits); int cycleTime, accessTime; int rdyToPauseTicks, cycleTicks; u32 *timings; /* Set feature on drive */ printk("%s: Enabling Ultra DMA %d\n", drive->name, feature & 0xf); if (pmac_ide_do_setfeature(drive, feature)) { printk("%s: Failed !\n", drive->name); return 0; } if (!drive->init_speed) drive->init_speed = feature; /* which drive is it ? */ if (drive->select.all & 0x10) timings = &pmac_ide[idx].timings[1]; else timings = &pmac_ide[idx].timings[0]; cycleTime = udma_timings[feature & 0xf].cycleTime; accessTime = udma_timings[feature & 0xf].accessTime; rdyToPauseTicks = SYSCLK_TICKS_UDMA(accessTime * 1000); cycleTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000); *timings = ((*timings) & 0xe00fffff) | ((cycleTicks << 1) | (rdyToPauseTicks << 5) | 1) << 20; drive->current_speed = feature; return 1;}static intpmac_ide_dma_onoff(ide_drive_t *drive, int enable){ int ata4, udma, idx; struct hd_driveid *id = drive->id; 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); if(enable) { if (ata4 && (drive->media == ide_disk) && (id->field_valid & 0x0004) && (id->dma_ultra & 0x17)) { /* UltraDMA modes. */ drive->using_dma = pmac_ide_udma_enable(drive, idx); } if (!drive->using_dma && (id->dma_mword & 0x0007)) { /* Normal MultiWord DMA modes. */ drive->using_dma = pmac_ide_mdma_enable(drive, idx); } /* Without this, strange things will happen on Keylargo-based * machines */ OUT_BYTE(0, IDE_CONTROL_REG); if (drive->select.all == IN_BYTE(IDE_SELECT_REG)) pmac_ide_selectproc(drive); } return 0;}int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive){ ide_hwif_t *hwif = HWIF(drive); int ix, dstat; volatile struct dbdma_regs *dma; /* 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; switch (func) { case ide_dma_on: case ide_dma_off: case ide_dma_off_quietly: pmac_ide_dma_onoff(drive, (func == ide_dma_on)); break; case ide_dma_check: printk("IDE-DMA check !\n"); if (hwif->autodma) pmac_ide_dma_onoff(drive, 1); break; case ide_dma_read: case ide_dma_write: if (!pmac_ide_build_dmatable(drive, ix, func==ide_dma_write)) return 1; 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); break; case ide_dma_end: 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: return (in_le32(&dma->status) & (RUN|ACTIVE)) == RUN; /* 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("ide_pmac_dmaproc: chipset supported %s func only: %d\n", ide_dmafunc_verbose(func), func); return 1; default: printk("ide_pmac_dmaproc: unsupported %s func: %d\n", ide_dmafunc_verbose(func), func); return 1; } return 0;}#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */#ifdef CONFIG_PMAC_PBOOKstatic void idepmac_sleep_disk(int i, unsigned long base){ struct device_node* np = pmac_ide[i].node; int j; /* FIXME: We only handle the master IDE */ if (ide_hwifs[i].drives[0].media == ide_disk) { /* Spin down the drive */ outb(0xa0, base+0x60); 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; } } feature_set(np, FEATURE_IDE0_reset); feature_clear(np, FEATURE_IDE0_enable); switch(pmac_ide[i].aapl_bus_id) { case 0: feature_set(np, FEATURE_IDE0_reset); feature_clear(np, FEATURE_IDE0_enable); break; case 1: feature_set(np, FEATURE_IDE1_reset); feature_clear(np, FEATURE_IDE1_enable); break; case 2: feature_set(np, FEATURE_IDE2_reset); break; } pmac_ide[i].timings[0] = 0; pmac_ide[i].timings[1] = 0;}static void idepmac_wake_disk(int i, unsigned long base){ struct device_node* np = pmac_ide[i].node; int j; /* Revive IDE disk and controller */ switch(pmac_ide[i].aapl_bus_id) { case 0: feature_set(np, FEATURE_IDE0_reset); mdelay(10); feature_set(np, FEATURE_IDE0_enable); mdelay(10); feature_clear(np, FEATURE_IDE0_reset); break; case 1: feature_set(np, FEATURE_IDE1_reset); mdelay(10); feature_set(np, FEATURE_IDE1_enable); mdelay(10); feature_clear(np, FEATURE_IDE1_reset); break; case 2: /* This one exists only for KL, I don't know about any enable bit */ feature_set(np, FEATURE_IDE2_reset); mdelay(10); feature_clear(np, FEATURE_IDE2_reset); break; } mdelay(IDE_WAKEUP_DELAY_MS); /* Reset timings */ pmac_ide_selectproc(&ide_hwifs[i].drives[0]); mdelay(10); /* Wait up to 10 seconds (enough for recent drives) */ for (j = 0; j < 100; j++) { int status; mdelay(100); status = inb(base + 0x70); if (!(status & BUSY_STAT)) break; }}/* Here we handle media bay devices */static voididepmac_wake_bay(int i, unsigned long base){ int timeout; /* Reset timings */ pmac_ide_selectproc(&ide_hwifs[i].drives[0]); mdelay(10); timeout = 10000; while ((inb(base + 0x70) & BUSY_STAT) && timeout) { mdelay(1); --timeout; }}/* 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 idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when){ int i, ret; unsigned long base; switch (when) { case PBOOK_SLEEP_REQUEST: break; case PBOOK_SLEEP_REJECT: break; case PBOOK_SLEEP_NOW: for (i = 0; i < pmac_ide_count; ++i) { if ((base = pmac_ide[i].regbase) == 0) continue; /* Disable irq during sleep */ disable_irq(pmac_ide[i].irq); ret = check_media_bay_by_base(base, MB_CD); if ((ret == -ENODEV) && ide_hwifs[i].drives[0].present) /* not media bay - put the disk to sleep */ idepmac_sleep_disk(i, base); } break; case PBOOK_WAKE: for (i = 0; i < pmac_ide_count; ++i) { ide_hwif_t *hwif; if ((base = pmac_ide[i].regbase) == 0) continue; hwif = &ide_hwifs[i]; /* We don't handle media bay devices this way */ ret = check_media_bay_by_base(base, MB_CD); if ((ret == -ENODEV) && ide_hwifs[i].drives[0].present) idepmac_wake_disk(i, base); else if (ret == 0) idepmac_wake_bay(i, base); enable_irq(pmac_ide[i].irq);#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC if (hwif->drives[0].present && hwif->drives[0].using_dma) pmac_ide_dma_onoff(&hwif->drives[0], 1);#endif } break; } return PBOOK_SLEEP_OK;}#endif /* CONFIG_PMAC_PBOOK */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -