Skip to content

Commit

Permalink
Merge branch 'for-4.5-fixes' of git://git.kernel.org/pub/scm/linux/ke…
Browse files Browse the repository at this point in the history
…rnel/git/tj/libata

Pull libata fixes from Tejun Heo:

 - PORTS_IMPL workaround for very early ahci controllers is misbehaving
   on new systems.  Disabled on recent ahci versions.

 - Old-style PIO state machine had a horrible locking problem.  Don't
   know how we've been getting away this far.  Fixed.

 - Other device specific updates.

* 'for-4.5-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata:
  ahci: Intel DNV device IDs SATA
  libata: fix sff host state machine locking while polling
  libata-sff: use WARN instead of BUG on illegal host state machine state
  libata: disable forced PORTS_IMPL for >= AHCI 1.3
  libata: blacklist a Viking flash model for MWDMA corruption
  drivers: ata: wake port before DMA stop for ALPM
  • Loading branch information
torvalds committed Feb 10, 2016
2 parents fb0dc5f + 342decf commit 4e54169
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 25 deletions.
20 changes: 20 additions & 0 deletions drivers/ata/ahci.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,26 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */
{ PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */
{ PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */
{ PCI_VDEVICE(INTEL, 0x19b0), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19b1), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19b2), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19b3), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19b4), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19b5), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19b6), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19b7), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19bE), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19bF), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19c0), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19c1), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19c2), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19c3), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19c4), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19c5), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19c6), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19c7), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19cE), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x19cF), board_ahci }, /* DNV AHCI */
{ PCI_VDEVICE(INTEL, 0x1c02), board_ahci }, /* CPT AHCI */
{ PCI_VDEVICE(INTEL, 0x1c03), board_ahci }, /* CPT AHCI */
{ PCI_VDEVICE(INTEL, 0x1c04), board_ahci }, /* CPT RAID */
Expand Down
1 change: 1 addition & 0 deletions drivers/ata/ahci.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ enum {
AHCI_HFLAG_MULTI_MSI = 0,
AHCI_HFLAG_MULTI_MSIX = 0,
#endif
AHCI_HFLAG_WAKE_BEFORE_STOP = (1 << 22), /* wake before DMA stop */

/* ap->flags bits */

Expand Down
1 change: 1 addition & 0 deletions drivers/ata/ahci_brcmstb.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ static int brcm_ahci_probe(struct platform_device *pdev)
if (IS_ERR(hpriv))
return PTR_ERR(hpriv);
hpriv->plat_data = priv;
hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP;

brcm_sata_alpm_init(hpriv);

Expand Down
27 changes: 24 additions & 3 deletions drivers/ata/libahci.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,8 +496,8 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
}
}

