RFC, RFT: AHCI driver reorganization (was: Re: ATA subsystem lost drive after resume process)

From: Andrey V. Elsukov <bu7cher_at_yandex.ru>
Date: Mon, 21 Jul 2008 14:35:56 +0400
Sergey G Nasonov wrote:
> to disk. So the basic issue preventing normal suspend/resume
> process on modern Lenovo laptops is ata subsystem. Does anyone can
> help with this problem? I can test any path or provide additional
> info.

Hi, All.

I wrote patch for AHCI driver and Sergey tested it.
So, He reported that now "suspend/resume" works on his laptop.
I'll try to describe all changes which my patch makes.

1. Initialization:

   * Global AHCI reset code moved to ata_ahci_reset_controller()
     function (it will be reused in ata_ahci_resume).

   * Detection of implemented ports moved to ata_ahci_allocate()
     function (i think it isn't needed to detect it on each reset).

   * From ata_ahci_allocate() function removed all working code,
     only initialization code is here.

2. Resetting:

   * ata_ahci_reset() function reorganized and splitted to several
     functions:
      ata_ahci_stop_channel() - stop all port activity;
      ata_ahci_fre_stop() - disable FIS receiving;
      ata_ahci_fre_start() - setup work areas and enable FIS receiving;
      ata_ahci_clo_enable() - enable command list override.

   * working code from ata_ahci_allocate moved to ata_ahci_reset.

   * CLO shall only be set immediately prior to setting the PxCMD.ST
     bit to '1' (from AHCI spec).

   * Software shall not set PxCMD.ST to 1 until it is determined that a
     functional device is present on the port (from AHCI spec).

   * removed hack when didn't detect signature asuming disk device (it
     may broke some systems, but it needs testing).

3. Interrupts handling:

   * Call ata_sata_phy_check_events() only when PHY changing detected.

   * Fatal error handling changed.

4. Suspend/resume:

   * Added new methods to atapci(4) driver

   * Added suspend/resume implementation for AHCI:
     + Software must disable interrupts prior to requesting a
       transition of the HBA to the D3 state.
     + Reset controller and enable interrupts before resume.


So, any comments and suggestions are welcome.

-- 
WBR, Andrey V. Elsukov


Index: src/sys/dev/ata/ata-all.h
===================================================================
RCS file: /ncvs/src/sys/dev/ata/ata-all.h,v
retrieving revision 1.133
diff -u -b -p -r1.133 ata-all.h
--- src/sys/dev/ata/ata-all.h	17 Apr 2008 12:29:35 -0000	1.133
+++ src/sys/dev/ata/ata-all.h	21 Jul 2008 09:23:31 -0000
_at_@ -188,6 +188,9 @@
 #define         ATA_AHCI_P_IX_HBF       0x20000000
 #define         ATA_AHCI_P_IX_TFE       0x40000000
 #define         ATA_AHCI_P_IX_CPD       0x80000000
+#define         ATA_AHCI_P_IX_FE \
+                        (ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |\
+                         ATA_AHCI_P_IX_HBD | ATA_AHCI_P_IX_IF)
 
 #define ATA_AHCI_P_CMD                  0x118
 #define         ATA_AHCI_P_CMD_ST       0x00000001
