📄 ide-pmac.c
字号:
/* * linux/drivers/ide/ide-pmac.c Version ?.?? Mar. 18, 2000 * * Support for IDE interfaces on PowerMacs. * These IDE interfaces are memory-mapped and have a DBDMA channel * for doing DMA. * * Copyright (C) 1998-2001 Paul Mackerras & Ben. Herrenschmidt * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Some code taken from drivers/ide/ide-dma.c: * * Copyright (c) 1995-1998 Mark Lord * */#include <linux/config.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/ide.h>#include <asm/prom.h>#include <asm/io.h>#include <asm/dbdma.h>#include <asm/ide.h>#include <asm/mediabay.h>#include <asm/feature.h>#ifdef CONFIG_PMAC_PBOOK#include <linux/adb.h>#include <linux/pmu.h>#include <asm/irq.h>#endif#include "ide_modes.h"extern char *ide_dmafunc_verbose(ide_dma_action_t dmafunc);#undef IDE_PMAC_DEBUG#define IDE_SYSCLK_NS 30#define IDE_SYSCLK_ULTRA_PS 0x1d4c /* (15 * 1000 / 2)*/struct pmac_ide_hwif { ide_ioreg_t regbase; int irq; int kind; int aapl_bus_id; struct device_node* node; u32 timings[2];#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC volatile struct dbdma_regs* dma_regs; struct dbdma_cmd* dma_table;#endif } pmac_ide[MAX_HWIFS];static int pmac_ide_count;enum { controller_ohare, /* OHare based */ controller_heathrow, /* Heathrow/Paddington */ controller_kl_ata3, /* KeyLargo ATA-3 */ controller_kl_ata4 /* KeyLargo ATA-4 */};#ifdef CONFIG_BLK_DEV_IDEDMA_PMACtypedef struct { int accessTime; int cycleTime;} pmac_ide_timing;/* Multiword DMA timings */static pmac_ide_timing mdma_timings[] ={ { 215, 480 }, /* Mode 0 */ { 80, 150 }, /* 1 */ { 70, 120 } /* 2 */};/* Ultra DMA timings (for use when I know how to calculate them */static pmac_ide_timing udma_timings[] ={ { 0, 114 }, /* Mode 0 */ { 0, 75 }, /* 1 */ { 0, 55 }, /* 2 */ { 100, 45 }, /* 3 */ { 100, 25 } /* 4 */};/* allow up to 256 DBDMA commands per xfer */#define MAX_DCMDS 256/* Wait 2s for disk to answer on IDE bus after * enable operation. * NOTE: There is at least one case I know of a disk that needs about 10sec * before anwering on the bus. I beleive we could add a kernel command * line arg to override this delay for such cases. */#define IDE_WAKEUP_DELAY_MS 2000static void pmac_ide_setup_dma(struct device_node *np, int ix);static int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive);static int pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr);static int pmac_ide_tune_chipset(ide_drive_t *drive, byte speed);static void pmac_ide_tuneproc(ide_drive_t *drive, byte pio);static void pmac_ide_selectproc(ide_drive_t *drive);#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */#ifdef CONFIG_PMAC_PBOOKstatic int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when);struct pmu_sleep_notifier idepmac_sleep_notifier = { idepmac_notify_sleep, SLEEP_LEVEL_BLOCK,};#endif /* CONFIG_PMAC_PBOOK */static intpmac_ide_find(ide_drive_t *drive){ ide_hwif_t *hwif = HWIF(drive); ide_ioreg_t base; int i; for (i=0; i<pmac_ide_count; i++) { base = pmac_ide[i].regbase; if (base && base == hwif->io_ports[0]) return i; } return -1;}/* * N.B. this can't be an initfunc, because the media-bay task can * call ide_[un]register at any time. */void pmac_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq){ int i, ix; if (data_port == 0) return; for (ix = 0; ix < MAX_HWIFS; ++ix) if (data_port == pmac_ide[ix].regbase) break; if (ix >= MAX_HWIFS) { /* Probably a PCI interface... */ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i) hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET; hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; return; } for (i = 0; i < 8; ++i) hw->io_ports[i] = data_port + i * 0x10; hw->io_ports[8] = data_port + 0x160; if (irq != NULL) *irq = pmac_ide[ix].irq; ide_hwifs[ix].tuneproc = pmac_ide_tuneproc; ide_hwifs[ix].selectproc = pmac_ide_selectproc; ide_hwifs[ix].speedproc = &pmac_ide_tune_chipset; if (pmac_ide[ix].dma_regs && pmac_ide[ix].dma_table) { ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc;#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO if (!noautodma) ide_hwifs[ix].autodma = 1;#endif }}#if 0/* This one could be later extended to handle CMD IDE and be used by some kind * of /proc interface. I want to be able to get the devicetree path of a block * device for yaboot configuration */struct device_node*pmac_ide_get_devnode(ide_drive_t *drive){ int i = pmac_ide_find(drive); if (i < 0) return NULL; return pmac_ide[i].node;}#endif/* Setup timings for the selected drive (master/slave). I still need to verify if this * is enough, I beleive selectproc will be called whenever an IDE command is started, * but... */static voidpmac_ide_selectproc(ide_drive_t *drive){ int i = pmac_ide_find(drive); if (i < 0) return; if (drive->select.all & 0x10) out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[1]); else out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[0]);}/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */#define SYSCLK_TICKS(t) (((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS)#define SYSCLK_TICKS_UDMA(t) (((t) + IDE_SYSCLK_ULTRA_PS - 1) / IDE_SYSCLK_ULTRA_PS)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(KERN_ERR "ide_pmac: wait_for_ready, error status: %x\n", stat); } return 1; } return 0;}/* Note: We don't use the generic routine here because some of Apple's * controller seem to be very sensitive about how things are done. * We should probably set the NIEN bit, but that's an example of thing * that can cause the controller to hang under some circumstances when * done on the media-bay CD-ROM during boot. We do get occasional * spurrious interrupts because of that. * --BenH */static intpmac_ide_do_setfeature(ide_drive_t *drive, byte command){ unsigned long flags; int result = 1; save_flags(flags); cli(); udelay(1); SELECT_DRIVE(HWIF(drive), drive); SELECT_MASK(HWIF(drive), drive, 0); udelay(1); if(wait_for_ready(drive)) { printk(KERN_ERR "pmac_ide_do_setfeature disk not ready before SET_FEATURE!\n"); goto out; } OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG); OUT_BYTE(command, IDE_NSECTOR_REG); OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG); udelay(1); result = wait_for_ready(drive); if (result) printk(KERN_ERR "pmac_ide_do_setfeature disk not ready after SET_FEATURE !\n");out: restore_flags(flags); return result;}/* Calculate PIO timings */static voidpmac_ide_tuneproc(ide_drive_t *drive, byte pio){ ide_pio_data_t d; int i; u32 *timings; int accessTicks, recTicks; i = pmac_ide_find(drive); if (i < 0) return; pio = ide_get_best_pio_mode(drive, pio, 4, &d); accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time); if (drive->select.all & 0x10) timings = &pmac_ide[i].timings[1]; else timings = &pmac_ide[i].timings[0]; if (pmac_ide[i].kind == controller_kl_ata4) { /* The "ata-4" IDE controller of Core99 machines */ accessTicks = SYSCLK_TICKS_UDMA(ide_pio_timings[pio].active_time * 1000); recTicks = SYSCLK_TICKS_UDMA(d.cycle_time * 1000) - accessTicks; *timings = ((*timings) & 0x1FFFFFC00) | accessTicks | (recTicks << 5); } else { /* The old "ata-3" IDE controller */ accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time); if (accessTicks < 4) accessTicks = 4; recTicks = SYSCLK_TICKS(d.cycle_time) - accessTicks - 4; if (recTicks < 1) recTicks = 1; *timings = ((*timings) & 0xFFFFFF800) | accessTicks | (recTicks << 5); }#ifdef IDE_PMAC_DEBUG printk(KERN_ERR "ide_pmac: Set PIO timing for mode %d, reg: 0x%08x\n", pio, *timings);#endif if (drive->select.all == IN_BYTE(IDE_SELECT_REG)) pmac_ide_selectproc(drive);}#ifdef CONFIG_BLK_DEV_IDEDMA_PMACstatic intset_timings_udma(int intf, u32 *timings, byte speed){ int cycleTime, accessTime; int rdyToPauseTicks, cycleTicks; if (pmac_ide[intf].kind != controller_kl_ata4) return 1; cycleTime = udma_timings[speed & 0xf].cycleTime; accessTime = udma_timings[speed & 0xf].accessTime; rdyToPauseTicks = SYSCLK_TICKS_UDMA(accessTime * 1000); cycleTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000); *timings = ((*timings) & 0xe00fffff) | ((cycleTicks << 1) | (rdyToPauseTicks << 5) | 1) << 20; return 0;}static intset_timings_mdma(int intf, u32 *timings, byte speed){ int cycleTime, accessTime; int accessTicks, recTicks; /* Calculate accesstime and cycle time */ cycleTime = mdma_timings[speed & 0xf].cycleTime; accessTime = mdma_timings[speed & 0xf].accessTime; if ((pmac_ide[intf].kind == controller_ohare) && (cycleTime < 150)) cycleTime = 150; /* For ata-4 controller */ if (pmac_ide[intf].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[intf].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; } return 0;}#endif /* #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC *//* You may notice we don't use this function on normal operation, * our, normal mdma function is supposed to be more precise */static intpmac_ide_tune_chipset (ide_drive_t *drive, byte speed){ int intf = pmac_ide_find(drive); int unit = (drive->select.all & 0x10) ? 1:0; int ret = 0; u32 *timings; if (intf < 0) return 1; timings = &pmac_ide[intf].timings[unit]; switch(speed) {#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC case XFER_UDMA_4: case XFER_UDMA_3: case XFER_UDMA_2: case XFER_UDMA_1: case XFER_UDMA_0: ret = set_timings_udma(intf, timings, speed); break; case XFER_MW_DMA_2: case XFER_MW_DMA_1: case XFER_MW_DMA_0: case XFER_SW_DMA_2: case XFER_SW_DMA_1: case XFER_SW_DMA_0: ret = set_timings_mdma(intf, timings, speed); break;#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ case XFER_PIO_4: case XFER_PIO_3: case XFER_PIO_2:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -