Author: mav
Date: Fri May 21 13:29:28 2010
New Revision: 208375
URL: http://svn.freebsd.org/changeset/base/208375

Log:
  Improve suspend/resume support. Make sure controller is idle on suspend
  and reset it on resume.

Modified:
  head/sys/dev/ahci/ahci.c
  head/sys/dev/ata/ata-all.c

Modified: head/sys/dev/ahci/ahci.c
==============================================================================
--- head/sys/dev/ahci/ahci.c    Fri May 21 10:36:29 2010        (r208374)
+++ head/sys/dev/ahci/ahci.c    Fri May 21 13:29:28 2010        (r208375)
@@ -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);
 }
 

Modified: head/sys/dev/ata/ata-all.c
==============================================================================
--- head/sys/dev/ata/ata-all.c  Fri May 21 10:36:29 2010        (r208374)
+++ head/sys/dev/ata/ata-all.c  Fri May 21 13:29:28 2010        (r208375)
@@ -432,7 +432,13 @@ ata_suspend(device_t dev)
     if (!dev || !(ch = device_get_softc(dev)))
        return ENXIO;
 
-#ifndef ATA_CAM
+#ifdef ATA_CAM
+       mtx_lock(&ch->state_mtx);
+       xpt_freeze_simq(ch->sim, 1);
+       while (ch->state != ATA_IDLE)
+               msleep(ch, &ch->state_mtx, PRIBIO, "atasusp", hz/100);
+       mtx_unlock(&ch->state_mtx);
+#else
     /* wait for the channel to be IDLE or detached before suspending */
     while (ch->r_irq) {
        mtx_lock(&ch->state_mtx);
@@ -452,16 +458,21 @@ ata_suspend(device_t dev)
 int
 ata_resume(device_t dev)
 {
+    struct ata_channel *ch;
     int error;
 
     /* check for valid device */
-    if (!dev || !device_get_softc(dev))
+    if (!dev || !(ch = device_get_softc(dev)))
        return ENXIO;
 
+#ifdef ATA_CAM
+       mtx_lock(&ch->state_mtx);
+       error = ata_reinit(dev);
+       xpt_release_simq(ch->sim, TRUE);
+       mtx_unlock(&ch->state_mtx);
+#else
     /* reinit the devices, we dont know what mode/state they are in */
     error = ata_reinit(dev);
-
-#ifndef ATA_CAM
     /* kick off requests on the queue */
     ata_start(dev);
 #endif
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to