Index: src/sys/dev/ata/ata-chipset.c
===================================================================
RCS file: /ncvs/src/sys/dev/ata/ata-chipset.c,v
retrieving revision 1.224
diff -u -b -p -r1.224 ata-chipset.c
--- src/sys/dev/ata/ata-chipset.c	10 Jul 2008 21:36:53 -0000	1.224
+++ src/sys/dev/ata/ata-chipset.c	21 Jul 2008 09:23:31 -0000
_at__at_ -62,6 +62,9 _at__at_ static int ata_sata_connect(struct ata_c
 static void ata_sata_setmode(device_t dev, int mode);
 static int ata_request2fis_h2d(struct ata_request *request, u_int8_t *fis);
 static int ata_ahci_chipinit(device_t dev);
+static int ata_ahci_suspend(device_t dev);
+static int ata_ahci_resume(device_t dev);
+static int ata_ahci_reset_controller(device_t dev);
 static int ata_ahci_allocate(device_t dev);
 static int ata_ahci_status(device_t dev);
 static int ata_ahci_begin_transaction(struct ata_request *request);
_at__at_ -69,6 +72,11 _at__at_ static int ata_ahci_end_transaction(stru
 static int ata_ahci_pm_read(device_t dev, int port, int reg, u_int32_t *result);
 static int ata_ahci_pm_write(device_t dev, int port, int reg, u_int32_t result);
 static u_int32_t ata_ahci_softreset(device_t dev, int port);
+static void ata_ahci_fre_start(struct ata_channel *ch);
+static void ata_ahci_fre_stop(struct ata_channel *ch);
+static void ata_ahci_stop_channel(struct ata_channel *ch);
+static void ata_ahci_restart_channel(struct ata_channel *ch);
+static void ata_ahci_clo_enable(struct ata_channel *ch);
 static void ata_ahci_reset(device_t dev);
 static void ata_ahci_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
 static void ata_ahci_dmainit(device_t dev);
_at__at_ -582,6 +590,29 _at__at_ ata_ahci_ident(device_t dev)
 }
 
 static int
+ata_ahci_reset_controller(device_t dev)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+    /* enable AHCI mode */
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
+
+    /* reset AHCI controller */
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_HR);
+    DELAY(1000000);
+    if (ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) & ATA_AHCI_GHC_HR) {
+	bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2);
+	device_printf(dev, "AHCI controller reset failure\n");
+	return ENXIO;
+    }
+
+    /* reenable AHCI mode */
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
+
+    return 0;
+}
+
+static int
 ata_ahci_chipinit(device_t dev)
 {
     struct ata_pci_controller *ctlr = device_get_softc(dev);
_at__at_ -602,20 +633,8 @@ ata_ahci_chipinit(device_t dev)
     else
 	device_printf(dev, "AHCI called from vendor specific driver\n");
 
-    /* enable AHCI mode */
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
-
-    /* reset AHCI controller */
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_HR);
-    DELAY(1000000);
-    if (ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) & ATA_AHCI_GHC_HR) {
-	bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2);
-	device_printf(dev, "AHCI controller reset failure\n");
+    if (ata_ahci_reset_controller(dev))
 	return ENXIO;
-    }
-
-    /* reenable AHCI mode */
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
 
     /* get the number of HW channels */
     ctlr->channels =
_at__at_ -633,6 +652,8 _at__at_ ata_ahci_chipinit(device_t dev)
     ctlr->dmainit = ata_ahci_dmainit;
     ctlr->allocate = ata_ahci_allocate;
     ctlr->setmode = ata_sata_setmode;
+    ctlr->resume = ata_ahci_resume;
+    ctlr->suspend = ata_ahci_suspend;
 
     /* enable PCI interrupt */
     pci_write_config(dev, PCIR_COMMAND,
_at__at_ -655,9 +676,13 _at__at_ ata_ahci_allocate(device_t dev)
 {
     struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
     struct ata_channel *ch = device_get_softc(dev);
-    u_int64_t work;
     int offset = ch->unit << 7;
 
+    if (!(ATA_INL(ctlr->r_res2, ATA_AHCI_PI) & (1 << ch->unit))) {
+	device_printf(dev, "port not implemented\n");
+	return ENXIO;
+    }
+
     /* set the SATA resources */
     ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
     ch->r_io[ATA_SSTATUS].offset = ATA_AHCI_P_SSTS + offset;
_at__at_ -676,30 +701,45 _at__at_ ata_ahci_allocate(device_t dev)
     ch->hw.pm_read = ata_ahci_pm_read;
     ch->hw.pm_write = ata_ahci_pm_write;
 
-    /* setup work areas */
-    work = ch->dma.work_bus + ATA_AHCI_CL_OFFSET;
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset, work & 0xffffffff);
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, work >> 32);
+    return 0;
+}
 
-    work = ch->dma.work_bus + ATA_AHCI_FB_OFFSET;
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset, work & 0xffffffff); 
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBU + offset, work >> 32);
+static int
+ata_ahci_suspend(device_t dev)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(dev);
 
-    /* enable wanted port interrupts */
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset,
-	     (ATA_AHCI_P_IX_CPD | ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |
-	      ATA_AHCI_P_IX_HBD | ATA_AHCI_P_IX_IF | ATA_AHCI_P_IX_OF |
-	      ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC | ATA_AHCI_P_IX_DP |
-	      ATA_AHCI_P_IX_UF | ATA_AHCI_P_IX_SDB | ATA_AHCI_P_IX_DS |
-	      ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR));
+    /* XXX: PxCMD.ST must be cleared to '0' before entry into the
+     * D3 power state.
+     */
 
-    /* enable FIS based switching */
-    //ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBS + offset, 0x00000003);
+    /* Software must disable interrupts (GHC.IE must be cleared to 0)
+     * prior to requesting a transition of the HBA to the D3 state.
+     */
 
