ahci: more robustness against flaky devices
- if supported, override BSY/DRQ flags and start the port anyway if it times out with DET=3h status during device detection; - no longer rely on the device signature to be set; unless a valid signature is obtained, try both ATA and ATAPI device identification.
This commit is contained in:
parent
eb89b33d39
commit
9d89e72a5c
|
@ -127,6 +127,7 @@ static struct {
|
|||
int nr_ports; /* addressable number of ports (1..NR_PORTS) */
|
||||
int nr_cmds; /* maximum number of commands per port */
|
||||
int has_ncq; /* NCQ support flag */
|
||||
int has_clo; /* CLO support flag */
|
||||
|
||||
int irq; /* IRQ number */
|
||||
int hook_id; /* IRQ hook ID */
|
||||
|
@ -1232,6 +1233,25 @@ static void port_hardreset(struct port_state *ps)
|
|||
port_write(ps, AHCI_PORT_SCTL, AHCI_PORT_SCTL_DET_NONE);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* port_override *
|
||||
*===========================================================================*/
|
||||
static void port_override(struct port_state *ps)
|
||||
{
|
||||
/* Override the port's BSY and/or DRQ flags. This may only be done
|
||||
* prior to starting the port.
|
||||
*/
|
||||
u32_t cmd;
|
||||
|
||||
cmd = port_read(ps, AHCI_PORT_CMD);
|
||||
port_write(ps, AHCI_PORT_CMD, cmd | AHCI_PORT_CMD_CLO);
|
||||
|
||||
SPIN_UNTIL(!(port_read(ps, AHCI_PORT_CMD) & AHCI_PORT_CMD_CLO),
|
||||
PORTREG_DELAY);
|
||||
|
||||
dprintf(V_INFO, ("%s: overridden\n", ahci_portname(ps)));
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* port_start *
|
||||
*===========================================================================*/
|
||||
|
@ -1350,9 +1370,21 @@ static void port_id_check(struct port_state *ps, int success)
|
|||
ps->flags &= ~FLAG_BUSY;
|
||||
cancel_timer(&ps->cmd_info[0].timer);
|
||||
|
||||
if (!success)
|
||||
if (!success) {
|
||||
if (!(ps->flags & FLAG_ATAPI) &&
|
||||
port_read(ps, AHCI_PORT_SIG) != ATA_SIG_ATA) {
|
||||
dprintf(V_INFO, ("%s: may not be ATA, trying ATAPI\n",
|
||||
ahci_portname(ps)));
|
||||
|
||||
ps->flags |= FLAG_ATAPI;
|
||||
|
||||
(void) gen_identify(ps, FALSE /*blocking*/);
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf(V_ERR,
|
||||
("%s: unable to identify\n", ahci_portname(ps)));
|
||||
}
|
||||
|
||||
/* If the identify command itself succeeded, check the results and
|
||||
* store some properties.
|
||||
|
@ -1436,24 +1468,6 @@ static void port_connect(struct port_state *ps)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Check the port's signature. We only support the normal ATA and ATAPI
|
||||
* signatures. We ignore devices reporting anything else.
|
||||
*/
|
||||
sig = port_read(ps, AHCI_PORT_SIG);
|
||||
|
||||
if (sig != ATA_SIG_ATA && sig != ATA_SIG_ATAPI) {
|
||||
dprintf(V_ERR, ("%s: unsupported signature (%08x)\n",
|
||||
ahci_portname(ps), sig));
|
||||
|
||||
port_stop(ps);
|
||||
|
||||
ps->state = STATE_BAD_DEV;
|
||||
port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PRCE);
|
||||
ps->flags &= ~FLAG_BUSY;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear all state flags except the busy flag, which may be relevant if
|
||||
* a BDEV_OPEN call is waiting for the device to become ready; the
|
||||
* barrier flag, which prevents access to the device until it is
|
||||
|
@ -1461,6 +1475,12 @@ static void port_connect(struct port_state *ps)
|
|||
*/
|
||||
ps->flags &= (FLAG_BUSY | FLAG_BARRIER | FLAG_SUSPENDED);
|
||||
|
||||
/* Check the port's signature. We only use the signature to speed up
|
||||
* identification; we will try both ATA and ATAPI if the signature is
|
||||
* neither ATA nor ATAPI.
|
||||
*/
|
||||
sig = port_read(ps, AHCI_PORT_SIG);
|
||||
|
||||
if (sig == ATA_SIG_ATAPI)
|
||||
ps->flags |= FLAG_ATAPI;
|
||||
|
||||
|
@ -1548,6 +1568,18 @@ static void port_dev_check(struct port_state *ps)
|
|||
* device present at all. In all cases, we change to another state.
|
||||
*/
|
||||
if (status == AHCI_PORT_SSTS_DET_PHY) {
|
||||
/* Some devices may not correctly clear BSY/DRQ. Upon timeout,
|
||||
* if we can override these flags, do so and start the
|
||||
* identification process anyway.
|
||||
*/
|
||||
if (hba_state.has_clo) {
|
||||
port_override(ps);
|
||||
|
||||
port_connect(ps);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* A device is present and initialized, but not ready. */
|
||||
ps->state = STATE_BAD_DEV;
|
||||
port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PRCE);
|
||||
|
@ -1569,6 +1601,7 @@ static void port_intr(struct port_state *ps)
|
|||
/* Process an interrupt on this port.
|
||||
*/
|
||||
u32_t smask, emask;
|
||||
int success;
|
||||
|
||||
if (ps->state == STATE_NO_PORT) {
|
||||
dprintf(V_ERR, ("%s: interrupt for invalid port!\n",
|
||||
|
@ -1660,12 +1693,8 @@ static void port_intr(struct port_state *ps)
|
|||
* later by obtaining per-command status results from the HBA.
|
||||
*/
|
||||
|
||||
/* If we were waiting for ID verification, check now. */
|
||||
if (ps->state == STATE_WAIT_ID) {
|
||||
port_id_check(ps, !(port_read(ps, AHCI_PORT_TFD) &
|
||||
(AHCI_PORT_TFD_STS_ERR |
|
||||
AHCI_PORT_TFD_STS_DF)));
|
||||
}
|
||||
success = !(port_read(ps, AHCI_PORT_TFD) &
|
||||
(AHCI_PORT_TFD_STS_ERR | AHCI_PORT_TFD_STS_DF));
|
||||
|
||||
/* Check now for failure. There are fatal failures, and there
|
||||
* are failures that set the TFD.STS.ERR field using a D2H
|
||||
|
@ -1677,6 +1706,10 @@ static void port_intr(struct port_state *ps)
|
|||
(smask & AHCI_PORT_IS_RESTART)) {
|
||||
port_restart(ps);
|
||||
}
|
||||
|
||||
/* If we were waiting for ID verification, check now. */
|
||||
if (ps->state == STATE_WAIT_ID)
|
||||
port_id_check(ps, success);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2087,6 +2120,7 @@ static void ahci_init(int devind)
|
|||
/* Note that we currently use only one command anyway. */
|
||||
cap = hba_read(AHCI_HBA_CAP);
|
||||
hba_state.has_ncq = !!(cap & AHCI_HBA_CAP_SNCQ);
|
||||
hba_state.has_clo = !!(cap & AHCI_HBA_CAP_SCLO);
|
||||
hba_state.nr_cmds = MIN(NR_CMDS,
|
||||
((cap >> AHCI_HBA_CAP_NCS_SHIFT) & AHCI_HBA_CAP_NCS_MASK) + 1);
|
||||
|
||||
|
|
|
@ -192,6 +192,7 @@
|
|||
#define AHCI_PORT_CMD_CR (1L << 15) /* Cmd List Running */
|
||||
#define AHCI_PORT_CMD_FR (1L << 14) /* FIS Recv Running */
|
||||
#define AHCI_PORT_CMD_FRE (1L << 4) /* FIS Recv Enabled */
|
||||
#define AHCI_PORT_CMD_CLO (1L << 3) /* Cmd List Override */
|
||||
#define AHCI_PORT_CMD_SUD (1L << 1) /* Spin-Up Device */
|
||||
#define AHCI_PORT_CMD_ST (1L << 0) /* Start */
|
||||
#define AHCI_PORT_TFD 8 /* Task File Data */
|
||||
|
|
Loading…
Reference in a new issue