mmc-omap: add support for 16-bit and 32-bit registers
[safe/jmp/linux-2.6] / drivers / scsi / mac_esp.c
index 887682a..3893337 100644 (file)
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/nubus.h>
+#include <linux/slab.h>
 
 #include <asm/irq.h>
 #include <asm/dma.h>
-
 #include <asm/macints.h>
 #include <asm/macintosh.h>
 
@@ -53,7 +53,7 @@ struct mac_esp_priv {
        void __iomem *pdma_io;
        int error;
 };
-static struct platform_device *internal_esp, *external_esp;
+static struct esp *esp_chips[2];
 
 #define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \
                               platform_get_drvdata((struct platform_device *) \
@@ -170,7 +170,7 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
 
 #define MAC_ESP_PDMA_LOOP(operands) \
        asm volatile ( \
-            "       tstw %2                   \n" \
+            "       tstw %1                   \n" \
             "       jbeq 20f                  \n" \
             "1:     movew " operands "        \n" \
             "2:     movew " operands "        \n" \
@@ -188,14 +188,14 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
             "14:    movew " operands "        \n" \
             "15:    movew " operands "        \n" \
             "16:    movew " operands "        \n" \
-            "       subqw #1,%2               \n" \
+            "       subqw #1,%1               \n" \
             "       jbne 1b                   \n" \
-            "20:    tstw %3                   \n" \
+            "20:    tstw %2                   \n" \
             "       jbeq 30f                  \n" \
             "21:    movew " operands "        \n" \
-            "       subqw #1,%3               \n" \
+            "       subqw #1,%2               \n" \
             "       jbne 21b                  \n" \
-            "30:    tstw %4                   \n" \
+            "30:    tstw %3                   \n" \
             "       jbeq 40f                  \n" \
             "31:    moveb " operands "        \n" \
             "32:    nop                       \n" \
@@ -223,8 +223,8 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
             "       .long  31b,40b            \n" \
             "       .long  32b,40b            \n" \
             "       .previous                 \n" \
-            : "+a" (addr) \
-            : "a" (mep->pdma_io), "r" (count32), "r" (count2), "g" (esp_count))
+            : "+a" (addr), "+r" (count32), "+r" (count2) \
+            : "g" (count1), "a" (mep->pdma_io))
 
 static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
                                  u32 dma_count, int write, u8 cmd)
