libata-pmp: hook PMP support and enable it
[safe/jmp/linux-2.6] / drivers / ata / libata-eh.c
index 3c31e10..5f2c0f3 100644 (file)
@@ -905,6 +905,79 @@ int ata_port_freeze(struct ata_port *ap)
 }
 
 /**
+ *     sata_async_notification - SATA async notification handler
+ *     @ap: ATA port where async notification is received
+ *
+ *     Handler to be called when async notification via SDB FIS is
+ *     received.  This function schedules EH if necessary.
+ *
+ *     LOCKING:
+ *     spin_lock_irqsave(host lock)
+ *
+ *     RETURNS:
+ *     1 if EH is scheduled, 0 otherwise.
+ */
+int sata_async_notification(struct ata_port *ap)
+{
+       u32 sntf;
+       int rc;
+
+       if (!(ap->flags & ATA_FLAG_AN))
+               return 0;
+
+       rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf);
+       if (rc == 0)
+               sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf);
+
+       if (!ap->nr_pmp_links || rc) {
+               /* PMP is not attached or SNTF is not available */
+               if (!ap->nr_pmp_links) {
+                       /* PMP is not attached.  Check whether ATAPI
+                        * AN is configured.  If so, notify media
+                        * change.
+                        */
+                       struct ata_device *dev = ap->link.device;
+
+                       if ((dev->class == ATA_DEV_ATAPI) &&
+                           (dev->flags & ATA_DFLAG_AN))
+                               ata_scsi_media_change_notify(dev);
+                       return 0;
+               } else {
+                       /* PMP is attached but SNTF is not available.
+                        * ATAPI async media change notification is
+                        * not used.  The PMP must be reporting PHY
+                        * status change, schedule EH.
+                        */
+                       ata_port_schedule_eh(ap);
+                       return 1;
+               }
+       } else {
+               /* PMP is attached and SNTF is available */
+               struct ata_link *link;
+
+               /* check and notify ATAPI AN */
+               ata_port_for_each_link(link, ap) {
+                       if (!(sntf & (1 << link->pmp)))
+                               continue;
+
+                       if ((link->device->class == ATA_DEV_ATAPI) &&
+                           (link->device->flags & ATA_DFLAG_AN))
+                               ata_scsi_media_change_notify(link->device);
+               }
+
+               /* If PMP is reporting that PHY status of some
+                * downstream ports has changed, schedule EH.
+                */
+               if (sntf & (1 << SATA_PMP_CTRL_PORT)) {
+                       ata_port_schedule_eh(ap);
+                       return 1;
+               }
+
+               return 0;
+       }
+}
+
+/**
  *     ata_eh_freeze_port - EH helper to freeze port
  *     @ap: ATA port to freeze
  *
@@ -1930,6 +2003,8 @@ static int ata_eh_followup_srst_needed(struct ata_link *link,
                return 1;
        if (rc != 0)
                return 0;
+       if ((link->ap->flags & ATA_FLAG_PMP) && ata_is_host_link(link))
+               return 1;
        if (classify && !(link->flags & ATA_LFLAG_ASSUME_CLASS) &&
            classes[0] == ATA_DEV_UNKNOWN)
                return 1;
@@ -2134,6 +2209,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
                        readid_flags |= ATA_READID_POSTRESET;
 
                if ((action & ATA_EH_REVALIDATE) && ata_dev_enabled(dev)) {
+                       WARN_ON(dev->class == ATA_DEV_PMP);
+
                        if (ata_link_offline(link)) {
                                rc = -EIO;
                                goto err;
@@ -2159,8 +2236,11 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
                           ata_class_enabled(ehc->classes[dev->devno])) {
                        dev->class = ehc->classes[dev->devno];
 
-                       rc = ata_dev_read_id(dev, &dev->class, readid_flags,
-                                            dev->id);
+                       if (dev->class == ATA_DEV_PMP)
+                               rc = sata_pmp_attach(dev);
+                       else
+                               rc = ata_dev_read_id(dev, &dev->class,
+                                                    readid_flags, dev->id);
                        switch (rc) {
                        case 0:
                                new_mask |= 1 << dev->devno;
@@ -2189,7 +2269,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
         * device detection messages backwards.
         */
        ata_link_for_each_dev(dev, link) {
-               if (!(new_mask & (1 << dev->devno)))
+               if (!(new_mask & (1 << dev->devno)) ||
+                   dev->class == ATA_DEV_PMP)
                        continue;
 
                ehc->i.flags |= ATA_EHI_PRINTINFO;
@@ -2446,6 +2527,12 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
                if (rc)
                        goto dev_fail;
 
+               /* if PMP got attached, return, pmp EH will take care of it */
+               if (link->device->class == ATA_DEV_PMP) {
+                       ehc->i.action = 0;
+                       return 0;
+               }
+
                /* configure transfer mode if necessary */
                if (ehc->i.flags & ATA_EHI_SETMODE) {
                        rc = ata_set_mode(link, &dev);