fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / ide / pci / sgiioc4.c
index d8e739f..b0bf018 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2003-2006 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
  * 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
@@ -34,6 +29,7 @@
 #include <linux/mm.h>
 #include <linux/ioport.h>
 #include <linux/blkdev.h>
+#include <linux/ioc4.h>
 #include <asm/io.h>
 
 #include <linux/ide.h>
@@ -192,16 +188,13 @@ sgiioc4_clearirq(ide_drive_t * drive)
        return intr_reg & 3;
 }
 
-static int
-sgiioc4_ide_dma_begin(ide_drive_t * drive)
+static void sgiioc4_ide_dma_start(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;
 }
 
 static u32
@@ -227,7 +220,7 @@ sgiioc4_ide_dma_end(ide_drive_t * drive)
        ide_hwif_t *hwif = HWIF(drive);
        u64 dma_base = hwif->dma_base;
        int dma_stat = 0;
-       unsigned long *ending_dma = (unsigned long *) hwif->dma_base2;
+       unsigned long *ending_dma = ide_get_hwifdata(hwif);
 
        hwif->OUTL(IOC4_S_DMA_STOP, dma_base + IOC4_DMA_CTRL * 4);
 
@@ -334,17 +327,6 @@ sgiioc4_ide_dma_host_off(ide_drive_t * drive)
        return 0;
 }
 
-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)
 {
@@ -363,17 +345,17 @@ sgiioc4_resetproc(ide_drive_t * drive)
 static u8
 sgiioc4_INB(unsigned long port)
 {
-       u8 reg = (u8) inb(port);
+       u8 reg = (u8) readb((void __iomem *) 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);
+                       unsigned int intr_reg = (u32) readl((void __iomem *) other_ir);
 
                        /* Clear the Interrupt, Error bits on the IOC4 */
                        if (intr_reg & 0x03) {
-                               outl(0x03, other_ir);
-                               intr_reg = (u32) inl(other_ir);
+                               writel(0x03, (void __iomem *) other_ir);
+                               intr_reg = (u32) readl((void __iomem *) other_ir);
                        }
                }
        }
@@ -382,15 +364,17 @@ sgiioc4_INB(unsigned long port)
 }
 
 /* Creates a dma map for the scatter-gather list entries */
-static void __init
+static void __devinit
 ide_dma_sgiioc4(ide_hwif_t * hwif, unsigned long dma_base)
 {
+       void __iomem *virt_dma_base;
        int num_ports = sizeof (ioc4_dma_regs_t);
+       void *pad;
 
        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)) {
+       if (!request_mem_region(dma_base, num_ports, hwif->name)) {
                printk(KERN_ERR
                       "%s(%s) -- ERROR, Addresses 0x%p to 0x%p "
                       "ALREADY in use\n",
@@ -399,34 +383,32 @@ ide_dma_sgiioc4(ide_hwif_t * hwif, unsigned long dma_base)
                goto dma_alloc_failure;
        }
 
-       hwif->dma_base = dma_base;
+       virt_dma_base = ioremap(dma_base, num_ports);
+       if (virt_dma_base == NULL) {
+               printk(KERN_ERR
+                      "%s(%s) -- ERROR, Unable to map addresses 0x%lx to 0x%lx\n",
+                      __FUNCTION__, hwif->name, dma_base, dma_base + num_ports - 1);
+               goto dma_remap_failure;
+       }
+       hwif->dma_base = (unsigned long) virt_dma_base;
+
        hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
                                          IOC4_PRD_ENTRIES * IOC4_PRD_BYTES,
                                          &hwif->dmatable_dma);
 
        if (!hwif->dmatable_cpu)
-               goto dma_alloc_failure;
-
-       hwif->sg_table =
-           kmalloc(sizeof (struct scatterlist) * IOC4_PRD_ENTRIES, GFP_KERNEL);
+               goto dma_pci_alloc_failure;
 
-       if (!hwif->sg_table)
-               goto dma_sgalloc_failure;
+       hwif->sg_max_nents = IOC4_PRD_ENTRIES;
 
-       hwif->dma_base2 = (unsigned long)
-               pci_alloc_consistent(hwif->pci_dev,
-                                    IOC4_IDE_CACHELINE_SIZE,
-                                    (dma_addr_t *) &(hwif->dma_status));
+       pad = pci_alloc_consistent(hwif->pci_dev, IOC4_IDE_CACHELINE_SIZE,
+                                  (dma_addr_t *) &(hwif->dma_status));
 
-       if (!hwif->dma_base2)
-               goto dma_base2alloc_failure;
-
-       return;
-
-dma_base2alloc_failure:
-       kfree(hwif->sg_table);
+       if (pad) {
+               ide_set_hwifdata(hwif, pad);
+               return;
+       }
 
-dma_sgalloc_failure:
        pci_free_consistent(hwif->pci_dev,
                            IOC4_PRD_ENTRIES * IOC4_PRD_BYTES,
                            hwif->dmatable_cpu, hwif->dmatable_dma);
@@ -436,6 +418,12 @@ dma_sgalloc_failure:
        printk(KERN_INFO
               "Changing from DMA to PIO mode for Drive %s\n", hwif->name);
 
+dma_pci_alloc_failure:
+       iounmap(virt_dma_base);
+
+dma_remap_failure:
+       release_mem_region(dma_base, num_ports);
+
 dma_alloc_failure:
        /* Disable DMA because we couldnot allocate any DMA maps */
        hwif->autodma = 0;
@@ -486,7 +474,7 @@ sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive)
        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);
