some problems in suspend/resume on ThinkPad X61
Takanori Watanabe
takawata at init-main.com
Mon Apr 7 15:52:59 UTC 2008
I bought a new laptop;ThinkPad X61 and I'm trying to hack
suspend/resume.
As the first step, I'm trying to make sure to suspend/resume work
in uniprocessor kernel.
Finally I did succeeded to suspend and resume with
1) fwohci disabled
2) if_em down
3) hw.acpi.reset_video=1
4) modify ata driver with following crude patch.
(Not generic solution.)
So I want ata_pci_controller->chipinit and ata_pci_controller->allocate
should split into two parts:Hardware initialization and
data structure initialization.
--- ata-chipset.c.~1.212.~ 2008-03-07 09:29:19.000000000 +0000
+++ ata-chipset.c 2008-04-08 00:23:34.000000000 +0000
@@ -540,7 +540,37 @@
(ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_NPMASK) + 1);
return 0;
}
+void ata_ahci_tell_dma(device_t dev);
+void ata_ahci_tell_dma(device_t dev)
+{
+ struct ata_pci_controller *ctlr;
+ struct ata_channel *ch = device_get_softc(dev);
+ u_int64_t work;
+ int offset = ch->unit << 7;
+
+ ctlr = device_get_softc(device_get_parent(dev));
+ /* 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);
+ /* 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));
+
+ /* 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));
+}
static int
ata_ahci_allocate(device_t dev)
{
--- ata-pci.c.~1.123.~ 2007-11-18 14:44:52.000000000 +0000
+++ ata-pci.c 2008-04-07 23:25:04.000000000 +0000
@@ -48,6 +48,7 @@
#include <dev/ata/ata-all.h>
#include <dev/ata/ata-pci.h>
#include <ata_if.h>
+extern void ata_ahci_tell_dma(device_t dev);
/* local vars */
static MALLOC_DEFINE(M_ATAPCI, "ata_pci", "ATA driver PCI");
@@ -186,6 +187,18 @@
return ENXIO;
}
+static int ata_pci_resume(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ int chans = ctlr->channels;
+ ctlr->chipinit(dev);
+
+ if(chans != ctlr->channels){
+ device_printf(dev, "WARNING: channel number changed\n");
+ }
+
+ return bus_generic_resume(dev);
+}
int
ata_pci_attach(device_t dev)
{
@@ -546,7 +559,7 @@
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_resume, ata_pci_resume),
/* bus methods */
DEVMETHOD(bus_alloc_resource, ata_pci_alloc_resource),
@@ -678,6 +691,15 @@
ctlr->setmode(dev, mode);
}
+static int ata_pcichannel_resume(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ if(ch->dma&&ctlr->r_res2)
+ ata_ahci_tell_dma(dev);
+
+ return ata_resume(dev);
+}
static device_method_t ata_pcichannel_methods[] = {
/* device interface */
DEVMETHOD(device_probe, ata_pcichannel_probe),
@@ -685,7 +707,7 @@
DEVMETHOD(device_detach, ata_pcichannel_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, ata_suspend),
- DEVMETHOD(device_resume, ata_resume),
+ DEVMETHOD(device_resume, ata_pcichannel_resume),
/* ATA methods */
DEVMETHOD(ata_setmode, ata_pcichannel_setmode),
More information about the freebsd-acpi
mailing list