/* fabricate port_map from cap.nr_ports */
if (!port_map) {
/* fabricate port_map from cap.nr_ports for < AHCI 1.3 */
if (!port_map && vers < 0x10300) {
port_map = (1 << ahci_nr_ports(cap)) - 1;
dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map);

Expand Down Expand Up @@ -593,8 +593,22 @@ EXPORT_SYMBOL_GPL(ahci_start_engine);
int ahci_stop_engine(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_host_priv *hpriv = ap->host->private_data;
u32 tmp;

/*
* On some controllers, stopping a port's DMA engine while the port
* is in ALPM state (partial or slumber) results in failures on
* subsequent DMA engine starts. For those controllers, put the
* port back in active state before stopping its DMA engine.
*/
if ((hpriv->flags & AHCI_HFLAG_WAKE_BEFORE_STOP) &&
(ap->link.lpm_policy > ATA_LPM_MAX_POWER) &&
ahci_set_lpm(&ap->link, ATA_LPM_MAX_POWER, ATA_LPM_WAKE_ONLY)) {
dev_err(ap->host->dev, "Failed to wake up port before engine stop\n");
return -EIO;
}

tmp = readl(port_mmio + PORT_CMD);

/* check if the HBA is idle */
Expand Down Expand Up @@ -689,6 +703,9 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
void __iomem *port_mmio = ahci_port_base(ap);

if (policy != ATA_LPM_MAX_POWER) {
/* wakeup flag only applies to the max power policy */
hints &= ~ATA_LPM_WAKE_ONLY;

/*
* Disable interrupts on Phy Ready. This keeps us from
* getting woken up due to spurious phy ready
Expand All @@ -704,14 +721,18 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
u32 cmd = readl(port_mmio + PORT_CMD);

if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) {
cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
if (!(hints & ATA_LPM_WAKE_ONLY))
cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
cmd |= PORT_CMD_ICC_ACTIVE;

writel(cmd, port_mmio + PORT_CMD);
readl(port_mmio + PORT_CMD);

/* wait 10ms to be sure we've come out of LPM state */
ata_msleep(ap, 10);

if (hints & ATA_LPM_WAKE_ONLY)
return 0;
} else {
cmd |= PORT_CMD_ALPE;
if (policy == ATA_LPM_MIN_POWER)
Expand Down
1 change: 1 addition & 0 deletions drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -4125,6 +4125,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
{ "SAMSUNG CD-ROM SN-124", "N001", ATA_HORKAGE_NODMA },
{ "Seagate STT20000A", NULL, ATA_HORKAGE_NODMA },
{ " 2GB ATA Flash Disk", "ADMA428M", ATA_HORKAGE_NODMA },
{ "VRFDFC22048UCHC-TE*", NULL, ATA_HORKAGE_NODMA },
/* Odd clown on sil3726/4726 PMPs */
{ "Config Disk", NULL, ATA_HORKAGE_DISABLE },

Expand Down
35 changes: 13 additions & 22 deletions drivers/ata/libata-sff.c
Original file line number Diff line number Diff line change
Expand Up @@ -997,12 +997,9 @@ static inline int ata_hsm_ok_in_wq(struct ata_port *ap,
static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq)
{
struct ata_port *ap = qc->ap;
unsigned long flags;

if (ap->ops->error_handler) {
if (in_wq) {
spin_lock_irqsave(ap->lock, flags);

/* EH might have kicked in while host lock is
* released.
*/
Expand All @@ -1014,8 +1011,6 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq)
} else
ata_port_freeze(ap);
}

spin_unlock_irqrestore(ap->lock, flags);
} else {
if (likely(!(qc->err_mask & AC_ERR_HSM)))
ata_qc_complete(qc);
Expand All @@ -1024,10 +1019,8 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq)
}
} else {
if (in_wq) {
spin_lock_irqsave(ap->lock, flags);
ata_sff_irq_on(ap);
ata_qc_complete(qc);
spin_unlock_irqrestore(ap->lock, flags);
} else
ata_qc_complete(qc);
}
Expand All @@ -1048,9 +1041,10 @@ int ata_sff_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
{
struct ata_link *link = qc->dev->link;
struct ata_eh_info *ehi = &link->eh_info;
unsigned long flags = 0;
int poll_next;

lockdep_assert_held(ap->lock);

WARN_ON_ONCE((qc->flags & ATA_QCFLAG_ACTIVE) == 0);

/* Make sure ata_sff_qc_issue() does not throw things
Expand Down Expand Up @@ -1112,14 +1106,6 @@ int ata_sff_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
}
}

/* Send the CDB (atapi) or the first data block (ata pio out).
* During the state transition, interrupt handler shouldn't
* be invoked before the data transfer is complete and
* hsm_task_state is changed. Hence, the following locking.
*/
if (in_wq)
spin_lock_irqsave(ap->lock, flags);

if (qc->tf.protocol == ATA_PROT_PIO) {
/* PIO data out protocol.
* send first data block.
Expand All @@ -1135,9 +1121,6 @@ int ata_sff_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
/* send CDB */
atapi_send_cdb(ap, qc);

if (in_wq)
spin_unlock_irqrestore(ap->lock, flags);

/* if polling, ata_sff_pio_task() handles the rest.
* otherwise, interrupt handler takes over from here.
*/
Expand Down Expand Up @@ -1296,7 +1279,8 @@ int ata_sff_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
break;
default:
poll_next = 0;
BUG();
WARN(true, "ata%d: SFF host state machine in invalid state %d",
ap->print_id, ap->hsm_task_state);
}

return poll_next;
Expand Down Expand Up @@ -1361,12 +1345,14 @@ static void ata_sff_pio_task(struct work_struct *work)
u8 status;
int poll_next;

spin_lock_irq(ap->lock);

BUG_ON(ap->sff_pio_task_link == NULL);
/* qc can be NULL if timeout occurred */
qc = ata_qc_from_tag(ap, link->active_tag);
if (!qc) {
ap->sff_pio_task_link = NULL;
return;
goto out_unlock;
}

fsm_start:
Expand All @@ -1381,11 +1367,14 @@ static void ata_sff_pio_task(struct work_struct *work)
*/
status = ata_sff_busy_wait(ap, ATA_BUSY, 5);
if (status & ATA_BUSY) {
spin_unlock_irq(ap->lock);
ata_msleep(ap, 2);
spin_lock_irq(ap->lock);

status = ata_sff_busy_wait(ap, ATA_BUSY, 10);
if (status & ATA_BUSY) {
ata_sff_queue_pio_task(link, ATA_SHORT_PAUSE);
return;
goto out_unlock;
}
}

Expand All @@ -1402,6 +1391,8 @@ static void ata_sff_pio_task(struct work_struct *work)
*/
if (poll_next)
goto fsm_start;
out_unlock:
spin_unlock_irq(ap->lock);
}

/**
Expand Down
1 change: 1 addition & 0 deletions include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ enum ata_lpm_policy {
enum ata_lpm_hints {
ATA_LPM_EMPTY = (1 << 0), /* port empty/probing */
ATA_LPM_HIPM = (1 << 1), /* may use HIPM */
ATA_LPM_WAKE_ONLY = (1 << 2), /* only wake up link */
};

/* forward declarations */
Expand Down

0 comments on commit 4e54169

Please sign in to comment.