linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / scsi / esp.c
index 5630868..87a8c3d 100644 (file)
@@ -1,6 +1,7 @@
-/* esp.c: ESP Sun SCSI driver.
+/* $Id: esp.c,v 1.101 2002/01/15 06:48:55 davem Exp $
+ * esp.c:  EnhancedScsiProcessor Sun SCSI driver code.
  *
- * Copyright (C) 1995, 1998, 2006 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu)
  */
 
 /* TODO:
@@ -12,6 +13,7 @@
  * 3) Add tagged queueing.
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/types.h>
@@ -183,6 +185,11 @@ enum {
 /*5*/  do_intr_end
 };
 
+/* The master ring of all esp hosts we are managing in this driver. */
+static struct esp *espchain;
+static DEFINE_SPINLOCK(espchain_lock);
+static int esps_running = 0;
+
 /* Forward declarations. */
 static irqreturn_t esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
 
@@ -687,6 +694,36 @@ static void __init esp_bootup_reset(struct esp *esp)
        sbus_readb(esp->eregs + ESP_INTRPT);
 }
 
+static void esp_chain_add(struct esp *esp)
+{
+       spin_lock_irq(&espchain_lock);
+       if (espchain) {
+               struct esp *elink = espchain;
+               while (elink->next)
+                       elink = elink->next;
+               elink->next = esp;
+       } else {
+               espchain = esp;
+       }
+       esp->next = NULL;
+       spin_unlock_irq(&espchain_lock);
+}
+
+static void esp_chain_del(struct esp *esp)
+{
+       spin_lock_irq(&espchain_lock);
+       if (espchain == esp) {
+               espchain = esp->next;
+       } else {
+               struct esp *elink = espchain;
+               while (elink->next != esp)
+                       elink = elink->next;
+               elink->next = esp->next;
+       }
+       esp->next = NULL;
+       spin_unlock_irq(&espchain_lock);
+}
+
 static int __init esp_find_dvma(struct esp *esp, struct sbus_dev *dma_sdev)
 {
        struct sbus_dev *sdev = esp->sdev;
@@ -778,14 +815,14 @@ static int __init esp_register_irq(struct esp *esp)
         * sanely maintain.
         */
        if (request_irq(esp->ehost->irq, esp_intr,
-                       IRQF_SHARED, "ESP SCSI", esp)) {
+                       SA_SHIRQ, "ESP SCSI", esp)) {
                printk("esp%d: Cannot acquire irq line\n",
                       esp->esp_id);
                return -1;
        }
 
-       printk("esp%d: IRQ %d ", esp->esp_id,
-              esp->ehost->irq);
+       printk("esp%d: IRQ %s ", esp->esp_id,
+              __irq_itoa(esp->ehost->irq));
 
        return 0;
 }
@@ -793,20 +830,19 @@ static int __init esp_register_irq(struct esp *esp)
 static void __init esp_get_scsi_id(struct esp *esp)
 {
        struct sbus_dev *sdev = esp->sdev;
-       struct device_node *dp = sdev->ofdev.node;
 
-       esp->scsi_id = of_getintprop_default(dp,
-                                            "initiator-id",
-                                            -1);
+       esp->scsi_id = prom_getintdefault(esp->prom_node,
+                                         "initiator-id",
+                                         -1);
        if (esp->scsi_id == -1)
-               esp->scsi_id = of_getintprop_default(dp,
-                                                    "scsi-initiator-id",
-                                                    -1);
+               esp->scsi_id = prom_getintdefault(esp->prom_node,
+                                                 "scsi-initiator-id",
+                                                 -1);
        if (esp->scsi_id == -1)
                esp->scsi_id = (sdev->bus == NULL) ? 7 :
-                       of_getintprop_default(sdev->bus->ofdev.node,
-                                             "scsi-initiator-id",
-                                             7);
+                       prom_getintdefault(sdev->bus->prom_node,
+                                          "scsi-initiator-id",
+                                          7);
        esp->ehost->this_id = esp->scsi_id;
        esp->scsi_id_mask = (1 << esp->scsi_id);
 
@@ -1031,30 +1067,28 @@ static void __init esp_init_swstate(struct esp *esp)
        esp->prev_hme_dmacsr = 0xffffffff;
 }
 
