📄 pmac.c
字号:
BUG(); ide_set_handler(drive, &ide_dma_intr, 2*WAIT_CMD, NULL); /* * FIX ME to use only ACB ide_task_t args Struct */#if 0 { ide_task_t *args = rq->special; command = args->tfRegister[IDE_COMMAND_OFFSET]; }#else command = (lba48) ? WIN_READDMA_EXT : WIN_READDMA; if (rq->cmd == IDE_DRIVE_TASKFILE) { ide_task_t *args = rq->special; command = args->tfRegister[IDE_COMMAND_OFFSET]; }#endif /* issue cmd to drive */ hwif->OUTB(command, IDE_COMMAND_REG); return pmac_ide_dma_begin(drive);}static int __pmacpmac_ide_dma_write (ide_drive_t *drive){ ide_hwif_t *hwif = HWIF(drive); pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data; struct request *rq = HWGROUP(drive)->rq;// ide_task_t *args = rq->special; u8 unit = (drive->select.b.unit & 0x01); u8 ata4; u8 lba48 = (drive->addressing == 1) ? 1 : 0; task_ioreg_t command = WIN_NOP; if (pmif == NULL) return 1; ata4 = (pmif->kind == controller_kl_ata4); if (!pmac_ide_build_dmatable(drive, rq, PCI_DMA_TODEVICE)) /* try PIO instead of DMA */ return 1; /* Apple adds 60ns to wrDataSetup on reads */ if (ata4 && (pmif->timings[unit] & TR_66_UDMA_EN)) { writel(pmif->timings[unit], (unsigned *)(IDE_DATA_REG+IDE_TIMING_CONFIG)); io_flush(readl((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG))); } drive->waiting_for_dma = 1; if (drive->media != ide_disk) return 0; /* paranoia check */ if (HWGROUP(drive)->handler != NULL) /* paranoia check */ BUG(); ide_set_handler(drive, &ide_dma_intr, 2*WAIT_CMD, NULL); /* * FIX ME to use only ACB ide_task_t args Struct */#if 0 { ide_task_t *args = rq->special; command = args->tfRegister[IDE_COMMAND_OFFSET]; }#else command = (lba48) ? WIN_WRITEDMA_EXT : WIN_WRITEDMA; if (rq->cmd == IDE_DRIVE_TASKFILE) { ide_task_t *args = rq->special; command = args->tfRegister[IDE_COMMAND_OFFSET]; }#endif /* issue cmd to drive */ hwif->OUTB(command, IDE_COMMAND_REG); return pmac_ide_dma_begin(drive);}static int __pmacpmac_ide_dma_count (ide_drive_t *drive){ return HWIF(drive)->ide_dma_begin(drive);}static int __pmacpmac_ide_dma_begin (ide_drive_t *drive){ pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; volatile struct dbdma_regs *dma; if (pmif == NULL) return 1; dma = pmif->dma_regs; writel((RUN << 16) | RUN, &dma->control); /* Make sure it gets to the controller right now */ io_flush(readl(&dma->control)); return 0;}static int __pmacpmac_ide_dma_end (ide_drive_t *drive){ pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; volatile struct dbdma_regs *dma; u32 dstat; if (pmif == NULL) return 0; dma = pmif->dma_regs; drive->waiting_for_dma = 0; dstat = readl(&dma->status); writel(((RUN|WAKE|DEAD) << 16), &dma->control); pmac_ide_destroy_dmatable(drive); /* verify good dma status */ return (dstat & (RUN|DEAD|ACTIVE)) != RUN;}static int __pmacpmac_ide_dma_test_irq (ide_drive_t *drive){ pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; volatile struct dbdma_regs *dma; unsigned long status; if (pmif == NULL) return 0; dma = pmif->dma_regs; /* 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. */ status = readl(&dma->status); if (!(status & ACTIVE)) return 1; if (!drive->waiting_for_dma) printk(KERN_WARNING "ide%d, ide_dma_test_irq \ called while not waiting\n", HWIF(drive)->index); /* 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", HWIF(drive)->index); return 1; } udelay(1); return 0;}static int __pmacpmac_ide_dma_host_off (ide_drive_t *drive){ return 0;}static int __pmacpmac_ide_dma_host_on (ide_drive_t *drive){ return 0;}static int __pmacpmac_ide_dma_lostirq (ide_drive_t *drive){ pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; volatile struct dbdma_regs *dma; unsigned long status; if (pmif == NULL) return 0; dma = pmif->dma_regs; status = readl(&dma->status); printk(KERN_ERR "ide-pmac lost interrupt, dma status: %lx\n", status); return 0;}static void __init pmac_ide_setup_dma(struct device_node *np, int ix){ struct pmac_ide_hwif *pmif = &pmac_ide[ix]; if (device_is_compatible(np, "kauai-ata")) { pmif->dma_regs = (volatile struct dbdma_regs*)(pmif->mapbase + 0x1000); } else { if (request_OF_resource(np, 1, " (mac-io IDE DMA)") == NULL) { printk(KERN_ERR "ide-pmac(%s): can't request DMA resource !\n", np->name); return; } } pmif->dma_regs = (volatile struct dbdma_regs*)ioremap(np->addrs[1].address, 0x200); /* * Allocate space for the DBDMA commands. * The +2 is +1 for the stop command and +1 to allow for * aligning the start address to a multiple of 16 bytes. */ pmif->dma_table_cpu = (struct dbdma_cmd*)pci_alloc_consistent( ide_hwifs[ix].pci_dev, (MAX_DCMDS + 2) * sizeof(struct dbdma_cmd), &pmif->dma_table_dma); if (pmif->dma_table_cpu == NULL) { printk(KERN_ERR "%s: unable to allocate DMA command list\n", ide_hwifs[ix].name); return; } pmif->sg_table = kmalloc(sizeof(struct scatterlist) * MAX_DCMDS, GFP_KERNEL); if (pmif->sg_table == NULL) { pci_free_consistent( ide_hwifs[ix].pci_dev, (MAX_DCMDS + 2) * sizeof(struct dbdma_cmd), pmif->dma_table_cpu, pmif->dma_table_dma); return; } ide_hwifs[ix].ide_dma_off = &__ide_dma_off; ide_hwifs[ix].ide_dma_off_quietly = &__ide_dma_off_quietly; ide_hwifs[ix].ide_dma_on = &__ide_dma_on; ide_hwifs[ix].ide_dma_check = &pmac_ide_dma_check; ide_hwifs[ix].ide_dma_read = &pmac_ide_dma_read; ide_hwifs[ix].ide_dma_write = &pmac_ide_dma_write; ide_hwifs[ix].ide_dma_count = &pmac_ide_dma_count; ide_hwifs[ix].ide_dma_begin = &pmac_ide_dma_begin; ide_hwifs[ix].ide_dma_end = &pmac_ide_dma_end; ide_hwifs[ix].ide_dma_test_irq = &pmac_ide_dma_test_irq; ide_hwifs[ix].ide_dma_host_off = &pmac_ide_dma_host_off; ide_hwifs[ix].ide_dma_host_on = &pmac_ide_dma_host_on; ide_hwifs[ix].ide_dma_good_drive = &__ide_dma_good_drive; ide_hwifs[ix].ide_dma_bad_drive = &__ide_dma_bad_drive; ide_hwifs[ix].ide_dma_verbose = &__ide_dma_verbose; ide_hwifs[ix].ide_dma_timeout = &__ide_dma_timeout; ide_hwifs[ix].ide_dma_retune = &__ide_dma_retune; ide_hwifs[ix].ide_dma_lostirq = &pmac_ide_dma_lostirq;#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO if (!noautodma) ide_hwifs[ix].autodma = 1;#endif ide_hwifs[ix].drives[0].autodma = ide_hwifs[ix].autodma; ide_hwifs[ix].drives[1].autodma = ide_hwifs[ix].autodma;}#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */static void __pmacidepmac_sleep_device(ide_drive_t *drive){ ide_hwif_t *hwif = HWIF(drive); 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 */ SELECT_DRIVE(drive); SELECT_MASK(drive, 0); hwif->OUTB(drive->select.all, IDE_SELECT_REG); io_flush(hwif->INB(IDE_SELECT_REG)); udelay(100); hwif->OUTB(0x00, IDE_SECTOR_REG); hwif->OUTB(0x00, IDE_NSECTOR_REG); hwif->OUTB(0x00, IDE_LCYL_REG); hwif->OUTB(0x00, IDE_HCYL_REG); hwif->OUTB(drive->ctl|2, IDE_CONTROL_REG); hwif->OUTB(WIN_STANDBYNOW1, IDE_COMMAND_REG); for (j = 0; j < 10; j++) { u8 status; mdelay(100); status = hwif->INB(IDE_STATUS_REG); 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_dma_check(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(pmac_ide_hwif_t *pmif, unsigned base, int mediabay){ struct device_node* np = pmif->node; /* We clear the timings */ pmif->timings[0] = 0; pmif->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, pmif->aapl_bus_id, 0);}static void __pmacidepmac_wake_interface(pmac_ide_hwif_t *pmif, unsigned long base, int mediabay){ struct device_node* np = pmif->node; if (!mediabay) { /* Revive IDE disk and controller */ ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 1); ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmif->aapl_bus_id, 1); mdelay(10); ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 0); }}static void __pmacidepmac_sleep_drive(ide_drive_t *drive){ 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); } if (unlock) spin_unlock_irq(&io_request_lock);}static void __pmacidepmac_wake_drive(ide_drive_t *drive, unsigned long base){ ide_hwif_t *hwif = HWIF(drive); unsigned long flags; int j; /* Reset timings */ pmac_ide_do_update_timings(drive); mdelay(10); /* Wait up to 20 seconds for the drive to be ready */ for (j = 0; j < 200; j++) { u8 status = 0; mdelay(100); hwif->OUTB(drive->select.all, base + 0x60); if ((hwif->INB(base + 0x60)) != drive->select.all) continue; status = hwif->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]); } /* 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(&pmac_ide[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(&pmac_ide[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(&pmac_ide[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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -