summaryrefslogtreecommitdiffstats
path: root/sys/dev/ahci/ahci.c
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2010-05-21 13:29:28 +0000
committermav <mav@FreeBSD.org>2010-05-21 13:29:28 +0000
commite4bb679c9979f72c905654db6d91def9c4c2340b (patch)
treec2c74ba78c41e535597ccdc90dfd2ea994bc193e /sys/dev/ahci/ahci.c
parent890c865dcff99aec8227610af427901ad973cb96 (diff)
downloadFreeBSD-src-e4bb679c9979f72c905654db6d91def9c4c2340b.zip
FreeBSD-src-e4bb679c9979f72c905654db6d91def9c4c2340b.tar.gz
Improve suspend/resume support. Make sure controller is idle on suspend
and reset it on resume.
Diffstat (limited to 'sys/dev/ahci/ahci.c')
-rw-r--r--sys/dev/ahci/ahci.c69
1 files changed, 49 insertions, 20 deletions
diff --git a/sys/dev/ahci/ahci.c b/sys/dev/ahci/ahci.c
index 1aa719e..2cd40a2 100644
--- a/sys/dev/ahci/ahci.c
+++ b/sys/dev/ahci/ahci.c
@@ -60,6 +60,8 @@ static void ahci_intr(void *data);
static void ahci_intr_one(void *data);
static int ahci_suspend(device_t dev);
static int ahci_resume(device_t dev);
+static int ahci_ch_init(device_t dev);
+static int ahci_ch_deinit(device_t dev);
static int ahci_ch_suspend(device_t dev);
static int ahci_ch_resume(device_t dev);
static void ahci_ch_pm(void *arg);
@@ -877,7 +879,7 @@ ahci_ch_attach(device_t dev)
return (ENXIO);
ahci_dmainit(dev);
ahci_slotsalloc(dev);
- ahci_ch_resume(dev);
+ ahci_ch_init(dev);
mtx_lock(&ch->mtx);
rid = ATA_IRQ_RID;
if (!(ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
@@ -969,7 +971,7 @@ ahci_ch_detach(device_t dev)
bus_teardown_intr(dev, ch->r_irq, ch->ih);
bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq);
- ahci_ch_suspend(dev);
+ ahci_ch_deinit(dev);
ahci_slotsfree(dev);
ahci_dmafini(dev);
@@ -979,7 +981,32 @@ ahci_ch_detach(device_t dev)
}
static int
-ahci_ch_suspend(device_t dev)
+ahci_ch_init(device_t dev)
+{
+ struct ahci_channel *ch = device_get_softc(dev);
+ uint64_t work;
+
+ /* Disable port interrupts */
+ ATA_OUTL(ch->r_mem, AHCI_P_IE, 0);
+ /* Setup work areas */
+ work = ch->dma.work_bus + AHCI_CL_OFFSET;
+ ATA_OUTL(ch->r_mem, AHCI_P_CLB, work & 0xffffffff);
+ ATA_OUTL(ch->r_mem, AHCI_P_CLBU, work >> 32);
+ work = ch->dma.rfis_bus;
+ ATA_OUTL(ch->r_mem, AHCI_P_FB, work & 0xffffffff);
+ ATA_OUTL(ch->r_mem, AHCI_P_FBU, work >> 32);
+ /* Activate the channel and power/spin up device */
+ ATA_OUTL(ch->r_mem, AHCI_P_CMD,
+ (AHCI_P_CMD_ACTIVE | AHCI_P_CMD_POD | AHCI_P_CMD_SUD |
+ ((ch->pm_level == 2 || ch->pm_level == 3) ? AHCI_P_CMD_ALPE : 0) |
+ ((ch->pm_level > 2) ? AHCI_P_CMD_ASP : 0 )));
+ ahci_start_fr(dev);
+ ahci_start(dev, 1);
+ return (0);
+}
+
+static int
+ahci_ch_deinit(device_t dev)
{
struct ahci_channel *ch = device_get_softc(dev);
@@ -1000,27 +1027,29 @@ ahci_ch_suspend(device_t dev)
}
static int
+ahci_ch_suspend(device_t dev)
+{
+ struct ahci_channel *ch = device_get_softc(dev);
+
+ mtx_lock(&ch->mtx);
+ xpt_freeze_simq(ch->sim, 1);
+ while (ch->oslots)
+ msleep(ch, &ch->mtx, PRIBIO, "ahcisusp", hz/100);
+ ahci_ch_deinit(dev);
+ mtx_unlock(&ch->mtx);
+ return (0);
+}
+
+static int
ahci_ch_resume(device_t dev)
{
struct ahci_channel *ch = device_get_softc(dev);
- uint64_t work;
- /* Disable port interrupts */
- ATA_OUTL(ch->r_mem, AHCI_P_IE, 0);
- /* Setup work areas */
- work = ch->dma.work_bus + AHCI_CL_OFFSET;
- ATA_OUTL(ch->r_mem, AHCI_P_CLB, work & 0xffffffff);
- ATA_OUTL(ch->r_mem, AHCI_P_CLBU, work >> 32);
- work = ch->dma.rfis_bus;
- ATA_OUTL(ch->r_mem, AHCI_P_FB, work & 0xffffffff);
- ATA_OUTL(ch->r_mem, AHCI_P_FBU, work >> 32);
- /* Activate the channel and power/spin up device */
- ATA_OUTL(ch->r_mem, AHCI_P_CMD,
- (AHCI_P_CMD_ACTIVE | AHCI_P_CMD_POD | AHCI_P_CMD_SUD |
- ((ch->pm_level == 2 || ch->pm_level == 3) ? AHCI_P_CMD_ALPE : 0) |
- ((ch->pm_level > 2) ? AHCI_P_CMD_ASP : 0 )));
- ahci_start_fr(dev);
- ahci_start(dev, 1);
+ mtx_lock(&ch->mtx);
+ ahci_ch_init(dev);
+ ahci_reset(dev);
+ xpt_release_simq(ch->sim, TRUE);
+ mtx_unlock(&ch->mtx);
return (0);
}
OpenPOWER on IntegriCloud