+       memset(ide_get_hwifdata(hwif), 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);
 
@@ -511,10 +499,7 @@ sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir)
        unsigned int count = 0, i = 1;
        struct scatterlist *sg;
 
-       if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE)
-               hwif->sg_nents = i = ide_raw_build_sglist(drive, rq);
-       else
-               hwif->sg_nents = i = ide_build_sglist(drive, rq);
+       hwif->sg_nents = i = ide_build_sglist(drive, rq);
 
        if (!i)
                return 0;       /* sglist of length Zero */
@@ -533,7 +518,7 @@ sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir)
                                       drive->name);
                                goto use_pio_instead;
                        } else {
-                               u32 xcount, bcount =
+                               u32 bcount =
                                    0x10000 - (cur_addr & 0xffff);
 
                                if (bcount > cur_len)
@@ -548,8 +533,7 @@ sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir)
                                *table = 0x0;
                                table++;
 
-                               xcount = bcount & 0xffff;
-                               *table = cpu_to_be32(xcount);
+                               *table = cpu_to_be32(bcount);
                                table++;
 
                                cur_addr += bcount;
@@ -570,45 +554,40 @@ sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir)
 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_ide_dma_read(ide_drive_t * drive)
+static int sgiioc4_ide_dma_setup(ide_drive_t *drive)
 {
        struct request *rq = HWGROUP(drive)->rq;
        unsigned int count = 0;
+       int ddir;
 
-       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 int
-sgiioc4_ide_dma_write(ide_drive_t * drive)
-{
-       struct request *rq = HWGROUP(drive)->rq;
-       unsigned int count = 0;
+       if (rq_data_dir(rq))
+               ddir = PCI_DMA_TODEVICE;
+       else
+               ddir = PCI_DMA_FROMDEVICE;
 
-       if (!(count = sgiioc4_build_dma_table(drive, rq, PCI_DMA_TODEVICE))) {
+       if (!(count = sgiioc4_build_dma_table(drive, rq, ddir))) {
                /* try PIO instead of DMA */
+               ide_map_sg(drive, rq);
                return 1;
        }
 
-       sgiioc4_configure_for_dma(IOC4_DMA_READ, drive);
-       /* Writes TO the IOC4 FROM Main Memory */
+       if (rq_data_dir(rq))
+               /* Writes TO the IOC4 FROM Main Memory */
+               ddir = IOC4_DMA_READ;
+       else
+               /* Writes FROM the IOC4 TO Main Memory */
+               ddir = IOC4_DMA_WRITE;
+
+       sgiioc4_configure_for_dma(ddir, drive);
 
        return 0;
 }
 
-static void __init
+static void __devinit
 ide_init_sgiioc4(ide_hwif_t * hwif)
 {
        hwif->mmio = 2;
@@ -617,7 +596,6 @@ ide_init_sgiioc4(ide_hwif_t * hwif)
        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 routine to select drive */
@@ -630,9 +608,8 @@ ide_init_sgiioc4(ide_hwif_t * hwif)
        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->dma_setup = &sgiioc4_ide_dma_setup;
+       hwif->dma_start = &sgiioc4_ide_dma_start;
        hwif->ide_dma_end = &sgiioc4_ide_dma_end;
        hwif->ide_dma_check = &sgiioc4_ide_dma_check;
        hwif->ide_dma_on = &sgiioc4_ide_dma_on;
@@ -640,44 +617,61 @@ ide_init_sgiioc4(ide_hwif_t * hwif)
        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_verbose = &sgiioc4_ide_dma_verbose;
        hwif->ide_dma_lostirq = &sgiioc4_ide_dma_lostirq;
        hwif->ide_dma_timeout = &__ide_dma_timeout;
+
        hwif->INB = &sgiioc4_INB;
 }
 
-static int __init
+static int __devinit
 sgiioc4_ide_setup_pci_device(struct pci_dev *dev, ide_pci_device_t * d)
 {
-       unsigned long base, ctl, dma_base, irqport;
+       unsigned long cmd_base, dma_base, irqport;
+       unsigned long bar0, cmd_phys_base, ctl;
+       void __iomem *virt_base;
        ide_hwif_t *hwif;
        int h;
 
+       /*
+        * Find an empty HWIF; if none available, return -ENOMEM.
+        */
        for (h = 0; h < MAX_HWIFS; ++h) {
                hwif = &ide_hwifs[h];
-               /* Find an empty HWIF */
                if (hwif->chipset == ide_unknown)
                        break;
        }
+       if (h == MAX_HWIFS) {
+               printk(KERN_ERR "%s: too many IDE interfaces, no room in table\n", d->name);
+               return -ENOMEM;
+       }
 
        /*  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;
+       bar0 = pci_resource_start(dev, 0);
+       virt_base = ioremap(bar0, pci_resource_len(dev, 0));
+       if (virt_base == NULL) {
+               printk(KERN_ERR "%s: Unable to remap BAR 0 address: 0x%lx\n",
+                       d->name, bar0);
+               return -ENOMEM;
+       }
+       cmd_base = (unsigned long) virt_base + IOC4_CMD_OFFSET;
+       ctl = (unsigned long) virt_base + IOC4_CTRL_OFFSET;
+       irqport = (unsigned long) virt_base + IOC4_INTR_OFFSET;
        dma_base = pci_resource_start(dev, 0) + IOC4_DMA_OFFSET;
 
-       if (!request_region(base, IOC4_CMD_CTL_BLK_SIZE, hwif->name)) {
+       cmd_phys_base = bar0 + IOC4_CMD_OFFSET;
+       if (!request_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE,
+           hwif->name)) {
                printk(KERN_ERR
-                       "%s : %s -- ERROR, Port Addresses "
+                       "%s : %s -- ERROR, Addresses "
                        "0x%p to 0x%p ALREADY in use\n",
-                      __FUNCTION__, hwif->name, (void *) base,
-                      (void *) base + IOC4_CMD_CTL_BLK_SIZE);
+                      __FUNCTION__, hwif->name, (void *) cmd_phys_base,
+                      (void *) cmd_phys_base + IOC4_CMD_CTL_BLK_SIZE);
                return -ENOMEM;
        }
 
-       if (hwif->io_ports[IDE_DATA_OFFSET] != base) {
+       if (hwif->io_ports[IDE_DATA_OFFSET] != cmd_base) {
                /* Initialize the IO registers */
-               sgiioc4_init_hwif_ports(&hwif->hw, base, ctl, irqport);
+               sgiioc4_init_hwif_ports(&hwif->hw, cmd_base, ctl, irqport);
                memcpy(hwif->io_ports, hwif->hw.io_ports,
                       sizeof (hwif->io_ports));
                hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
@@ -690,6 +684,9 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev, ide_pci_device_t * d)
        hwif->cds = (struct ide_pci_device_s *) d;
        hwif->gendev.parent = &dev->dev;/* setup proper ancestral information */
 
+       /* The IOC4 uses MMIO rather than Port IO. */
+       default_hwif_mmiops(hwif);
+
        /* Initializing chipset IRQ Registers */
        hwif->OUTL(0x03, irqport + IOC4_INTR_SET * 4);
 
@@ -701,64 +698,40 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev, ide_pci_device_t * d)
                printk(KERN_INFO "%s: %s Bus-Master DMA disabled\n",
                       hwif->name, d->name);
 
-       probe_hwif_init(hwif);
+       if (probe_hwif_init(hwif))
+               return -EIO;
+
+       /* Create /proc/ide entries */
+       create_proc_ide_interfaces();
+
        return 0;
 }
 
