Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S270383AbTGRXD1 (ORCPT ); Fri, 18 Jul 2003 19:03:27 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S270326AbTGRXD1 (ORCPT ); Fri, 18 Jul 2003 19:03:27 -0400 Received: from tolkor.SGI.COM ([198.149.18.6]:12760 "EHLO tolkor.sgi.com") by vger.kernel.org with ESMTP id S271825AbTGRXAG (ORCPT ); Fri, 18 Jul 2003 19:00:06 -0400 Message-ID: <3F187E67.227651F@sgi.com> Date: Fri, 18 Jul 2003 16:10:31 -0700 From: Aniket Malatpure Organization: Silicon Graphics Inc. X-Mailer: Mozilla 4.72 [en] (X11; U; Linux 2.4.21-rc1 i686) X-Accept-Language: en MIME-Version: 1.0 To: marcelo@conectiva.com.br, alan@redhat.com, wildos@sgi.com, linux-kernel@vger.kernel.org, jgarzik@pobox.com CC: aniket@sgi.com Subject: [PATCH] (2.4.22-pre6 BK) new & changed (IDE) driver: SGI IOC4] References: <20030718020431.GA19018@taniwha.engr.sgi.com> Content-Type: multipart/mixed; boundary="------------DB59DE08B2C77343F78DDFBD" Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 53488 Lines: 1654 This is a multi-part message in MIME format. --------------DB59DE08B2C77343F78DDFBD Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Hi This patch (against the 2.4 BK tree as of yesterday evening) adds support for SGI IOC4 IDE driver as found in SN2/Altix systems (http://sgi.com/altix for more details). No generic/non-sn2 code paths should be affected by this. Documentation/Configure.help | 6 MAINTAINERS | 5 drivers/ide/Config.in | 1 drivers/ide/pci/Makefile | 1 drivers/ide/pci/sgiioc4.c | 922 +++++++++++++++++++++++++++++++++++ drivers/ide/pci/sgiioc4.h | 177 ++++++ include/linux/pci_ids.h | 1 7 files changed, 1113 insertions The driver has been changed in various places in accordance with the suggestions on this list. A detailed explanation of which suggestions could and couldnot be implemented follows : > > +static inline void > > +IDE_DBG(int debuglevel, char *format, ...) > > +{ > > + va_list ap; > > + char tempstr[256]; > > + > > + if (sgiioc4_debug >= debuglevel) { > > + va_start(ap, format); > > + vsprintf(tempstr, format, ap); > > + printk(tempstr); > > + va_end(ap); > > + } > > +} > > I'm impressed that varargs static inline works. (not complaining, just > shocked :)) IDE_DBG() has been removed entirely from this version. > > +static inline void > > +xide_delay(long ticks) > > +{ > > + if (!ticks) > > + return; > > + > > + current->state = TASK_INTERRUPTIBLE; > > + schedule_timeout(ticks); > > + return; > > +} > > ITYM TASK_UNINTERRUPTIBLE, because you definitely don't handle being > interrupted... :) current->state has been set to TASK_UNINTERRUPTIBLE as has been suggested. > > +unsigned int __init > > +pci_init_sgiioc4(struct pci_dev *dev, const char *name) > > +{ > > + unsigned int class_rev; > > + > > + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); > > + class_rev &= 0xff; > > this appears to be dead code. This code has been removed. > > + pci_enable_device(dev); > > you _definitely_ want to check for errors. Error check has been included. > > +void > > +sgiioc4_maskproc(ide_drive_t * drive, int mask) > > +{ > > + ide_hwif_t *hwif = HWIF(drive); > > + if (IDE_CONTROL_REG) > > + hwif->OUTB(mask ? (drive->ctl | 2) : (drive->ctl & ~2), > > + IDE_CONTROL_REG); > > +} > > I'm pretty sure you have a control reg :) Test should not be necessary. This test has been removed. > > +void __init > > +ide_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; > > style: Under Linux we normally don't explicitly set to NULL. > > > +static int > > +sgiioc4_ide_dma_lostirq(ide_drive_t * drive) > > +{ > > + if (HWIF(drive)->resetproc != NULL) > > + HWIF(drive)->resetproc(drive); > > + return __ide_dma_lostirq(drive); > > +} > > don't you know if you have a resetproc? :) Removed the if condition in the new patch. > > +static u8 > > +sgiioc4_INB(unsigned long port) > > +{ > > + u8 reg = (u8) inb(port); > > + > > + if ((port & 0xFFF) == 0x11C) { /* Status register of IOC4 */ > > + if (reg & 0x50) { /* Not busy...check for interrupt */ > > hmmm, you probably want OK_STAT macro invocation that's used throughout > the IDE code. Here we just want to verify that the status is "not busy". The status could be GOOD (0x50) or Error (0x51) as long as its not busy. This check has been changed as : + if ((port & 0xFFF) == 0x11C) { /* Status register of IOC4 */ + if (reg & 0x51) { /* Not busy...check for interrupt */ in the new patch. > > +/* Creates a dma map for the scatter-gather list entries */ > > +void __init > > +ide_dma_sgiioc4(ide_hwif_t * hwif, unsigned long dma_base) > > +{ > > + int num_ports = sizeof (ioc4_dma_regs_t); > > + > > + IDE_DBG(0, "%s: BM-DMA at 0x%04lx-0x%04lx\n", hwif->name, dma_base, > > + dma_base + num_ports - 1); > > + if (check_region(dma_base, num_ports)) { > > + IDE_DBG(0, > > + "ide_dma_sgiioc4(%s) -- Error, Port Addresses 0x%p to 0x%p ALREADY in use\n", > > + hwif->name, dma_base, dma_base + num_ports - 1); > > + return; > > + } > > + request_region(dma_base, num_ports, hwif->name); > > deprecated from the get-go... eliminate check_region, and test > request_region return value. Check region has been eliminated and the code now tests the request_region value. > > + hwif->dma_base = dma_base; > > + hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev, > > + IOC4_PRD_ENTRIES * IOC4_PRD_BYTES, /* 1 Page */ > > + &hwif->dmatable_dma); > > + if (hwif->dmatable_cpu == NULL) > > + goto dma_alloc_failure; > > + > > + hwif->sg_table = kmalloc(sizeof (struct scatterlist) * IOC4_PRD_ENTRIES, > > + GFP_KERNEL); > > + if (hwif->sg_table == NULL) { > > + pci_free_consistent(hwif->pci_dev, > > + IOC4_PRD_ENTRIES * IOC4_PRD_BYTES, > > + hwif->dmatable_cpu, hwif->dmatable_dma); > > + goto dma_alloc_failure; > > + } > > + > > + > > + hwif->dma_base2 = (unsigned long) pci_alloc_consistent(hwif->pci_dev, > > + IOC4_IDE_CACHELINE_SIZE, > > + (dma_addr_t*)&(hwif->dma_status)); > > + if (!(hwif->dma_base2)) { > > + IDE_DBG(0, > > + "ide_dma_sgiioc4(%s) - Couldnot allocate map for Ending DMA \n", > > + hwif->name); > > + } > > no error handling for this last alloc, unlike the previous two? Error handling has been added for the last alloc as well. > > +/* Sets the ATAPI Drive to do DMA in Multimode-2 */ > > +void > > +sgiioc4_config_drive_for_dma(ide_drive_t * drive) > > +{ > > + u8 status_val; > > + ide_hwif_t *hwif = HWIF(drive); > > + > > + hwif->OUTB(drive->select.all, IDE_SELECT_REG); > > + hwif->OUTB(drive->ctl | 2, IDE_CONTROL_REG); > > + hwif->OUTB(0x0, IDE_DATA_REG); > > + hwif->OUTB(0x0, IDE_SECTOR_REG); > > + hwif->OUTB(0x0, IDE_LCYL_REG); > > + hwif->OUTB(0x0, IDE_HCYL_REG); > > + hwif->OUTB(0x22, IDE_NSECTOR_REG); > > + hwif->OUTB(0x03, IDE_FEATURE_REG); > > + hwif->OUTB(0xEF, IDE_COMMAND_REG); > > + > > + do { > > + xide_delay(HZ); > > + status_val = hwif->INB(IDE_STATUS_REG); > > + } while (status_val & 0x80); > > + > > + if (status_val == 0x50) { > > + IDE_DBG(1, "Successfully set %s in Multimode-2 DMA mode \n", > > + drive->name); > > + drive->using_dma = 1; > > + } else { /* Go to PIO mode if this occurs */ > > + IDE_DBG(0, > > + "Couldnot set %s in Multimode-2 DMA mode | status 0x%x | Drive %s using PIO \n", > > + drive->name, status_val, drive->name); > > + drive->using_dma = 0; > > + } > > + return; > > +} > > isn't there a standard function to do this? > This is the standard method of setting device transfer mode. This function has been removed and the standard function has been used as : + 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; > > > +/* Creates the scatter gather list, DMA Table */ > > +unsigned int > > +sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir) > > +{ > > + ide_hwif_t *hwif = HWIF(drive); > > + unsigned int *table = hwif->dmatable_cpu; > > + unsigned int count = 0, i = 1; > > + struct scatterlist *sg; > > + > > + if (rq->cmd == IDE_DRIVE_TASKFILE) > > + hwif->sg_nents = i = sgiioc4_ide_raw_build_sglist(hwif, rq); > > + else > > + hwif->sg_nents = i = sgiioc4_ide_build_sglist(hwif, rq, ddir); > > + if (!i) > > + return 0; /* sglist of length Zero */ > > + > > + sg = hwif->sg_table; > > + while (i && sg_dma_len(sg)) { > > + dma_addr_t cur_addr; > > + int cur_len; > > + cur_addr = sg_dma_address(sg); > > + cur_len = sg_dma_len(sg); > > + > > + while (cur_len) { > > + if (count++ >= IOC4_PRD_ENTRIES) { > > + printk("%s: DMA table too small\n", > > + drive->name); > > + goto use_pio_instead; > > + } else { > > + uint32_t xcount, bcount = > > + 0x10000 - (cur_addr & 0xffff); > > + > > + if (bcount > cur_len) > > + bcount = cur_len; > > + > > + /* put the addr, length in the IOC4 dma-table format */ > > + *table = 0x0; > > + table++; > > + *table = cpu_to_be32(cur_addr); > > + table++; > > + *table = 0x0; > > + table++; > > + > > + xcount = bcount & 0xffff; > > + *table = cpu_to_be32(xcount); > > + table++; > > + > > + cur_addr += bcount; > > + cur_len -= bcount; > > + } > > + } > > + > > + sg++; > > + i--; > > + } > > + > > + if (count) { > > + table--; > > + *table |= cpu_to_be32(0x80000000); > > + return count; > > + } > > + use_pio_instead: > > + pci_unmap_sg(hwif->pci_dev, > > + hwif->sg_table, hwif->sg_nents, hwif->sg_dma_direction); > > + hwif->sg_dma_active = 0; > > + return 0; /* revert to PIO for this request */ > > +} > > hrm... did all this code really need duplicating from ide-dma.c? The IOC4 doesnot support the generic IDE Multiword-2 DMA scatter-gather list format. IOC4 Features : a) Big Endian Card b) Scatter Gather List Entry is 128 bits. Bits 0-63 are present to support 64 bit addressing in the future. Currently only 32 bit addresses (in bit 0-31) are supported and hence bits 32-63 are 0. Bits 64-80 have the length of the data being transferred, bit 95 has the End of List marker(EOL) and bit 96-127 are 0. i.e while the generic IDE scatter-gather list format is a) Little Endian b) Of this form : ----------------------------------- | Address (bits 0-31) | ----------------------------------- |EOL| 0 | Data Length (bits 32-48)| ----------------------------------- The IOC4 scatter gather list format is a) Big Endian b) Of this form : --------------------------------------------------------------------------- | Bits 63-32 : Resv for 64 bit address | Bits 31-0 : 32 bit address | --------------------------------------------------------------------------- | Bit 127-96 : 0 |EOL| 0 | Data Length (bits 64-96) | --------------------------------------------------------------------------- Hence we cannot use the generic code to generate the IOC4's scatter-gather list. > > +static int > > +sgiioc4_clearirq(ide_drive_t * drive) > > +{ > > + u32 intr_reg; > > + ide_hwif_t *hwif = HWIF(drive); > > + ide_ioreg_t other_ir = > > + hwif->io_ports[IDE_IRQ_OFFSET] + (IOC4_INTR_REG << 2); > > + > > + /* Code to check for PCI error conditions */ > > + intr_reg = hwif->INL(other_ir); > > + if (intr_reg & 0x03) { > > + /* Valid IOC4-IDE interrupt */ > > + u8 stat = hwif->INB(IDE_STATUS_REG); > > + int count = 0; > > + do { > > + xide_delay(count); > > + stat = hwif->INB(IDE_STATUS_REG); /* Removes Interrupt from IDE Device */ > > + } while ((stat & 0x80) && (count++ < 1024)); > > + > > + if (intr_reg & 0x02) { > > + /* Error when transferring DMA data on PCI bus */ > > + uint32_t pci_err_addr_low, pci_err_addr_high, > > + pci_stat_cmd_reg; > > + > > + pci_err_addr_low = > > + hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET]); > > + pci_err_addr_high = > > + hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET] + 4); > > + pci_read_config_dword(hwif->pci_dev, PCI_COMMAND, > > + &pci_stat_cmd_reg); > > + IDE_DBG(0, > > + "sgiioc4_clearirq(%s) : PCI Bus Error when doing DMA : status-cmd reg is 0x%x \n", > > + drive->name, pci_stat_cmd_reg); > > + IDE_DBG(0, > > + "sgiioc4_clearirq(%s) : PCI Error Address is 0x%x%x \n", > > + drive->name, pci_err_addr_high, > > + pci_err_addr_low); > > + /* Clear the PCI Error indicator */ > > + pci_write_config_dword(hwif->pci_dev, PCI_COMMAND, > > + 0x00000146); > > + } > > + hwif->OUTL(0x03, other_ir); /* Clear the Interrupt, Error bits on the IOC4 */ > > + > > + intr_reg = hwif->INL(other_ir); > > + } > > + return intr_reg; > > +} > > nice... this code is actually applicable to several other PCI host > controllers, too. Thanks. > > +/* Weeds out non-IDE interrupts to the IOC4 */ > > +#define ide_ack_intr(hwif) ((hwif)->hw.ack_intr ? (hwif)->hw.ack_intr(hwif) : 1) > > does this deal with shared interrupts too? Yes. The IOC4 is a single PCI device having multiple independent functions. It incorporates : a) 4 Serial Ports b) Single Channel IDE c) keyboard, Mouse functions All the above functions share the same interrupt line (but have an interrupt register with different bits set depending upon which function's device has interrupted). The above code is used to return from the ide_intr() function without doing anything, since the interrupt is not for the IDE device. The IOC4 IDE part is different from most generic IDE cards in the following aspects : a) Big Endian PCI device b) All register offsets are from a single BAR register c) Different scattergather list format d) The interrupt from the card still stands (even after the interrupt from the ide device behind the card goes away). This interrupt from the card has to be explicitly turned off by writing a bit in 1 particular register. Please apply this patch if it seems satisfactory. Thanks Aniket --------------DB59DE08B2C77343F78DDFBD Content-Type: text/plain; charset=us-ascii; name="ide-new.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="ide-new.patch" # This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.1016 -> 1.1017 # drivers/ide/pci/Makefile 1.4 -> 1.5 # include/linux/pci_ids.h 1.66 -> 1.67 # MAINTAINERS 1.102 -> 1.103 # drivers/ide/Config.in 1.34 -> 1.35 # Documentation/Configure.help 1.171 -> 1.172 # (new) -> 1.1 drivers/ide/pci/sgiioc4.h # (new) -> 1.2 drivers/ide/pci/sgiioc4.c # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 03/07/17 cw@taniwha.engr.sgi.com 1.1017 # foo # -------------------------------------------- # diff -Nru a/Documentation/Configure.help b/Documentation/Configure.help --- a/Documentation/Configure.help Thu Jul 17 17:43:20 2003 +++ b/Documentation/Configure.help Thu Jul 17 17:43:20 2003 @@ -1203,6 +1203,12 @@ This driver adds PIO/(U)DMA support for the ServerWorks OSB4/CSB5 chipsets. +SGI IOC4 chipset support +CONFIG_BLK_DEV_SGIIOC4 + This driver adds PIO & MultiMode DMA-2 support for the SGI IOC4 + chipset. Please say Y here, if you have an Altix System from + Silicon Graphics Inc. + Intel PIIXn chipsets support CONFIG_BLK_DEV_PIIX This driver adds PIO mode setting and tuning for all PIIX IDE diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Thu Jul 17 17:43:20 2003 +++ b/MAINTAINERS Thu Jul 17 17:43:20 2003 @@ -950,6 +950,11 @@ L: linux-mips@linux-mips.org S: Maintained +IOC4 IDE DRIVER +P: Aniket Malatpure +M: aniket@sgi.com +S: Maintained + IP MASQUERADING: P: Juanjo Ciarlante M: jjciarla@raiz.uncu.edu.ar diff -Nru a/drivers/ide/Config.in b/drivers/ide/Config.in --- a/drivers/ide/Config.in Thu Jul 17 17:43:20 2003 +++ b/drivers/ide/Config.in Thu Jul 17 17:43:20 2003 @@ -71,6 +71,7 @@ dep_tristate ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_X86 dep_tristate ' SCx200 chipset support' CONFIG_BLK_DEV_SC1200 $CONFIG_BLK_DEV_IDEDMA_PCI dep_tristate ' ServerWorks OSB4/CSB5/CSB6 chipsets support' CONFIG_BLK_DEV_SVWKS $CONFIG_BLK_DEV_IDEDMA_PCI + dep_tristate ' SGI IOC4 chipset support' CONFIG_BLK_DEV_SGIIOC4 $CONFIG_BLK_DEV_IDEDMA_PCI dep_tristate ' Silicon Image chipset support' CONFIG_BLK_DEV_SIIMAGE $CONFIG_BLK_DEV_IDEDMA_PCI dep_tristate ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86 dep_tristate ' SLC90E66 chipset support' CONFIG_BLK_DEV_SLC90E66 $CONFIG_BLK_DEV_IDEDMA_PCI diff -Nru a/drivers/ide/pci/Makefile b/drivers/ide/pci/Makefile --- a/drivers/ide/pci/Makefile Thu Jul 17 17:43:20 2003 +++ b/drivers/ide/pci/Makefile Thu Jul 17 17:43:20 2003 @@ -25,6 +25,7 @@ obj-$(CONFIG_BLK_DEV_RZ1000) += rz1000.o obj-$(CONFIG_BLK_DEV_SVWKS) += serverworks.o obj-$(CONFIG_BLK_DEV_SC1200) += sc1200.o +obj-$(CONFIG_BLK_DEV_SGIIOC4) += sgiioc4.o obj-$(CONFIG_BLK_DEV_SIIMAGE) += siimage.o obj-$(CONFIG_BLK_DEV_SIS5513) += sis5513.o obj-$(CONFIG_BLK_DEV_SL82C105) += sl82c105.o diff -Nru a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/pci/sgiioc4.c Thu Jul 17 17:43:20 2003 @@ -0,0 +1,922 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sgiioc4.h" + +extern int dma_timer_expiry(ide_drive_t * drive); + +#ifdef CONFIG_PROC_FS +static u8 sgiioc4_proc = 0; +#endif /* CONFIG_PROC_FS */ + +static int n_sgiioc4_devs = 0; + +#define SGIIOC4_HD_SUPPORT 0 + +static inline void +xide_delay(long ticks) +{ + if (!ticks) + return; + + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(ticks); +} + +static void __init +sgiioc4_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); +} + +static unsigned int __init +pci_init_sgiioc4(struct pci_dev *dev, const char *name) +{ + extern pciio_endian_t snia_pciio_endian_set(struct pci_dev *pci_dev, + pciio_endian_t device_end, + pciio_endian_t desired_end); + + 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); +#if defined(CONFIG_IA64_SGI_SN2) || defined(CONFIG_IA64_GENERIC) + /* Enable Byte Swapping in the PIC */ + snia_pciio_endian_set(dev, PCIDMA_ENDIAN_LITTLE, PCIDMA_ENDIAN_BIG); +#endif + +#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 void +sgiioc4_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 void +sgiioc4_resetproc(ide_drive_t * drive) +{ + sgiioc4_ide_dma_end(drive); + sgiioc4_clearirq(drive); +} + +static void +sgiioc4_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 __init +ide_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 int +sgiioc4_ide_dma_read(ide_drive_t * drive) +{ + struct request *rq = HWGROUP(drive)->rq; + unsigned int count = 0; +#if SGIIOC4_HD_SUPPORT + task_ioreg_t command = WIN_NOP; + u8 lba48 = (drive->addressing == 1) ? 1 : 0; +#endif /* SGIIOC4_HD_SUPPORT */ + + 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); + +#if SGIIOC4_HD_SUPPORT + if (drive->media != ide_disk) /* Command will be issued by the cdrom.c layer */ + return 0; + + /* Hard Disks not supported on IOC4 as yet (05/22/2003) ... this code will be useful when Disks are supported */ + 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]; + } + + /* issue cmd to drive */ + ide_execute_command(drive, command, &ide_dma_intr, 2 * WAIT_CMD, + dma_timer_expiry); + return HWIF(drive)->ide_dma_count(drive); +#else /* SGIIOC4_HD_SUPPORT */ + return 0; +#endif /* SGIIOC4_HD_SUPPORT */ +} + +static int +sgiioc4_ide_dma_write(ide_drive_t * drive) +{ + struct request *rq = HWGROUP(drive)->rq; + unsigned int count = 0; +#if SGIIOC4_HD_SUPPORT + task_ioreg_t command = WIN_NOP; + u8 lba48 = (drive->addressing == 1) ? 1 : 0; +#endif /* SGIIOC4_HD_SUPPORT */ + + 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 */ + +#if SGIIOC4_HD_SUPPORT + if (drive->media != ide_disk) /* Command will be issued by the cdrom.c layer */ + return 0; + + /* Hard Disks not supported on IOC4 as yet (05/22/2003) ... this code will be useful when Disks are supported */ + 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]; + } + + /* issue cmd to drive */ + ide_execute_command(drive, command, &ide_dma_intr, 2 * WAIT_CMD, + dma_timer_expiry); + return HWIF(drive)->ide_dma_count(drive); +#else /* SGIIOC4_HD_SUPPORT */ + return 0; +#endif /* SGIIOC4_HD_SUPPORT */ +} + +static int +sgiioc4_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 int +sgiioc4_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 int +sgiioc4_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 int +sgiioc4_ide_dma_on(ide_drive_t * drive) +{ + drive->using_dma = 1; + + return HWIF(drive)->ide_dma_host_on(drive); +} + +static int +sgiioc4_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 int +sgiioc4_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 int +sgiioc4_ide_dma_test_irq(ide_drive_t * drive) +{ + return sgiioc4_checkirq(HWIF(drive)); +} + +static int +sgiioc4_ide_dma_host_on(ide_drive_t * drive) +{ + if (drive->using_dma) + return 0; + + return 1; +} + +static int +sgiioc4_ide_dma_host_off(ide_drive_t * drive) +{ + sgiioc4_clearirq(drive); + + return 0; +} + +static int +sgiioc4_ide_dma_count(ide_drive_t * drive) +{ + return HWIF(drive)->ide_dma_begin(drive); +} + +static int +sgiioc4_ide_dma_verbose(ide_drive_t * drive) +{ + if (drive->using_dma == 1) + printk(", UDMA(16)"); + else + printk(", PIO"); + + return 1; +} + +static int +sgiioc4_ide_dma_lostirq(ide_drive_t * drive) +{ + HWIF(drive)->resetproc(drive); + + return __ide_dma_lostirq(drive); +} + +static int +sgiioc4_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 u8 +sgiioc4_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 __init +ide_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", + hwif->name, (void *)dma_base, (void *)dma_base + num_ports - 1); + return; + } + + hwif->dma_base = dma_base; + hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev, + IOC4_PRD_ENTRIES * IOC4_PRD_BYTES, /* 1 Page */ + &hwif->dmatable_dma); + + if (!hwif->dmatable_cpu) + goto dma_alloc_failure; + + hwif->sg_table = kmalloc(sizeof (struct scatterlist) * IOC4_PRD_ENTRIES, GFP_KERNEL); + + if (!hwif->sg_table) { + pci_free_consistent(hwif->pci_dev, IOC4_PRD_ENTRIES * IOC4_PRD_BYTES, hwif->dmatable_cpu, hwif->dmatable_dma); + goto dma_alloc_failure; + } + + hwif->dma_base2 = (unsigned long) pci_alloc_consistent(hwif->pci_dev, IOC4_IDE_CACHELINE_SIZE, + (dma_addr_t*)&(hwif->dma_status)); + + if (!hwif->dma_base2) { + pci_free_consistent(hwif->pci_dev, IOC4_PRD_ENTRIES * IOC4_PRD_BYTES, hwif->dmatable_cpu, hwif->dmatable_dma); + kfree(hwif->sg_table); + goto dma_alloc_failure; + } + + return; + + dma_alloc_failure: + printk(KERN_INFO "ide_dma_sgiioc4() -- Error! Unable to allocate DMA Maps for drive %s\n", hwif->name); + printk(KERN_INFO "Changing from DMA to PIO mode for Drive %s \n", hwif->name); + + /* Disable DMA because we couldnot allocate any DMA maps */ + hwif->autodma = 0; + hwif->atapi_dma = 0; +} + +/* Initializes the IOC4 DMA Engine */ +static void +sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive) +{ + u32 ioc4_dma; + int count; + ide_hwif_t *hwif = HWIF(drive); + uint64_t dma_base = hwif->dma_base; + uint32_t dma_addr, ending_dma_addr; + + ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + + if (ioc4_dma & IOC4_S_DMA_ACTIVE) { + printk(KERN_WARNING "sgiioc4_configure_for_dma(%s):Warning!! IOC4 DMA from previous transfer was still active\n", + drive->name); + 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_configure_for__dma(%s) : IOC4 Dma STOP bit is still 1\n", drive->name); + } + + ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + if (ioc4_dma & IOC4_S_DMA_ERROR) { + printk(KERN_WARNING "sgiioc4_configure_for__dma(%s) : Warning!! - DMA Error during Previous transfer | status 0x%x \n", + drive->name, ioc4_dma); + hwif->OUTL(IOC4_S_DMA_STOP, dma_base + IOC4_DMA_CTRL * 4); + count = 0; + do { + ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + xide_delay(count); + count += 10; + } while ((ioc4_dma & IOC4_S_DMA_STOP) && (count < 100)); + + if (ioc4_dma & IOC4_S_DMA_STOP) + printk(KERN_ERR "sgiioc4_configure_for__dma(%s) : IOC4 DMA STOP bit is still 1\n", drive->name); + } + + /* Address of the Scatter Gather List */ + dma_addr = cpu_to_le32(hwif->dmatable_dma); + hwif->OUTL(dma_addr, dma_base + IOC4_DMA_PTR_L * 4); + + /* Address of the Ending DMA */ + memset((unsigned int *) hwif->dma_base2, 0,IOC4_IDE_CACHELINE_SIZE); + ending_dma_addr = cpu_to_le32(hwif->dma_status); + hwif->OUTL(ending_dma_addr,dma_base + IOC4_DMA_END_ADDR * 4); + + hwif->OUTL(dma_direction, dma_base + IOC4_DMA_CTRL * 4); + drive->waiting_for_dma = 1; +} + +/* IOC4 Scatter Gather list Format */ +/* 128 Bit entries to support 64 bit addresses in the future */ +/* The Scatter Gather list Entry should be in the BIG-ENDIAN Format */ +/* --------------------------------------------------------------------------- */ +/* | Upper 32 bits - Zero | Lower 32 bits- address | */ +/* --------------------------------------------------------------------------- */ +/* | Upper 32 bits - Zero |EOL| 16 Bit Data Length | */ +/* --------------------------------------------------------------------------- */ + +/* Creates the scatter gather list, DMA Table */ +static unsigned int +sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int *table = hwif->dmatable_cpu; + unsigned int count = 0, i = 1; + struct scatterlist *sg; + + if (rq->cmd == IDE_DRIVE_TASKFILE) + hwif->sg_nents = i = sgiioc4_ide_raw_build_sglist(hwif, rq); + else + hwif->sg_nents = i = sgiioc4_ide_build_sglist(hwif, rq, ddir); + + if (!i) + return 0; /* sglist of length Zero */ + + sg = hwif->sg_table; + while (i && sg_dma_len(sg)) { + dma_addr_t cur_addr; + int cur_len; + cur_addr = sg_dma_address(sg); + cur_len = sg_dma_len(sg); + + while (cur_len) { + if (count++ >= IOC4_PRD_ENTRIES) { + printk(KERN_WARNING "%s: DMA table too small\n", drive->name); + goto use_pio_instead; + } else { + uint32_t xcount, bcount = 0x10000 - (cur_addr & 0xffff); + + if (bcount > cur_len) + bcount = cur_len; + + /* put the addr, length in the IOC4 dma-table format */ + *table = 0x0; + table++; + *table = cpu_to_be32(cur_addr); + table++; + *table = 0x0; + table++; + + xcount = bcount & 0xffff; + *table = cpu_to_be32(xcount); + table++; + + cur_addr += bcount; + cur_len -= bcount; + } + } + + sg++; + i--; + } + + if (count) { + table--; + *table |= cpu_to_be32(0x80000000); + return count; + } + + use_pio_instead: + pci_unmap_sg(hwif->pci_dev, hwif->sg_table, hwif->sg_nents, hwif->sg_dma_direction); + hwif->sg_dma_active = 0; + + return 0; /* revert to PIO for this request */ +} + +static int +sgiioc4_checkirq(ide_hwif_t * hwif) +{ + uint8_t intr_reg = hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET] + IOC4_INTR_REG * 4); + + if (intr_reg & 0x03) + return 1; + + return 0; +} + +static int +sgiioc4_clearirq(ide_drive_t * drive) +{ + u32 intr_reg; + ide_hwif_t *hwif = HWIF(drive); + ide_ioreg_t other_ir = hwif->io_ports[IDE_IRQ_OFFSET] + (IOC4_INTR_REG << 2); + + /* Code to check for PCI error conditions */ + intr_reg = hwif->INL(other_ir); + if (intr_reg & 0x03) { + /* Valid IOC4-IDE interrupt */ + u8 stat = hwif->INB(IDE_STATUS_REG); + int count = 0; + do { + xide_delay(count); + stat = hwif->INB(IDE_STATUS_REG); /* Removes Interrupt from IDE Device */ + } while ((stat & 0x80) && (count++ < 1024)); + + if (intr_reg & 0x02) { + /* Error when transferring DMA data on PCI bus */ + uint32_t pci_err_addr_low, pci_err_addr_high, pci_stat_cmd_reg; + + pci_err_addr_low = hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET]); + pci_err_addr_high = hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET] + 4); + pci_read_config_dword(hwif->pci_dev, PCI_COMMAND, &pci_stat_cmd_reg); + printk(KERN_ERR "sgiioc4_clearirq(%s) : PCI Bus Error when doing DMA : status-cmd reg is 0x%x \n", drive->name, pci_stat_cmd_reg); + printk(KERN_ERR "sgiioc4_clearirq(%s) : PCI Error Address is 0x%x%x \n", drive->name, pci_err_addr_high, pci_err_addr_low); + /* Clear the PCI Error indicator */ + pci_write_config_dword(hwif->pci_dev, PCI_COMMAND, 0x00000146); + } + + hwif->OUTL(0x03, other_ir); /* Clear the Interrupt, Error bits on the IOC4 */ + + intr_reg = hwif->INL(other_ir); + } + + return intr_reg; +} + +/** + * "Copied from drivers/ide/ide-dma.c" + * sgiioc4_ide_build_sglist - map IDE scatter gather for DMA I/O + * @hwif: the interface to build the DMA table for + * @rq: the request holding the sg list + * @ddir: data direction + * + * Perform the PCI mapping magic neccessary to access the source + * or target buffers of a request via PCI DMA. The lower layers + * of the kernel provide the neccessary cache management so that + * we can operate in a portable fashion. + * + * This code is identical to ide_build_sglist in ide-dma.c + * however that it not exported and even if it were would create + * dependancy problems for modular drivers. + */ +static int +sgiioc4_ide_build_sglist(ide_hwif_t * hwif, struct request *rq, int ddir) +{ + struct buffer_head *bh; + struct scatterlist *sg = hwif->sg_table; + unsigned long lastdataend = ~0UL; + int nents = 0; + + if (hwif->sg_dma_active) + BUG(); + + bh = rq->bh; + do { + int contig = 0; + + if (bh->b_page) { + if (bh_phys(bh) == lastdataend) + contig = 1; + } else { + if ((unsigned long) bh->b_data == lastdataend) + contig = 1; + } + + if (contig) { + sg[nents - 1].length += bh->b_size; + lastdataend += bh->b_size; + continue; + } + + if (nents >= PRD_ENTRIES) + return 0; + + memset(&sg[nents], 0, sizeof (*sg)); + + if (bh->b_page) { + sg[nents].page = bh->b_page; + sg[nents].offset = bh_offset(bh); + lastdataend = bh_phys(bh) + bh->b_size; + } else { + if ((unsigned long) bh->b_data < PAGE_SIZE) + BUG(); + + sg[nents].address = bh->b_data; + lastdataend = (unsigned long) bh->b_data + bh->b_size; + } + + sg[nents].length = bh->b_size; + nents++; + } while ((bh = bh->b_reqnext) != NULL); + + if (nents == 0) + BUG(); + + hwif->sg_dma_direction = ddir; + return pci_map_sg(hwif->pci_dev, sg, nents, ddir); +} + +/** + * Copied from drivers/ide/ide-dma.c + * sgiioc4_ide_raw_build_sglist - map IDE scatter gather for DMA + * @hwif: the interface to build the DMA table for + * @rq: the request holding the sg list + * + * Perform the PCI mapping magic neccessary to access the source or + * target buffers of a taskfile request via PCI DMA. The lower layers + * of the kernel provide the neccessary cache management so that we can + * operate in a portable fashion + * + * This code is identical to ide_raw_build_sglist in ide-dma.c + * however that it not exported and even if it were would create + * dependancy problems for modular drivers. + */ +static int +sgiioc4_ide_raw_build_sglist(ide_hwif_t * hwif, struct request *rq) +{ + struct scatterlist *sg = hwif->sg_table; + int nents = 0; + ide_task_t *args = rq->special; + u8 *virt_addr = rq->buffer; + int sector_count = rq->nr_sectors; + + if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE) + hwif->sg_dma_direction = PCI_DMA_TODEVICE; + else + hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; +#if 1 + if (sector_count > 128) { + memset(&sg[nents], 0, sizeof (*sg)); + sg[nents].address = virt_addr; + sg[nents].length = 128 * SECTOR_SIZE; + nents++; + virt_addr = virt_addr + (128 * SECTOR_SIZE); + sector_count -= 128; + } + memset(&sg[nents], 0, sizeof (*sg)); + sg[nents].address = virt_addr; + sg[nents].length = sector_count * SECTOR_SIZE; + nents++; +#else + while (sector_count > 128) { + memset(&sg[nents], 0, sizeof (*sg)); + sg[nents].address = virt_addr; + sg[nents].length = 128 * SECTOR_SIZE; + nents++; + virt_addr = virt_addr + (128 * SECTOR_SIZE); + sector_count -= 128; + }; + memset(&sg[nents], 0, sizeof (*sg)); + sg[nents].address = virt_addr; + sg[nents].length = sector_count * SECTOR_SIZE; + nents++; +#endif + return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction); +} + +#ifdef CONFIG_PROC_FS + +static int +sgiioc4_get_info(char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + unsigned int class_rev; + int i = 0; + + while (i < n_sgiioc4_devs) { + pci_read_config_dword(sgiioc4_devs[i], PCI_CLASS_REVISION, + &class_rev); + class_rev &= 0xff; + + if (sgiioc4_devs[i]->device == PCI_DEVICE_ID_SGI_IOC4) { + p += sprintf(p, "\n SGI IOC4 Chipset rev %d. ", class_rev); + p += sprintf(p, "\n Chipset has 1 IDE channel and supports 2 devices on that channel."); + p += sprintf(p, "\n Chipset supports DMA in MultiMode-2 data transfer protocol.\n"); + /* Do we need more info. here? */ + } + i++; + } + + return p - buffer; +} + +#endif /* CONFIG_PROC_FS */ + +static int __devinit +sgiioc4_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + unsigned int class_rev; + ide_pci_device_t *d = &sgiioc4_chipsets[id->driver_data]; + if (dev->device != d->device) { + printk(KERN_ERR "Error in sgiioc4_init_one(dev 0x%p | id 0x%p )\n", (void *) dev, (void *) id); + BUG(); + } + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + if (class_rev < IOC4_SUPPORTED_FIRMWARE_REV) { + printk(KERN_INFO "Disabling the IOC4 IDE Part due to unsupported Firmware Rev (%d). \n",class_rev); + printk(KERN_INFO "Please upgrade to Firmware Rev 46 or higher \n"); + return 0; + } + + printk(KERN_INFO "%s: IDE controller at PCI slot %s\n", d->name, dev->slot_name); + + if (pci_init_sgiioc4(dev, d->name)) + return 0; + + MOD_INC_USE_COUNT; + + return 0; +} + +static struct pci_device_id sgiioc4_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID, PCI_ANY_ID, 0x0b4000, 0xFFFFFF, 0 }, + { 0 } +}; + +static struct pci_driver driver = { + .name = "SGI-IOC4 IDE", + .id_table = sgiioc4_pci_tbl, + .probe = sgiioc4_init_one, +}; + +static int +sgiioc4_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +static void +sgiioc4_ide_exit(void) +{ + ide_pci_unregister_driver(&driver); +} + +module_init(sgiioc4_ide_init); +module_exit(sgiioc4_ide_exit); + +MODULE_AUTHOR("Aniket Malatpure - Silicon Graphics Inc. (SGI)"); +MODULE_DESCRIPTION("PCI driver module for SGI IOC4 Base-IO Card"); +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; diff -Nru a/drivers/ide/pci/sgiioc4.h b/drivers/ide/pci/sgiioc4.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/pci/sgiioc4.h Thu Jul 17 17:43:20 2003 @@ -0,0 +1,177 @@ +/* + * 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 + */ + +#ifndef SGIIOC4_H +#define SGIIOC4_H + +#define IDE_ARCH_ACK_INTR 1 +#include + +/* IOC4 Specific Definitions */ +#define IOC4_CMD_OFFSET 0x100 +#define IOC4_CTRL_OFFSET 0x120 +#define IOC4_DMA_OFFSET 0x140 +#define IOC4_INTR_OFFSET 0x0 + +#define IOC4_TIMING 0x00 +#define IOC4_DMA_PTR_L 0x01 +#define IOC4_DMA_PTR_H 0x02 +#define IOC4_DMA_ADDR_L 0x03 +#define IOC4_DMA_ADDR_H 0x04 +#define IOC4_BC_DEV 0x05 +#define IOC4_BC_MEM 0x06 +#define IOC4_DMA_CTRL 0x07 +#define IOC4_DMA_END_ADDR 0x08 + +/* Bits in the IOC4 Control/Status Register */ +#define IOC4_S_DMA_START 0x01 +#define IOC4_S_DMA_STOP 0x02 +#define IOC4_S_DMA_DIR 0x04 +#define IOC4_S_DMA_ACTIVE 0x08 +#define IOC4_S_DMA_ERROR 0x10 +#define IOC4_ATA_MEMERR 0x02 + +/* Read/Write Directions */ +#define IOC4_DMA_WRITE 0x04 +#define IOC4_DMA_READ 0x00 + +/* Interrupt Register Offsets */ +#define IOC4_INTR_REG 0x03 +#define IOC4_INTR_SET 0x05 +#define IOC4_INTR_CLEAR 0x07 + +#define IOC4_IDE_CACHELINE_SIZE 128 +#define IOC4_SUPPORTED_FIRMWARE_REV 46 + + +/* Weeds out non-IDE interrupts to the IOC4 */ +#define ide_ack_intr(hwif) ((hwif)->hw.ack_intr ? (hwif)->hw.ack_intr(hwif) : 1) + +#define SGIIOC4_MAX_DEVS 32 + +#if defined(CONFIG_PROC_FS) +#include +#include + +static u8 sgiioc4_proc; + +static struct pci_dev *sgiioc4_devs[SGIIOC4_MAX_DEVS]; +static int sgiioc4_get_info(char *, char **, off_t, int); + +static ide_pci_host_proc_t sgiioc4_procs[] __initdata = { + { + .name = "sgiioc4", + .set = 1, + .get_info = sgiioc4_get_info, + .parent = NULL, + } +}; +#endif + +typedef volatile struct { + u32 timing_reg0; + u32 timing_reg1; + u32 low_mem_ptr; + u32 high_mem_ptr; + u32 low_mem_addr; + u32 high_mem_addr; + u32 dev_byte_count; + u32 mem_byte_count; + u32 status; +} ioc4_dma_regs_t; + +/* Each Physical Region Descriptor Entry size is 16 bytes (2 * 64 bits) */ +/* IOC4 has only 1 IDE channel */ +#define IOC4_PRD_BYTES 16 +#define IOC4_PRD_ENTRIES (PAGE_SIZE /IOC4_PRD_BYTES) + +typedef enum pciio_endian_e { + PCIDMA_ENDIAN_BIG, + PCIDMA_ENDIAN_LITTLE +} pciio_endian_t; + +static void sgiioc4_init_hwif_ports(hw_regs_t * hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, ide_ioreg_t irq_port); +static void sgiioc4_ide_setup_pci_device(struct pci_dev *dev, const char *name); +static void sgiioc4_resetproc(ide_drive_t * drive); +static void sgiioc4_maskproc(ide_drive_t * drive, int mask); +static void sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive); +static void __init ide_init_sgiioc4(ide_hwif_t * hwif); +static void __init ide_dma_sgiioc4(ide_hwif_t * hwif, unsigned long dma_base); +static int sgiioc4_checkirq(ide_hwif_t * hwif); +static int sgiioc4_clearirq(ide_drive_t * drive); +static int sgiioc4_get_info(char *buffer, char **addr, off_t offset, int count); +static int sgiioc4_ide_dma_read(ide_drive_t * drive); +static int sgiioc4_ide_dma_write(ide_drive_t * drive); +static int sgiioc4_ide_dma_begin(ide_drive_t * drive); +static int sgiioc4_ide_dma_end(ide_drive_t * drive); +static int sgiioc4_ide_dma_check(ide_drive_t * drive); +static int sgiioc4_ide_dma_on(ide_drive_t * drive); +static int sgiioc4_ide_dma_off(ide_drive_t * drive); +static int sgiioc4_ide_dma_off_quietly(ide_drive_t * drive); +static int sgiioc4_ide_dma_test_irq(ide_drive_t * drive); +static int sgiioc4_ide_dma_host_on(ide_drive_t * drive); +static int sgiioc4_ide_dma_host_off(ide_drive_t * drive); +static int sgiioc4_ide_dma_count(ide_drive_t * drive); +static int sgiioc4_ide_dma_verbose(ide_drive_t * drive); +static int sgiioc4_ide_dma_lostirq(ide_drive_t * drive); +static int sgiioc4_ide_dma_timeout(ide_drive_t * drive); +static int sgiioc4_ide_build_sglist(ide_hwif_t * hwif, struct request *rq, + int ddir); +static int sgiioc4_ide_raw_build_sglist(ide_hwif_t * hwif, struct request *rq); + +static u8 sgiioc4_INB(unsigned long port); +static inline void xide_delay(long ticks); +extern int (*sgiioc4_display_info) (char *, char **, off_t, int); /* ide-proc.c */ +static unsigned int sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, + int ddir); +static unsigned int __init pci_init_sgiioc4(struct pci_dev *dev, const char *name); + +static ide_pci_device_t sgiioc4_chipsets[] __devinitdata = { + { + /* Channel 0 */ + .vendor = PCI_VENDOR_ID_SGI, + .device = PCI_DEVICE_ID_SGI_IOC4, + .name = "SGIIOC4", + .init_chipset = pci_init_sgiioc4, + .init_iops = NULL, + .init_hwif = ide_init_sgiioc4, + .init_dma = ide_dma_sgiioc4, + .channels = 1, + .autodma = AUTODMA, + .enablebits = { { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 } }, + .bootable = ON_BOARD, + .extra = 0, + } +}; + +#endif diff -Nru a/include/linux/pci_ids.h b/include/linux/pci_ids.h --- a/include/linux/pci_ids.h Thu Jul 17 17:43:20 2003 +++ b/include/linux/pci_ids.h Thu Jul 17 17:43:20 2003 @@ -839,6 +839,7 @@ #define PCI_VENDOR_ID_SGI 0x10a9 #define PCI_DEVICE_ID_SGI_IOC3 0x0003 +#define PCI_DEVICE_ID_SGI_IOC4 0x100a #define PCI_VENDOR_ID_ACC 0x10aa #define PCI_DEVICE_ID_ACC_2056 0x0000 --------------DB59DE08B2C77343F78DDFBD-- - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/