pata_radisys: fix UDMA handling
[safe/jmp/linux-2.6] / drivers / ata / pata_cmd64x.c
index 1c9a8d9..dadfc35 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * pata_cmd64x.c       - CMD64x PATA for new ATA layer
  *                       (C) 2005 Red Hat Inc
- *                       Alan Cox <alan@redhat.com>
+ *                       Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  * Based upon
  * linux/drivers/ide/pci/cmd64x.c              Version 1.30    Sept 10, 2002
@@ -31,7 +31,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME "pata_cmd64x"
-#define DRV_VERSION "0.2.5"
+#define DRV_VERSION "0.3.1"
 
 /*
  * CMD64x specific registers definition.
@@ -254,181 +254,177 @@ static void cmd648_bmdma_stop(struct ata_queued_cmd *qc)
 }
 
 /**
- *     cmd646r1_dma_stop       -       DMA stop callback
+ *     cmd64x_bmdma_stop       -       DMA stop callback
  *     @qc: Command in progress
  *
- *     Stub for now while investigating the r1 quirk in the old driver.
+ *     Track the completion of live DMA commands and clear the
+ *     host->private_data DMA tracking flag as we do.
  */
 
-static void cmd646r1_bmdma_stop(struct ata_queued_cmd *qc)
+static void cmd64x_bmdma_stop(struct ata_queued_cmd *qc)
 {
+       struct ata_port *ap = qc->ap;
        ata_bmdma_stop(qc);
+       WARN_ON(ap->host->private_data != ap);
+       ap->host->private_data = NULL;
 }
 
-static struct scsi_host_template cmd64x_sht = {
-       .module                 = THIS_MODULE,
-       .name                   = DRV_NAME,
-       .ioctl                  = ata_scsi_ioctl,
-       .queuecommand           = ata_scsi_queuecmd,
-       .can_queue              = ATA_DEF_QUEUE,
-       .this_id                = ATA_SHT_THIS_ID,
-       .sg_tablesize           = LIBATA_MAX_PRD,
-       .cmd_per_lun            = ATA_SHT_CMD_PER_LUN,
-       .emulated               = ATA_SHT_EMULATED,
-       .use_clustering         = ATA_SHT_USE_CLUSTERING,
-       .proc_name              = DRV_NAME,
-       .dma_boundary           = ATA_DMA_BOUNDARY,
-       .slave_configure        = ata_scsi_slave_config,
-       .slave_destroy          = ata_scsi_slave_destroy,
-       .bios_param             = ata_std_bios_param,
-};
+/**
+ *     cmd64x_qc_defer         -       Defer logic for chip limits
+ *     @qc: queued command
+ *
+ *     Decide whether we can issue the command. Called under the host lock.
+ */
 
-static struct ata_port_operations cmd64x_port_ops = {
-       .set_piomode    = cmd64x_set_piomode,
-       .set_dmamode    = cmd64x_set_dmamode,
-       .mode_filter    = ata_pci_default_filter,
-       .tf_load        = ata_tf_load,
-       .tf_read        = ata_tf_read,
-       .check_status   = ata_check_status,
-       .exec_command   = ata_exec_command,
-       .dev_select     = ata_std_dev_select,
-
-       .freeze         = ata_bmdma_freeze,
-       .thaw           = ata_bmdma_thaw,
-       .error_handler  = ata_bmdma_error_handler,
-       .post_internal_cmd = ata_bmdma_post_internal_cmd,
-       .cable_detect   = ata_cable_40wire,
+static int cmd64x_qc_defer(struct ata_queued_cmd *qc)
+{
+       struct ata_host *host = qc->ap->host;
+       struct ata_port *alt = host->ports[1 ^ qc->ap->port_no];
+       int rc;
+       int dma = 0;
 
-       .bmdma_setup    = ata_bmdma_setup,
-       .bmdma_start    = ata_bmdma_start,
-       .bmdma_stop     = ata_bmdma_stop,
-       .bmdma_status   = ata_bmdma_status,
+       /* Apply the ATA rules first */
+       rc = ata_std_qc_defer(qc);
+       if (rc)
+               return rc;
 
-       .qc_prep        = ata_qc_prep,
-       .qc_issue       = ata_qc_issue_prot,
+       if (qc->tf.protocol == ATAPI_PROT_DMA ||
+                       qc->tf.protocol == ATA_PROT_DMA)
+               dma = 1;
 
-       .data_xfer      = ata_data_xfer,
+       /* If the other port is not live then issue the command */
+       if (alt == NULL || !alt->qc_active) {
+               if (dma)
+                       host->private_data = qc->ap;
+               return 0;
+       }
+       /* If there is a live DMA command then wait */
+       if (host->private_data != NULL)
+               return  ATA_DEFER_PORT;
+       if (dma)
+               /* Cannot overlap our DMA command */
+               return ATA_DEFER_PORT;
+       return 0;
+}
 
-       .irq_handler    = ata_interrupt,
-       .irq_clear      = ata_bmdma_irq_clear,
-       .irq_on         = ata_irq_on,
+/**
+ *     cmd64x_interrupt - ATA host interrupt handler
+ *     @irq: irq line (unused)
+ *     @dev_instance: pointer to our ata_host information structure
+ *
+ *     Our interrupt handler for PCI IDE devices.  Calls
+ *     ata_sff_host_intr() for each port that is flagging an IRQ. We cannot
+ *     use the defaults as we need to avoid touching status/altstatus during
+ *     a DMA.
+ *
+ *     LOCKING:
+ *     Obtains host lock during operation.
+ *
+ *     RETURNS:
+ *     IRQ_NONE or IRQ_HANDLED.
+ */
+irqreturn_t cmd64x_interrupt(int irq, void *dev_instance)
+{
+       struct ata_host *host = dev_instance;
+       struct pci_dev *pdev = to_pci_dev(host->dev);
+       unsigned int i;
+       unsigned int handled = 0;
+       unsigned long flags;
+       static const u8 irq_reg[2] = { CFR, ARTTIM23 };
+       static const u8 irq_mask[2] = { 1 << 2, 1 << 4 };
+
+       /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */
+       spin_lock_irqsave(&host->lock, flags);
+
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_port *ap;
+               u8 reg;
+
+               pci_read_config_byte(pdev, irq_reg[i], &reg);
+               ap = host->ports[i];
+               if (ap && (reg & irq_mask[i]) &&
+                   !(ap->flags & ATA_FLAG_DISABLED)) {
+                       struct ata_queued_cmd *qc;
+
+                       qc = ata_qc_from_tag(ap, ap->link.active_tag);
+                       if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) &&
+                           (qc->flags & ATA_QCFLAG_ACTIVE))
+                               handled |= ata_sff_host_intr(ap, qc);
+               }
+       }
+
+       spin_unlock_irqrestore(&host->lock, flags);
 