@@ -247,19 +247,20 @@ static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
        do {
                unsigned int count32 = esp_count >> 5;
                unsigned int count2 = (esp_count & 0x1F) >> 1;
+               unsigned int count1 = esp_count & 1;
                unsigned int start_addr = addr;
 
                if (mac_esp_wait_for_dreq(esp))
                        break;
 
                if (write) {
-                       MAC_ESP_PDMA_LOOP("%1@,%0@+");
+                       MAC_ESP_PDMA_LOOP("%4@,%0@+");
 
                        esp_count -= addr - start_addr;
                } else {
                        unsigned int n;
 
-                       MAC_ESP_PDMA_LOOP("%0@+,%1@");
+                       MAC_ESP_PDMA_LOOP("%0@+,%4@");
 
                        if (mac_esp_wait_for_empty_fifo(esp))
                                break;
@@ -277,24 +278,27 @@ static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
  * Programmed IO routines follow.
  */
 
-static inline int mac_esp_wait_for_fifo(struct esp *esp)
+static inline unsigned int mac_esp_wait_for_fifo(struct esp *esp)
 {
        int i = 500000;
 
        do {
-               if (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES)
-                       return 0;
+               unsigned int fbytes = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+
+               if (fbytes)
+                       return fbytes;
 
                udelay(2);
        } while (--i);
 
        printk(KERN_ERR PFX "FIFO is empty (sreg %02x)\n",
               esp_read8(ESP_STATUS));
-       return 1;
+       return 0;
 }
 
 static inline int mac_esp_wait_for_intr(struct esp *esp)
 {
+       struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
        int i = 500000;
 
        do {
@@ -306,6 +310,7 @@ static inline int mac_esp_wait_for_intr(struct esp *esp)
        } while (--i);
 
        printk(KERN_ERR PFX "IRQ timeout (sreg %02x)\n", esp->sreg);
+       mep->error = 1;
        return 1;
 }
 
@@ -345,11 +350,10 @@ static inline int mac_esp_wait_for_intr(struct esp *esp)
 static void mac_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
                                 u32 dma_count, int write, u8 cmd)
 {
-       unsigned long flags;
        struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
        u8 *fifo = esp->regs + ESP_FDATA * 16;
 
-       local_irq_save(flags);
+       disable_irq(esp->host->irq);
 
        cmd &= ~ESP_CMD_DMA;
        mep->error = 0;
@@ -357,11 +361,35 @@ static void mac_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
        if (write) {
                scsi_esp_cmd(esp, cmd);
 
-               if (!mac_esp_wait_for_intr(esp)) {
-                       if (mac_esp_wait_for_fifo(esp))
-                               esp_count = 0;
-               } else {
-                       esp_count = 0;
+               while (1) {
+                       unsigned int n;
+
+                       n = mac_esp_wait_for_fifo(esp);
+                       if (!n)
+                               break;
+
+                       if (n > esp_count)
+                               n = esp_count;
+                       esp_count -= n;
+
+                       MAC_ESP_PIO_LOOP("%2@,%0@+", n);
+
+                       if (!esp_count)
+                               break;
+
+                       if (mac_esp_wait_for_intr(esp))
+                               break;
+
+                       if (((esp->sreg & ESP_STAT_PMASK) != ESP_DIP) &&
+                           ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP))
+                               break;
+
+                       esp->ireg = esp_read8(ESP_INTRPT);
+                       if ((esp->ireg & (ESP_INTR_DC | ESP_INTR_BSERV)) !=
+                           ESP_INTR_BSERV)
+                               break;
+
+                       scsi_esp_cmd(esp, ESP_CMD_TI);
                }
        } else {
                scsi_esp_cmd(esp, ESP_CMD_FLUSH);
@@ -372,47 +400,24 @@ static void mac_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
                        MAC_ESP_PIO_LOOP("%0@+,%2@", esp_count);
 
                scsi_esp_cmd(esp, cmd);
-       }
 
-       while (esp_count) {
-               unsigned int n;
-
-               if (mac_esp_wait_for_intr(esp)) {
-                       mep->error = 1;
-                       break;
-               }
-
-               if (esp->sreg & ESP_STAT_SPAM) {
-                       printk(KERN_ERR PFX "gross error\n");
-                       mep->error = 1;
-                       break;
-               }
-
-               n = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
-
-               if (write) {
-                       if (n > esp_count)
-                               n = esp_count;
-                       esp_count -= n;
-
-                       MAC_ESP_PIO_LOOP("%2@,%0@+", n);
+               while (esp_count) {
+                       unsigned int n;
 
-                       if ((esp->sreg & ESP_STAT_PMASK) == ESP_STATP)
+                       if (mac_esp_wait_for_intr(esp))
                                break;
 
-                       if (esp_count) {
-                               esp->ireg = esp_read8(ESP_INTRPT);
-                               if (esp->ireg & ESP_INTR_DC)
-                                       break;
+                       if (((esp->sreg & ESP_STAT_PMASK) != ESP_DOP) &&
+                           ((esp->sreg & ESP_STAT_PMASK) != ESP_MOP))
+                               break;
 
-                               scsi_esp_cmd(esp, ESP_CMD_TI);
-                       }
-               } else {
                        esp->ireg = esp_read8(ESP_INTRPT);
-                       if (esp->ireg & ESP_INTR_DC)
+                       if ((esp->ireg & (ESP_INTR_DC | ESP_INTR_BSERV)) !=
+                           ESP_INTR_BSERV)
                                break;
 
-                       n = MAC_ESP_FIFO_SIZE - n;
+                       n = MAC_ESP_FIFO_SIZE -
+                           (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES);
                        if (n > esp_count)
                                n = esp_count;
 
@@ -427,7 +432,7 @@ static void mac_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
                }
        }
 
-       local_irq_restore(flags);
+       enable_irq(esp->host->irq);
 }
 
 static int mac_esp_irq_pending(struct esp *esp)
@@ -442,6 +447,32 @@ static u32 mac_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
        return dma_len > 0xFFFF ? 0xFFFF : dma_len;
 }
 
+static irqreturn_t mac_scsi_esp_intr(int irq, void *dev_id)
+{
+       int got_intr;
+
+       /*
+        * This is an edge triggered IRQ, so we have to be careful to
+        * avoid missing a transition when it is shared by two ESP devices.
+        */
+
+       do {
+               got_intr = 0;
+               if (esp_chips[0] &&
+                   (mac_esp_read8(esp_chips[0], ESP_STATUS) & ESP_STAT_INTR)) {
+                       (void)scsi_esp_intr(irq, esp_chips[0]);
+                       got_intr = 1;
+               }
+               if (esp_chips[1] &&
+                   (mac_esp_read8(esp_chips[1], ESP_STATUS) & ESP_STAT_INTR)) {
+                       (void)scsi_esp_intr(irq, esp_chips[1]);
+                       got_intr = 1;
+               }
+       } while (got_intr);
+
+       return IRQ_HANDLED;
+}
+
 static struct esp_driver_ops mac_esp_ops = {
        .esp_write8       = mac_esp_write8,
        .esp_read8        = mac_esp_read8,
@@ -464,29 +495,12 @@ static int __devinit esp_mac_probe(struct platform_device *dev)
        struct Scsi_Host *host;
        struct esp *esp;
        int err;
-       int chips_present;
        struct mac_esp_priv *mep;
 
        if (!MACH_IS_MAC)
                return -ENODEV;
 
-       switch (macintosh_config->scsi_type) {
-       case MAC_SCSI_QUADRA:
-       case MAC_SCSI_QUADRA3:
-               chips_present = 1;
-               break;
-       case MAC_SCSI_QUADRA2:
-               if ((macintosh_config->ident == MAC_MODEL_Q900) ||
-                   (macintosh_config->ident == MAC_MODEL_Q950))
-                       chips_present = 2;
-               else
-                       chips_present = 1;
-               break;
-       default:
-               chips_present = 0;
-       }
-
-       if (dev->id + 1 > chips_present)
+       if (dev->id > 1)
                return -ENODEV;
 
        host = scsi_host_alloc(tpnt, sizeof(struct esp));
@@ -556,10 +570,16 @@ static int __devinit esp_mac_probe(struct platform_device *dev)
        }
 
        host->irq = IRQ_MAC_SCSI;
-       err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "Mac ESP",
-                         esp);
-       if (err < 0)
-               goto fail_free_priv;
+       esp_chips[dev->id] = esp;
+       mb();
+       if (esp_chips[!dev->id] == NULL) {
+               err = request_irq(host->irq, mac_scsi_esp_intr, 0,
+                                 "Mac ESP", NULL);
+               if (err < 0) {
+                       esp_chips[dev->id] = NULL;
+                       goto fail_free_priv;
+               }
+       }
 
        err = scsi_esp_register(esp, &dev->dev);
        if (err)
