RFC, RFT: AHCI driver reorganization (was: Re: ATA subsystem lost
drive after resume process)
Andrey V. Elsukov
bu7cher at yandex.ru
Mon Jul 21 10:49:34 UTC 2008
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
-------------- next part --------------
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
@@ -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
@@ -62,6 +62,9 @@ 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);
@@ -69,6 +72,11 @@ 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);
@@ -582,6 +590,29 @@ 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);
@@ -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 =
@@ -633,6 +652,8 @@ 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,
@@ -655,9 +676,13 @@ 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;
@@ -676,30 +701,45 @@ 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;
}
@@ -716,38 +756,24 @@ 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
@@ -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));
@@ -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
@@ -1065,7 +1153,7 @@ 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);
@@ -1094,7 +1182,7 @@ 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;
}
@@ -1110,19 +1198,35 @@ 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;
}
@@ -1146,13 +1250,24 @@ 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
@@ -262,6 +262,32 @@ 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)
@@ -556,8 +582,8 @@ 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
@@ -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;
@@ -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);
More information about the freebsd-current
mailing list