Patch to fix SATA resume from Suspend-to-RAM.
This combines the following patches from OpenSuSE Kernel
kernel-source-2.6.16-12:
patches.fixes/libata-increase-timeout-for-resume
patches.fixes/ahci-suspend
patches.fixes/ahci-atapi-sense-request
patches.drivers/libata-acpi-suspend
patches.fixes/libata-resume-drive_port-mode
patches.fixes/ahci-init-on-resume
patches.drivers/libata-device-spindown
against a vanilla 2.6.16.5
diff -urN linux-2.6.16.5/Documentation/DocBook/libata.tmpl linux-2.6.16.5-ro/Documentation/DocBook/libata.tmpl
--- linux-2.6.16.5/Documentation/DocBook/libata.tmpl 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/Documentation/DocBook/libata.tmpl 2006-04-16 00:19:38.000000000 +0200
@@ -787,6 +787,12 @@
!Idrivers/scsi/libata-scsi.c
+
+ libata ACPI interfaces/methods
+!Edrivers/scsi/ata_acpi.c
+!Idrivers/scsi/ata_acpi.c
+
+
ATA errors & exceptions
diff -urN linux-2.6.16.5/Documentation/kernel-parameters.txt linux-2.6.16.5-ro/Documentation/kernel-parameters.txt
--- linux-2.6.16.5/Documentation/kernel-parameters.txt 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/Documentation/kernel-parameters.txt 2006-04-16 00:19:38.000000000 +0200
@@ -41,6 +41,7 @@
ISAPNP ISA PnP code is enabled.
ISDN Appropriate ISDN support is enabled.
JOY Appropriate joystick support is enabled.
+ LIBATA libata driver is enabled.
LP Printer support is enabled.
LOOP Loopback device support is enabled.
M68k M68k architecture is enabled.
@@ -242,6 +243,9 @@
ataflop= [HW,M68k]
+ atapi_enabled= [LIBATA] Enable discovery & support of ATAPI devices
+ Format: (0=off, 1=on)
+
atarimouse= [HW,MOUSE] Atari Mouse
atascsi= [HW,SCSI] Atari SCSI
@@ -987,6 +991,10 @@
emulation library even if a 387 maths coprocessor
is present.
+ noacpi= [LIBATA] Disables use of ACPI in libata suspend/resume
+ when set.
+ Format:
+
noalign [KNL,ARM]
noapic [SMP,APIC] Tells the kernel to not make use of any
@@ -1235,6 +1243,11 @@
autoconfiguration.
Ranges are in pairs (memory base and size).
+ printk= [LIBATA] Set libata printk level (mask).
+ The values are defined in include/linux/libata.h.
+ The default value is 1 (ATA_MSG_DRV).
+ Format:
+
profile= [KNL] Enable kernel profiling via /proc/profile
Format: [schedule,]
Param: "schedule" - profile schedule points.
diff -urN linux-2.6.16.5/drivers/scsi/ahci.c linux-2.6.16.5-ro/drivers/scsi/ahci.c
--- linux-2.6.16.5/drivers/scsi/ahci.c 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/drivers/scsi/ahci.c 2006-04-16 12:21:10.000000000 +0200
@@ -40,10 +40,12 @@
#include
#include
#include
+#include
#include
#include
#include
#include
+#include
#include
#include
@@ -85,6 +87,11 @@
/* HOST_CAP bits */
HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */
+ HOST_CAP_SIS = (1 << 28), /* Interlock switch */
+ HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */
+ HOST_CAP_SPM = (1 << 17), /* Port multiplier */
+ HOST_CAP_SSC = (1 << 14), /* Slumber capable */
+ HOST_CAP_PSC = (1 << 13), /* Partial capable */
/* registers for each SATA port */
PORT_LST_ADDR = 0x00, /* command list DMA addr */
@@ -135,13 +142,16 @@
/* PORT_CMD bits */
PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */
+ PORT_CMD_CPD = (1 << 20), /* Cold presence detection */
PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */
PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */
PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */
+ PORT_CMD_CLO = (1 << 3), /* Command list override */
PORT_CMD_POWER_ON = (1 << 2), /* Power up device */
PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */
PORT_CMD_START = (1 << 0), /* Enable port DMA engine */
+ PORT_CMD_ICC_MASK = (0xf << 28), /* i/f ICC state mask */
PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */
PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */
PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */
@@ -169,6 +179,7 @@
unsigned long flags;
u32 cap; /* cache of HOST_CAP register */
u32 port_map; /* cache of HOST_PORTS_IMPL reg */
+ u32 dev_map; /* connected devices */
};
struct ahci_port_priv {
@@ -186,15 +197,30 @@
static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
static int ahci_qc_issue(struct ata_queued_cmd *qc);
static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
+static int ahci_start_engine(void __iomem *port_mmio);
+static int ahci_stop_engine(void __iomem *port_mmio);
+static int ahci_stop_fis_rx(void __iomem *port_mmio);
+static void ahci_start_fis_rx(void __iomem *port_mmio,
+ struct ahci_port_priv *pp,
+ struct ahci_host_priv *hpriv);
static void ahci_phy_reset(struct ata_port *ap);
static void ahci_irq_clear(struct ata_port *ap);
static void ahci_eng_timeout(struct ata_port *ap);
static int ahci_port_start(struct ata_port *ap);
+static int ahci_port_suspend(struct ata_port *ap, pm_message_t state);
+static int ahci_port_resume(struct ata_port *ap);
static void ahci_port_stop(struct ata_port *ap);
+static int ahci_port_standby(void __iomem *port_mmio, u32 cap);
+static int ahci_port_spinup(void __iomem *port_mmio, u32 cap);
+static void ahci_port_disable(struct ata_port *ap);
static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
static void ahci_qc_prep(struct ata_queued_cmd *qc);
static u8 ahci_check_status(struct ata_port *ap);
static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
+static int ahci_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state);
+static int ahci_scsi_device_resume(struct scsi_device *sdev);
+static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t state);
+static int ahci_pci_device_resume(struct pci_dev *pdev);
static void ahci_remove_one (struct pci_dev *pdev);
static struct scsi_host_template ahci_sht = {
@@ -214,10 +240,13 @@
.dma_boundary = AHCI_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ahci_scsi_device_resume,
+ .suspend = ahci_scsi_device_suspend,
+ .shutdown = ata_scsi_device_shutdown,
};
static const struct ata_port_operations ahci_ops = {
- .port_disable = ata_port_disable,
+ .port_disable = ahci_port_disable,
.check_status = ahci_check_status,
.check_altstatus = ahci_check_status,
@@ -299,6 +328,8 @@
.id_table = ahci_pci_tbl,
.probe = ahci_init_one,
.remove = ahci_remove_one,
+ .suspend = ahci_pci_device_suspend,
+ .resume = ahci_pci_device_resume,
};
@@ -372,42 +403,32 @@
ap->private_data = pp;
- if (hpriv->cap & HOST_CAP_64)
- writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
- writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
- readl(port_mmio + PORT_LST_ADDR); /* flush */
-
- if (hpriv->cap & HOST_CAP_64)
- writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
- writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
- readl(port_mmio + PORT_FIS_ADDR); /* flush */
-
- writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
- PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
- PORT_CMD_START, port_mmio + PORT_CMD);
- readl(port_mmio + PORT_CMD); /* flush */
+ /*
+ * Driver is setup; initialize the HBA
+ */
+ ahci_start_fis_rx(port_mmio, pp, hpriv);
+ rc = ahci_port_spinup(port_mmio, hpriv->cap);
+ if (rc)
+ printk(KERN_WARNING "ata%d: could not spinup device (%d)\n",
+ ap->id, rc);
+ /*
+ * Do not enable DMA here; according to the spec
+ * (section 10.1.1) we should first enable FIS reception,
+ * then check if the port is enabled before we try to
+ * switch on DMA.
+ * And as the port check is done during probe
+ * we really shouldn't be doing it here.
+ */
return 0;
}
-
static void ahci_port_stop(struct ata_port *ap)
{
struct device *dev = ap->host_set->dev;
struct ahci_port_priv *pp = ap->private_data;
- void __iomem *mmio = ap->host_set->mmio_base;
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
- u32 tmp;
-
- tmp = readl(port_mmio + PORT_CMD);
- tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX);
- writel(tmp, port_mmio + PORT_CMD);
- readl(port_mmio + PORT_CMD); /* flush */
- /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so
- * this is slightly incorrect.
- */
- msleep(500);
+ ahci_port_suspend(ap, PMSG_SUSPEND);
ap->private_data = NULL;
dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ,
@@ -416,6 +437,116 @@
kfree(pp);
}
+static int ahci_port_resume(struct ata_port *ap)
+{
+ void __iomem *mmio = ap->host_set->mmio_base;
+ void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+ struct ahci_host_priv *hpriv = ap->host_set->private_data;
+ struct ahci_port_priv *pp = ap->private_data;
+ int rc;
+ u32 tmp;
+
+ /*
+ * Enable FIS reception
+ */
+ ahci_start_fis_rx(port_mmio, pp, hpriv);
+
+ rc = ahci_port_spinup(port_mmio, hpriv->cap);
+ if (rc)
+ printk(KERN_WARNING "ata%d: could not spinup device (%d)\n",
+ ap->id, rc);
+
+ /*
+ * Clear error status
+ */
+ tmp = readl(port_mmio + PORT_SCR_ERR);
+ writel(tmp, port_mmio + PORT_SCR_ERR);
+ /*
+ * Clear interrupt status
+ */
+ tmp = readl(mmio + HOST_CTL);
+ if (!(tmp & HOST_IRQ_EN)) {
+ u32 irq_stat;
+
+ /* ack any pending irq events for this port */
+ irq_stat = readl(port_mmio + PORT_IRQ_STAT);
+ if (irq_stat)
+ writel(irq_stat, port_mmio + PORT_IRQ_STAT);
+
+ /* set irq mask (enables interrupts) */
+ writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
+
+ if ((hpriv->dev_map >> (ap->port_no + 1)) == 0) {
+ /*
+ * Enable interrupts if this was the last port
+ */
+ printk(KERN_WARNING "ata%d: enabling interrupts\n",
+ ap->id);
+
+ irq_stat = readl(mmio + HOST_IRQ_STAT);
+ if (irq_stat)
+ writel(irq_stat, mmio + HOST_IRQ_STAT);
+
+ tmp |= HOST_IRQ_EN;
+ writel(tmp, mmio + HOST_CTL);
+ (void) readl(mmio + HOST_CTL);
+ }
+ }
+
+ /*
+ * Enable DMA
+ */
+ rc = ahci_start_engine(port_mmio);
+ if (rc)
+ printk(KERN_WARNING "ata%d: cannot start DMA engine (rc %d)\n",
+ ap->id, rc);
+
+ return rc;
+}
+
+static int ahci_port_suspend(struct ata_port *ap, pm_message_t state)
+{
+ void __iomem *mmio = ap->host_set->mmio_base;
+ void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+ struct ahci_host_priv *hpriv = ap->host_set->private_data;
+ int rc;
+
+ /*
+ * Disable DMA
+ */
+ rc = ahci_stop_engine(port_mmio);
+ if (rc) {
+ printk(KERN_WARNING "ata%u: DMA engine busy\n", ap->id);
+ return rc;
+ }
+
+ /*
+ * Disable FIS reception
+ */
+ rc = ahci_stop_fis_rx(port_mmio);
+ if (rc)
+ printk(KERN_WARNING "ata%d: FIS RX still running (rc %d)\n",
+ ap->id, rc);
+
+ /*
+ * Put device into slumber mode
+ */
+ if (!rc && state.event != PM_EVENT_FREEZE)
+ ahci_port_standby(port_mmio, hpriv->cap);
+
+ return rc;
+}
+
+static void ahci_port_disable(struct ata_port *ap)
+{
+ struct ahci_host_priv *hpriv = ap->host_set->private_data;
+
+ ata_port_disable(ap);
+
+ hpriv->dev_map &= ~(1 << ap->port_no);
+}
+
+
static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in)
{
unsigned int sc_reg;
@@ -450,15 +581,270 @@
writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4));
}
+static int ahci_stop_engine(void __iomem *port_mmio)
+{
+ int work;
+ u32 tmp;
+
+ tmp = readl(port_mmio + PORT_CMD);
+ /* Check if the HBA is idle */
+ if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0)
+ return 0;
+
+ /* Setting HBA to idle */
+ tmp &= ~PORT_CMD_START;
+ writel(tmp, port_mmio + PORT_CMD);
+
+ /*
+ * wait for engine to become idle
+ */
+ work = 1000;
+ while (work-- > 0) {
+ tmp = readl(port_mmio + PORT_CMD);
+ if ((tmp & PORT_CMD_LIST_ON) == 0)
+ return 0;
+ udelay(10);
+ }
+
+ return -EIO;
+}
+
+static int ahci_start_engine(void __iomem *port_mmio)
+{
+ u32 tmp;
+ int work = 1000;
+
+ /*
+ * Get current status
+ */
+ tmp = readl(port_mmio + PORT_CMD);
+
+ /*
+ * AHCI rev 1.1 section 10.3.1:
+ * Software shall not set PxCMD.ST to ‘1’ until it verifies
+ * that PxCMD.CR is ‘0’ and has set PxCMD.FRE to ‘1’.
+ */
+ if ((tmp & PORT_CMD_FIS_RX) == 0)
+ return -EPERM;
+
+ /*
+ * wait for engine to become idle.
+ */
+ while (work-- > 0) {
+ tmp = readl(port_mmio + PORT_CMD);
+ if ((tmp & PORT_CMD_LIST_ON) == 0)
+ break;
+ udelay(10);
+ }
+
+ if (!work) {
+ /*
+ * We need to do a port reset / HBA reset here
+ */
+ return -EBUSY;
+ }
+
+ /*
+ * Start DMA
+ */
+ tmp |= PORT_CMD_START;
+ writel(tmp, port_mmio + PORT_CMD);
+ readl(port_mmio + PORT_CMD); /* flush */
+
+ return 0;
+}
+
+static int ahci_stop_fis_rx(void __iomem *port_mmio)
+{
+ u32 tmp;
+ int work = 1000;
+
+ /*
+ * Get current status
+ */
+ tmp = readl(port_mmio + PORT_CMD);
+
+ /* Check if FIS RX is already disabled */
+ if ((tmp & PORT_CMD_FIS_RX) == 0)
+ return 0;
+
+ /*
+ * AHCI Rev 1.1 section 10.3.2
+ * Software shall not clear PxCMD.FRE while
+ * PxCMD.ST or PxCMD.CR is set to ‘1’.
+ */
+ if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_START)) {
+ return -EPERM;
+ }
+
+ /*
+ * Disable FIS reception
+ *
+ * AHCI Rev 1.1 Section 10.1.2:
+ * If PxCMD.FRE is set to '1', software should clear it
+ * to '0' and wait at least 500 milliseconds for PxCMD.FR
+ * to return '0' when read. If PxCMD.FR does not clear
+ * '0' correctly, then software may attempt a port reset
+ * of a full HBA reset to recover.
+ */
+ tmp &= ~(PORT_CMD_FIS_RX);
+ writel(tmp, port_mmio + PORT_CMD);
+
+ mdelay(500);
+ work = 1000;
+ while (work-- > 0) {
+ tmp = readl(port_mmio + PORT_CMD);
+ if ((tmp & PORT_CMD_FIS_ON) == 0)
+ return 0;
+ udelay(10);
+ }
+
+ return -EBUSY;
+}
+
+static void ahci_start_fis_rx(void __iomem *port_mmio,
+ struct ahci_port_priv *pp,
+ struct ahci_host_priv *hpriv)
+{
+ u32 tmp;
+
+ /*
+ * Set FIS registers
+ */
+ if (hpriv->cap & HOST_CAP_64)
+ writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
+ writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
+ readl(port_mmio + PORT_LST_ADDR); /* flush */
+
+ if (hpriv->cap & HOST_CAP_64)
+ writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
+ writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
+ readl(port_mmio + PORT_FIS_ADDR); /* flush */
+
+ /*
+ * Enable FIS reception
+ */
+ tmp = readl(port_mmio + PORT_CMD);
+ tmp |= PORT_CMD_FIS_RX;
+ writel(tmp, port_mmio + PORT_CMD);
+ readl(port_mmio + PORT_CMD); /* flush */
+}
+
+static int ahci_port_spinup(void __iomem *port_mmio, u32 cap)
+{
+ u32 tmp;
+
+ tmp = readl(port_mmio + PORT_CMD);
+ /*
+ * AHCI Rev1.1 Section 5.3.2.3:
+ * Software is only allowed to program the PxCMD.FRE,
+ * PxCMD.POD, PxSCTL.DET, and PxCMD.SUD register bits
+ * when PxCMD.ST is set to ‘0’.
+ */
+ if (tmp & PORT_CMD_START)
+ return -EBUSY;
+
+ /*
+ * Power on device if supported
+ */
+ if (tmp & PORT_CMD_CPD) {
+ tmp |= PORT_CMD_POWER_ON;
+ writel(tmp, port_mmio + PORT_CMD);
+ tmp = readl(port_mmio + PORT_CMD);
+ }
+
+ /*
+ * Spin up device
+ */
+ if (cap & HOST_CAP_SSS) {
+ tmp |= PORT_CMD_SPIN_UP;
+ writel(tmp, port_mmio + PORT_CMD);
+ tmp = readl(port_mmio + PORT_CMD);
+ }
+
+ if ((tmp & PORT_CMD_ICC_MASK) != PORT_CMD_ICC_ACTIVE) {
+ tmp |= PORT_CMD_ICC_ACTIVE;
+ writel(tmp, port_mmio + PORT_CMD);
+ tmp = readl(port_mmio + PORT_CMD);
+ }
+
+ return 0;
+}
+
+static int ahci_port_standby(void __iomem *port_mmio, u32 cap)
+{
+ u32 tmp, scontrol, sstatus;
+
+ tmp = readl(port_mmio + PORT_CMD);
+ /*
+ * AHCI Rev1.1 Section 5.3.2.3:
+ * Software is only allowed to program the PxCMD.FRE,
+ * PxCMD.POD, PxSCTL.DET, and PxCMD.SUD register bits
+ * when PxCMD.ST is set to ‘0’.
+ */
+ if (tmp & PORT_CMD_START)
+ return -EBUSY;
+
+ if (cap & HOST_CAP_SSC) {
+ /*
+ * Enable transitions to slumber mode
+ */
+ scontrol = readl(port_mmio + PORT_SCR_CTL);
+ if ((scontrol & 0x0f00) > 0x100) {
+ scontrol &= ~0xf00;
+ writel(scontrol, port_mmio + PORT_SCR_CTL);
+ }
+ /*
+ * Put device into slumber mode
+ */
+ tmp |= PORT_CMD_ICC_SLUMBER;
+ writel(tmp, port_mmio + PORT_CMD);
+ tmp = readl(port_mmio + PORT_CMD);
+
+ /*
+ * Actually, we should wait for the device to
+ * enter slumber mode by checking
+ * sstatus & 0xf00 == 6
+ */
+ sstatus = readl(port_mmio + PORT_SCR_STAT);
+ }
+
+ /*
+ * Put device into listen mode
+ */
+ scontrol = readl(port_mmio + PORT_SCR_CTL);
+ scontrol &= ~0xf;
+ writel(scontrol, port_mmio + PORT_SCR_CTL);
+
+ tmp = readl(port_mmio + PORT_CMD);
+ if (cap & HOST_CAP_SSS) {
+ /*
+ * Spin down the device for staggered spin-up support
+ */
+ tmp &= ~PORT_CMD_SPIN_UP;
+ writel(tmp, port_mmio + PORT_CMD);
+ readl(port_mmio + PORT_CMD); /* flush */
+ }
+
+ return 0;
+}
+
static void ahci_phy_reset(struct ata_port *ap)
{
void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+ struct ahci_host_priv *hpriv = ap->host_set->private_data;
struct ata_taskfile tf;
struct ata_device *dev = &ap->device[0];
u32 new_tmp, tmp;
+ ahci_stop_engine(port_mmio);
+
__sata_phy_reset(ap);
+ /* clear SATA phy error, if any */
+ tmp = readl(port_mmio + PORT_SCR_ERR);
+ writel(tmp, port_mmio + PORT_SCR_ERR);
+
if (ap->flags & ATA_FLAG_PORT_DISABLED)
return;
@@ -470,7 +856,7 @@
dev->class = ata_dev_classify(&tf);
if (!ata_dev_present(dev)) {
- ata_port_disable(ap);
+ ap->ops->port_disable(ap);
return;
}
@@ -484,6 +870,10 @@
writel(new_tmp, port_mmio + PORT_CMD);
readl(port_mmio + PORT_CMD); /* flush */
}
+
+ ahci_start_engine(port_mmio);
+
+ hpriv->dev_map |= (1 << ap->port_no);
}
static u8 ahci_check_status(struct ata_port *ap)
@@ -576,7 +966,7 @@
void __iomem *mmio = ap->host_set->mmio_base;
void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
u32 tmp;
- int work;
+ int rc;
if ((ap->device[0].class != ATA_DEV_ATAPI) ||
((irq_stat & PORT_IRQ_TF_ERR) == 0))
@@ -592,20 +982,7 @@
readl(port_mmio + PORT_SCR_ERR));
/* stop DMA */
- tmp = readl(port_mmio + PORT_CMD);
- tmp &= ~PORT_CMD_START;
- writel(tmp, port_mmio + PORT_CMD);
-
- /* wait for engine to stop. TODO: this could be
- * as long as 500 msec
- */
- work = 1000;
- while (work-- > 0) {
- tmp = readl(port_mmio + PORT_CMD);
- if ((tmp & PORT_CMD_LIST_ON) == 0)
- break;
- udelay(10);
- }
+ rc = ahci_stop_engine(port_mmio);
/* clear SATA phy error, if any */
tmp = readl(port_mmio + PORT_SCR_ERR);
@@ -615,7 +992,7 @@
* if so, issue COMRESET
*/
tmp = readl(port_mmio + PORT_TFDATA);
- if (tmp & (ATA_BUSY | ATA_DRQ)) {
+ if (rc || (tmp & (ATA_BUSY | ATA_DRQ))) {
writel(0x301, port_mmio + PORT_SCR_CTL);
readl(port_mmio + PORT_SCR_CTL); /* flush */
udelay(10);
@@ -624,10 +1001,10 @@
}
/* re-start DMA */
- tmp = readl(port_mmio + PORT_CMD);
- tmp |= PORT_CMD_START;
- writel(tmp, port_mmio + PORT_CMD);
- readl(port_mmio + PORT_CMD); /* flush */
+ rc = ahci_start_engine(port_mmio);
+ if (rc)
+ printk(KERN_WARNING "ata%u: cannot start DMA (rc %d)\n",
+ ap->id, rc);
}
static void ahci_eng_timeout(struct ata_port *ap)
@@ -697,7 +1074,7 @@
ahci_restart_port(ap, status);
if (qc) {
- qc->err_mask |= AC_ERR_OTHER;
+ qc->err_mask |= err_mask;
ata_qc_complete(qc);
}
}
@@ -781,6 +1158,71 @@
return 0;
}
+int ahci_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state)
+{
+ struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+ struct ata_device *dev = &ap->device[sdev->id];
+ int rc;
+
+ rc = ata_device_suspend(ap, dev, state);
+
+ if (!rc)
+ rc = ahci_port_suspend(ap, state);
+
+ return rc;
+}
+
+int ahci_scsi_device_resume(struct scsi_device *sdev)
+{
+ struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+ struct ata_device *dev = &ap->device[sdev->id];
+
+ ahci_port_resume(ap);
+
+ return ata_device_resume(ap, dev);
+}
+
+int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct device *dev = pci_dev_to_dev(pdev);
+ struct ata_host_set *host_set = dev_get_drvdata(dev);
+ void __iomem *mmio = host_set->mmio_base;
+ u32 tmp;
+
+ /*
+ * AHCI spec rev1.1 section 8.3.3:
+ * Software must disable interrupts prior to
+ * requesting a transition of the HBA to
+ * D3 state.
+ */
+ tmp = readl(mmio + HOST_CTL);
+ tmp &= ~HOST_IRQ_EN;
+ writel(tmp, mmio + HOST_CTL);
+ tmp = readl(mmio + HOST_CTL); /* flush */
+
+ return ata_pci_device_suspend(pdev, state);
+}
+
+int ahci_pci_device_resume(struct pci_dev *pdev)
+{
+ struct device *dev = pci_dev_to_dev(pdev);
+ struct ata_host_set *host_set = dev_get_drvdata(dev);
+ void __iomem *mmio = host_set->mmio_base;
+ u32 tmp;
+
+ /*
+ * Enabling AHCI mode
+ */
+ tmp = readl(mmio + HOST_CTL);
+ if (!(tmp & HOST_AHCI_EN)) {
+ tmp |= HOST_AHCI_EN;
+ writel(tmp, mmio + HOST_CTL);
+ tmp = readl(mmio + HOST_CTL);
+ }
+
+ return ata_pci_device_resume(pdev);
+}
+
static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
unsigned int port_idx)
{
@@ -805,8 +1247,15 @@
void __iomem *port_mmio;
cap_save = readl(mmio + HOST_CAP);
- cap_save &= ( (1<<28) | (1<<17) );
- cap_save |= (1 << 27);
+
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
+ /*
+ * Intel ICHx specific
+ * AHCI spec defines HOST_CAP as R/O
+ */
+ cap_save &= ( HOST_CAP_SIS | HOST_CAP_SPM );
+ cap_save |= HOST_CAP_SSS;
+ }
/* global controller reset */
tmp = readl(mmio + HOST_CTL);
@@ -843,6 +1292,7 @@
hpriv->cap = readl(mmio + HOST_CAP);
hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
+ hpriv->dev_map = 0;
probe_ent->n_ports = (hpriv->cap & 0x1f) + 1;
VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n",
@@ -888,23 +1338,27 @@
(unsigned long) mmio, i);
/* make sure port is not active */
- tmp = readl(port_mmio + PORT_CMD);
- VPRINTK("PORT_CMD 0x%x\n", tmp);
- if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
- PORT_CMD_FIS_RX | PORT_CMD_START)) {
- tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
- PORT_CMD_FIS_RX | PORT_CMD_START);
- writel(tmp, port_mmio + PORT_CMD);
- readl(port_mmio + PORT_CMD); /* flush */
-
- /* spec says 500 msecs for each bit, so
- * this is slightly incorrect.
- */
- msleep(500);
- }
-
+ rc = ahci_stop_engine(port_mmio);
+ if (rc)
+ printk(KERN_WARNING "ata%u: DMA engine busy (rc %d)\n",
+ i, rc);
+
+ rc = ahci_stop_fis_rx(port_mmio);
+ if (rc)
+ printk(KERN_WARNING "ata%u: FIS RX not stopped (rc %d)\n",
+ i, rc);
+
+ /*
+ * Actually, this is wrong again.
+ * AHCI spec says that we first should
+ * enable FIS reception before sending
+ * SPIN_UP to the device ...
+ */
writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD);
+ /*
+ * Wait for the communications link to establish
+ */
j = 0;
while (j < 100) {
msleep(10);
@@ -992,7 +1446,7 @@
dev_printk(KERN_INFO, &pdev->dev,
"flags: "
"%s%s%s%s%s%s"
- "%s%s%s%s%s%s%s\n"
+ "%s%s%s%s%s%s%s%s%s%s\n"
,
cap & (1 << 31) ? "64bit " : "",
@@ -1008,7 +1462,10 @@
cap & (1 << 17) ? "pmp " : "",
cap & (1 << 15) ? "pio " : "",
cap & (1 << 14) ? "slum " : "",
- cap & (1 << 13) ? "part " : ""
+ cap & (1 << 13) ? "part " : "",
+ cap & (1 << 7) ? "coal " : "",
+ cap & (1 << 6) ? "enc " : "",
+ cap & (1 << 5) ? "ext " : ""
);
}
diff -urN linux-2.6.16.5/drivers/scsi/ata_piix.c linux-2.6.16.5-ro/drivers/scsi/ata_piix.c
--- linux-2.6.16.5/drivers/scsi/ata_piix.c 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/drivers/scsi/ata_piix.c 2006-04-16 00:21:33.000000000 +0200
@@ -192,6 +192,7 @@
.bios_param = ata_std_bios_param,
.resume = ata_scsi_device_resume,
.suspend = ata_scsi_device_suspend,
+ .shutdown = ata_scsi_device_shutdown,
};
static const struct ata_port_operations piix_pata_ops = {
diff -urN linux-2.6.16.5/drivers/scsi/Kconfig linux-2.6.16.5-ro/drivers/scsi/Kconfig
--- linux-2.6.16.5/drivers/scsi/Kconfig 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/drivers/scsi/Kconfig 2006-04-16 00:19:38.000000000 +0200
@@ -599,6 +599,19 @@
depends on IDE=y && !BLK_DEV_IDE_SATA && (SCSI_SATA_AHCI || SCSI_ATA_PIIX)
default y
+config SCSI_SATA_ACPI
+ bool
+ depends on SCSI_SATA && ACPI && PCI
+ default y
+ help
+ This option adds support for SATA-related ACPI objects.
+ These ACPI objects add the ability to retrieve taskfiles
+ from the ACPI BIOS and write them to the disk controller.
+ These objects may be related to performance, security,
+ power management, or other areas.
+ You can disable this at kernel boot time by using the
+ option 'libata.noacpi'.
+
config SCSI_BUSLOGIC
tristate "BusLogic SCSI support"
depends on (PCI || ISA || MCA) && SCSI && ISA_DMA_API
diff -urN linux-2.6.16.5/drivers/scsi/libata-acpi.c linux-2.6.16.5-ro/drivers/scsi/libata-acpi.c
--- linux-2.6.16.5/drivers/scsi/libata-acpi.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.16.5-ro/drivers/scsi/libata-acpi.c 2006-04-16 00:19:38.000000000 +0200
@@ -0,0 +1,970 @@
+/*
+ * libata-acpi.c
+ * Provides ACPI support for PATA/SATA.
+ *
+ * Copyright (C) 2005 Intel Corp.
+ * Copyright (C) 2005 Randy Dunlap
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "scsi.h"
+#include
+#include
+#include "libata.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define SATA_ROOT_PORT(x) (((x) >> 16) & 0xffff)
+#define SATA_PORT_NUMBER(x) ((x) & 0xffff) /* or NO_PORT_MULT */
+#define NO_PORT_MULT 0xffff
+#define SATA_ADR_RSVD 0xffffffff
+
+#define REGS_PER_GTF 7
+struct taskfile_array {
+ u8 tfa[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */
+};
+
+struct GTM_buffer {
+ __u32 PIO_speed0;
+ __u32 DMA_speed0;
+ __u32 PIO_speed1;
+ __u32 DMA_speed1;
+ __u32 GTM_flags;
+};
+
+#define DEBUGGING 1
+/* note: adds function name and KERN_DEBUG */
+#ifdef DEBUGGING
+#define DEBPRINT(fmt, args...) \
+ printk(KERN_DEBUG "%s: " fmt, __FUNCTION__, ## args)
+#else
+#define DEBPRINT(fmt, args...) do {} while (0)
+#endif /* DEBUGGING */
+
+/**
+ * sata_get_dev_handle - finds acpi_handle and PCI device.function
+ * @dev: device to locate
+ * @handle: returned acpi_handle for @dev
+ * @pcidevfn: return PCI device.func for @dev
+ *
+ * This function is somewhat SATA-specific. Or at least the
+ * IDE and SCSI versions of this function are different,
+ * so it's not entirely generic code.
+ *
+ * Returns 0 on success, <0 on error.
+ */
+static int sata_get_dev_handle(struct device *dev, acpi_handle *handle,
+ acpi_integer *pcidevfn)
+{
+ struct pci_dev *pci_dev;
+ acpi_integer addr;
+
+ pci_dev = to_pci_dev(dev); /* NOTE: PCI-specific */
+ /* Please refer to the ACPI spec for the syntax of _ADR. */
+ addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
+ *pcidevfn = addr;
+ *handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
+ printk(KERN_DEBUG "%s: SATA dev addr=0x%llx, handle=0x%p\n",
+ __FUNCTION__, (unsigned long long)addr, *handle);
+ if (!*handle)
+ return -ENODEV;
+ return 0;
+}
+
+/**
+ * pata_get_dev_handle - finds acpi_handle and PCI device.function
+ * @dev: device to locate
+ * @handle: returned acpi_handle for @dev
+ * @pcidevfn: return PCI device.func for @dev
+ *
+ * The PATA and SATA versions of this function are different.
+ *
+ * Returns 0 on success, <0 on error.
+ */
+static int pata_get_dev_handle(struct device *dev, acpi_handle *handle,
+ acpi_integer *pcidevfn)
+{
+ unsigned int domain, bus, devnum, func;
+ acpi_integer addr;
+ acpi_handle dev_handle, parent_handle;
+ int scanned;
+ struct acpi_buffer buffer = {.length = ACPI_ALLOCATE_BUFFER,
+ .pointer = NULL};
+ acpi_status status;
+ struct acpi_device_info *dinfo = NULL;
+ int ret = -ENODEV;
+
+ printk(KERN_DEBUG "%s: ENTER: dev->bus_id='%s'\n",
+ __FUNCTION__, dev->bus_id);
+ if ((scanned = sscanf(dev->bus_id, "%x:%x:%x.%x",
+ &domain, &bus, &devnum, &func)) != 4) {
+ printk(KERN_DEBUG "%s: sscanf ret. %d\n",
+ __FUNCTION__, scanned);
+ goto err;
+ }
+
+ dev_handle = DEVICE_ACPI_HANDLE(dev);
+ parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
+
+ status = acpi_get_object_info(parent_handle, &buffer);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_DEBUG "%s: get_object_info for parent failed\n",
+ __FUNCTION__);
+ goto err;
+ }
+ dinfo = buffer.pointer;
+ if (dinfo && (dinfo->valid & ACPI_VALID_ADR) &&
+ dinfo->address == bus) {
+ /* ACPI spec for _ADR for PCI bus: */
+ addr = (acpi_integer)(devnum << 16 | func);
+ *pcidevfn = addr;
+ *handle = dev_handle;
+ } else {
+ printk(KERN_DEBUG "%s: get_object_info for parent has wrong "
+ " bus: %llu, should be %d\n",
+ __FUNCTION__,
+ dinfo ? (unsigned long long)dinfo->address : -1ULL,
+ bus);
+ goto err;
+ }
+
+ printk(KERN_DEBUG "%s: dev_handle: 0x%p, parent_handle: 0x%p\n",
+ __FUNCTION__, dev_handle, parent_handle);
+ printk(KERN_DEBUG
+ "%s: for dev=0x%x.%x, addr=0x%llx, parent=0x%p, *handle=0x%p\n",
+ __FUNCTION__, devnum, func, (unsigned long long)addr,
+ dev->parent, *handle);
+ if (!*handle)
+ goto err;
+ ret = 0;
+err:
+ acpi_os_free(dinfo);
+ return ret;
+}
+
+struct walk_info { /* can be trimmed some */
+ struct device *dev;
+ struct acpi_device *adev;
+ acpi_handle handle;
+ acpi_integer pcidevfn;
+ unsigned int drivenum;
+ acpi_handle obj_handle;
+ struct ata_port *ataport;
+ struct ata_device *atadev;
+ u32 sata_adr;
+ int status;
+ char basepath[ACPI_PATHNAME_MAX];
+ int basepath_len;
+};
+
+static acpi_status get_devices(acpi_handle handle,
+ u32 level, void *context, void **return_value)
+{
+ acpi_status status;
+ struct walk_info *winfo = context;
+ struct acpi_buffer namebuf = {ACPI_ALLOCATE_BUFFER, NULL};
+ char *pathname;
+ struct acpi_buffer buffer;
+ struct acpi_device_info *dinfo;
+
+ status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &namebuf);
+ if (status)
+ goto ret;
+ pathname = namebuf.pointer;
+
+ buffer.length = ACPI_ALLOCATE_BUFFER;
+ buffer.pointer = NULL;
+ status = acpi_get_object_info(handle, &buffer);
+
+ if (ACPI_SUCCESS(status)) {
+ dinfo = buffer.pointer;
+
+ /* find full device path name for pcidevfn */
+ if (dinfo && (dinfo->valid & ACPI_VALID_ADR) &&
+ dinfo->address == winfo->pcidevfn) {
+ if (ata_msg_probe(winfo->ataport))
+ printk(KERN_DEBUG
+ ":%s: matches pcidevfn (0x%llx)\n",
+ pathname, winfo->pcidevfn);
+ strlcpy(winfo->basepath, pathname,
+ sizeof(winfo->basepath));
+ winfo->basepath_len = strlen(pathname);
+ goto out;
+ }
+
+ /* if basepath is not yet known, ignore this object */
+ if (!winfo->basepath_len)
+ goto out;
+
+ /* if this object is in scope of basepath, maybe use it */
+ if (strncmp(pathname, winfo->basepath,
+ winfo->basepath_len) == 0) {
+ if (!(dinfo->valid & ACPI_VALID_ADR))
+ goto out;
+ if (ata_msg_probe(winfo->ataport))
+ printk(KERN_DEBUG "GOT ONE: (%s) "
+ "root_port = 0x%llx, port_num = 0x%llx\n",
+ pathname,
+ SATA_ROOT_PORT(dinfo->address),
+ SATA_PORT_NUMBER(dinfo->address));
+ /* heuristics: */
+ if (SATA_PORT_NUMBER(dinfo->address) != NO_PORT_MULT)
+ if (ata_msg_probe(winfo->ataport))
+ printk(KERN_DEBUG
+ "warning: don't know how to handle SATA port multiplier\n");
+ if (SATA_ROOT_PORT(dinfo->address) ==
+ winfo->ataport->port_no &&
+ SATA_PORT_NUMBER(dinfo->address) == NO_PORT_MULT) {
+ if (ata_msg_probe(winfo->ataport))
+ printk(KERN_DEBUG
+ "THIS ^^^^^ is the requested SATA drive (handle = 0x%p)\n",
+ handle);
+ winfo->sata_adr = dinfo->address;
+ winfo->obj_handle = handle;
+ }
+ }
+out:
+ acpi_os_free(dinfo);
+ }
+ acpi_os_free(pathname);
+
+ret:
+ return status;
+}
+
+/* Get the SATA drive _ADR object. */
+static int get_sata_adr(struct device *dev, acpi_handle handle,
+ acpi_integer pcidevfn, unsigned int drive,
+ struct ata_port *ap,
+ struct ata_device *atadev, u32 *dev_adr)
+{
+ acpi_status status;
+ struct walk_info *winfo;
+ int err = -ENOMEM;
+
+ winfo = kzalloc(sizeof(struct walk_info), GFP_KERNEL);
+ if (!winfo)
+ goto out;
+
+ winfo->dev = dev;
+ winfo->atadev = atadev;
+ winfo->ataport = ap;
+ if (acpi_bus_get_device(handle, &winfo->adev) < 0)
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "acpi_bus_get_device failed\n");
+ winfo->handle = handle;
+ winfo->pcidevfn = pcidevfn;
+ winfo->drivenum = drive;
+
+ status = acpi_get_devices(NULL, get_devices, winfo, NULL);
+ if (ACPI_FAILURE(status)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: acpi_get_devices failed\n",
+ __FUNCTION__);
+ err = -ENODEV;
+ } else {
+ *dev_adr = winfo->sata_adr;
+ atadev->obj_handle = winfo->obj_handle;
+ err = 0;
+ }
+ kfree(winfo);
+out:
+ return err;
+}
+
+/**
+ * ata_acpi_push_id - send Identify data to a drive
+ * @ap: the ata_port for the drive
+ * @ix: drive index
+ *
+ * _SDD ACPI object: for SATA mode only.
+ * Must be after Identify (Packet) Device -- uses its data.
+ */
+int ata_acpi_push_id(struct ata_port *ap, unsigned int ix)
+{
+ acpi_handle handle;
+ acpi_integer pcidevfn;
+ int err = -ENODEV;
+ struct device *dev = ap->host_set->dev;
+ struct ata_device *atadev = &ap->device[ix];
+ u32 dev_adr;
+ acpi_status status;
+ struct acpi_object_list input;
+ union acpi_object in_params[1];
+
+ if (noacpi)
+ return 0;
+
+ if (ap->legacy_mode) {
+ printk(KERN_DEBUG "%s: skipping for PATA mode\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: ap->id: %d, ix = %d, port#: %d, hard_port#: %d\n",
+ __FUNCTION__, ap->id, ix,
+ ap->port_no, ap->hard_port_no);
+
+ /* Don't continue if not a SATA device. */
+ if (!ata_id_is_sata(atadev->id)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ata_id_is_sata is False\n",
+ __FUNCTION__);
+ goto out;
+ }
+
+ /* Don't continue if device has no _ADR method.
+ * _SDD is intended for known motherboard devices. */
+ err = sata_get_dev_handle(dev, &handle, &pcidevfn);
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: sata_get_dev_handle failed (%d\n",
+ __FUNCTION__, err);
+ goto out;
+ }
+
+ /* Get this drive's _ADR info. if not already known. */
+ if (!atadev->obj_handle) {
+ dev_adr = SATA_ADR_RSVD;
+ err = get_sata_adr(dev, handle, pcidevfn, ix, ap, atadev,
+ &dev_adr);
+ if (err < 0 || dev_adr == SATA_ADR_RSVD ||
+ !atadev->obj_handle) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: get_sata_adr failed: "
+ "err=%d, dev_adr=%u, obj_handle=0x%p\n",
+ __FUNCTION__, err, dev_adr,
+ atadev->obj_handle);
+ goto out;
+ }
+ }
+
+ /* Give the drive Identify data to the drive via the _SDD method */
+ /* _SDD: set up input parameters */
+ input.count = 1;
+ input.pointer = in_params;
+ in_params[0].type = ACPI_TYPE_BUFFER;
+ in_params[0].buffer.length = sizeof(atadev->id);
+ in_params[0].buffer.pointer = (u8 *)atadev->id;
+ /* Output buffer: _SDD has no output */
+
+ /* It's OK for _SDD to be missing too. */
+ swap_buf_le16(atadev->id, ATA_ID_WORDS);
+ status = acpi_evaluate_object(atadev->obj_handle, "_SDD", &input, NULL);
+ swap_buf_le16(atadev->id, ATA_ID_WORDS);
+
+ err = ACPI_FAILURE(status) ? -EIO : 0;
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "ata%u(%u): %s _SDD error: status = 0x%x\n",
+ ap->id, ap->device->devno,
+ __FUNCTION__, status);
+ }
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(ata_acpi_push_id);
+
+/**
+ * do_drive_get_GTF - get the drive bootup default taskfile settings
+ * @ap: the ata_port for the drive
+ * @ix: target ata_device (drive) index
+ * @gtf_length: number of bytes of _GTF data returned at @gtf_address
+ * @gtf_address: buffer containing _GTF taskfile arrays
+ *
+ * This applies to both PATA and SATA drives.
+ *
+ * The _GTF method has no input parameters.
+ * It returns a variable number of register set values (registers
+ * hex 1F1..1F7, taskfiles).
+ * The is not known in advance, so have ACPI-CA
+ * allocate the buffer as needed and return it, then free it later.
+ *
+ * The returned @gtf_length and @gtf_address are only valid if the
+ * function return value is 0.
+ */
+int do_drive_get_GTF(struct ata_port *ap, int ix,
+ unsigned int *gtf_length, unsigned long *gtf_address,
+ unsigned long *obj_loc)
+{
+ acpi_status status;
+ acpi_handle dev_handle;
+ acpi_handle chan_handle, drive_handle;
+ acpi_integer pcidevfn;
+ u32 dev_adr;
+ struct acpi_buffer output;
+ union acpi_object *out_obj;
+ struct device *dev = ap->host_set->dev;
+ struct ata_device *atadev = &ap->device[ix];
+ int err = -ENODEV;
+
+ *gtf_length = 0;
+ *gtf_address = 0UL;
+ *obj_loc = 0UL;
+
+ if (noacpi)
+ return 0;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: ENTER: ap->id: %d, port#: %d, hard_port#: %d\n",
+ __FUNCTION__, ap->id,
+ ap->port_no, ap->hard_port_no);
+
+ if (!ata_dev_present(atadev) ||
+ (ap->flags & ATA_FLAG_PORT_DISABLED)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ERR: "
+ "ata_dev_present: %d, PORT_DISABLED: %lu\n",
+ __FUNCTION__, ata_dev_present(atadev),
+ ap->flags & ATA_FLAG_PORT_DISABLED);
+ goto out;
+ }
+
+ /* Don't continue if device has no _ADR method.
+ * _GTF is intended for known motherboard devices. */
+ if (ap->legacy_mode) {
+ err = pata_get_dev_handle(dev, &dev_handle, &pcidevfn);
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: pata_get_dev_handle failed (%d)\n",
+ __FUNCTION__, err);
+ goto out;
+ }
+ } else {
+ err = sata_get_dev_handle(dev, &dev_handle, &pcidevfn);
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: sata_get_dev_handle failed (%d\n",
+ __FUNCTION__, err);
+ goto out;
+ }
+ }
+
+ /* Get this drive's _ADR info. if not already known. */
+ if (!atadev->obj_handle) {
+ if (ap->legacy_mode) {
+ /* get child objects of dev_handle == channel objects,
+ * + _their_ children == drive objects */
+ /* channel is ap->hard_port_no */
+ chan_handle = acpi_get_child(dev_handle,
+ ap->hard_port_no);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: chan adr=%d: chan_handle=0x%p\n",
+ __FUNCTION__, ap->hard_port_no,
+ chan_handle);
+ if (!chan_handle) {
+ err = -ENODEV;
+ goto out;
+ }
+ /* TBD: could also check ACPI object VALID bits */
+ drive_handle = acpi_get_child(chan_handle, ix);
+ printk(KERN_DEBUG "%s: drive w/ adr=%d: %c: 0x%p\n",
+ __FUNCTION__, ix,
+ ap->device[0].class == ATA_DEV_NONE ? 'n' : 'v',
+ drive_handle);
+ if (!drive_handle) {
+ err = -ENODEV;
+ goto out;
+ }
+ dev_adr = ix;
+ atadev->obj_handle = drive_handle;
+ } else { /* for SATA mode */
+ dev_adr = SATA_ADR_RSVD;
+ err = get_sata_adr(dev, dev_handle, pcidevfn, 0,
+ ap, atadev, &dev_adr);
+ }
+ if (err < 0 || dev_adr == SATA_ADR_RSVD ||
+ !atadev->obj_handle) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: get_sata/pata_adr failed: "
+ "err=%d, dev_adr=%u, obj_handle=0x%p\n",
+ __FUNCTION__, err, dev_adr,
+ atadev->obj_handle);
+ goto out;
+ }
+ }
+
+ /* Setting up output buffer */
+ output.length = ACPI_ALLOCATE_BUFFER;
+ output.pointer = NULL; /* ACPI-CA sets this; save/free it later */
+
+ /* _GTF has no input parameters */
+ err = -EIO;
+ status = acpi_evaluate_object(atadev->obj_handle, "_GTF",
+ NULL, &output);
+ if (ACPI_FAILURE(status)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: Run _GTF error: status = 0x%x\n",
+ __FUNCTION__, status);
+ goto out;
+ }
+
+ if (!output.length || !output.pointer) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: Run _GTF: "
+ "length or ptr is NULL (0x%llx, 0x%p)\n",
+ __FUNCTION__,
+ (unsigned long long)output.length,
+ output.pointer);
+ acpi_os_free(output.pointer);
+ goto out;
+ }
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
+ acpi_os_free(output.pointer);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: Run _GTF: error: "
+ "expected object type of ACPI_TYPE_BUFFER, "
+ "got 0x%x\n",
+ __FUNCTION__, out_obj->type);
+ err = -ENOENT;
+ goto out;
+ }
+
+ if (!out_obj->buffer.length || !out_obj->buffer.pointer ||
+ out_obj->buffer.length % REGS_PER_GTF) {
+ if (ata_msg_drv(ap))
+ printk(KERN_ERR
+ "%s: unexpected GTF length (%d) or addr (0x%p)\n",
+ __FUNCTION__, out_obj->buffer.length,
+ out_obj->buffer.pointer);
+ err = -ENOENT;
+ goto out;
+ }
+
+ *gtf_length = out_obj->buffer.length;
+ *gtf_address = (unsigned long)out_obj->buffer.pointer;
+ *obj_loc = (unsigned long)out_obj;
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: returning "
+ "gtf_length=%d, gtf_address=0x%lx, obj_loc=0x%lx\n",
+ __FUNCTION__, *gtf_length, *gtf_address, *obj_loc);
+ err = 0;
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(do_drive_get_GTF);
+
+/**
+ * taskfile_load_raw - send taskfile registers to host controller
+ * @ap: Port to which output is sent
+ * @gtf: raw ATA taskfile register set (0x1f1 - 0x1f7)
+ *
+ * Outputs ATA taskfile to standard ATA host controller using MMIO
+ * or PIO as indicated by the ATA_FLAG_MMIO flag.
+ * Writes the control, feature, nsect, lbal, lbam, and lbah registers.
+ * Optionally (ATA_TFLAG_LBA48) writes hob_feature, hob_nsect,
+ * hob_lbal, hob_lbam, and hob_lbah.
+ *
+ * This function waits for idle (!BUSY and !DRQ) after writing
+ * registers. If the control register has a new value, this
+ * function also waits for idle after writing control and before
+ * writing the remaining registers.
+ *
+ * LOCKING: TBD:
+ * Inherited from caller.
+ */
+static void taskfile_load_raw(struct ata_port *ap,
+ struct ata_device *atadev,
+ const struct taskfile_array *gtf)
+{
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: (0x1f1-1f7): hex: "
+ "%02x %02x %02x %02x %02x %02x %02x\n",
+ __FUNCTION__,
+ gtf->tfa[0], gtf->tfa[1], gtf->tfa[2],
+ gtf->tfa[3], gtf->tfa[4], gtf->tfa[5], gtf->tfa[6]);
+
+ if (ap->ops->qc_issue) {
+ struct ata_taskfile tf;
+ unsigned int err;
+
+ ata_tf_init(ap, &tf, atadev->devno);
+
+ /* convert gtf to tf */
+ tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; /* TBD */
+ tf.protocol = atadev->class == ATA_DEV_ATAPI ?
+ ATA_PROT_ATAPI_NODATA : ATA_PROT_NODATA;
+ tf.feature = gtf->tfa[0]; /* 0x1f1 */
+ tf.nsect = gtf->tfa[1]; /* 0x1f2 */
+ tf.lbal = gtf->tfa[2]; /* 0x1f3 */
+ tf.lbam = gtf->tfa[3]; /* 0x1f4 */
+ tf.lbah = gtf->tfa[4]; /* 0x1f5 */
+ tf.device = gtf->tfa[5]; /* 0x1f6 */
+ tf.command = gtf->tfa[6]; /* 0x1f7 */
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "call ata_exec_internal:\n");
+ err = ata_exec_internal(ap, atadev, &tf, DMA_NONE, NULL, 0);
+ if (err && ata_msg_probe(ap))
+ printk(KERN_ERR "%s: ata_exec_internal failed: %u\n",
+ __FUNCTION__, err);
+ } else
+ if (ata_msg_warn(ap))
+ printk(KERN_WARNING
+ "%s: SATA driver is missing qc_issue function entry points\n",
+ __FUNCTION__);
+}
+
+/**
+ * do_drive_set_taskfiles - write the drive taskfile settings from _GTF
+ * @ap: the ata_port for the drive
+ * @atadev: target ata_device
+ * @gtf_length: total number of bytes of _GTF taskfiles
+ * @gtf_address: location of _GTF taskfile arrays
+ *
+ * This applies to both PATA and SATA drives.
+ *
+ * Write {gtf_address, length gtf_length} in groups of
+ * REGS_PER_GTF bytes.
+ */
+int do_drive_set_taskfiles(struct ata_port *ap, struct ata_device *atadev,
+ unsigned int gtf_length, unsigned long gtf_address)
+{
+ int err = -ENODEV;
+ int gtf_count = gtf_length / REGS_PER_GTF;
+ int ix;
+ struct taskfile_array *gtf;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: ENTER: ap->id: %d, port#: %d, hard_port#: %d\n",
+ __FUNCTION__, ap->id,
+ ap->port_no, ap->hard_port_no);
+
+ if (noacpi)
+ return 0;
+
+ if (!ata_dev_present(atadev) ||
+ (ap->flags & ATA_FLAG_PORT_DISABLED))
+ goto out;
+ if (!gtf_count) /* shouldn't be here */
+ goto out;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: total GTF bytes=%u (0x%x), gtf_count=%d, addr=0x%lx\n",
+ __FUNCTION__, gtf_length, gtf_length, gtf_count,
+ gtf_address);
+ if (gtf_length % REGS_PER_GTF) {
+ if (ata_msg_drv(ap))
+ printk(KERN_ERR "%s: unexpected GTF length (%d)\n",
+ __FUNCTION__, gtf_length);
+ goto out;
+ }
+
+ for (ix = 0; ix < gtf_count; ix++) {
+ gtf = (struct taskfile_array *)
+ (gtf_address + ix * REGS_PER_GTF);
+
+ /* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */
+ taskfile_load_raw(ap, atadev, gtf);
+ }
+
+ err = 0;
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(do_drive_set_taskfiles);
+
+/**
+ * ata_acpi_exec_tfs - get then write drive taskfile settings
+ * @ap: the ata_port for the drive
+ *
+ * This applies to both PATA and SATA drives.
+ */
+int ata_acpi_exec_tfs(struct ata_port *ap)
+{
+ int ix;
+ int ret;
+ unsigned int gtf_length;
+ unsigned long gtf_address;
+ unsigned long obj_loc;
+
+ if (noacpi)
+ return 0;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ENTER:\n", __FUNCTION__);
+
+ for (ix = 0; ix < ATA_MAX_DEVICES; ix++) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: call get_GTF, ix=%d\n",
+ __FUNCTION__, ix);
+ ret = do_drive_get_GTF(ap, ix,
+ >f_length, >f_address, &obj_loc);
+ if (ret < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: get_GTF error (%d)\n",
+ __FUNCTION__, ret);
+ break;
+ }
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: call set_taskfiles, ix=%d\n",
+ __FUNCTION__, ix);
+ ret = do_drive_set_taskfiles(ap, &ap->device[ix],
+ gtf_length, gtf_address);
+ acpi_os_free((void *)obj_loc);
+ if (ret < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: set_taskfiles error (%d)\n",
+ __FUNCTION__, ret);
+ break;
+ }
+ }
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ret=%d\n", __FUNCTION__, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ata_acpi_exec_tfs);
+
+/**
+ * ata_acpi_get_timing - get the channel (controller) timings
+ * @ap: target ata_port (channel)
+ *
+ * For PATA ACPI, this function executes the _GTM ACPI method for the
+ * target channel.
+ *
+ * _GTM only applies to ATA controllers in PATA (legacy) mode, not to SATA.
+ * In legacy mode, ap->hard_port_no is channel (controller) number.
+ */
+void ata_acpi_get_timing(struct ata_port *ap)
+{
+ struct device *dev = ap->dev;
+ int err;
+ acpi_handle dev_handle;
+ acpi_integer pcidevfn;
+ acpi_handle chan_handle;
+ acpi_status status;
+ struct acpi_buffer output;
+ union acpi_object *out_obj;
+ struct GTM_buffer *gtm;
+
+ if (noacpi)
+ goto out;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ENTER:\n", __FUNCTION__);
+
+ if (!ap->legacy_mode) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: channel/controller not in legacy mode (%s)\n",
+ __FUNCTION__, dev->bus_id);
+ goto out;
+ }
+
+ err = pata_get_dev_handle(dev, &dev_handle, &pcidevfn);
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: pata_get_dev_handle failed (%d)\n",
+ __FUNCTION__, err);
+ goto out;
+ }
+
+ /* get child objects of dev_handle == channel objects,
+ * + _their_ children == drive objects */
+ /* channel is ap->hard_port_no */
+ chan_handle = acpi_get_child(dev_handle, ap->hard_port_no);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: chan adr=%d: handle=0x%p\n",
+ __FUNCTION__, ap->hard_port_no, chan_handle);
+ if (!chan_handle)
+ goto out;
+
+ /* Setting up output buffer for _GTM */
+ output.length = ACPI_ALLOCATE_BUFFER;
+ output.pointer = NULL; /* ACPI-CA sets this; save/free it later */
+
+ /* _GTM has no input parameters */
+ status = acpi_evaluate_object(chan_handle, "_GTM",
+ NULL, &output);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: _GTM status: %d, outptr: 0x%p, outlen: 0x%llx\n",
+ __FUNCTION__, status, output.pointer,
+ (unsigned long long)output.length);
+ if (ACPI_FAILURE(status)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: Run _GTM error: status = 0x%x\n",
+ __FUNCTION__, status);
+ goto out;
+ }
+
+ if (!output.length || !output.pointer) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: Run _GTM: "
+ "length or ptr is NULL (0x%llx, 0x%p)\n",
+ __FUNCTION__,
+ (unsigned long long)output.length,
+ output.pointer);
+ acpi_os_free(output.pointer);
+ goto out;
+ }
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
+ acpi_os_free(output.pointer);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: Run _GTM: error: "
+ "expected object type of ACPI_TYPE_BUFFER, "
+ "got 0x%x\n",
+ __FUNCTION__, out_obj->type);
+ goto out;
+ }
+
+ if (!out_obj->buffer.length || !out_obj->buffer.pointer ||
+ out_obj->buffer.length != sizeof(struct GTM_buffer)) {
+ acpi_os_free(output.pointer);
+ if (ata_msg_drv(ap))
+ printk(KERN_ERR
+ "%s: unexpected _GTM length (0x%x)[should be 0x%x] or addr (0x%p)\n",
+ __FUNCTION__, out_obj->buffer.length,
+ (int) sizeof(struct GTM_buffer), out_obj->buffer.pointer);
+ goto out;
+ }
+
+ gtm = (struct GTM_buffer *)out_obj->buffer.pointer;
+ if (ata_msg_probe(ap)) {
+ printk(KERN_DEBUG "%s: _GTM info: ptr: 0x%p, len: 0x%x, exp.len: 0x%Zx\n",
+ __FUNCTION__, out_obj->buffer.pointer,
+ out_obj->buffer.length, sizeof(struct GTM_buffer));
+ printk(KERN_DEBUG "%s: _GTM fields: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+ __FUNCTION__, gtm->PIO_speed0, gtm->DMA_speed0,
+ gtm->PIO_speed1, gtm->DMA_speed1, gtm->GTM_flags);
+ }
+
+ /* TBD: when to free gtm */
+ ap->gtm = gtm;
+ kfree(ap->gtm_object_area); /* free previous then store new one */
+ ap->gtm_object_area = out_obj;
+out:;
+}
+EXPORT_SYMBOL_GPL(ata_acpi_get_timing);
+
+/**
+ * platform_set_timing - set the channel (controller) timings
+ * @ap: target ata_port (channel)
+ *
+ * For PATA ACPI, this function executes the _STM ACPI method for the
+ * target channel.
+ *
+ * _STM only applies to ATA controllers in PATA (legacy) mode, not to SATA.
+ * In legacy mode, ap->hard_port_no is channel (controller) number.
+ *
+ * _STM requires Identify Drive data, which must already be present in
+ * ata_device->id[] (i.e., it's not fetched here).
+ */
+void ata_acpi_push_timing(struct ata_port *ap)
+{
+ struct device *dev = ap->dev;
+ int err;
+ acpi_handle dev_handle;
+ acpi_integer pcidevfn;
+ acpi_handle chan_handle;
+ acpi_status status;
+ struct acpi_object_list input;
+ union acpi_object in_params[1];
+
+ if (noacpi)
+ goto out;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ENTER:\n", __FUNCTION__);
+
+ if (!ap->legacy_mode) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: channel/controller not in legacy mode (%s)\n",
+ __FUNCTION__, dev->bus_id);
+ goto out;
+ }
+
+ if (ap->device[0].id[49] || ap->device[1].id[49]) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: drive(s) on channel %d: missing Identify data\n",
+ __FUNCTION__, ap->hard_port_no);
+ goto out;
+ }
+
+ err = pata_get_dev_handle(dev, &dev_handle, &pcidevfn);
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: pata_get_dev_handle failed (%d)\n",
+ __FUNCTION__, err);
+ goto out;
+ }
+
+ /* get child objects of dev_handle == channel objects,
+ * + _their_ children == drive objects */
+ /* channel is ap->hard_port_no */
+ chan_handle = acpi_get_child(dev_handle, ap->hard_port_no);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: chan adr=%d: handle=0x%p\n",
+ __FUNCTION__, ap->hard_port_no, chan_handle);
+ if (!chan_handle)
+ goto out;
+
+ /* Give the GTM buffer + drive Identify data to the channel via the
+ * _STM method: */
+ /* setup input parameters buffer for _STM */
+ input.count = 3;
+ input.pointer = in_params;
+ in_params[0].type = ACPI_TYPE_BUFFER;
+ in_params[0].buffer.length = sizeof(struct GTM_buffer);
+ in_params[0].buffer.pointer = (u8 *)ap->gtm;
+ in_params[1].type = ACPI_TYPE_BUFFER;
+ in_params[1].buffer.length = sizeof(ap->device[0].id);
+ in_params[1].buffer.pointer = (u8 *)ap->device[0].id;
+ in_params[2].type = ACPI_TYPE_BUFFER;
+ in_params[2].buffer.length = sizeof(ap->device[1].id);
+ in_params[2].buffer.pointer = (u8 *)ap->device[1].id;
+ /* Output buffer: _STM has no output */
+
+ swap_buf_le16(ap->device[0].id, ATA_ID_WORDS);
+ swap_buf_le16(ap->device[1].id, ATA_ID_WORDS);
+ status = acpi_evaluate_object(chan_handle, "_STM", &input, NULL);
+ swap_buf_le16(ap->device[0].id, ATA_ID_WORDS);
+ swap_buf_le16(ap->device[1].id, ATA_ID_WORDS);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: _STM status: %d\n",
+ __FUNCTION__, status);
+ if (ACPI_FAILURE(status)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: Run _STM error: status = 0x%x\n",
+ __FUNCTION__, status);
+ goto out;
+ }
+
+out:;
+}
+EXPORT_SYMBOL_GPL(ata_acpi_push_timing);
diff -urN linux-2.6.16.5/drivers/scsi/libata-core.c linux-2.6.16.5-ro/drivers/scsi/libata-core.c
--- linux-2.6.16.5/drivers/scsi/libata-core.c 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/drivers/scsi/libata-core.c 2006-04-16 12:21:10.000000000 +0200
@@ -86,6 +86,14 @@
module_param_named(fua, libata_fua, int, 0444);
MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)");
+int noacpi = 0;
+module_param(noacpi, int, 0444);
+MODULE_PARM_DESC(noacpi, "Disables use of ACPI in suspend/resume when set");
+
+int libata_printk = ATA_MSG_DRV;
+module_param_named(printk, libata_printk, int, 0644);
+MODULE_PARM_DESC(printk, "Set libata printk flags"); /* in linux/libata.h */
+
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Library module for ATA devices");
MODULE_LICENSE("GPL");
@@ -1116,7 +1124,7 @@
* None. Should be called with kernel context, might sleep.
*/
-static unsigned
+unsigned int
ata_exec_internal(struct ata_port *ap, struct ata_device *dev,
struct ata_taskfile *tf,
int dma_dir, void *buf, unsigned int buflen)
@@ -1305,11 +1313,11 @@
/* print device capabilities */
printk(KERN_DEBUG "ata%u: dev %u cfg "
- "49:%04x 82:%04x 83:%04x 84:%04x 85:%04x 86:%04x 87:%04x 88:%04x\n",
- ap->id, device, dev->id[49],
+ "00:%04x 49:%04x 82:%04x 83:%04x 84:%04x 85:%04x 86:%04x 87:%04x 88:%04x 93:%04x\n",
+ ap->id, device, dev->id[0], dev->id[49],
dev->id[82], dev->id[83], dev->id[84],
dev->id[85], dev->id[86], dev->id[87],
- dev->id[88]);
+ dev->id[88], dev->id[93]);
/*
* common ATA, ATAPI feature tests
@@ -1465,6 +1473,8 @@
if (ap->ops->dev_config)
ap->ops->dev_config(ap, &ap->device[i]);
+
+ ata_acpi_push_id(ap, i);
}
/**
@@ -1505,6 +1515,8 @@
if (ap->flags & ATA_FLAG_PORT_DISABLED)
goto err_out_disable;
+ ata_acpi_exec_tfs(ap);
+
return 0;
err_out_disable:
@@ -1582,14 +1594,14 @@
} else {
printk(KERN_INFO "ata%u: SATA link down (SStatus %X)\n",
ap->id, sstatus);
- ata_port_disable(ap);
+ ap->ops->port_disable(ap);
}
if (ap->flags & ATA_FLAG_PORT_DISABLED)
return;
if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) {
- ata_port_disable(ap);
+ ap->ops->port_disable(ap);
return;
}
@@ -1922,7 +1934,7 @@
return;
err_out:
- ata_port_disable(ap);
+ ap->ops->port_disable(ap);
}
/**
@@ -2413,7 +2425,7 @@
if (ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0)) {
printk(KERN_ERR "ata%u: failed to set xfermode, disabled\n",
ap->id);
- ata_port_disable(ap);
+ ap->ops->port_disable(ap);
}
DPRINTK("EXIT\n");
@@ -2457,7 +2469,7 @@
return;
err_out:
printk(KERN_ERR "ata%u: failed to reread ID, disabled\n", ap->id);
- ata_port_disable(ap);
+ ap->ops->port_disable(ap);
}
/**
@@ -2491,7 +2503,7 @@
if (ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0)) {
printk(KERN_ERR "ata%u: failed to init parameters, disabled\n",
ap->id);
- ata_port_disable(ap);
+ ap->ops->port_disable(ap);
}
DPRINTK("EXIT\n");
@@ -4292,12 +4304,20 @@
*/
int ata_device_resume(struct ata_port *ap, struct ata_device *dev)
{
+ printk(KERN_DEBUG "ata%d: resume device\n", ap->id);
+
+ WARN_ON (irqs_disabled());
+
+ if (!ata_dev_present(dev))
+ return 0;
+
+ ap->ops->phy_reset(ap);
+ ata_acpi_exec_tfs(ap);
+
if (ap->flags & ATA_FLAG_SUSPENDED) {
ap->flags &= ~ATA_FLAG_SUSPENDED;
ata_set_mode(ap);
}
- if (!ata_dev_present(dev))
- return 0;
if (dev->class == ATA_DEV_ATA)
ata_start_drive(ap, dev);
@@ -4311,15 +4331,39 @@
* standbynow command.
*
*/
-int ata_device_suspend(struct ata_port *ap, struct ata_device *dev)
+int ata_device_suspend(struct ata_port *ap, struct ata_device *dev,
+ pm_message_t state)
{
+ printk(KERN_DEBUG "ata%d: suspend device\n", ap->id);
if (!ata_dev_present(dev))
return 0;
if (dev->class == ATA_DEV_ATA)
ata_flush_cache(ap, dev);
+ if (state.event != PM_EVENT_FREEZE)
+ ata_standby_drive(ap, dev);
+ ap->flags |= ATA_FLAG_SUSPENDED;
+ return 0;
+}
+
+/**
+ * ata_device_shutdown - send Standby Immediate command to drive
+ * @ap: target ata_port
+ * @dev: target device on the ata_port
+ *
+ * This command makes it safe to power-off a drive.
+ * Otherwise the heads may be flying at the wrong place
+ * when the power is removed.
+ */
+int ata_device_shutdown(struct ata_port *ap, struct ata_device *dev)
+{
+
+ if (!ata_dev_present(dev))
+ return 0;
+
ata_standby_drive(ap, dev);
ap->flags |= ATA_FLAG_SUSPENDED;
+
return 0;
}
@@ -4427,6 +4471,7 @@
ap->port_no = port_no;
ap->hard_port_no =
ent->legacy_mode ? ent->hard_port_no : port_no;
+ ap->legacy_mode = ent->legacy_mode;
ap->pio_mask = ent->pio_mask;
ap->mwdma_mask = ent->mwdma_mask;
ap->udma_mask = ent->udma_mask;
@@ -4435,6 +4480,7 @@
ap->cbl = ATA_CBL_NONE;
ap->active_tag = ATA_TAG_POISON;
ap->last_ctl = 0xFF;
+ ap->dev = ent->dev;
INIT_WORK(&ap->packet_task, atapi_packet_task, ap);
INIT_WORK(&ap->pio_task, ata_pio_task, ap);
@@ -4547,10 +4593,13 @@
(ap->mwdma_mask << ATA_SHIFT_MWDMA) |
(ap->pio_mask << ATA_SHIFT_PIO);
+ ap->msg_enable = libata_printk;
+
/* print per-port info to dmesg */
printk(KERN_INFO "ata%u: %cATA max %s cmd 0x%lX ctl 0x%lX "
"bmdma 0x%lX irq %lu\n",
ap->id,
+ ap->flags & ATA_FLAG_PATA_MODE ? 'P' :
ap->flags & ATA_FLAG_SATA ? 'S' : 'P',
ata_mode_string(xfer_mode_mask),
ap->ioaddr.cmd_addr,
@@ -4612,6 +4661,12 @@
ata_scsi_scan_host(ap);
}
+ for (i = 0; i < ent->n_ports; i++) {
+ struct ata_port *ap = host_set->ports[i];
+
+ ata_acpi_get_timing(ap);
+ }
+
dev_set_drvdata(dev, host_set);
VPRINTK("EXIT, returning %u\n", ent->n_ports);
@@ -4831,6 +4886,7 @@
probe_ent->n_ports = 1;
probe_ent->hard_port_no = port_num;
probe_ent->private_data = port->private_data;
+ probe_ent->host_flags = port->host_flags;
switch(port_num)
{
@@ -4891,14 +4947,21 @@
else
port[1] = port[0];
+ printk(KERN_DEBUG "%s: pci_dev class+intf: 0x%x\n",
+ __FUNCTION__, pdev->class);
if ((port[0]->host_flags & ATA_FLAG_NO_LEGACY) == 0
&& (pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+ printk(KERN_DEBUG "%s: NO_LEGACY == 0\n", __FUNCTION__);
+ port[0]->host_flags |= ATA_FLAG_PATA_MODE;
+ port[0]->host_flags &= ~ATA_FLAG_SATA;
/* TODO: What if one channel is in native mode ... */
pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
mask = (1 << 2) | (1 << 0);
if ((tmp8 & mask) != mask)
legacy_mode = (1 << 3);
}
+ else
+ printk(KERN_DEBUG "%s: NO_LEGACY == 1\n", __FUNCTION__);
/* FIXME... */
if ((!legacy_mode) && (n_ports > 2)) {
@@ -5074,6 +5137,7 @@
int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
{
+ dev_printk(KERN_DEBUG, &pdev->dev, "suspend PCI device\n");
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
@@ -5082,6 +5146,7 @@
int ata_pci_device_resume(struct pci_dev *pdev)
{
+ dev_printk(KERN_DEBUG, &pdev->dev, "resume PCI device\n");
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
pci_enable_device(pdev);
@@ -5198,5 +5263,7 @@
EXPORT_SYMBOL_GPL(ata_device_suspend);
EXPORT_SYMBOL_GPL(ata_device_resume);
+EXPORT_SYMBOL_GPL(ata_device_shutdown);
EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
+EXPORT_SYMBOL_GPL(ata_scsi_device_shutdown);
diff -urN linux-2.6.16.5/drivers/scsi/libata.h linux-2.6.16.5-ro/drivers/scsi/libata.h
--- linux-2.6.16.5/drivers/scsi/libata.h 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/drivers/scsi/libata.h 2006-04-16 00:19:38.000000000 +0200
@@ -42,6 +42,9 @@
/* libata-core.c */
extern int atapi_enabled;
extern int libata_fua;
+extern int noacpi;
+extern int libata_printk;
+
extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
struct ata_device *dev);
extern int ata_rwcmd_protocol(struct ata_queued_cmd *qc);
@@ -53,6 +56,51 @@
extern void swap_buf_le16(u16 *buf, unsigned int buf_words);
extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
+extern unsigned int ata_exec_internal(struct ata_port *ap,
+ struct ata_device *dev,
+ struct ata_taskfile *tf,
+ int dma_dir, void *buf, unsigned int buflen);
+
+
+/* libata-acpi.c */
+#ifdef CONFIG_SCSI_SATA_ACPI
+extern int ata_acpi_push_id(struct ata_port *ap, unsigned int ix);
+extern int do_drive_get_GTF(struct ata_port *ap, int ix,
+ unsigned int *gtf_length, unsigned long *gtf_address,
+ unsigned long *obj_loc);
+extern int do_drive_set_taskfiles(struct ata_port *ap, struct ata_device *atadev,
+ unsigned int gtf_length, unsigned long gtf_address);
+extern int ata_acpi_exec_tfs(struct ata_port *ap);
+extern void ata_acpi_get_timing(struct ata_port *ap);
+extern void ata_acpi_push_timing(struct ata_port *ap);
+#else
+static inline int ata_acpi_push_id(struct ata_port *ap, unsigned int ix)
+{
+ return 0;
+}
+static inline int do_drive_get_GTF(struct ata_port *ap, int ix,
+ unsigned int *gtf_length, unsigned long *gtf_address,
+ unsigned long *obj_loc)
+{
+ return 0;
+}
+static inline int do_drive_set_taskfiles(struct ata_port *ap,
+ struct ata_device *atadev,
+ unsigned int gtf_length, unsigned long gtf_address)
+{
+ return 0;
+}
+static inline int ata_acpi_exec_tfs(struct ata_port *ap)
+{
+ return 0;
+}
+static void ata_acpi_get_timing(struct ata_port *ap)
+{
+}
+static void ata_acpi_push_timing(struct ata_port *ap)
+{
+}
+#endif
/* libata-scsi.c */
diff -urN linux-2.6.16.5/drivers/scsi/libata-scsi.c linux-2.6.16.5-ro/drivers/scsi/libata-scsi.c
--- linux-2.6.16.5/drivers/scsi/libata-scsi.c 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/drivers/scsi/libata-scsi.c 2006-04-16 12:21:10.000000000 +0200
@@ -404,12 +404,20 @@
return ata_device_resume(ap, dev);
}
-int ata_scsi_device_suspend(struct scsi_device *sdev)
+int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state)
{
struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
struct ata_device *dev = &ap->device[sdev->id];
- return ata_device_suspend(ap, dev);
+ return ata_device_suspend(ap, dev, state);
+}
+
+int ata_scsi_device_shutdown(struct scsi_device *sdev)
+{
+ struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+ struct ata_device *dev = &ap->device[sdev->id];
+
+ return ata_device_shutdown(ap, dev);
}
/**
diff -urN linux-2.6.16.5/drivers/scsi/Makefile linux-2.6.16.5-ro/drivers/scsi/Makefile
--- linux-2.6.16.5/drivers/scsi/Makefile 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/drivers/scsi/Makefile 2006-04-16 00:19:38.000000000 +0200
@@ -163,7 +163,8 @@
CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m)
zalon7xx-objs := zalon.o ncr53c8xx.o
NCR_Q720_mod-objs := NCR_Q720.o ncr53c8xx.o
-libata-objs := libata-core.o libata-scsi.o
+libata-y := libata-core.o libata-scsi.o
+libata-$(CONFIG_SCSI_SATA_ACPI) += libata-acpi.o
oktagon_esp_mod-objs := oktagon_esp.o oktagon_io.o
# Files generated that shall be removed upon make clean
diff -urN linux-2.6.16.5/drivers/scsi/scsi_sysfs.c linux-2.6.16.5-ro/drivers/scsi/scsi_sysfs.c
--- linux-2.6.16.5/drivers/scsi/scsi_sysfs.c 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/drivers/scsi/scsi_sysfs.c 2006-04-16 12:21:10.000000000 +0200
@@ -284,7 +284,7 @@
return err;
if (sht->suspend)
- err = sht->suspend(sdev);
+ err = sht->suspend(sdev, state);
return err;
}
@@ -302,11 +302,27 @@
return err;
}
+static void scsi_bus_shutdown(struct device * dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+ int err;
+
+ err = scsi_device_quiesce(sdev);
+ if (err)
+ printk(KERN_DEBUG "%s: error (0x%x) during shutdown\n",
+ __FUNCTION__, err);
+
+ if (sht->shutdown)
+ sht->shutdown(sdev);
+}
+
struct bus_type scsi_bus_type = {
.name = "scsi",
.match = scsi_bus_match,
.suspend = scsi_bus_suspend,
.resume = scsi_bus_resume,
+ .shutdown = scsi_bus_shutdown,
};
int scsi_sysfs_register(void)
diff -urN linux-2.6.16.5/include/linux/libata.h linux-2.6.16.5-ro/include/linux/libata.h
--- linux-2.6.16.5/include/linux/libata.h 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/include/linux/libata.h 2006-04-16 12:21:10.000000000 +0200
@@ -33,9 +33,13 @@
#include
#include
#include
+#ifdef CONFIG_ACPI
+# include
+#endif
/*
- * compile-time options
+ * compile-time options: to be removed as soon as all the drivers are
+ * converted to the new debugging mechanism
*/
#undef ATA_DEBUG /* debugging output */
#undef ATA_VERBOSE_DEBUG /* yet more debugging output */
@@ -71,6 +75,38 @@
}
#endif
+/* NEW: debug levels */
+#define HAVE_LIBATA_MSG 1
+
+enum {
+ ATA_MSG_DRV = 0x0001,
+ ATA_MSG_INFO = 0x0002,
+ ATA_MSG_PROBE = 0x0004,
+ ATA_MSG_WARN = 0x0008,
+ ATA_MSG_MALLOC = 0x0010,
+ ATA_MSG_CTL = 0x0020,
+ ATA_MSG_INTR = 0x0040,
+ ATA_MSG_ERR = 0x0080,
+};
+
+#define ata_msg_drv(p) ((p)->msg_enable & ATA_MSG_DRV)
+#define ata_msg_info(p) ((p)->msg_enable & ATA_MSG_INFO)
+#define ata_msg_probe(p) ((p)->msg_enable & ATA_MSG_PROBE)
+#define ata_msg_warn(p) ((p)->msg_enable & ATA_MSG_WARN)
+#define ata_msg_malloc(p) ((p)->msg_enable & ATA_MSG_MALLOC)
+#define ata_msg_ctl(p) ((p)->msg_enable & ATA_MSG_CTL)
+#define ata_msg_intr(p) ((p)->msg_enable & ATA_MSG_INTR)
+#define ata_msg_err(p) ((p)->msg_enable & ATA_MSG_ERR)
+
+static inline u32 ata_msg_init(int dval, int default_msg_enable_bits)
+{
+ if (dval < 0 || dval >= (sizeof(u32) * 8))
+ return default_msg_enable_bits; /* should be 0x1 - only driver info msgs */
+ if (!dval)
+ return 0;
+ return (1 << dval) - 1;
+}
+
/* defines only for the constants which don't work well as enums */
#define ATA_TAG_POISON 0xfafbfcfdU
@@ -123,11 +159,10 @@
* proper HSM is in place. */
ATA_FLAG_DEBUGMSG = (1 << 10),
ATA_FLAG_NO_ATAPI = (1 << 11), /* No ATAPI support */
-
ATA_FLAG_SUSPENDED = (1 << 12), /* port is suspended */
-
ATA_FLAG_PIO_LBA48 = (1 << 13), /* Host DMA engine is LBA28 only */
ATA_FLAG_IRQ_MASK = (1 << 14), /* Mask IRQ in PIO xfers */
+ ATA_FLAG_PATA_MODE = (1 << 15), /* port in PATA mode */
ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */
ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */
@@ -200,6 +235,7 @@
struct ata_port_operations;
struct ata_port;
struct ata_queued_cmd;
+struct GTM_buffer;
/* typedefs */
typedef int (*ata_qc_cb_t) (struct ata_queued_cmd *qc);
@@ -318,6 +354,11 @@
u16 cylinders; /* Number of cylinders */
u16 heads; /* Number of heads */
u16 sectors; /* Number of sectors per track */
+
+#ifdef CONFIG_SCSI_SATA_ACPI
+ /* ACPI objects info */
+ acpi_handle obj_handle;
+#endif
};
struct ata_port {
@@ -338,6 +379,7 @@
u8 ctl; /* cache of ATA control register */
u8 last_ctl; /* Cache last written value */
+ u8 legacy_mode;
unsigned int pio_mask;
unsigned int mwdma_mask;
unsigned int udma_mask;
@@ -358,6 +400,13 @@
struct work_struct pio_task;
unsigned int hsm_task_state;
unsigned long pio_task_timeout;
+ struct device *dev;
+
+ u32 msg_enable;
+#ifdef CONFIG_SCSI_SATA_ACPI
+ struct GTM_buffer *gtm;
+ void *gtm_object_area;
+#endif
void *private_data;
};
@@ -453,9 +502,11 @@
extern int ata_scsi_release(struct Scsi_Host *host);
extern unsigned int ata_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
extern int ata_scsi_device_resume(struct scsi_device *);
-extern int ata_scsi_device_suspend(struct scsi_device *);
+extern int ata_scsi_device_suspend(struct scsi_device *, pm_message_t);
+extern int ata_scsi_device_shutdown(struct scsi_device *);
extern int ata_device_resume(struct ata_port *, struct ata_device *);
-extern int ata_device_suspend(struct ata_port *, struct ata_device *);
+extern int ata_device_suspend(struct ata_port *, struct ata_device *, pm_message_t);
+extern int ata_device_shutdown(struct ata_port *, struct ata_device *);
extern int ata_ratelimit(void);
/*
@@ -653,13 +704,13 @@
static inline u8 ata_wait_idle(struct ata_port *ap)
{
- u8 status = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);
+ u8 status = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 100000); /* 1000msec */
if (status & (ATA_BUSY | ATA_DRQ)) {
unsigned long l = ap->ioaddr.status_addr;
- printk(KERN_WARNING
- "ATA: abnormal status 0x%X on port 0x%lX\n",
- status, l);
+ if (ata_msg_warn(ap))
+ printk(KERN_WARNING "ATA: abnormal status 0x%X on port 0x%lX\n",
+ status, l);
}
return status;
@@ -751,7 +802,8 @@
status = ata_busy_wait(ap, bits, 1000);
if (status & bits)
- DPRINTK("abnormal status 0x%X\n", status);
+ if (ata_msg_err(ap))
+ printk(KERN_ERR "abnormal status 0x%X\n", status);
/* get controller status; clear intr, err bits */
if (ap->flags & ATA_FLAG_MMIO) {
@@ -769,8 +821,10 @@
post_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
}
- VPRINTK("irq ack: host_stat 0x%X, new host_stat 0x%X, drv_stat 0x%X\n",
- host_stat, post_stat, status);
+ if (ata_msg_intr(ap))
+ printk(KERN_INFO "%s: irq ack: host_stat 0x%X, new host_stat 0x%X, drv_stat 0x%X\n",
+ __FUNCTION__,
+ host_stat, post_stat, status);
return status;
}
diff -urN linux-2.6.16.5/include/scsi/scsi_host.h linux-2.6.16.5-ro/include/scsi/scsi_host.h
--- linux-2.6.16.5/include/scsi/scsi_host.h 2006-04-12 22:27:57.000000000 +0200
+++ linux-2.6.16.5-ro/include/scsi/scsi_host.h 2006-04-16 12:21:10.000000000 +0200
@@ -300,7 +300,8 @@
* suspend support
*/
int (*resume)(struct scsi_device *);
- int (*suspend)(struct scsi_device *);
+ int (*suspend)(struct scsi_device *, pm_message_t);
+ int (*shutdown)(struct scsi_device *);
/*
* Name of proc directory