sgiioc4.c
来自「linux-2.4.29操作系统的源码」· C语言 代码 · 共 892 行 · 第 1/2 页
C
892 行
/* * Copyright (c) 2003 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License * as published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public * License along with this program; if not, write the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/NoticeExplan */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/hdreg.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/mm.h>#include <linux/ioport.h>#include <linux/blkdev.h>#include <asm/io.h>#include "sgiioc4.h"extern int dma_timer_expiry(ide_drive_t * drive);#ifdef CONFIG_PROC_FSstatic u8 sgiioc4_proc;#endif /* CONFIG_PROC_FS */static int n_sgiioc4_devs ;static inline voidxide_delay(long ticks){ if (!ticks) return; current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(ticks);}static void __initsgiioc4_ide_setup_pci_device(struct pci_dev *dev, const char *name){ unsigned long base = 0, ctl = 0, dma_base = 0, irqport = 0; ide_hwif_t *hwif = NULL; int h = 0; /* Get the CmdBlk and CtrlBlk Base Registers */ base = pci_resource_start(dev, 0) + IOC4_CMD_OFFSET; ctl = pci_resource_start(dev, 0) + IOC4_CTRL_OFFSET; irqport = pci_resource_start(dev, 0) + IOC4_INTR_OFFSET; dma_base = pci_resource_start(dev, 0) + IOC4_DMA_OFFSET; for (h = 0; h < MAX_HWIFS; ++h) { hwif = &ide_hwifs[h]; /* Find an empty HWIF */ if (hwif->chipset == ide_unknown) break; } if (hwif->io_ports[IDE_DATA_OFFSET] != base) { /* Initialize the IO registers */ sgiioc4_init_hwif_ports(&hwif->hw, base, ctl, irqport); memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof (hwif->io_ports)); hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; } hwif->chipset = ide_pci; hwif->pci_dev = dev; hwif->channel = 0; /* Single Channel chip */ hwif->hw.ack_intr = &sgiioc4_checkirq; /* MultiFunction Chip */ /* Initializing chipset IRQ Registers */ hwif->OUTL(0x03, irqport + IOC4_INTR_SET * 4); (void) ide_init_sgiioc4(hwif); if (dma_base) ide_dma_sgiioc4(hwif, dma_base); else printk(KERN_INFO "%s: %s Bus-Master DMA disabled \n", hwif->name, name);}/* XXX Hack to ensure we can build this for generic kernels without * having all the SN2 code sync'd and merged. For now this is * acceptable but this should be resolved ASAP. PV#: 896401 */pciio_endian_t __attribute__((weak)) snia_pciio_endian_set(struct pci_dev *pci_dev, pciio_endian_t device_end, pciio_endian_t desired_end);static unsigned int __initpci_init_sgiioc4(struct pci_dev *dev, const char *name){ if (pci_enable_device(dev)) { printk(KERN_INFO "Failed to enable device %s at slot %s \n",name,dev->slot_name); return 1; } pci_set_master(dev); /* Enable Byte Swapping in the PIC... */ if (snia_pciio_endian_set) { /* ... if the symbol exists (hack to get this to build * for SuSE before we merge the SN2 code */ snia_pciio_endian_set(dev, PCIDMA_ENDIAN_LITTLE, PCIDMA_ENDIAN_BIG); } else { printk(KERN_INFO "Failed to set endianness for device %s at slot %s \n", name, dev->slot_name); return 1; }#ifdef CONFIG_PROC_FS sgiioc4_devs[n_sgiioc4_devs++] = dev; if (!sgiioc4_proc) { sgiioc4_proc = 1; ide_pci_register_host_proc(&sgiioc4_procs[0]); }#endif sgiioc4_ide_setup_pci_device(dev, name); return 0;}static voidsgiioc4_init_hwif_ports(hw_regs_t * hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, ide_ioreg_t irq_port){ ide_ioreg_t reg = data_port; int i; for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) hw->io_ports[i] = reg + i * 4; /* Registers are word (32 bit) aligned */ if (ctrl_port) hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; if (irq_port) hw->io_ports[IDE_IRQ_OFFSET] = irq_port;}static voidsgiioc4_resetproc(ide_drive_t * drive){ sgiioc4_ide_dma_end(drive); sgiioc4_clearirq(drive);}static voidsgiioc4_maskproc(ide_drive_t * drive, int mask){ ide_hwif_t *hwif = HWIF(drive); hwif->OUTB(mask ? (drive->ctl | 2) : (drive->ctl & ~2), IDE_CONTROL_REG);}static void __initide_init_sgiioc4(ide_hwif_t * hwif){ hwif->autodma = 1; hwif->index = 0; /* Channel 0 */ hwif->channel = 0; hwif->atapi_dma = 1; hwif->ultra_mask = 0x0; /* Disable Ultra DMA */ hwif->mwdma_mask = 0x2; /* Multimode-2 DMA */ hwif->swdma_mask = 0x2; hwif->identify = NULL; hwif->tuneproc = NULL; /* Sets timing for PIO mode */ hwif->speedproc = NULL; /* Sets timing for DMA &/or PIO modes */ hwif->selectproc = NULL; /* Use the default selection routine to select drive */ hwif->reset_poll = NULL; /* No HBA specific reset_poll needed */ hwif->pre_reset = NULL; /* No HBA specific pre_set needed */ hwif->resetproc = &sgiioc4_resetproc; /* Reset the IOC4 DMA engine, clear interrupts etc */ hwif->intrproc = NULL; /* Enable or Disable interrupt from drive */ hwif->maskproc = &sgiioc4_maskproc; /* Mask on/off NIEN register */ hwif->quirkproc = NULL; hwif->busproc = NULL; hwif->ide_dma_read = &sgiioc4_ide_dma_read; hwif->ide_dma_write = &sgiioc4_ide_dma_write; hwif->ide_dma_begin = &sgiioc4_ide_dma_begin; hwif->ide_dma_end = &sgiioc4_ide_dma_end; hwif->ide_dma_check = &sgiioc4_ide_dma_check; hwif->ide_dma_on = &sgiioc4_ide_dma_on; hwif->ide_dma_off = &sgiioc4_ide_dma_off; hwif->ide_dma_off_quietly = &sgiioc4_ide_dma_off_quietly; hwif->ide_dma_test_irq = &sgiioc4_ide_dma_test_irq; hwif->ide_dma_host_on = &sgiioc4_ide_dma_host_on; hwif->ide_dma_host_off = &sgiioc4_ide_dma_host_off; hwif->ide_dma_bad_drive = &__ide_dma_bad_drive; hwif->ide_dma_good_drive = &__ide_dma_good_drive; hwif->ide_dma_count = &sgiioc4_ide_dma_count; hwif->ide_dma_verbose = &sgiioc4_ide_dma_verbose; hwif->ide_dma_retune = &__ide_dma_retune; hwif->ide_dma_lostirq = &sgiioc4_ide_dma_lostirq; hwif->ide_dma_timeout = &sgiioc4_ide_dma_timeout; hwif->INB = &sgiioc4_INB;}static intsgiioc4_ide_dma_read(ide_drive_t * drive){ struct request *rq = HWGROUP(drive)->rq; unsigned int count = 0; if (!(count = sgiioc4_build_dma_table(drive, rq, PCI_DMA_FROMDEVICE))) { /* try PIO instead of DMA */ return 1; } /* Writes FROM the IOC4 TO Main Memory */ sgiioc4_configure_for_dma(IOC4_DMA_WRITE, drive); return 0;}static intsgiioc4_ide_dma_write(ide_drive_t * drive){ struct request *rq = HWGROUP(drive)->rq; unsigned int count = 0; if (!(count = sgiioc4_build_dma_table(drive, rq, PCI_DMA_TODEVICE))) { /* try PIO instead of DMA */ return 1; } sgiioc4_configure_for_dma(IOC4_DMA_READ, drive); /* Writes TO the IOC4 FROM Main Memory */ return 0;}static intsgiioc4_ide_dma_begin(ide_drive_t * drive){ ide_hwif_t *hwif = HWIF(drive); unsigned int reg = hwif->INL(hwif->dma_base + IOC4_DMA_CTRL * 4); unsigned int temp_reg = reg | IOC4_S_DMA_START; hwif->OUTL(temp_reg, hwif->dma_base + IOC4_DMA_CTRL * 4); return 0;}/* Stops the IOC4 DMA Engine */static intsgiioc4_ide_dma_end(ide_drive_t * drive){ u32 ioc4_dma, bc_dev, bc_mem, num, valid = 0, cnt = 0; ide_hwif_t *hwif = HWIF(drive); uint64_t dma_base = hwif->dma_base; int dma_stat = 0, count; unsigned long *ending_dma = (unsigned long *) hwif->dma_base2; hwif->OUTL(IOC4_S_DMA_STOP, dma_base + IOC4_DMA_CTRL * 4); count = 0; do { xide_delay(count); ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); count += 10; } while ((ioc4_dma & IOC4_S_DMA_STOP) && (count < 100)); if (ioc4_dma & IOC4_S_DMA_STOP) { printk(KERN_ERR "sgiioc4_stopdma(%s): IOC4 DMA STOP bit is still 1 : ioc4_dma_reg 0x%x\n", drive->name, ioc4_dma); dma_stat = 1; } if (ending_dma) { do { for (num = 0; num < 16; num++) { if (ending_dma[num] & (~0ul)) { valid = 1; break; } } xide_delay(cnt); } while ((cnt++ < 100) && (!valid)); } if (!valid) printk(KERN_INFO "sgiioc4_ide_dma_end(%s) : Stale DMA Data in Memory\n", drive->name); bc_dev = hwif->INL(dma_base + IOC4_BC_DEV * 4); bc_mem = hwif->INL(dma_base + IOC4_BC_MEM * 4); if ((bc_dev & 0x01FF) || (bc_mem & 0x1FF)) { if (bc_dev > bc_mem + 8) { printk(KERN_ERR "sgiioc4_ide_dma_end(%s) : WARNING!!! byte_count_at_dev %d != byte_count_at_mem %d\n", drive->name, bc_dev, bc_mem); } } drive->waiting_for_dma = 0; ide_destroy_dmatable(drive); return dma_stat;}static intsgiioc4_ide_dma_check(ide_drive_t * drive){ if (ide_config_drive_speed(drive,XFER_MW_DMA_2)!=0) { printk(KERN_INFO "Couldnot set %s in Multimode-2 DMA mode | Drive %s using PIO instead\n", drive->name, drive->name); drive->using_dma = 0; } else drive->using_dma = 1; return 0;}static intsgiioc4_ide_dma_on(ide_drive_t * drive){ drive->using_dma = 1; return HWIF(drive)->ide_dma_host_on(drive);}static intsgiioc4_ide_dma_off(ide_drive_t * drive){ printk(KERN_INFO "%s: DMA disabled\n", drive->name); return HWIF(drive)->ide_dma_off_quietly(drive);}static intsgiioc4_ide_dma_off_quietly(ide_drive_t * drive){ drive->using_dma = 0; return HWIF(drive)->ide_dma_host_off(drive);}/* returns 1 if dma irq issued, 0 otherwise */static intsgiioc4_ide_dma_test_irq(ide_drive_t * drive){ return sgiioc4_checkirq(HWIF(drive));}static intsgiioc4_ide_dma_host_on(ide_drive_t * drive){ if (drive->using_dma) return 0; return 1;}static intsgiioc4_ide_dma_host_off(ide_drive_t * drive){ sgiioc4_clearirq(drive); return 0;}static intsgiioc4_ide_dma_count(ide_drive_t * drive){ return HWIF(drive)->ide_dma_begin(drive);}static intsgiioc4_ide_dma_verbose(ide_drive_t * drive){ if (drive->using_dma == 1) printk(", UDMA(16)"); else printk(", PIO"); return 1;}static intsgiioc4_ide_dma_lostirq(ide_drive_t * drive){ HWIF(drive)->resetproc(drive); return __ide_dma_lostirq(drive);}static intsgiioc4_ide_dma_timeout(ide_drive_t * drive){ printk(KERN_ERR "%s: timeout waiting for DMA\n", drive->name); if (HWIF(drive)->ide_dma_test_irq(drive)) return 0; return HWIF(drive)->ide_dma_end(drive);}static u8sgiioc4_INB(unsigned long port){ u8 reg = (u8) inb(port); if ((port & 0xFFF) == 0x11C) { /* Status register of IOC4 */ if (reg & 0x51) { /* Not busy...check for interrupt */ unsigned long other_ir = port - 0x110; unsigned int intr_reg = (u32) inl(other_ir); if (intr_reg & 0x03) { /* Clear the Interrupt, Error bits on the IOC4 */ outl(0x03, other_ir); intr_reg = (u32) inl(other_ir); } } } return reg;}/* Creates a dma map for the scatter-gather list entries */static void __initide_dma_sgiioc4(ide_hwif_t * hwif, unsigned long dma_base){ int num_ports = sizeof (ioc4_dma_regs_t); printk(KERN_INFO "%s: BM-DMA at 0x%04lx-0x%04lx\n", hwif->name, dma_base, dma_base + num_ports - 1); if (!request_region(dma_base, num_ports, hwif->name)) { printk(KERN_ERR "ide_dma_sgiioc4(%s) -- Error, Port Addresses 0x%p to 0x%p ALREADY in use\n",
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?