-    /* start operations on this channel */
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
-	     (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
-	      ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC,
+	     ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) & (~ATA_AHCI_GHC_IE));
+
+    return 0;
+}
+
+static int
+ata_ahci_resume(device_t dev)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+    /* reset controller */
+    if (ata_ahci_reset_controller(dev))
+	return ENXIO; /* XXX */
+
+    /* clear interrupts */
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, ATA_INL(ctlr->r_res2, ATA_AHCI_IS));
+
+    /* enable AHCI interrupts */
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC,
+	     ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) | ATA_AHCI_GHC_IE);
+
+    /* next part will be done by ata_resume */
     return 0;
 }
 
_at__at_ -716,38 +756,24 _at__at_ ata_ahci_status(device_t dev)
 	u_int32_t cstatus = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CI + offset);
 
 	/* clear interrupt(s) */
-	ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, action & (1 << ch->unit));
 	ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset, istatus);
+	ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, action & (1 << ch->unit));
 
 	/* do we have any PHY events ? */
-	/* XXX SOS check istatus phy bits */
+	if (istatus & (ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC))
 	ata_sata_phy_check_events(dev);
 
 	/* do we have a potentially hanging engine to take care of? */
 	/* XXX SOS what todo on NCQ */
-	if ((istatus & 0x78400050) && (cstatus & 1)) {
-
-	    u_int32_t cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
-	    int timeout = 0;
-
-	    /* kill off all activity on this channel */
-	    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
-		     cmd & ~(ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
-
-	    /* XXX SOS this is not entirely wrong */
-	    do {
-		DELAY(1000);
-		if (timeout++ > 1000) {
-		    device_printf(dev, "stopping AHCI engine failed\n");
-		    break;
-		}
-    	    } while (ATA_INL(ctlr->r_res2,
-			     ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_CR);
-
-	    /* start operations on this channel */
-	    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
-		     cmd | (ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
-
+	if ((istatus & ATA_AHCI_P_IX_FE) && (cstatus & 1)) {
+	    if (bootverbose)
+		device_printf(dev, "PHY fatal error: PxIS = 0x%08x\n",
+			    istatus);
+	    /* To recover fatal errors, the port must be restarted;
+	     * the port is restarted by clearing PxCMD.ST to '0' and
+	     * then setting PxCMD.ST to '1'.
+	     */
+	    ata_ahci_restart_channel(ch);
 	    return 1;
 	}
 	else
_at__at_ -998,46 +1024,107 @@ ata_ahci_pm_write(device_t dev, int port
     return (ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + offset) >> 8) & 0xff;
 }
 
+/* CLO shall only be set immediately prior to setting
+ * the PxCMD.ST bit to '1' from a previous value of '0'
+ */
 static void
-ata_ahci_restart(device_t dev)
+ata_ahci_clo_enable(struct ata_channel *ch)
 {
-    struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
-    struct ata_channel *ch = device_get_softc(dev);
-    u_int32_t cmd;
+    struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(ch->dev));
     int offset = ch->unit << 7;
-    int timeout;
+    int timeout = 0;
+    u_int32_t cmd;
 
-    /* kill off all activity on this channel */
+    if (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_CLO) {
     cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
     ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
-	     cmd & ~(ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
-
-    /* XXX SOS this is not entirely wrong */
-    timeout = 0;
+			cmd | ATA_AHCI_P_CMD_CLO);
     do {
 	DELAY(1000);
 	if (timeout++ > 1000) {
-	    device_printf(dev, "stopping AHCI engine failed\n");
+		device_printf(ch->dev, "executing CLO failed\n");
 	    break;
 	}
+        } while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset)
+			& ATA_AHCI_P_CMD_CLO);
     }
-    while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_CR);
+}
+
+/* When PxCMD.FR and PxCMD.FRE are both cleared to '0', software may update
+ * the values of PxFB and PxFBU. Prior to setting PxCMD.FRE to '1', software
+ * shall ensure that PxFB and PxFBU are set to valid values. Software shall
+ * not write PxFB and PxFBU while PxCMD.FRE is set to '1'.
+ */
+static void
+ata_ahci_fre_start(struct ata_channel *ch)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(ch->dev));
+    u_int32_t offset = ch->unit << 7;
+    u_int64_t work;
+
+    /* setup work areas */
+    work = ch->dma.work_bus + ATA_AHCI_CL_OFFSET;
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset, work & 0xffffffff);
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, work >> 32);
+
+    work = ch->dma.work_bus + ATA_AHCI_FB_OFFSET;
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset, work & 0xffffffff); 
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBU + offset, work >> 32);
+
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+	    ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) | ATA_AHCI_P_CMD_FRE);
+}
+
+static void
+ata_ahci_fre_stop(struct ata_channel *ch)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(ch->dev));
+    u_int32_t offset = ch->unit << 7;
+    int timeout = 0;
+
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+	    ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) & (~ATA_AHCI_P_CMD_FRE));
 