-/* This ensures that we can build this for generic kernels without
- * having all the SN2 code sync'd and merged.
- */
-typedef enum pciio_endian_e {
-       PCIDMA_ENDIAN_BIG,
-       PCIDMA_ENDIAN_LITTLE
-} pciio_endian_t;
-pciio_endian_t snia_pciio_endian_set(struct pci_dev
-                                    *pci_dev, pciio_endian_t device_end,
-                                    pciio_endian_t desired_end);
-
-static unsigned int __init
+static unsigned int __devinit
 pci_init_sgiioc4(struct pci_dev *dev, ide_pci_device_t * d)
 {
        unsigned int class_rev;
-       pciio_endian_t endian_status;
-
-       if (pci_enable_device(dev)) {
-               printk(KERN_ERR
-                      "Failed to enable device %s at slot %s\n",
-                      d->name, dev->slot_name);
-               return -ENODEV;
-       }
-       pci_set_master(dev);
+       int ret;
 
        pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
        class_rev &= 0xff;
        printk(KERN_INFO "%s: IDE controller at PCI slot %s, revision %d\n",
-                       d->name, dev->slot_name, class_rev);
+                       d->name, pci_name(dev), class_rev);
        if (class_rev < IOC4_SUPPORTED_FIRMWARE_REV) {
                printk(KERN_ERR "Skipping %s IDE controller in slot %s: "
                        "firmware is obsolete - please upgrade to revision"
-                       "46 or higher\n", d->name, dev->slot_name);
-               return -ENODEV;
+                       "46 or higher\n", d->name, pci_name(dev));
+               ret = -EAGAIN;
+               goto out;
        }
