#include "ahci.h"
static void ahci_pm_dummy_done(struct ata_xfer *xa);
int
ahci_pm_port_init(struct ahci_port *ap, struct ata_port *at)
{
at->at_probe = ATA_PROBE_NEED_HARD_RESET;
return (0);
}
int
ahci_pm_port_probe(struct ahci_port *ap, int orig_error)
{
struct ahci_cmd_hdr *cmd_slot;
struct ata_port *at;
struct ahci_ccb *ccb = NULL;
u_int8_t *fis = NULL;
int error;
u_int32_t cmd;
u_int32_t fbs;
int count;
int rstcount;
int i;
int fbsmode;
int sig;
count = 2;
rstcount = 2;
retry:
fbsmode = 0;
ahci_port_stop(ap, 0);
ap->ap_state = AP_S_NORMAL;
cmd = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC;
#if 1
if ((cmd & AHCI_PREG_CMD_PMA) == 0) {
cmd |= AHCI_PREG_CMD_PMA;
ahci_pwrite(ap, AHCI_PREG_CMD, cmd);
}
#endif
if (ap->ap_sc->sc_cap & AHCI_REG_CAP_FBSS) {
const char *str1 = "";
const char *str2 = "";
const char *str3 = "";
if (cmd & AHCI_PREG_CMD_FBSCP) {
str1 = "HW indicates support";
} else if (ap->ap_sc->sc_flags & AHCI_F_FORCE_FBSS) {
cmd |= AHCI_PREG_CMD_FBSCP;
str1 = "HW indicates no-support, force";
} else {
str1 = "HW indicates no-support";
}
if (cmd & AHCI_PREG_CMD_FBSCP) {
fbs = ahci_pread(ap, AHCI_PREG_FBS);
ahci_pwrite(ap, AHCI_PREG_FBS, fbs | AHCI_PREG_FBS_EN);
fbs = ahci_pread(ap, AHCI_PREG_FBS);
if (fbs & AHCI_PREG_FBS_EN) {
str2 = ", enable succeeded";
fbsmode = 1;
} else {
str2 = ", enable failed";
}
ahci_pwrite(ap, AHCI_PREG_FBS, fbs & ~AHCI_PREG_FBS_EN);
}
kprintf("%s: FBS Cap detected: %s%s%s\n",
PORTNAME(ap), str1, str2, str3);
}
if (fbsmode == 0 && (ap->ap_flags & AP_F_FBSS_ENABLED)) {
ap->ap_flags &= ~AP_F_FBSS_ENABLED;
ahci_pwrite(ap, AHCI_PREG_FBS, 0);
}
ahci_flush_tfd(ap);
ahci_port_clo(ap);
if (ahci_port_start(ap)) {
kprintf("%s: PMPROBE cannot start port\n",
PORTNAME(ap));
error = EIO;
goto err;
}
if (ahci_pwait_clr(ap, AHCI_PREG_TFD,
AHCI_PREG_TFD_STS_BSY | AHCI_PREG_TFD_STS_DRQ)) {
if (--rstcount) {
int pmdetect;
kprintf("%s: PMPROBE BUSY, comreset and retry\n",
PORTNAME(ap));
ahci_comreset(ap, &pmdetect);
if (pmdetect)
goto retry;
}
kprintf("%s: PMPROBE BUSY, giving up\n",
PORTNAME(ap));
error = EBUSY;
goto err;
}
ccb = ahci_get_err_ccb(ap);
ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_SILENT;
ccb->ccb_xa.complete = ahci_pm_dummy_done;
ccb->ccb_xa.at = ap->ap_ata[15];
cmd_slot = ccb->ccb_cmd_hdr;
KKASSERT(ccb->ccb_slot == 1);
fis = ccb->ccb_cmd_table->cfis;
bzero(fis, sizeof(ccb->ccb_cmd_table->cfis));
fis[0] = ATA_FIS_TYPE_H2D;
fis[1] = 0x0F;
fis[15] = ATA_FIS_CONTROL_SRST | ATA_FIS_CONTROL_4BIT;
cmd_slot->prdtl = 0;
cmd_slot->flags = htole16(5);
cmd_slot->flags |= htole16(AHCI_CMD_LIST_FLAG_C);
cmd_slot->flags |= htole16(AHCI_CMD_LIST_FLAG_R);
cmd_slot->flags |= htole16(AHCI_CMD_LIST_FLAG_PMP);
ccb->ccb_xa.state = ATA_S_PENDING;
if (bootverbose) {
kprintf("%s: PMPROBE PreStatus 0x%pb%i\n", PORTNAME(ap),
AHCI_PFMT_TFD_STS, ahci_pread(ap, AHCI_PREG_TFD));
}
if (ahci_poll(ccb, 1000, ahci_quick_timeout) != ATA_S_COMPLETE) {
kprintf("%s: PMPROBE(1) No Port Multiplier was found.\n",
PORTNAME(ap));
if (--count) {
rstcount = 2;
ahci_put_err_ccb(ccb);
goto retry;
}
error = EBUSY;
goto err;
}
if (bootverbose) {
kprintf("%s: PMPROBE PosStatus 0x%pb%i\n", PORTNAME(ap),
AHCI_PFMT_TFD_STS, ahci_pread(ap, AHCI_PREG_TFD));
}
#if 0
if (ahci_pwait_clr(ap, AHCI_PREG_TFD,
AHCI_PREG_TFD_STS_BSY | AHCI_PREG_TFD_STS_DRQ)) {
kprintf("%s: PMPROBE Busy after first FIS\n", PORTNAME(ap));
} else if (bootverbose) {
kprintf("%s: PMPROBE Clean after first FIS\n", PORTNAME(ap));
}
#endif
ahci_flush_tfd(ap);
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_SILENT;
bzero(fis, sizeof(ccb->ccb_cmd_table->cfis));
fis[0] = ATA_FIS_TYPE_H2D;
fis[1] = 0x0F;
fis[15] = ATA_FIS_CONTROL_4BIT;
cmd_slot->prdtl = 0;
cmd_slot->flags = htole16(5);
cmd_slot->flags |= htole16(AHCI_CMD_LIST_FLAG_PMP);
ccb->ccb_xa.state = ATA_S_PENDING;
if (ahci_poll(ccb, 5000, ahci_quick_timeout) != ATA_S_COMPLETE) {
kprintf("%s: PMPROBE(2) No Port Multiplier was found.\n",
PORTNAME(ap));
if (--count) {
rstcount = 2;
ahci_put_err_ccb(ccb);
goto retry;
}
error = EBUSY;
goto err;
}
ahci_os_sleep(500);
sig = ahci_port_signature_detect(ap, NULL);
if (sig == ATA_PORT_T_PM) {
kprintf("%s: PMPROBE PM Signature detected\n",
PORTNAME(ap));
ap->ap_ata[15]->at_probe = ATA_PROBE_GOOD;
error = 0;
} else if (--count == 0) {
kprintf("%s: PMPROBE PM Signature not detected\n",
PORTNAME(ap));
error = EBUSY;
} else {
rstcount = 2;
fis[15] = 0;
ahci_put_err_ccb(ccb);
if (bootverbose) {
kprintf("%s: PMPROBE retry on count\n",
PORTNAME(ap));
}
goto retry;
}
err:
if (ccb != NULL)
ahci_put_err_ccb(ccb);
if (error == 0 && ahci_pm_identify(ap)) {
ahci_os_sleep(500);
if (ahci_pm_identify(ap)) {
kprintf("%s: PM - cannot identify port multiplier\n",
PORTNAME(ap));
error = EBUSY;
} else {
kprintf("%s: PM - Had to identify twice\n",
PORTNAME(ap));
}
}
if (error == 0 && fbsmode) {
ahci_port_stop(ap, 0);
ap->ap_flags |= AP_F_FBSS_ENABLED;
fbs = ahci_pread(ap, AHCI_PREG_FBS);
fbs &= ~AHCI_PREG_FBS_DEV;
fbs |= AHCI_PREG_FBS_DEC;
ahci_pwrite(ap, AHCI_PREG_FBS, fbs | AHCI_PREG_FBS_EN);
ahci_os_sleep(100);
if (ahci_port_start(ap)) {
kprintf("%s: PMPROBE failed to restart port "
"after FBS enable\n",
PORTNAME(ap));
ahci_pwrite(ap, AHCI_PREG_FBS, fbs & ~AHCI_PREG_FBS_EN);
ap->ap_flags &= ~AP_F_FBSS_ENABLED;
}
}
if (error == 0) {
for (i = 0; i < AHCI_MAX_PMPORTS; ++i) {
at = ap->ap_ata[i];
at->at_probe = ATA_PROBE_NEED_INIT;
at->at_features |= ATA_PORT_F_RESCAN;
}
ap->ap_type = ATA_PORT_T_PM;
return (0);
}
ahci_port_stop(ap, 0);
ahci_port_clo(ap);
#if 1
cmd = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC;
cmd &= ~AHCI_PREG_CMD_PMA;
ahci_pwrite(ap, AHCI_PREG_CMD, cmd);
#endif
if (orig_error == 0) {
if (ahci_pwait_clr(ap, AHCI_PREG_TFD,
AHCI_PREG_TFD_STS_BSY | AHCI_PREG_TFD_STS_DRQ)) {
kprintf("%s: PM probe: port will not come ready\n",
PORTNAME(ap));
orig_error = EBUSY;
ahci_port_init(ap);
}
}
return(orig_error);
}
int
ahci_pm_identify(struct ahci_port *ap)
{
u_int32_t chipid;
u_int32_t rev;
u_int32_t nports;
u_int32_t data1;
u_int32_t data2;
int has_dummy_port;
ap->ap_probe = ATA_PROBE_FAILED;
if (ahci_pm_read(ap, 15, 0, &chipid))
goto err;
if (ahci_pm_read(ap, 15, 1, &rev))
goto err;
if (ahci_pm_read(ap, 15, 2, &nports))
goto err;
nports &= 0x0000000F;
ap->ap_probe = ATA_PROBE_GOOD;
if ((rev & SATA_PMREV_MASK) == 0) {
if (bootverbose)
kprintf("%s: PM identify register empty!\n",
PORTNAME(ap));
return EIO;
}
switch(chipid) {
case 0x37261095:
has_dummy_port = 1;
break;
default:
has_dummy_port = 0;
break;
}
if (has_dummy_port) {
if (nports > 1)
--nports;
}
kprintf("%s: Port multiplier: chip=%08x rev=0x%pb%i nports=%d\n",
PORTNAME(ap), chipid, SATA_PFMT_PM_REV, rev, nports);
if (has_dummy_port) {
kprintf("%s: Port multiplier: Ignoring dummy port #%d\n",
PORTNAME(ap), nports);
}
ap->ap_pmcount = nports;
if (ahci_pm_read(ap, 15, SATA_PMREG_FEA, &data1)) {
kprintf("%s: Port multiplier: Warning, "
"cannot read feature register\n", PORTNAME(ap));
} else {
kprintf("%s: Port multiplier features: 0x%pb%i\n",
PORTNAME(ap), SATA_PFMT_PM_FEA, data1);
}
if (ahci_pm_read(ap, 15, SATA_PMREG_FEAEN, &data2) == 0) {
kprintf("%s: Port multiplier defaults: 0x%pb%i\n",
PORTNAME(ap), SATA_PFMT_PM_FEA, data2);
}
if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SSNTF) &&
(data1 & SATA_PMFEA_ASYNCNOTIFY)) {
u_int32_t serr_bits = AHCI_PREG_SERR_DIAG_N |
AHCI_PREG_SERR_DIAG_X;
data2 |= SATA_PMFEA_ASYNCNOTIFY;
if (ahci_pm_write(ap, 15, SATA_PMREG_FEAEN, data2)) {
kprintf("%s: Port multiplier: AsyncNotify cannot be "
"enabled\n", PORTNAME(ap));
} else if (ahci_pm_write(ap, 15, SATA_PMREG_EEENA, serr_bits)) {
kprintf("%s: Port mulltiplier: AsyncNotify unable "
"to enable error info bits\n", PORTNAME(ap));
} else {
kprintf("%s: Port multiplier: AsyncNotify enabled\n",
PORTNAME(ap));
}
}
return (0);
err:
kprintf("%s: Port multiplier cannot be identified\n", PORTNAME(ap));
return (EIO);
}
int
ahci_pm_hardreset(struct ahci_port *ap, int target, int hard)
{
struct ata_port *at;
u_int32_t data;
int loop;
int error = EIO;
at = ap->ap_ata[target];
data = ap->ap_sc->sc_ipm_disable;
if (hard == 2)
data |= AHCI_PREG_SCTL_DET_DISABLE;
if (ahci_pm_write(ap, target, SATA_PMREG_SERR, -1))
goto err;
if (ahci_pm_write(ap, target, SATA_PMREG_SCTL, data))
goto err;
ahci_os_sleep(10);
at->at_probe = ATA_PROBE_FAILED;
at->at_type = ATA_PORT_T_NONE;
data = ap->ap_sc->sc_ipm_disable | AHCI_PREG_SCTL_DET_INIT;
switch(AhciForceGen) {
case 0:
data |= AHCI_PREG_SCTL_SPD_ANY;
break;
case 1:
data |= AHCI_PREG_SCTL_SPD_GEN1;
break;
case 2:
data |= AHCI_PREG_SCTL_SPD_GEN2;
break;
case 3:
data |= AHCI_PREG_SCTL_SPD_GEN3;
break;
default:
data |= AHCI_PREG_SCTL_SPD_GEN3;
break;
}
if (ahci_pm_write(ap, target, SATA_PMREG_SCTL, data))
goto err;
ahci_os_sleep(100);
if (ahci_pm_phy_status(ap, target, &data)) {
kprintf("%s: (A)Cannot clear phy status\n",
ATANAME(ap ,at));
}
ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
data = ap->ap_sc->sc_ipm_disable | AHCI_PREG_SCTL_DET_NONE;
if (ahci_pm_write(ap, target, SATA_PMREG_SCTL, data))
goto err;
for (loop = 3; loop; --loop) {
if (ahci_pm_read(ap, target, SATA_PMREG_SSTS, &data))
goto err;
if (data & AHCI_PREG_SSTS_DET)
break;
ahci_os_sleep(100);
}
if (loop == 0) {
kprintf("%s.%d: Port appears to be unplugged\n",
PORTNAME(ap), target);
error = ENODEV;
goto err;
}
for (loop = 30; loop; --loop) {
if (ahci_pm_read(ap, target, SATA_PMREG_SSTS, &data))
goto err;
if ((data & AHCI_PREG_SSTS_DET) == AHCI_PREG_SSTS_DET_DEV)
break;
ahci_os_sleep(100);
}
if (loop == 0) {
kprintf("%s: Device may be powered down\n",
PORTNAME(ap));
error = ENODEV;
goto err;
}
kprintf("%s.%d: Device detected data=%08x\n",
PORTNAME(ap), target, data);
ahci_os_sleep(100);
ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
error = 0;
err:
at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_SOFT_RESET;
return (error);
}
int
ahci_pm_softreset(struct ahci_port *ap, int target)
{
struct ata_port *at;
struct ahci_ccb *ccb;
struct ahci_cmd_hdr *cmd_slot;
u_int8_t *fis;
int count;
int error;
u_int32_t data;
error = EIO;
at = ap->ap_ata[target];
DPRINTF(AHCI_D_VERBOSE, "%s: soft reset\n", PORTNAME(ap));
count = 2;
retry:
if (ahci_pm_phy_status(ap, target, &data)) {
kprintf("%s: (B)Cannot clear phy status\n",
ATANAME(ap ,at));
}
ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
ccb = ahci_get_err_ccb(ap);
ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE;
ccb->ccb_xa.complete = ahci_pm_dummy_done;
ccb->ccb_xa.at = at;
fis = ccb->ccb_cmd_table->cfis;
bzero(fis, sizeof(ccb->ccb_cmd_table->cfis));
fis[0] = ATA_FIS_TYPE_H2D;
fis[1] = at->at_target;
fis[15] = ATA_FIS_CONTROL_SRST|ATA_FIS_CONTROL_4BIT;
cmd_slot = ccb->ccb_cmd_hdr;
cmd_slot->prdtl = 0;
cmd_slot->flags = htole16(5);
cmd_slot->flags |= htole16(AHCI_CMD_LIST_FLAG_C);
cmd_slot->flags |= htole16(AHCI_CMD_LIST_FLAG_R);
cmd_slot->flags |= htole16(at->at_target <<
AHCI_CMD_LIST_FLAG_PMP_SHIFT);
ccb->ccb_xa.state = ATA_S_PENDING;
ap->ap_flags |= AP_F_IGNORE_IFS;
ap->ap_flags &= ~AP_F_IFS_IGNORED;
if (ahci_poll(ccb, 1000, ahci_ata_cmd_timeout) != ATA_S_COMPLETE) {
kprintf("%s: Soft-reset through PM failed, %s\n",
ATANAME(ap, at),
(count > 1 ? "retrying" : "giving up"));
ahci_put_err_ccb(ccb);
if (--count) {
if (ap->ap_flags & AP_F_IFS_IGNORED)
ahci_os_sleep(5000);
else
ahci_os_sleep(1000);
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
goto retry;
}
goto err;
}
bzero(fis, sizeof(ccb->ccb_cmd_table->cfis));
fis[0] = ATA_FIS_TYPE_H2D;
fis[1] = at->at_target;
fis[15] = ATA_FIS_CONTROL_4BIT;
cmd_slot->prdtl = 0;
cmd_slot->flags = htole16(5);
cmd_slot->flags |= htole16(at->at_target <<
AHCI_CMD_LIST_FLAG_PMP_SHIFT);
ccb->ccb_xa.state = ATA_S_PENDING;
ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE;
ap->ap_flags &= ~AP_F_IFS_IGNORED;
if (ahci_poll(ccb, 1000, ahci_ata_cmd_timeout) != ATA_S_COMPLETE) {
kprintf("%s: Soft-reset(2) through PM failed, %s\n",
ATANAME(ap, at),
(count > 1 ? "retrying" : "giving up"));
if (--count) {
ahci_os_sleep(1000);
ahci_put_err_ccb(ccb);
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
goto retry;
}
goto err;
}
ahci_put_err_ccb(ccb);
ahci_os_sleep(100);
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
if (ahci_pm_phy_status(ap, target, &data)) {
kprintf("%s: (C)Cannot clear phy status\n",
ATANAME(ap ,at));
}
ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
if (--count) {
fis[15] = 0;
goto retry;
}
if (at->at_type == ATA_PORT_T_NONE) {
at->at_type = ahci_port_signature_detect(ap, at);
} else {
if (ahci_port_signature_detect(ap, at) != at->at_type) {
kprintf("%s: device signature unexpectedly "
"changed\n", ATANAME(ap, at));
error = EBUSY;
}
}
error = 0;
err:
ahci_os_sleep(100);
if (ahci_pm_write(ap, target, SATA_PMREG_SERR, -1)) {
kprintf("%s: ahci_pm_softreset unable to clear SERR\n",
ATANAME(ap, at));
}
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
ap->ap_flags &= ~(AP_F_IGNORE_IFS | AP_F_IFS_IGNORED);
at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_IDENT;
return (error);
}
int
ahci_pm_phy_status(struct ahci_port *ap, int target, u_int32_t *datap)
{
int error;
error = ahci_pm_read(ap, target, SATA_PMREG_SSTS, datap);
if (error == 0)
error = ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
if (error)
*datap = 0;
return(error);
}
void
ahci_pm_check_good(struct ahci_port *ap, int target)
{
struct ata_port *at;
u_int32_t data;
if (ahci_pm_read(ap, 15, SATA_PMREG_EINFO, &data)) {
kprintf("%s: Port multiplier EINFO could not be read\n",
PORTNAME(ap));
}
if (ahci_pm_write(ap, target, SATA_PMREG_SERR, -1)) {
kprintf("%s: Port multiplier: SERR could not be cleared\n",
PORTNAME(ap));
}
if (target == CAM_TARGET_WILDCARD || target >= ap->ap_pmcount)
return;
at = ap->ap_ata[target];
if (at->at_probe <= ATA_PROBE_NEED_HARD_RESET) {
ahci_pm_hardreset(ap, target, 1);
}
if (ahci_pm_read(ap, target, SATA_PMREG_SSTS, &data)) {
kprintf("%s: Unable to access PM SSTS register target %d\n",
PORTNAME(ap), target);
return;
}
if ((data & AHCI_PREG_SSTS_DET) != AHCI_PREG_SSTS_DET_DEV) {
if (at->at_probe != ATA_PROBE_FAILED) {
at->at_probe = ATA_PROBE_FAILED;
at->at_type = ATA_PORT_T_NONE;
at->at_features |= ATA_PORT_F_RESCAN;
kprintf("%s: HOTPLUG (PM) - Device removed\n",
ATANAME(ap, at));
}
} else {
if (at->at_probe == ATA_PROBE_FAILED) {
at->at_probe = ATA_PROBE_NEED_HARD_RESET;
at->at_features |= ATA_PORT_F_RESCAN;
kprintf("%s: HOTPLUG (PM) - Device inserted\n",
ATANAME(ap, at));
}
}
}
int
ahci_pm_read(struct ahci_port *ap, int target, int which, u_int32_t *datap)
{
struct ata_xfer *xa;
int error;
xa = ahci_ata_get_xfer(ap, ap->ap_ata[15]);
xa->fis->type = ATA_FIS_TYPE_H2D;
xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
xa->fis->command = ATA_C_READ_PM;
xa->fis->features = which;
xa->fis->device = target | ATA_H2D_DEVICE_LBA;
xa->fis->control = ATA_FIS_CONTROL_4BIT;
xa->complete = ahci_pm_dummy_done;
xa->datalen = 0;
xa->flags = ATA_F_POLL | ATA_F_AUTOSENSE;
xa->timeout = 1000;
if (ahci_ata_cmd(xa) == ATA_S_COMPLETE) {
*datap = xa->rfis.sector_count | (xa->rfis.lba_low << 8) |
(xa->rfis.lba_mid << 16) | (xa->rfis.lba_high << 24);
error = 0;
} else {
kprintf("%s.%d pm_read SCA[%d] failed\n",
PORTNAME(ap), target, which);
*datap = 0;
error = EIO;
}
ahci_ata_put_xfer(xa);
return (error);
}
int
ahci_pm_write(struct ahci_port *ap, int target, int which, u_int32_t data)
{
struct ata_xfer *xa;
int error;
xa = ahci_ata_get_xfer(ap, ap->ap_ata[15]);
xa->fis->type = ATA_FIS_TYPE_H2D;
xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
xa->fis->command = ATA_C_WRITE_PM;
xa->fis->features = which;
xa->fis->device = target | ATA_H2D_DEVICE_LBA;
xa->fis->sector_count = (u_int8_t)data;
xa->fis->lba_low = (u_int8_t)(data >> 8);
xa->fis->lba_mid = (u_int8_t)(data >> 16);
xa->fis->lba_high = (u_int8_t)(data >> 24);
xa->fis->control = ATA_FIS_CONTROL_4BIT;
xa->complete = ahci_pm_dummy_done;
xa->datalen = 0;
xa->flags = ATA_F_POLL;
xa->timeout = 1000;
if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
error = 0;
else
error = EIO;
ahci_ata_put_xfer(xa);
return(error);
}
static void
ahci_pm_dummy_done(struct ata_xfer *xa)
{
}