-static int __init detect_one_esp(struct scsi_host_template *tpnt,
-                                struct device *dev,
-                                struct sbus_dev *esp_dev,
-                                struct sbus_dev *espdma,
-                                struct sbus_bus *sbus,
-                                int hme)
+static int __init detect_one_esp(struct scsi_host_template *tpnt, struct sbus_dev *esp_dev,
+                                struct sbus_dev *espdma, struct sbus_bus *sbus,
+                                int id, int hme)
 {
-       static int instance;
-       struct Scsi_Host *esp_host = scsi_host_alloc(tpnt, sizeof(struct esp));
+       struct Scsi_Host *esp_host = scsi_register(tpnt, sizeof(struct esp));
        struct esp *esp;
        
-       if (!esp_host)
-               return -ENOMEM;
-
+       if (!esp_host) {
+               printk("ESP: Cannot register SCSI host\n");
+               return -1;
+       }
        if (hme)
                esp_host->max_id = 16;
        esp = (struct esp *) esp_host->hostdata;
        esp->ehost = esp_host;
        esp->sdev = esp_dev;
-       esp->esp_id = instance;
+       esp->esp_id = id;
        esp->prom_node = esp_dev->prom_node;
        prom_getstring(esp->prom_node, "name", esp->prom_name,
                       sizeof(esp->prom_name));
 
+       esp_chain_add(esp);
        if (esp_find_dvma(esp, espdma) < 0)
                goto fail_unlink;
        if (esp_map_regs(esp, hme) < 0) {
@@ -1081,19 +1115,8 @@ static int __init detect_one_esp(struct scsi_host_template *tpnt,
 
        esp_bootup_reset(esp);
 
-       if (scsi_add_host(esp_host, dev))
-               goto fail_free_irq;
-
-       dev_set_drvdata(&esp_dev->ofdev.dev, esp);
-
-       scsi_scan_host(esp_host);
-       instance++;
-
        return 0;
 
-fail_free_irq:
-       free_irq(esp->ehost->irq, esp);
-
 fail_unmap_cmdarea:
        sbus_free_consistent(esp->sdev, 16,
                             (void *) esp->esp_command,
@@ -1106,99 +1129,119 @@ fail_dvma_release:
        esp->dma->allocated = 0;
 
 fail_unlink:
-       scsi_host_put(esp_host);
+       esp_chain_del(esp);
+       scsi_unregister(esp_host);
        return -1;
 }
 
 /* Detecting ESP chips on the machine.  This is the simple and easy
  * version.
  */
-static int __devexit esp_remove_common(struct esp *esp)
-{
-       unsigned int irq = esp->ehost->irq;
-
-       scsi_remove_host(esp->ehost);
-
-       ESP_INTSOFF(esp->dregs);
-#if 0
-       esp_reset_dma(esp);
-       esp_reset_esp(esp);
-#endif
-
-       free_irq(irq, esp);
-       sbus_free_consistent(esp->sdev, 16,
-                            (void *) esp->esp_command, esp->esp_command_dvma);
-       sbus_iounmap(esp->eregs, ESP_REG_SIZE);
-       esp->dma->allocated = 0;
-
-       scsi_host_put(esp->ehost);
-
-       return 0;
-}
-
 
 #ifdef CONFIG_SUN4
 
 #include <asm/sun4paddr.h>
 
-static struct sbus_dev sun4_esp_dev;
-
-static int __init esp_sun4_probe(struct scsi_host_template *tpnt)
+static int __init esp_detect(struct scsi_host_template *tpnt)
 {
+       static struct sbus_dev esp_dev;
+       int esps_in_use = 0;
+
+       espchain = NULL;
+
        if (sun4_esp_physaddr) {
-               memset(&sun4_esp_dev, 0, sizeof(sun4_esp_dev));
-               sun4_esp_dev.reg_addrs[0].phys_addr = sun4_esp_physaddr;
-               sun4_esp_dev.irqs[0] = 4;
-               sun4_esp_dev.resource[0].start = sun4_esp_physaddr;
-               sun4_esp_dev.resource[0].end =
-                       sun4_esp_physaddr + ESP_REG_SIZE - 1;
-               sun4_esp_dev.resource[0].flags = IORESOURCE_IO;
-
-               return detect_one_esp(tpnt, NULL,
-                                     &sun4_esp_dev, NULL, NULL, 0);
+               memset (&esp_dev, 0, sizeof(esp_dev));
+               esp_dev.reg_addrs[0].phys_addr = sun4_esp_physaddr;
+               esp_dev.irqs[0] = 4;
+               esp_dev.resource[0].start = sun4_esp_physaddr;
+               esp_dev.resource[0].end = sun4_esp_physaddr + ESP_REG_SIZE - 1;
+               esp_dev.resource[0].flags = IORESOURCE_IO;
+
+               if (!detect_one_esp(tpnt, &esp_dev, NULL, NULL, 0, 0))
+                       esps_in_use++;
+               printk("ESP: Total of 1 ESP hosts found, %d actually in use.\n", esps_in_use);
+               esps_running =  esps_in_use;
        }
-       return 0;
+       return esps_in_use;
 }
 
-static int __devexit esp_sun4_remove(void)
+#else /* !CONFIG_SUN4 */
+
+static int __init esp_detect(struct scsi_host_template *tpnt)
 {
-       struct of_device *dev = &sun4_esp_dev.ofdev;
-       struct esp *esp = dev_get_drvdata(&dev->dev);
+       struct sbus_bus *sbus;
+       struct sbus_dev *esp_dev, *sbdev_iter;
+       int nesps = 0, esps_in_use = 0;
 
-       return esp_remove_common(esp);
+       espchain = 0;
+       if (!sbus_root) {
+#ifdef CONFIG_PCI
+               return 0;
+#else
+               panic("No SBUS in esp_detect()");
+#endif
+       }
+       for_each_sbus(sbus) {
+               for_each_sbusdev(sbdev_iter, sbus) {
+                       struct sbus_dev *espdma = NULL;
+                       int hme = 0;
+
+                       /* Is it an esp sbus device? */
+                       esp_dev = sbdev_iter;
+                       if (strcmp(esp_dev->prom_name, "esp") &&
+                           strcmp(esp_dev->prom_name, "SUNW,esp")) {
+                               if (!strcmp(esp_dev->prom_name, "SUNW,fas")) {
+                                       hme = 1;
+                                       espdma = esp_dev;
+                               } else {
+                                       if (!esp_dev->child ||
+                                           (strcmp(esp_dev->prom_name, "espdma") &&
+                                            strcmp(esp_dev->prom_name, "dma")))
+                                               continue; /* nope... */
+                                       espdma = esp_dev;
+                                       esp_dev = esp_dev->child;
+                                       if (strcmp(esp_dev->prom_name, "esp") &&
+                                           strcmp(esp_dev->prom_name, "SUNW,esp"))
+                                               continue; /* how can this happen? */
+                               }
+                       }
+                       
+                       if (detect_one_esp(tpnt, esp_dev, espdma, sbus, nesps++, hme) < 0)
+                               continue;
+                               
+                       esps_in_use++;
+               } /* for each sbusdev */
+       } /* for each sbus */
+       printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,
+              esps_in_use);
+       esps_running = esps_in_use;
+       return esps_in_use;
 }
 
-#else /* !CONFIG_SUN4 */
+#endif /* !CONFIG_SUN4 */
 
-static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+/*
+ */
+static int esp_release(struct Scsi_Host *host)
 {
-       struct sbus_dev *sdev = to_sbus_device(&dev->dev);
-       struct device_node *dp = dev->node;
-       struct sbus_dev *dma_sdev = NULL;
-       int hme = 0;
-
-       if (dp->parent &&
-           (!strcmp(dp->parent->name, "espdma") ||
-            !strcmp(dp->parent->name, "dma")))
-               dma_sdev = sdev->parent;
-       else if (!strcmp(dp->name, "SUNW,fas")) {
-               dma_sdev = sdev;
-               hme = 1;
-       }
+       struct esp *esp = (struct esp *) host->hostdata;
 
-       return detect_one_esp(match->data, &dev->dev,
-                             sdev, dma_sdev, sdev->bus, hme);
-}
+       ESP_INTSOFF(esp->dregs);
+#if 0
+       esp_reset_dma(esp);
+       esp_reset_esp(esp);
+#endif
 
-static int __devexit esp_sbus_remove(struct of_device *dev)
-{
-       struct esp *esp = dev_get_drvdata(&dev->dev);
+       free_irq(esp->ehost->irq, esp);
+       sbus_free_consistent(esp->sdev, 16,
+                            (void *) esp->esp_command, esp->esp_command_dvma);
+       sbus_iounmap(esp->eregs, ESP_REG_SIZE);
+       esp->dma->allocated = 0;
+       esp_chain_del(esp);
 
-       return esp_remove_common(esp);
+        return 0;
 }
 
-#endif /* !CONFIG_SUN4 */
-
 /* The info function will return whatever useful
  * information the developer sees fit.  If not provided, then
  * the name field will be used instead.
@@ -1372,11 +1415,18 @@ static int esp_host_info(struct esp *esp, char *ptr, off_t offset, int len)
 static int esp_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
                         int length, int inout)
 {
-       struct esp *esp = (struct esp *) host->hostdata;
+       struct esp *esp;
 
        if (inout)
                return -EINVAL; /* not yet */
 
+       for_each_esp(esp) {
+               if (esp->ehost == host)
+                       break;
+       }
+       if (!esp)
+               return -EINVAL;
+
        if (start)
                *start = buffer;
 
@@ -1398,7 +1448,7 @@ static void esp_get_dmabufs(struct esp *esp, struct scsi_cmnd *sp)
                        sp->SCp.ptr = NULL;
                }
        } else {
-               sp->SCp.buffer = (struct scatterlist *) sp->request_buffer;
+               sp->SCp.buffer = (struct scatterlist *) sp->buffer;
                sp->SCp.buffers_residual = sbus_map_sg(esp->sdev,
                                                       sp->SCp.buffer,
                                                       sp->use_sg,
@@ -1411,7 +1461,7 @@ static void esp_get_dmabufs(struct esp *esp, struct scsi_cmnd *sp)
 static void esp_release_dmabufs(struct esp *esp, struct scsi_cmnd *sp)
 {
        if (sp->use_sg) {
-               sbus_unmap_sg(esp->sdev, sp->request_buffer, sp->use_sg,
+               sbus_unmap_sg(esp->sdev, sp->buffer, sp->use_sg,
                              sp->sc_data_direction);
        } else if (sp->request_bufflen) {
                sbus_unmap_single(esp->sdev,
@@ -2755,15 +2805,18 @@ static int esp_do_data_finale(struct esp *esp)
  */
 static int esp_should_clear_sync(struct scsi_cmnd *sp)
 {
-       u8 cmd = sp->cmnd[0];
+       u8 cmd1 = sp->cmnd[0];
+       u8 cmd2 = sp->data_cmnd[0];
 
        /* These cases are for spinning up a disk and
         * waiting for that spinup to complete.
         */
-       if (cmd == START_STOP)
+       if (cmd1 == START_STOP ||
+           cmd2 == START_STOP)
                return 0;
 
-       if (cmd == TEST_UNIT_READY)
+       if (cmd1 == TEST_UNIT_READY ||
+           cmd2 == TEST_UNIT_READY)
                return 0;
 
        /* One more special case for SCSI tape drives,
@@ -2771,7 +2824,8 @@ static int esp_should_clear_sync(struct scsi_cmnd *sp)
         * completion of a rewind or tape load operation.
         */
        if (sp->device->type == TYPE_TAPE) {
-               if (cmd == MODE_SENSE)
+               if (cmd1 == MODE_SENSE ||
+                   cmd2 == MODE_SENSE)
                        return 0;
        }
 
@@ -4323,12 +4377,15 @@ static void esp_slave_destroy(struct scsi_device *SDptr)
        SDptr->hostdata = NULL;
 }
 
-static struct scsi_host_template esp_template = {
-       .module                 = THIS_MODULE,
-       .name                   = "esp",
-       .info                   = esp_info,
+static struct scsi_host_template driver_template = {
+       .proc_name              = "esp",
+       .proc_info              = esp_proc_info,
+       .name                   = "Sun ESP 100/100a/200",
+       .detect                 = esp_detect,
        .slave_alloc            = esp_slave_alloc,
        .slave_destroy          = esp_slave_destroy,
+       .release                = esp_release,
+       .info                   = esp_info,
        .queuecommand           = esp_queue,
        .eh_abort_handler       = esp_abort,
        .eh_bus_reset_handler   = esp_reset,
@@ -4337,58 +4394,12 @@ static struct scsi_host_template esp_template = {
        .sg_tablesize           = SG_ALL,
        .cmd_per_lun            = 1,
        .use_clustering         = ENABLE_CLUSTERING,
-       .proc_name              = "esp",
-       .proc_info              = esp_proc_info,
-};
-
-#ifndef CONFIG_SUN4
-static struct of_device_id esp_match[] = {
-       {
-               .name = "SUNW,esp",
-               .data = &esp_template,
-       },
-       {
-               .name = "SUNW,fas",
-               .data = &esp_template,
-       },
-       {
-               .name = "esp",
-               .data = &esp_template,
-       },
-       {},
-};
-MODULE_DEVICE_TABLE(of, esp_match);
-
-static struct of_platform_driver esp_sbus_driver = {
-       .name           = "esp",
-       .match_table    = esp_match,
-       .probe          = esp_sbus_probe,
-       .remove         = __devexit_p(esp_sbus_remove),
 };
-#endif
-
-static int __init esp_init(void)
-{
-#ifdef CONFIG_SUN4
-       return esp_sun4_probe(&esp_template);
-#else
-       return of_register_driver(&esp_sbus_driver, &sbus_bus_type);
-#endif
-}
 
-static void __exit esp_exit(void)
-{
-#ifdef CONFIG_SUN4
-       esp_sun4_remove();
-#else
-       of_unregister_driver(&esp_sbus_driver);
-#endif
-}
+#include "scsi_module.c"
 
-MODULE_DESCRIPTION("ESP Sun SCSI driver");
-MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_DESCRIPTION("EnhancedScsiProcessor Sun SCSI driver");
+MODULE_AUTHOR("David S. Miller (davem@redhat.com)");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
 
-module_init(esp_init);
-module_exit(esp_exit);