@@ -568,7 +588,8 @@ static int __devinit esp_mac_probe(struct platform_device *dev)
        return 0;
 
 fail_free_irq:
-       free_irq(host->irq, esp);
+       if (esp_chips[!dev->id] == NULL)
+               free_irq(host->irq, esp);
 fail_free_priv:
        kfree(mep);
 fail_free_command_block:
@@ -587,7 +608,9 @@ static int __devexit esp_mac_remove(struct platform_device *dev)
 
        scsi_esp_unregister(esp);
 
-       free_irq(irq, esp);
+       esp_chips[dev->id] = NULL;
+       if (!(esp_chips[0] || esp_chips[1]))
+               free_irq(irq, NULL);
 
        kfree(mep);
 
@@ -602,56 +625,26 @@ static struct platform_driver esp_mac_driver = {
        .probe    = esp_mac_probe,
        .remove   = __devexit_p(esp_mac_remove),
        .driver   = {
-               .name     = DRV_MODULE_NAME,
+               .name   = DRV_MODULE_NAME,
+               .owner  = THIS_MODULE,
        },
 };
 
 static int __init mac_esp_init(void)
 {
-       int err;
-
-       err = platform_driver_register(&esp_mac_driver);
-       if (err)
-               return err;
-
-       internal_esp = platform_device_alloc(DRV_MODULE_NAME, 0);
-       if (internal_esp && platform_device_add(internal_esp)) {
-               platform_device_put(internal_esp);
-               internal_esp = NULL;
-       }
-
-       external_esp = platform_device_alloc(DRV_MODULE_NAME, 1);
-       if (external_esp && platform_device_add(external_esp)) {
-               platform_device_put(external_esp);
-               external_esp = NULL;
-       }
-
-       if (internal_esp || external_esp) {
-               return 0;
-       } else {
-               platform_driver_unregister(&esp_mac_driver);
-               return -ENOMEM;
-       }
+       return platform_driver_register(&esp_mac_driver);
 }
 
 static void __exit mac_esp_exit(void)
 {
        platform_driver_unregister(&esp_mac_driver);
-
-       if (internal_esp) {
-               platform_device_unregister(internal_esp);
-               internal_esp = NULL;
-       }
-       if (external_esp) {
-               platform_device_unregister(external_esp);
-               external_esp = NULL;
-       }
 }
 
 MODULE_DESCRIPTION("Mac ESP SCSI driver");
 MODULE_AUTHOR("Finn Thain <fthain@telegraphics.com.au>");
 MODULE_LICENSE("GPL v2");
 MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:" DRV_MODULE_NAME);
 
 module_init(mac_esp_init);
 module_exit(mac_esp_exit);