*/
#include <linux/kernel.h>
+#include <linux/pci.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_eh.h>
return nr_aborted;
}
+/**
+ * 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
* RETURNS:
* Descriptive string for @err_mask
*/
-static const char * ata_err_string(unsigned int err_mask)
+static const char *ata_err_string(unsigned int err_mask)
{
if (err_mask & AC_ERR_HOST_BUS)
return "host bus error";
tf.protocol = ATA_PROT_PIO;
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE,
- buf, sectors * ATA_SECT_SIZE);
+ buf, sectors * ATA_SECT_SIZE, 0);
DPRINTK("EXIT, err_mask=%x\n", err_mask);
return err_mask;
}
return ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
- sense_buf, SCSI_SENSE_BUFFERSIZE);
+ sense_buf, SCSI_SENSE_BUFFERSIZE, 0);
}
/**
struct ata_eh_context *ehc = &link->eh_context;
u32 serror = ehc->i.serror;
unsigned int err_mask = 0, action = 0;
+ u32 hotplug_mask;
if (serror & SERR_PERSISTENT) {
err_mask |= AC_ERR_ATA_BUS;
err_mask |= AC_ERR_SYSTEM;
action |= ATA_EH_HARDRESET;
}
- if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG))
+
+ /* Determine whether a hotplug event has occurred. Both
+ * SError.N/X are considered hotplug events for enabled or
+ * host links. For disabled PMP links, only N bit is
+ * considered as X bit is left at 1 for link plugging.
+ */
+ hotplug_mask = 0;
+
+ if (!(link->flags & ATA_LFLAG_DISABLED) || ata_is_host_link(link))
+ hotplug_mask = SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+ else
+ hotplug_mask = SERR_PHYRDY_CHG;
+
+ if (serror & hotplug_mask)
ata_ehi_hotplugged(&ehc->i);
ehc->i.err_mask |= err_mask;
char tries_buf[6];
int tag, nr_failed = 0;
+ if (ehc->i.flags & ATA_EHI_QUIET)
+ return;
+
desc = NULL;
if (ehc->i.desc[0] != '\0')
desc = ehc->i.desc;
ata_link_printk(link, KERN_ERR, "%s\n", desc);
}
+ if (ehc->i.serror)
+ ata_port_printk(ap, KERN_ERR,
+ "SError: { %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s}\n",
+ ehc->i.serror & SERR_DATA_RECOVERED ? "RecovData " : "",
+ ehc->i.serror & SERR_COMM_RECOVERED ? "RecovComm " : "",
+ ehc->i.serror & SERR_DATA ? "UnrecovData " : "",
+ ehc->i.serror & SERR_PERSISTENT ? "Persist " : "",
+ ehc->i.serror & SERR_PROTOCOL ? "Proto " : "",
+ ehc->i.serror & SERR_INTERNAL ? "HostInt " : "",
+ ehc->i.serror & SERR_PHYRDY_CHG ? "PHYRdyChg " : "",
+ ehc->i.serror & SERR_PHY_INT_ERR ? "PHYInt " : "",
+ ehc->i.serror & SERR_COMM_WAKE ? "CommWake " : "",
+ ehc->i.serror & SERR_10B_8B_ERR ? "10B8B " : "",
+ ehc->i.serror & SERR_DISPARITY ? "Dispar " : "",
+ ehc->i.serror & SERR_CRC ? "BadCRC " : "",
+ ehc->i.serror & SERR_HANDSHAKE ? "Handshk " : "",
+ ehc->i.serror & SERR_LINK_SEQ_ERR ? "LinkSeq " : "",
+ ehc->i.serror & SERR_TRANS_ST_ERROR ? "TrStaTrns " : "",
+ ehc->i.serror & SERR_UNRECOG_FIS ? "UnrecFIS " : "",
+ ehc->i.serror & SERR_DEV_XCHG ? "DevExch " : "");
+
for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
static const char *dma_str[] = {
[DMA_BIDIRECTIONAL] = "bidi",
res->hob_lbal, res->hob_lbam, res->hob_lbah,
res->device, qc->err_mask, ata_err_string(qc->err_mask),
qc->err_mask & AC_ERR_NCQ ? " <F>" : "");
+
+ if (res->command & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ |
+ ATA_ERR)) {
+ if (res->command & ATA_BUSY)
+ ata_dev_printk(qc->dev, KERN_ERR,
+ "status: { Busy }\n");
+ else
+ ata_dev_printk(qc->dev, KERN_ERR,
+ "status: { %s%s%s%s}\n",
+ res->command & ATA_DRDY ? "DRDY " : "",
+ res->command & ATA_DF ? "DF " : "",
+ res->command & ATA_DRQ ? "DRQ " : "",
+ res->command & ATA_ERR ? "ERR " : "");
+ }
+
+ if (cmd->command != ATA_CMD_PACKET &&
+ (res->feature & (ATA_ICRC | ATA_UNC | ATA_IDNF |
+ ATA_ABORTED)))
+ ata_dev_printk(qc->dev, KERN_ERR,
+ "error: { %s%s%s%s}\n",
+ res->feature & ATA_ICRC ? "ICRC " : "",
+ res->feature & ATA_UNC ? "UNC " : "",
+ res->feature & ATA_IDNF ? "IDNF " : "",
+ res->feature & ATA_ABORTED ? "ABRT " : "");
}
}
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;
ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
{
+ struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
unsigned int *classes = ehc->classes;
int verbose = !(ehc->i.flags & ATA_EHI_QUIET);
int try = 0;
struct ata_device *dev;
unsigned long deadline;
- unsigned int action;
+ unsigned int tmp_action;
ata_reset_fn_t reset;
+ unsigned long flags;
int rc;
/* about to reset */
+ spin_lock_irqsave(ap->lock, flags);
+ ap->pflags |= ATA_PFLAG_RESETTING;
+ spin_unlock_irqrestore(ap->lock, flags);
+
ata_eh_about_to_do(link, NULL, ehc->i.action & ATA_EH_RESET_MASK);
/* Determine which reset to use and record in ehc->i.action.
* prereset() may examine and modify it.
*/
- action = ehc->i.action;
- ehc->i.action &= ~ATA_EH_RESET_MASK;
if (softreset && (!hardreset || (!(link->flags & ATA_LFLAG_NO_SRST) &&
!sata_set_spd_needed(link) &&
- !(action & ATA_EH_HARDRESET))))
- ehc->i.action |= ATA_EH_SOFTRESET;
+ !(ehc->i.action & ATA_EH_HARDRESET))))
+ tmp_action = ATA_EH_SOFTRESET;
else
- ehc->i.action |= ATA_EH_HARDRESET;
+ tmp_action = ATA_EH_HARDRESET;
+
+ ehc->i.action = (ehc->i.action & ~ATA_EH_RESET_MASK) | tmp_action;
if (prereset) {
rc = prereset(link, jiffies + ATA_EH_PRERESET_TIMEOUT);
if (rc == -EAGAIN)
rc = 0;
- if (rc && try < ARRAY_SIZE(ata_eh_reset_timeouts)) {
+ if (rc && rc != -ERESTART && try < ARRAY_SIZE(ata_eh_reset_timeouts)) {
unsigned long now = jiffies;
if (time_before(now, deadline)) {
"(errno=%d), retrying in %u secs\n",
rc, (jiffies_to_msecs(delta) + 999) / 1000);
- schedule_timeout_uninterruptible(delta);
+ while (delta)
+ delta = schedule_timeout_uninterruptible(delta);
}
if (rc == -EPIPE ||
out:
/* clear hotplug flag */
ehc->i.flags &= ~ATA_EHI_HOTPLUGGED;
+
+ spin_lock_irqsave(ap->lock, flags);
+ ap->pflags &= ~ATA_PFLAG_RESETTING;
+ spin_unlock_irqrestore(ap->lock, flags);
+
return rc;
}
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;
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;
* 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;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev;
+ /* skip disabled links */
+ if (link->flags & ATA_LFLAG_DISABLED)
+ return 1;
+
/* thaw frozen port, resume link and recover failed devices */
if ((link->ap->pflags & ATA_PFLAG_FROZEN) ||
(ehc->i.flags & ATA_EHI_RESUME_LINK) || ata_link_nr_enabled(link))
struct ata_device *dev;
int nr_failed_devs, nr_disabled_devs;
int reset, rc;
+ unsigned long flags;
DPRINTK("ENTER\n");
ata_port_for_each_link(link, ap) {
struct ata_eh_context *ehc = &link->eh_context;
+ /* re-enable link? */
+ if (ehc->i.action & ATA_EH_ENABLE_LINK) {
+ ata_eh_about_to_do(link, NULL, ATA_EH_ENABLE_LINK);
+ spin_lock_irqsave(ap->lock, flags);
+ link->flags &= ~ATA_LFLAG_DISABLED;
+ spin_unlock_irqrestore(ap->lock, flags);
+ ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK);
+ }
+
ata_link_for_each_dev(dev, link) {
if (link->flags & ATA_LFLAG_NO_RETRY)
ehc->tries[dev->devno] = 1;
/* reset */
if (reset) {
- ata_eh_freeze_port(ap);
+ /* if PMP is attached, this function only deals with
+ * downstream links, port should stay thawed.
+ */
+ if (!ap->nr_pmp_links)
+ ata_eh_freeze_port(ap);
ata_port_for_each_link(link, ap) {
struct ata_eh_context *ehc = &link->eh_context;
}
}
- ata_eh_thaw_port(ap);
+ if (!ap->nr_pmp_links)
+ ata_eh_thaw_port(ap);
}
/* the rest */
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);
ehc->i.flags = 0;
continue;
- dev_fail:
+dev_fail:
nr_failed_devs++;
if (ata_eh_handle_dev_fail(dev, rc))
nr_disabled_devs++;
- if (ap->pflags & ATA_PFLAG_FROZEN)
+ if (ap->pflags & ATA_PFLAG_FROZEN) {
+ /* PMP reset requires working host port.
+ * Can't retry if it's frozen.
+ */
+ if (ap->nr_pmp_links)
+ goto out;
break;
+ }
}
if (nr_failed_devs) {