diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2011-07-14 16:24:01 +0200 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2011-07-24 23:28:57 -0400 |
commit | 6f85049296d63d8e21946d0bb927047d66aaa16a (patch) | |
tree | 84b70a52380da82227e92f71933ca1cadc1d86b4 | |
parent | 07532971328210fc6182d4f72b00147294dd484a (diff) | |
download | seabios-6f85049296d63d8e21946d0bb927047d66aaa16a.zip seabios-6f85049296d63d8e21946d0bb927047d66aaa16a.tar.gz seabios-6f85049296d63d8e21946d0bb927047d66aaa16a.tar.bz2 |
ahci: add error recovery code
By Scott Duplichan.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-rw-r--r-- | src/ahci.c | 50 |
1 files changed, 47 insertions, 3 deletions
@@ -105,7 +105,7 @@ static void ahci_port_writel(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg, u32 val static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi, void *buffer, u32 bsize) { - u32 val, status, success, flags, intbits; + u32 val, status, success, flags, intbits, error; struct ahci_ctrl_s *ctrl = GET_GLOBAL(port->ctrl); struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd); struct ahci_fis_s *fis = GET_GLOBAL(port->fis); @@ -141,10 +141,12 @@ static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi, ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits); if (intbits & 0x02) { status = GET_FLATPTR(fis->psfis[2]); + error = GET_FLATPTR(fis->psfis[3]); break; } if (intbits & 0x01) { status = GET_FLATPTR(fis->rfis[2]); + error = GET_FLATPTR(fis->rfis[3]); break; } } @@ -157,8 +159,50 @@ static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi, success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR)) && ATA_CB_STAT_RDY == (status & (ATA_CB_STAT_RDY))); - dprintf(2, "AHCI/%d: ... finished, status 0x%x, %s\n", pnr, - status, success ? "OK" : "ERROR"); + if (success) { + dprintf(2, "AHCI/%d: ... finished, status 0x%x, OK\n", pnr, + status); + } else { + dprintf(2, "AHCI/%d: ... finished, status 0x%x, ERROR 0x%x\n", pnr, + status, error); + + // non-queued error recovery (AHCI 1.3 section 6.2.2.1) + // Clears PxCMD.ST to 0 to reset the PxCI register + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + ahci_port_writel(ctrl, pnr, PORT_CMD, val & ~PORT_CMD_START); + + // waits for PxCMD.CR to clear to 0 + while (1) { + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + if ((val & PORT_CMD_LIST_ON) == 0) + break; + yield(); + } + + // Clears any error bits in PxSERR to enable capturing new errors + val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR); + ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val); + + // Clears status bits in PxIS as appropriate + val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val); + + // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to 1, issue + // a COMRESET to the device to put it in an idle state + val = ahci_port_readl(ctrl, pnr, PORT_TFDATA); + if (val & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ)) { + dprintf(2, "AHCI/%d: issue comreset\n", pnr); + val = ahci_port_readl(ctrl, pnr, PORT_SCR_CTL); + // set Device Detection Initialization (DET) to 1 for 1 ms for comreset + ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val | 1); + mdelay (1); + ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val); + } + + // Sets PxCMD.ST to 1 to enable issuing new commands + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START); + } return success ? 0 : -1; } |