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 + -
显示快捷键?