+ /* 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_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 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 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 unsigned int ahci_dev_classify(struct ata_port *ap)
+{
+ void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+ struct ata_taskfile tf;
+ u32 tmp;