-       .port_start     = ata_port_start,
+       return IRQ_RETVAL(handled);
+}
+static struct scsi_host_template cmd64x_sht = {
+       ATA_BMDMA_SHT(DRV_NAME),
 };
 
-static struct ata_port_operations cmd646r1_port_ops = {
+static const struct ata_port_operations cmd64x_base_ops = {
+       .inherits       = &ata_bmdma_port_ops,
        .set_piomode    = cmd64x_set_piomode,
        .set_dmamode    = cmd64x_set_dmamode,
-       .mode_filter    = ata_pci_default_filter,
-       .tf_load        = ata_tf_load,
-       .tf_read        = ata_tf_read,
-       .check_status   = ata_check_status,
-       .exec_command   = ata_exec_command,
-       .dev_select     = ata_std_dev_select,
-
-       .freeze         = ata_bmdma_freeze,
-       .thaw           = ata_bmdma_thaw,
-       .error_handler  = ata_bmdma_error_handler,
-       .post_internal_cmd = ata_bmdma_post_internal_cmd,
-       .cable_detect   = ata_cable_40wire,
-
-       .bmdma_setup    = ata_bmdma_setup,
-       .bmdma_start    = ata_bmdma_start,
-       .bmdma_stop     = cmd646r1_bmdma_stop,
-       .bmdma_status   = ata_bmdma_status,
-
-       .qc_prep        = ata_qc_prep,
-       .qc_issue       = ata_qc_issue_prot,
-
-       .data_xfer      = ata_data_xfer,
+       .bmdma_stop     = cmd64x_bmdma_stop,
+       .qc_defer       = cmd64x_qc_defer,
+};
 
-       .irq_handler    = ata_interrupt,
-       .irq_clear      = ata_bmdma_irq_clear,
-       .irq_on         = ata_irq_on,
+static struct ata_port_operations cmd64x_port_ops = {
+       .inherits       = &cmd64x_base_ops,
+       .cable_detect   = ata_cable_40wire,
+};
 
-       .port_start     = ata_port_start,
+static struct ata_port_operations cmd646r1_port_ops = {
+       .inherits       = &cmd64x_base_ops,
+       .cable_detect   = ata_cable_40wire,
 };
 
 static struct ata_port_operations cmd648_port_ops = {
-       .set_piomode    = cmd64x_set_piomode,
-       .set_dmamode    = cmd64x_set_dmamode,
-       .mode_filter    = ata_pci_default_filter,
-       .tf_load        = ata_tf_load,
-       .tf_read        = ata_tf_read,
-       .check_status   = ata_check_status,
-       .exec_command   = ata_exec_command,
-       .dev_select     = ata_std_dev_select,
-
-       .freeze         = ata_bmdma_freeze,
-       .thaw           = ata_bmdma_thaw,
-       .error_handler  = ata_bmdma_error_handler,
-       .post_internal_cmd = ata_bmdma_post_internal_cmd,
-       .cable_detect   = cmd648_cable_detect,
-
-       .bmdma_setup    = ata_bmdma_setup,
-       .bmdma_start    = ata_bmdma_start,
+       .inherits       = &cmd64x_base_ops,
        .bmdma_stop     = cmd648_bmdma_stop,
-       .bmdma_status   = ata_bmdma_status,
-
-       .qc_prep        = ata_qc_prep,
-       .qc_issue       = ata_qc_issue_prot,
-
-       .data_xfer      = ata_data_xfer,
-
-       .irq_handler    = ata_interrupt,
-       .irq_clear      = ata_bmdma_irq_clear,
-       .irq_on         = ata_irq_on,
-
-       .port_start     = ata_port_start,
+       .cable_detect   = cmd648_cable_detect,
+       .qc_defer       = ata_std_qc_defer
 };
 
 static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
 {
-       u32 class_rev;
-
        static const struct ata_port_info cmd_info[6] = {
                {       /* CMD 643 - no UDMA */
-                       .sht = &cmd64x_sht,
                        .flags = ATA_FLAG_SLAVE_POSS,
-                       .pio_mask = 0x1f,
-                       .mwdma_mask = 0x07,
+                       .pio_mask = ATA_PIO4,
+                       .mwdma_mask = ATA_MWDMA2,
                        .port_ops = &cmd64x_port_ops
                },
                {       /* CMD 646 with broken UDMA */
-                       .sht = &cmd64x_sht,
                        .flags = ATA_FLAG_SLAVE_POSS,
-                       .pio_mask = 0x1f,
-                       .mwdma_mask = 0x07,
+                       .pio_mask = ATA_PIO4,
+                       .mwdma_mask = ATA_MWDMA2,
                        .port_ops = &cmd64x_port_ops
                },
                {       /* CMD 646 with working UDMA */
-                       .sht = &cmd64x_sht,
                        .flags = ATA_FLAG_SLAVE_POSS,
-                       .pio_mask = 0x1f,
-                       .mwdma_mask = 0x07,
+                       .pio_mask = ATA_PIO4,
+                       .mwdma_mask = ATA_MWDMA2,
                        .udma_mask = ATA_UDMA2,
                        .port_ops = &cmd64x_port_ops
                },
                {       /* CMD 646 rev 1  */
-                       .sht = &cmd64x_sht,
                        .flags = ATA_FLAG_SLAVE_POSS,
-                       .pio_mask = 0x1f,
-                       .mwdma_mask = 0x07,
+                       .pio_mask = ATA_PIO4,
+                       .mwdma_mask = ATA_MWDMA2,
                        .port_ops = &cmd646r1_port_ops
                },
                {       /* CMD 648 */
-                       .sht = &cmd64x_sht,
                        .flags = ATA_FLAG_SLAVE_POSS,
-                       .pio_mask = 0x1f,
-                       .mwdma_mask = 0x07,
+                       .pio_mask = ATA_PIO4,
+                       .mwdma_mask = ATA_MWDMA2,
                        .udma_mask = ATA_UDMA4,
                        .port_ops = &cmd648_port_ops
                },
                {       /* CMD 649 */
-                       .sht = &cmd64x_sht,
                        .flags = ATA_FLAG_SLAVE_POSS,
-                       .pio_mask = 0x1f,
-                       .mwdma_mask = 0x07,
+                       .pio_mask = ATA_PIO4,
+                       .mwdma_mask = ATA_MWDMA2,
                        .udma_mask = ATA_UDMA5,
                        .port_ops = &cmd648_port_ops
                }
@@ -436,40 +432,43 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
        const struct ata_port_info *ppi[] = { &cmd_info[id->driver_data], NULL };
        u8 mrdmode;
        int rc;
+       struct ata_host *host;
 
        rc = pcim_enable_device(pdev);
        if (rc)
                return rc;
 
-       pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class_rev);
-       class_rev &= 0xFF;
-
        if (id->driver_data == 0)       /* 643 */
-               ata_pci_clear_simplex(pdev);
+               ata_pci_bmdma_clear_simplex(pdev);
 
        if (pdev->device == PCI_DEVICE_ID_CMD_646) {
                /* Does UDMA work ? */
-               if (class_rev > 4)
+               if (pdev->revision > 4)
                        ppi[0] = &cmd_info[2];
                /* Early rev with other problems ? */
-               else if (class_rev == 1)
+               else if (pdev->revision == 1)
                        ppi[0] = &cmd_info[3];
        }
 
+
        pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
        pci_read_config_byte(pdev, MRDMODE, &mrdmode);
        mrdmode &= ~ 0x30;      /* IRQ set up */
        mrdmode |= 0x02;        /* Memory read line enable */
        pci_write_config_byte(pdev, MRDMODE, mrdmode);
 
-       /* Force PIO 0 here.. */
-
        /* PPC specific fixup copied from old driver */
 #ifdef CONFIG_PPC
        pci_write_config_byte(pdev, UDIDETCR0, 0xF0);
 #endif
+       rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+       if (rc)
+               return rc;
+       /* We use this pointer to track the AP which has DMA running */
+       host->private_data = NULL;
 
-       return ata_pci_init_one(pdev, ppi);
+       pci_set_master(pdev);
+       return ata_pci_sff_activate_host(host, cmd64x_interrupt, &cmd64x_sht);
 }
 
 #ifdef CONFIG_PM