-
-       /* Enable Byte Swapping in the PIC... */
-       endian_status = snia_pciio_endian_set(dev, PCIDMA_ENDIAN_LITTLE,
-                                             PCIDMA_ENDIAN_BIG);
-       if (endian_status != PCIDMA_ENDIAN_BIG) {
-               printk(KERN_ERR
-                      "Failed to set endianness for device %s at slot %s\n",
-                      d->name, dev->slot_name);
-               return -ENODEV;
-       }
-
-       return sgiioc4_ide_setup_pci_device(dev, d);
+       ret = sgiioc4_ide_setup_pci_device(dev, d);
+out:
+       return ret;
 }
 
 static ide_pci_device_t sgiioc4_chipsets[] __devinitdata = {
        {
         /* Channel 0 */
-        .vendor = PCI_VENDOR_ID_SGI,
-        .device = PCI_DEVICE_ID_SGI_IOC4,
         .name = "SGIIOC4",
         .init_hwif = ide_init_sgiioc4,
         .init_dma = ide_dma_sgiioc4,
@@ -769,43 +742,33 @@ static ide_pci_device_t sgiioc4_chipsets[] __devinitdata = {
        }
 };
 
-static int __devinit
-sgiioc4_init_one(struct pci_dev *dev, const struct pci_device_id *id)
+int
+ioc4_ide_attach_one(struct ioc4_driver_data *idd)
 {
-       ide_pci_device_t *d = &sgiioc4_chipsets[id->driver_data];
-       if (dev->device != d->device) {
-               printk(KERN_ERR "Error in %s(dev 0x%p | id 0x%p )\n",
-                      __FUNCTION__, (void *) dev, (void *) id);
-               BUG();
-       }
-
-       if (pci_init_sgiioc4(dev, d))
+       /* PCI-RT does not bring out IDE connection.
+        * Do not attach to this particular IOC4.
+        */
+       if (idd->idd_variant == IOC4_VARIANT_PCI_RT)
                return 0;
 
-       return 0;
+       return pci_init_sgiioc4(idd->idd_pdev,
+                               &sgiioc4_chipsets[idd->idd_pci_id->driver_data]);
 }
 
-static struct pci_device_id sgiioc4_pci_tbl[] = {
-       {PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID,
-        PCI_ANY_ID, 0x0b4000, 0xFFFFFF, 0},
-       {0}
-};
-MODULE_DEVICE_TABLE(pci, sgiioc4_pci_tbl);
-
-static struct pci_driver driver = {
-       .name = "SGI-IOC4 IDE",
-       .id_table = sgiioc4_pci_tbl,
-       .probe = sgiioc4_init_one,
+static struct ioc4_submodule ioc4_ide_submodule = {
+       .is_name = "IOC4_ide",
+       .is_owner = THIS_MODULE,
+       .is_probe = ioc4_ide_attach_one,
+/*     .is_remove = ioc4_ide_remove_one,       */
 };
 
-static int
-sgiioc4_ide_init(void)
+static int __init ioc4_ide_init(void)
 {
-       return ide_pci_register_driver(&driver);
+       return ioc4_register_submodule(&ioc4_ide_submodule);
 }
 
-module_init(sgiioc4_ide_init);
+late_initcall(ioc4_ide_init); /* Call only after IDE init is done */
 
-MODULE_AUTHOR("Aniket Malatpure - Silicon Graphics Inc. (SGI)");
-MODULE_DESCRIPTION("PCI driver module for SGI IOC4 Base-IO Card");
+MODULE_AUTHOR("Aniket Malatpure/Jeremy Higdon");
+MODULE_DESCRIPTION("IDE PCI driver module for SGI IOC4 Base-IO Card");
 MODULE_LICENSE("GPL");