Some changes around hot-plug and interface power-management:
- use ATA_SE_EXCHANGED (SError.DIAG.X) bit to detect hot-plug events when power-management enabled and ATA_SE_PHY_CHANGED (SError.DIAG.N) can't be trusted; - on controllers supporting staggered spin-up (SS) put unused channels into Listen state instead of Off. It should still save some power, but allow plug-in events to be detected; - on controllers supporting cold presence detection (CPD), when power management enabled, use CPD events to detect hot-plug in addition to PHY events.
This commit is contained in:
parent
900999dc14
commit
2a9c0a02ca
@ -1272,34 +1272,66 @@ ahci_slotsfree(device_t dev)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
ahci_phy_check_events(device_t dev, u_int32_t serr)
|
||||
{
|
||||
struct ahci_channel *ch = device_get_softc(dev);
|
||||
|
||||
if ((serr & ATA_SE_PHY_CHANGED) && (ch->pm_level == 0)) {
|
||||
if (((ch->pm_level == 0) && (serr & ATA_SE_PHY_CHANGED)) ||
|
||||
((ch->pm_level != 0 || ch->listening) && (serr & ATA_SE_EXCHANGED))) {
|
||||
u_int32_t status = ATA_INL(ch->r_mem, AHCI_P_SSTS);
|
||||
union ccb *ccb;
|
||||
|
||||
if (bootverbose) {
|
||||
if (((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_ONLINE) &&
|
||||
((status & ATA_SS_SPD_MASK) != ATA_SS_SPD_NO_SPEED) &&
|
||||
((status & ATA_SS_IPM_MASK) == ATA_SS_IPM_ACTIVE)) {
|
||||
if ((status & ATA_SS_DET_MASK) != ATA_SS_DET_NO_DEVICE)
|
||||
device_printf(dev, "CONNECT requested\n");
|
||||
} else
|
||||
else
|
||||
device_printf(dev, "DISCONNECT requested\n");
|
||||
}
|
||||
ahci_reset(dev);
|
||||
if ((ccb = xpt_alloc_ccb_nowait()) == NULL)
|
||||
return;
|
||||
return (0);
|
||||
if (xpt_create_path(&ccb->ccb_h.path, NULL,
|
||||
cam_sim_path(ch->sim),
|
||||
CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
|
||||
xpt_free_ccb(ccb);
|
||||
return;
|
||||
return (0);
|
||||
}
|
||||
xpt_rescan(ccb);
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
ahci_cpd_check_events(device_t dev)
|
||||
{
|
||||
struct ahci_channel *ch = device_get_softc(dev);
|
||||
u_int32_t status;
|
||||
union ccb *ccb;
|
||||
|
||||
if (ch->pm_level == 0)
|
||||
return;
|
||||
|
||||
status = ATA_INL(ch->r_mem, AHCI_P_CMD);
|
||||
if ((status & AHCI_P_CMD_CPD) == 0)
|
||||
return;
|
||||
|
||||
if (bootverbose) {
|
||||
if (status & AHCI_P_CMD_CPS) {
|
||||
device_printf(dev, "COLD CONNECT requested\n");
|
||||
} else
|
||||
device_printf(dev, "COLD DISCONNECT requested\n");
|
||||
}
|
||||
ahci_reset(dev);
|
||||
if ((ccb = xpt_alloc_ccb_nowait()) == NULL)
|
||||
return;
|
||||
if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim),
|
||||
CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
|
||||
xpt_free_ccb(ccb);
|
||||
return;
|
||||
}
|
||||
xpt_rescan(ccb);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1359,7 +1391,7 @@ ahci_ch_intr(void *data)
|
||||
struct ahci_channel *ch = device_get_softc(dev);
|
||||
uint32_t istatus, sstatus, cstatus, serr = 0, sntf = 0, ok, err;
|
||||
enum ahci_err_type et;
|
||||
int i, ccs, port;
|
||||
int i, ccs, port, reset = 0;
|
||||
|
||||
/* Read and clear interrupt statuses. */
|
||||
istatus = ATA_INL(ch->r_mem, AHCI_P_IS);
|
||||
@ -1395,9 +1427,12 @@ ahci_ch_intr(void *data)
|
||||
serr = ATA_INL(ch->r_mem, AHCI_P_SERR);
|
||||
if (serr) {
|
||||
ATA_OUTL(ch->r_mem, AHCI_P_SERR, serr);
|
||||
ahci_phy_check_events(dev, serr);
|
||||
reset = ahci_phy_check_events(dev, serr);
|
||||
}
|
||||
}
|
||||
/* Process cold presence detection events */
|
||||
if ((istatus & AHCI_P_IX_CPD) && !reset)
|
||||
ahci_cpd_check_events(dev);
|
||||
/* Process command errors */
|
||||
if (istatus & (AHCI_P_IX_OF | AHCI_P_IX_IF |
|
||||
AHCI_P_IX_HBD | AHCI_P_IX_HBF | AHCI_P_IX_TFE)) {
|
||||
@ -2446,7 +2481,8 @@ ahci_reset(device_t dev)
|
||||
ch->devices = 0;
|
||||
/* Enable wanted port interrupts */
|
||||
ATA_OUTL(ch->r_mem, AHCI_P_IE,
|
||||
(AHCI_P_IX_CPD | AHCI_P_IX_PRC | AHCI_P_IX_PC));
|
||||
(((ch->pm_level != 0) ? AHCI_P_IX_CPD | AHCI_P_IX_MP : 0) |
|
||||
AHCI_P_IX_PRC | AHCI_P_IX_PC));
|
||||
xpt_release_simq(ch->sim, TRUE);
|
||||
return;
|
||||
}
|
||||
@ -2463,9 +2499,10 @@ ahci_reset(device_t dev)
|
||||
ch->devices = 1;
|
||||
/* Enable wanted port interrupts */
|
||||
ATA_OUTL(ch->r_mem, AHCI_P_IE,
|
||||
(AHCI_P_IX_CPD | AHCI_P_IX_TFE | AHCI_P_IX_HBF |
|
||||
(((ch->pm_level != 0) ? AHCI_P_IX_CPD | AHCI_P_IX_MP : 0) |
|
||||
AHCI_P_IX_TFE | AHCI_P_IX_HBF |
|
||||
AHCI_P_IX_HBD | AHCI_P_IX_IF | AHCI_P_IX_OF |
|
||||
((ch->pm_level == 0) ? AHCI_P_IX_PRC | AHCI_P_IX_PC : 0) |
|
||||
((ch->pm_level == 0) ? AHCI_P_IX_PRC : 0) | AHCI_P_IX_PC |
|
||||
AHCI_P_IX_DP | AHCI_P_IX_UF | (ctlr->ccc ? 0 : AHCI_P_IX_SDB) |
|
||||
AHCI_P_IX_DS | AHCI_P_IX_PS | (ctlr->ccc ? 0 : AHCI_P_IX_DHR)));
|
||||
if (ch->resetting)
|
||||
@ -2570,6 +2607,12 @@ ahci_sata_phy_reset(device_t dev)
|
||||
int sata_rev;
|
||||
uint32_t val;
|
||||
|
||||
if (ch->listening) {
|
||||
val = ATA_INL(ch->r_mem, AHCI_P_CMD);
|
||||
val |= AHCI_P_CMD_SUD;
|
||||
ATA_OUTL(ch->r_mem, AHCI_P_CMD, val);
|
||||
ch->listening = 0;
|
||||
}
|
||||
sata_rev = ch->user[ch->pm_present ? 15 : 0].revision;
|
||||
if (sata_rev == 1)
|
||||
val = ATA_SC_SPD_SPEED_GEN1;
|
||||
@ -2588,7 +2631,12 @@ ahci_sata_phy_reset(device_t dev)
|
||||
(ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER)));
|
||||
DELAY(5000);
|
||||
if (!ahci_sata_connect(ch)) {
|
||||
if (ch->pm_level > 0)
|
||||
if (ch->caps & AHCI_CAP_SSS) {
|
||||
val = ATA_INL(ch->r_mem, AHCI_P_CMD);
|
||||
val &= ~AHCI_P_CMD_SUD;
|
||||
ATA_OUTL(ch->r_mem, AHCI_P_CMD, val);
|
||||
ch->listening = 1;
|
||||
} else if (ch->pm_level > 0)
|
||||
ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_DISABLE);
|
||||
return (0);
|
||||
}
|
||||
|
@ -118,6 +118,7 @@
|
||||
#define ATA_SE_LINKSEQ_ERR 0x00800000
|
||||
#define ATA_SE_TRANSPORT_ERR 0x01000000
|
||||
#define ATA_SE_UNKNOWN_FIS 0x02000000
|
||||
#define ATA_SE_EXCHANGED 0x04000000
|
||||
|
||||
#define ATA_SCONTROL 15
|
||||
#define ATA_SC_DET_MASK 0x0000000f
|
||||
@ -221,7 +222,7 @@
|
||||
#define AHCI_P_IX_UF 0x00000010
|
||||
#define AHCI_P_IX_DP 0x00000020
|
||||
#define AHCI_P_IX_PC 0x00000040
|
||||
#define AHCI_P_IX_DI 0x00000080
|
||||
#define AHCI_P_IX_MP 0x00000080
|
||||
|
||||
#define AHCI_P_IX_PRC 0x00400000
|
||||
#define AHCI_P_IX_IPM 0x00800000
|
||||
@ -413,6 +414,7 @@ struct ahci_channel {
|
||||
int lastslot; /* Last used slot */
|
||||
int taggedtarget; /* Last tagged target */
|
||||
int resetting; /* Hard-reset in progress. */
|
||||
int listening; /* SUD bit is cleared. */
|
||||
union ccb *frozen; /* Frozen command */
|
||||
struct callout pm_timer; /* Power management events */
|
||||
struct callout reset_timer; /* Hard-reset timeout */
|
||||
|
Loading…
Reference in New Issue
Block a user