Skip to content

Commit

Permalink
libata: use ata_exec_internal() for PMP register access
Browse files Browse the repository at this point in the history
PMP registers used to be accessed with dedicated accessors ->pmp_read
and ->pmp_write.  During reset, those callbacks are called with the
port frozen so they should be able to run without depending on
interrupt delivery.  To achieve this, they were implemented polling.

However, as resetting the host port makes the PMP to isolate fan-out
ports until SError.X is cleared, resetting fan-out ports while port is
frozen doesn't buy much additional safety.

This patch updates libata PMP support such that PMP registers are
accessed using regular ata_exec_internal() mechanism and kills
->pmp_read/write() callbacks.  The following changes are made.

* PMP access helpers - sata_pmp_read_init_tf(), sata_pmp_read_val(),
  sata_pmp_write_init_tf() are folded into sata_pmp_read/write() which
  are now standalone PMP register access functions.

* sata_pmp_read/write() returns err_mask instead of rc.  This is
  consistent with other functions which issue internal commands and
  allows more detailed error reporting.

* ahci interrupt handler is modified to ignore BAD_PMP and
  spurious/illegal completion IRQs while reset is in progress.  These
  conditions are expected during reset.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
  • Loading branch information
Tejun Heo authored and Jeff Garzik committed Oct 12, 2007
1 parent afaa5c3 commit b06ce3e
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 197 deletions.
48 changes: 12 additions & 36 deletions drivers/ata/ahci.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,6 @@ static void ahci_freeze(struct ata_port *ap);
static void ahci_thaw(struct ata_port *ap);
static void ahci_pmp_attach(struct ata_port *ap);
static void ahci_pmp_detach(struct ata_port *ap);
static int ahci_pmp_read(struct ata_device *dev, int pmp, int reg, u32 *r_val);
static int ahci_pmp_write(struct ata_device *dev, int pmp, int reg, u32 val);
static void ahci_error_handler(struct ata_port *ap);
static void ahci_vt8251_error_handler(struct ata_port *ap);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
Expand Down Expand Up @@ -297,8 +295,6 @@ static const struct ata_port_operations ahci_ops = {

.pmp_attach = ahci_pmp_attach,
.pmp_detach = ahci_pmp_detach,
.pmp_read = ahci_pmp_read,
.pmp_write = ahci_pmp_write,

#ifdef CONFIG_PM
.port_suspend = ahci_port_suspend,
Expand Down Expand Up @@ -333,8 +329,6 @@ static const struct ata_port_operations ahci_vt8251_ops = {

.pmp_attach = ahci_pmp_attach,
.pmp_detach = ahci_pmp_detach,
.pmp_read = ahci_pmp_read,
.pmp_write = ahci_pmp_write,

#ifdef CONFIG_PM
.port_suspend = ahci_port_suspend,
Expand Down Expand Up @@ -1421,12 +1415,17 @@ static void ahci_port_intr(struct ata_port *ap)
struct ata_eh_info *ehi = &ap->link.eh_info;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
u32 status, qc_active;
int rc, known_irq = 0;

status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);

/* ignore BAD_PMP while resetting */
if (unlikely(resetting))
status &= ~PORT_IRQ_BAD_PMP;

if (unlikely(status & PORT_IRQ_ERROR)) {
ahci_error_intr(ap, status);
return;
Expand Down Expand Up @@ -1464,6 +1463,13 @@ static void ahci_port_intr(struct ata_port *ap)
qc_active = readl(port_mmio + PORT_CMD_ISSUE);

rc = ata_qc_complete_multiple(ap, qc_active, NULL);

/* If resetting, spurious or invalid completions are expected,
* return unconditionally.
*/
if (resetting)
return;

if (rc > 0)
return;
if (rc < 0) {
Expand Down Expand Up @@ -1701,36 +1707,6 @@ static void ahci_pmp_detach(struct ata_port *ap)
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}

static int ahci_pmp_read(struct ata_device *dev, int pmp, int reg, u32 *r_val)
{
struct ata_port *ap = dev->link->ap;
struct ata_taskfile tf;
int rc;

ahci_kick_engine(ap, 0);

sata_pmp_read_init_tf(&tf, dev, pmp, reg);
rc = ahci_exec_polled_cmd(ap, SATA_PMP_CTRL_PORT, &tf, 1, 0,
SATA_PMP_SCR_TIMEOUT);
if (rc == 0) {
ahci_tf_read(ap, &tf);
*r_val = sata_pmp_read_val(&tf);
}
return rc;
}

static int ahci_pmp_write(struct ata_device *dev, int pmp, int reg, u32 val)
{
struct ata_port *ap = dev->link->ap;
struct ata_taskfile tf;

ahci_kick_engine(ap, 0);

sata_pmp_write_init_tf(&tf, dev, pmp, reg, val);
return ahci_exec_polled_cmd(ap, SATA_PMP_CTRL_PORT, &tf, 1, 0,
SATA_PMP_SCR_TIMEOUT);
}

static int ahci_port_resume(struct ata_port *ap)
{
ahci_power_up(ap);
Expand Down
3 changes: 0 additions & 3 deletions drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -7354,9 +7354,6 @@ EXPORT_SYMBOL_GPL(ata_pci_clear_simplex);
#endif /* CONFIG_PCI */

EXPORT_SYMBOL_GPL(sata_pmp_qc_defer_cmd_switch);
EXPORT_SYMBOL_GPL(sata_pmp_read_init_tf);
EXPORT_SYMBOL_GPL(sata_pmp_read_val);
EXPORT_SYMBOL_GPL(sata_pmp_write_init_tf);
EXPORT_SYMBOL_GPL(sata_pmp_std_prereset);
EXPORT_SYMBOL_GPL(sata_pmp_std_hardreset);
EXPORT_SYMBOL_GPL(sata_pmp_std_postreset);
Expand Down
17 changes: 14 additions & 3 deletions drivers/ata/libata-eh.c
Original file line number Diff line number Diff line change
Expand Up @@ -2557,7 +2557,11 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,

/* 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;
Expand All @@ -2575,7 +2579,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
}
}

ata_eh_thaw_port(ap);
if (!ap->nr_pmp_links)
ata_eh_thaw_port(ap);
}

/* the rest */
Expand Down Expand Up @@ -2610,8 +2615,14 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
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) {
Expand Down
Loading

0 comments on commit b06ce3e

Please sign in to comment.