-    /* issue Command List Override if supported */ 
-    if (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_CLO) {
-	cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
-	cmd |= ATA_AHCI_P_CMD_CLO;
-	ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, cmd);
-	timeout = 0;
 	do {
 	    DELAY(1000);
 	    if (timeout++ > 1000) {
-		device_printf(dev, "executing CLO failed\n");
+	    device_printf(ch->dev, "ata_ahci_fre_stop failed\n");
 		break;
 	    }
+    } while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_FR);
+}
+
+static void
+ata_ahci_stop_channel(struct ata_channel *ch)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(ch->dev));
+    u_int32_t cmd, offset = ch->unit << 7;
+    int timeout = 0;
+
+    cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
+    if ((cmd & (ATA_AHCI_P_CMD_ST | ATA_AHCI_P_CMD_CR)) == 0)
+	return;
+
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+		    cmd & (~ATA_AHCI_P_CMD_ST));
+    do {
+	DELAY(1000);
+	if (timeout++ > 1000) {
+	    device_printf(ch->dev, "ata_ahci_stop_channel failed\n");
+	    break;
         }
-	while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD+offset)&ATA_AHCI_P_CMD_CLO);
-    }
+    } while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_CR);
+}
+
+
+static void
+ata_ahci_restart_channel(struct ata_channel *ch)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(ch->dev));
+    int offset = ch->unit << 7;
+
+    /* kill off all activity on this channel */
+    ata_ahci_stop_channel(ch);
 
     /* clear SATA error register */
     ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
_at__at_ -1046,11 +1133,12 @@ ata_ahci_restart(device_t dev)
     ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset,
 	     ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
 
+    /* issue Command List Override if supported */
+    ata_ahci_clo_enable(ch);
+
     /* start operations on this channel */
     ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
-	     (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
-	      ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST)
-	     | (ch->devices & ATA_PORTMULTIPLIER ? ATA_AHCI_P_CMD_PMA : 0));
+	    ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) | ATA_AHCI_P_CMD_ST);
 }
 
 static u_int32_t
_at__at_ -1065,7 +1153,7 _at_@ ata_ahci_softreset(device_t dev, int por
 	(struct ata_ahci_cmd_tab *)(ch->dma.work + ATA_AHCI_CT_OFFSET);
 
     /* kick controller into sane state if needed */
-    ata_ahci_restart(dev);
+    ata_ahci_restart_channel(ch);
 
     /* pull reset active */
     bzero(ctp->cfis, 64);
_at__at_ -1094,7 +1182,7 _at__at_ ata_ahci_softreset(device_t dev, int por
 #endif
     do {
 	    DELAY(1000);
-	    if (timeout++ > 1000) {
+	    if (timeout++ > 5000) {
 		device_printf(dev, "still BUSY after softreset\n");
 		break;
 	    }
_at__at_ -1110,19 +1198,35 _at_@ ata_ahci_reset(device_t dev)
 {
     struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
     struct ata_channel *ch = device_get_softc(dev);
-    u_int32_t signature;
+    u_int32_t signature, offset = ch->unit << 7;
+    ch->devices = 0;
 
-    if (!(ATA_INL(ctlr->r_res2, ATA_AHCI_PI) & (1 << ch->unit))) {
-	device_printf(dev, "port not implemented\n");
-	return;
-    }
+    /* kill off all activity on this channel */
+    ata_ahci_stop_channel(ch);
+    ata_ahci_fre_stop(ch);
+
+    /* setup work areas and enable FIS receiving */
+    ata_ahci_fre_start(ch);
+
+    /* clear SATA error register */
+    ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
+
+    /* clear port interrupts */
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset,
+		ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, (1 << ch->unit));
 
-    ata_ahci_restart(dev);
+    /* enable wanted port interrupts */
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset,
+	     (ATA_AHCI_P_IX_CPD | ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |
+	      ATA_AHCI_P_IX_HBD | ATA_AHCI_P_IX_IF | ATA_AHCI_P_IX_OF |
+	      ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC | ATA_AHCI_P_IX_DP |
+	      ATA_AHCI_P_IX_UF | ATA_AHCI_P_IX_SDB | ATA_AHCI_P_IX_DS |
+	      ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR));
 
     if (!ata_sata_phy_reset(dev)) {
 	if (bootverbose)
 	    device_printf(dev, "phy reset found no device\n");
-	ch->devices = 0;
 	return;
     }
 
_at__at_ -1146,13 +1250,24 _at__at_ ata_ahci_reset(device_t dev)
     case 0xeb140101:
 	ch->devices = ATA_ATAPI_MASTER;
 	break;
-    default: /* SOS XXX */
+    default:
 	if (bootverbose)
 	    device_printf(dev, "No signature, asuming disk device\n");
-	ch->devices = ATA_ATA_MASTER;
     }
     if (bootverbose)
         device_printf(dev, "ahci_reset devices=%08x\n", ch->devices);
+
+    /* Software shall not set PxCMD.ST to 1 until it is determined
+     * that a functional device is present on the port.
+     */
+    if (ch->devices) {
+        /* issue Command List Override if supported */
+        ata_ahci_clo_enable(ch);
+	ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+		ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) | ATA_AHCI_P_CMD_SUD |
+		      ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_ST |
+		      (ch->devices & ATA_PORTMULTIPLIER ? ATA_AHCI_P_CMD_PMA : 0));
+    }
 }
 
 static void
