libata: Correct IORDY handling
[cascardo/linux.git] / drivers / ata / libata-core.c
index 73f66f4..02425e4 100644 (file)
@@ -2171,7 +2171,7 @@ int ata_bus_probe(struct ata_port *ap)
        }
 
        /* configure transfer mode */
-       rc = ata_set_mode(ap, &dev);
+       rc = ata_set_mode(&ap->link, &dev);
        if (rc)
                goto fail;
 
@@ -2759,7 +2759,11 @@ static int ata_dev_set_mode(struct ata_device *dev)
        /* Old CFA may refuse this command, which is just fine */
        if (dev->xfer_shift == ATA_SHIFT_PIO && ata_id_is_cfa(dev->id))
                err_mask &= ~AC_ERR_DEV;
-
+       /* Some very old devices and some bad newer ones fail any kind of
+          SET_XFERMODE request but support PIO0-2 timings and no IORDY */
+       if (dev->xfer_shift == ATA_SHIFT_PIO && !ata_id_has_iordy(dev->id) &&
+                       dev->pio_mode <= XFER_PIO_2)
+               err_mask &= ~AC_ERR_DEV;
        if (err_mask) {
                ata_dev_printk(dev, KERN_ERR, "failed to set xfermode "
                               "(err_mask=0x%x)\n", err_mask);
@@ -2782,7 +2786,7 @@ static int ata_dev_set_mode(struct ata_device *dev)
 
 /**
  *     ata_do_set_mode - Program timings and issue SET FEATURES - XFER
- *     @ap: port on which timings will be programmed
+ *     @link: link on which timings will be programmed
  *     @r_failed_dev: out paramter for failed device
  *
  *     Standard implementation of the function used to tune and set
@@ -2797,9 +2801,9 @@ static int ata_dev_set_mode(struct ata_device *dev)
  *     0 on success, negative errno otherwise
  */
 
-int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
+int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
 {
-       struct ata_link *link = &ap->link;
+       struct ata_port *ap = link->ap;
        struct ata_device *dev;
        int rc = 0, used_dma = 0, found = 0;
 
@@ -2877,7 +2881,7 @@ int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
 
 /**
  *     ata_set_mode - Program timings and issue SET FEATURES - XFER
- *     @ap: port on which timings will be programmed
+ *     @link: link on which timings will be programmed
  *     @r_failed_dev: out paramter for failed device
  *
  *     Set ATA device disk transfer mode (PIO3, UDMA6, etc.).  If
@@ -2890,12 +2894,14 @@ int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
  *     RETURNS:
  *     0 on success, negative errno otherwise
  */
-int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
+int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
 {
+       struct ata_port *ap = link->ap;
+
        /* has private set_mode? */
        if (ap->ops->set_mode)
-               return ap->ops->set_mode(ap, r_failed_dev);
-       return ata_do_set_mode(ap, r_failed_dev);
+               return ap->ops->set_mode(link, r_failed_dev);
+       return ata_do_set_mode(link, r_failed_dev);
 }
 
 /**
@@ -3330,7 +3336,7 @@ int ata_std_prereset(struct ata_link *link, unsigned long deadline)
 
        /* handle link resume */
        if ((ehc->i.flags & ATA_EHI_RESUME_LINK) &&
-           (ap->flags & ATA_FLAG_HRST_TO_RESUME))
+           (link->flags & ATA_LFLAG_HRST_TO_RESUME))
                ehc->i.action |= ATA_EH_HARDRESET;
 
        /* if we're about to do hardreset, nothing more to do */
@@ -3349,7 +3355,7 @@ int ata_std_prereset(struct ata_link *link, unsigned long deadline)
        /* Wait for !BSY if the controller can wait for the first D2H
         * Reg FIS and we don't know that no device is attached.
         */
-       if (!(ap->flags & ATA_FLAG_SKIP_D2H_BSY) && !ata_link_offline(link)) {
+       if (!(link->flags & ATA_LFLAG_SKIP_D2H_BSY) && !ata_link_offline(link)) {
                rc = ata_wait_ready(ap, deadline);
                if (rc && rc != -ENODEV) {
                        ata_link_printk(link, KERN_WARNING, "device not ready "
@@ -5910,6 +5916,7 @@ static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg,
 
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap = host->ports[i];
+               struct ata_link *link;
 
                /* Previous resume operation might still be in
                 * progress.  Wait for PM_PENDING to clear.
@@ -5929,8 +5936,10 @@ static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg,
                }
 
                ap->pflags |= ATA_PFLAG_PM_PENDING;
-               ap->link.eh_info.action |= action;
-               ap->link.eh_info.flags |= ehi_flags;
+               __ata_port_for_each_link(link, ap) {
+                       link->eh_info.action |= action;
+                       link->eh_info.flags |= ehi_flags;
+               }
 
                ata_port_schedule_eh(ap);
 
@@ -6058,6 +6067,70 @@ void ata_dev_init(struct ata_device *dev)
        dev->udma_mask = UINT_MAX;
 }
 
+/**
+ *     ata_link_init - Initialize an ata_link structure
+ *     @ap: ATA port link is attached to
+ *     @link: Link structure to initialize
+ *     @pmp: Port multiplier port number
+ *
+ *     Initialize @link.
+ *
+ *     LOCKING:
+ *     Kernel thread context (may sleep)
+ */
+static void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
+{
+       int i;
+
+       /* clear everything except for devices */
+       memset(link, 0, offsetof(struct ata_link, device[0]));
+
+       link->ap = ap;
+       link->pmp = pmp;
+       link->active_tag = ATA_TAG_POISON;
+       link->hw_sata_spd_limit = UINT_MAX;
+
+       /* can't use iterator, ap isn't initialized yet */
+       for (i = 0; i < ATA_MAX_DEVICES; i++) {
+               struct ata_device *dev = &link->device[i];
+
+               dev->link = link;
+               dev->devno = dev - link->device;
+               ata_dev_init(dev);
+       }
+}
+
+/**
+ *     sata_link_init_spd - Initialize link->sata_spd_limit
+ *     @link: Link to configure sata_spd_limit for
+ *
+ *     Initialize @link->[hw_]sata_spd_limit to the currently
+ *     configured value.
+ *
+ *     LOCKING:
+ *     Kernel thread context (may sleep).
+ *
+ *     RETURNS:
+ *     0 on success, -errno on failure.
+ */
+static int sata_link_init_spd(struct ata_link *link)
+{
+       u32 scontrol, spd;
+       int rc;
+
+       rc = sata_scr_read(link, SCR_CONTROL, &scontrol);
+       if (rc)
+               return rc;
+
+       spd = (scontrol >> 4) & 0xf;
+       if (spd)
+               link->hw_sata_spd_limit &= (1 << spd) - 1;
+
+       link->sata_spd_limit = link->hw_sata_spd_limit;
+
+       return 0;
+}
+
 /**
  *     ata_port_alloc - allocate and initialize basic ATA port resources
  *     @host: ATA host this allocated port belongs to
@@ -6073,7 +6146,6 @@ void ata_dev_init(struct ata_device *dev)
 struct ata_port *ata_port_alloc(struct ata_host *host)
 {
        struct ata_port *ap;
-       unsigned int i;
 
        DPRINTK("ENTER\n");
 
@@ -6088,9 +6160,6 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
        ap->ctl = ATA_DEVCTL_OBS;
        ap->host = host;
        ap->dev = host->dev;
-
-       ap->link.hw_sata_spd_limit = UINT_MAX;
-       ap->link.active_tag = ATA_TAG_POISON;
        ap->last_ctl = 0xFF;
 
 #if defined(ATA_VERBOSE_DEBUG)
@@ -6113,15 +6182,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
 
        ap->cbl = ATA_CBL_NONE;
 
-       ap->link.ap = ap;
-
-       /* can't use iterator, ap isn't initialized yet */
-       for (i = 0; i < ATA_MAX_DEVICES; i++) {
-               struct ata_device *dev = &ap->link.device[i];
-               dev->link = &ap->link;
-               dev->devno = i;
-               ata_dev_init(dev);
-       }
+       ata_link_init(ap, &ap->link, 0);
 
 #ifdef ATA_IRQ_TRAP
        ap->stats.unhandled_irq = 1;
@@ -6267,6 +6328,7 @@ struct ata_host *ata_host_alloc_pinfo(struct device *dev,
                ap->mwdma_mask = pi->mwdma_mask;
                ap->udma_mask = pi->udma_mask;
                ap->flags |= pi->flags;
+               ap->link.flags |= pi->link_flags;
                ap->ops = pi->port_ops;
 
                if (!host->ops && (pi->port_ops != &ata_dummy_port_ops))
@@ -6403,7 +6465,6 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap = host->ports[i];
                int irq_line;
-               u32 scontrol;
                unsigned long xfer_mask;
 
                /* set SATA cable type if still unset */
@@ -6411,12 +6472,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
                        ap->cbl = ATA_CBL_SATA;
 
                /* init sata_spd_limit to the current value */
-               if (sata_scr_read(&ap->link, SCR_CONTROL, &scontrol) == 0) {
-                       int spd = (scontrol >> 4) & 0xf;
-                       if (spd)
-                               ap->link.hw_sata_spd_limit &= (1 << spd) - 1;
-               }
-               ap->link.sata_spd_limit = ap->link.hw_sata_spd_limit;
+               sata_link_init_spd(&ap->link);
 
                /* report the secondary IRQ for second channel legacy */
                irq_line = host->irq;
@@ -6555,6 +6611,7 @@ int ata_host_activate(struct ata_host *host, int irq,
 void ata_port_detach(struct ata_port *ap)
 {
        unsigned long flags;
+       struct ata_link *link;
        struct ata_device *dev;
 
        if (!ap->ops->error_handler)
@@ -6572,8 +6629,10 @@ void ata_port_detach(struct ata_port *ap)
         */
        spin_lock_irqsave(ap->lock, flags);
 
-       ata_link_for_each_dev(dev, &ap->link)
-               ata_dev_disable(dev);
+       ata_port_for_each_link(link, ap) {
+               ata_link_for_each_dev(dev, link)
+                       ata_dev_disable(dev);
+       }
 
        spin_unlock_irqrestore(ap->lock, flags);
 
@@ -7003,6 +7062,7 @@ EXPORT_SYMBOL_GPL(ata_ehi_push_desc);
 EXPORT_SYMBOL_GPL(ata_ehi_clear_desc);
 EXPORT_SYMBOL_GPL(ata_eng_timeout);
 EXPORT_SYMBOL_GPL(ata_port_schedule_eh);
+EXPORT_SYMBOL_GPL(ata_link_abort);
 EXPORT_SYMBOL_GPL(ata_port_abort);
 EXPORT_SYMBOL_GPL(ata_port_freeze);
 EXPORT_SYMBOL_GPL(ata_eh_freeze_port);