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