Index: src/sys/dev/ata/ata-pci.c
===================================================================
RCS file: /ncvs/src/sys/dev/ata/ata-pci.c,v
retrieving revision 1.128
diff -u -b -p -r1.128 ata-pci.c
--- src/sys/dev/ata/ata-pci.c	11 Jun 2008 06:44:58 -0000	1.128
+++ src/sys/dev/ata/ata-pci.c	21 Jul 2008 09:23:31 -0000
_at__at_ -262,6 +262,32 _at__at_ ata_pci_detach(device_t dev)
     return 0;
 }
 
+int
+ata_pci_suspend(device_t dev)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(dev);
+    int error = 0;
+
+    bus_generic_suspend(dev);
+    if (ctlr->suspend)
+	error = ctlr->suspend(dev);
+
+    return error;
+}
+
+int
+ata_pci_resume(device_t dev)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(dev);
+    int error = 0;
+
+    if (ctlr->resume)
+	error = ctlr->resume(dev);
+    bus_generic_resume(dev);
+
+    return error;
+}
+
 struct resource *
 ata_pci_alloc_resource(device_t dev, device_t child, int type, int *rid,
 		       u_long start, u_long end, u_long count, u_int flags)
_at__at_ -556,8 +582,8 _at__at_ static device_method_t ata_pci_methods[]
     DEVMETHOD(device_attach,            ata_pci_attach),
     DEVMETHOD(device_detach,            ata_pci_detach),
     DEVMETHOD(device_shutdown,          bus_generic_shutdown),
-    DEVMETHOD(device_suspend,           bus_generic_suspend),
-    DEVMETHOD(device_resume,            bus_generic_resume),
+    DEVMETHOD(device_suspend,           ata_pci_suspend),
+    DEVMETHOD(device_resume,            ata_pci_resume),
 
     /* bus methods */
     DEVMETHOD(bus_alloc_resource,       ata_pci_alloc_resource),
Index: src/sys/dev/ata/ata-pci.h
===================================================================
RCS file: /ncvs/src/sys/dev/ata/ata-pci.h,v
retrieving revision 1.89
diff -u -b -p -r1.89 ata-pci.h
--- src/sys/dev/ata/ata-pci.h	10 Jul 2008 21:36:53 -0000	1.89
+++ src/sys/dev/ata/ata-pci.h	21 Jul 2008 09:23:31 -0000
_at__at_ -55,6 +55,8 @@ struct ata_pci_controller {
     void                (*reset)(device_t);
     void                (*dmainit)(device_t);
     void                (*setmode)(device_t, int);
+    int                 (*suspend)(device_t);
+    int                 (*resume)(device_t);
     struct {
     void                (*function)(void *);
     void                *argument;
_at__at_ -460,6 +462,8 @@ struct ata_connect_task {
 int ata_pci_probe(device_t dev);
 int ata_pci_attach(device_t dev);
 int ata_pci_detach(device_t dev);
+int ata_pci_suspend(device_t dev);
+int ata_pci_resume(device_t dev);
 struct resource * ata_pci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags);
 int ata_pci_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r);
 int ata_pci_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_filter_t *filter, driver_intr_t *function, void *argument, void **cookiep);
Received on Mon Jul 21 2008 - 08:49:34 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:39:33 UTC