From: zhao, forrest <forrest.zhao@intel.com> Date: Thu, 13 Jul 2006 05:38:32 +0000 (+0800) Subject: [PATCH] The redefinition of ahci_start_engine() and ahci_stop_engine() X-Git-Tag: v2.6.12-rc2 X-Git-Url: http://git.kernel.org/git/?p=linux/kernel/git/jgarzik/libata-dev.git;a=commitdiff;h=5457f2194ad198a0aba4190ec99a6a81846fdca5 [PATCH] The redefinition of ahci_start_engine() and ahci_stop_engine() - Make ahci_start_engine() and ahci_stop_engine() more consistent with AHCI spec 1.1 - Change their input parameter from ap to port_mmio - Update the existing users of ahci_start_engine() and ahci_stop_engine() Signed-off-by: Forrest Zhao <forrest.zhao@intel.com> Signed-off-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Jens Axboe <axboe@suse.de> Signed-off-by: Jeff Garzik <jeff@garzik.org> --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -205,6 +205,8 @@ static irqreturn_t ahci_interrupt (int i static void ahci_irq_clear(struct ata_port *ap); static int ahci_port_start(struct ata_port *ap); static void ahci_port_stop(struct ata_port *ap); +static int ahci_start_engine(void __iomem *port_mmio); +static int ahci_stop_engine(void __iomem *port_mmio); 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); @@ -508,41 +510,64 @@ static void ahci_scr_write (struct ata_p writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } -static int ahci_stop_engine(struct ata_port *ap) +static int ahci_stop_engine(void __iomem *port_mmio) { - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - 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 stop. TODO: this could be + /* wait for engine to stop. 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) - return 0; - udelay(10); - } + tmp = ata_wait_register(port_mmio + PORT_CMD, + PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); + if(tmp & PORT_CMD_LIST_ON) + return -EIO; - return -EIO; + return 0; } -static void ahci_start_engine(struct ata_port *ap) +static int ahci_start_engine(void __iomem *port_mmio) { - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); u32 tmp; + /* + * 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. + */ + tmp = ata_wait_register(port_mmio + PORT_CMD, + PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1,500); + if(tmp & PORT_CMD_LIST_ON) + return -EBUSY; + + /* + * Start DMA + */ tmp |= PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); readl(port_mmio + PORT_CMD); /* flush */ + + return 0; } static unsigned int ahci_dev_classify(struct ata_port *ap) @@ -626,7 +651,7 @@ static int ahci_softreset(struct ata_por } /* prepare for SRST (AHCI-1.1 10.4.1) */ - rc = ahci_stop_engine(ap); + rc = ahci_stop_engine(port_mmio); if (rc) { reason = "failed to stop engine"; goto fail_restart; @@ -647,7 +672,7 @@ static int ahci_softreset(struct ata_por } /* restart engine */ - ahci_start_engine(ap); + ahci_start_engine(port_mmio); ata_tf_init(ap->device, &tf); fis = pp->cmd_tbl; @@ -706,7 +731,7 @@ static int ahci_softreset(struct ata_por return 0; fail_restart: - ahci_start_engine(ap); + ahci_start_engine(port_mmio); fail: ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason); return rc; @@ -717,11 +742,13 @@ static int ahci_hardreset(struct ata_por struct ahci_port_priv *pp = ap->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); int rc; DPRINTK("ENTER\n"); - ahci_stop_engine(ap); + ahci_stop_engine(port_mmio); /* clear D2H reception area to properly wait for D2H FIS */ ata_tf_init(ap->device, &tf); @@ -730,7 +757,7 @@ static int ahci_hardreset(struct ata_por rc = sata_std_hardreset(ap, class); - ahci_start_engine(ap); + ahci_start_engine(port_mmio); if (rc == 0 && ata_port_online(ap)) *class = ahci_dev_classify(ap); @@ -1052,10 +1079,13 @@ static void ahci_thaw(struct ata_port *a static void ahci_error_handler(struct ata_port *ap) { + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { /* restart engine */ - ahci_stop_engine(ap); - ahci_start_engine(ap); + ahci_stop_engine(port_mmio); + ahci_start_engine(port_mmio); } /* perform recovery */ @@ -1066,14 +1096,16 @@ static void ahci_error_handler(struct at static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); if (qc->flags & ATA_QCFLAG_FAILED) qc->err_mask |= AC_ERR_OTHER; if (qc->err_mask) { /* make DMA engine forget about the failed command */ - ahci_stop_engine(ap); - ahci_start_engine(ap); + ahci_stop_engine(port_mmio); + ahci_start_engine(port_mmio); } } * move ahci_port_start/stop() below EH functions. This makes ahci more consistent with other drivers and makes prototypes for ahci_start/stop_engine() unnecessary. * swap positions between ahci_start_engine() and ahci_stop_engine() for readability. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com> 0353fed85301f2f2af7fdce02f6b9c1f55160462 diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index f1516ca..92e2b95 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -205,8 +205,6 @@ static irqreturn_t ahci_interrupt (int i static void ahci_irq_clear(struct ata_port *ap); static int ahci_port_start(struct ata_port *ap); static void ahci_port_stop(struct ata_port *ap); -static int ahci_start_engine(void __iomem *port_mmio); -static int ahci_stop_engine(void __iomem *port_mmio); 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); @@ -374,108 +372,6 @@ static inline void __iomem *ahci_port_ba return (void __iomem *) ahci_port_base_ul((unsigned long)base, port); } -static int ahci_port_start(struct ata_port *ap) -{ - struct device *dev = ap->host_set->dev; - struct ahci_host_priv *hpriv = ap->host_set->private_data; - struct ahci_port_priv *pp; - void __iomem *mmio = ap->host_set->mmio_base; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - void *mem; - dma_addr_t mem_dma; - int rc; - - pp = kmalloc(sizeof(*pp), GFP_KERNEL); - if (!pp) - return -ENOMEM; - memset(pp, 0, sizeof(*pp)); - - rc = ata_pad_alloc(ap, dev); - if (rc) { - kfree(pp); - return rc; - } - - mem = dma_alloc_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL); - if (!mem) { - ata_pad_free(ap, dev); - kfree(pp); - return -ENOMEM; - } - memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ); - - /* - * First item in chunk of DMA memory: 32-slot command table, - * 32 bytes each in size - */ - pp->cmd_slot = mem; - pp->cmd_slot_dma = mem_dma; - - mem += AHCI_CMD_SLOT_SZ; - mem_dma += AHCI_CMD_SLOT_SZ; - - /* - * Second item: Received-FIS area - */ - pp->rx_fis = mem; - pp->rx_fis_dma = mem_dma; - - mem += AHCI_RX_FIS_SZ; - mem_dma += AHCI_RX_FIS_SZ; - - /* - * Third item: data area for storing a single command - * and its scatter-gather table - */ - pp->cmd_tbl = mem; - pp->cmd_tbl_dma = mem_dma; - - 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 */ - - 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); - - ap->private_data = NULL; - dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, - pp->cmd_slot, pp->cmd_slot_dma); - ata_pad_free(ap, dev); - kfree(pp); -} - static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) { unsigned int sc_reg; @@ -510,31 +406,6 @@ static void ahci_scr_write (struct ata_p writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } -static int ahci_stop_engine(void __iomem *port_mmio) -{ - 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 stop. This could be - * as long as 500 msec - */ - tmp = ata_wait_register(port_mmio + PORT_CMD, - PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); - if(tmp & PORT_CMD_LIST_ON) - return -EIO; - - return 0; -} - static int ahci_start_engine(void __iomem *port_mmio) { u32 tmp; @@ -570,6 +441,31 @@ static int ahci_start_engine(void __iome return 0; } +static int ahci_stop_engine(void __iomem *port_mmio) +{ + 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 stop. This could be + * as long as 500 msec + */ + tmp = ata_wait_register(port_mmio + PORT_CMD, + PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); + if(tmp & PORT_CMD_LIST_ON) + return -EIO; + + return 0; +} + static unsigned int ahci_dev_classify(struct ata_port *ap) { void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; @@ -1109,6 +1005,107 @@ static void ahci_post_internal_cmd(struc } } +static int ahci_port_start(struct ata_port *ap) +{ + struct device *dev = ap->host_set->dev; + struct ahci_host_priv *hpriv = ap->host_set->private_data; + struct ahci_port_priv *pp; + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void *mem; + dma_addr_t mem_dma; + int rc; + + pp = kmalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) + return -ENOMEM; + memset(pp, 0, sizeof(*pp)); + + rc = ata_pad_alloc(ap, dev); + if (rc) { + kfree(pp); + return rc; + } + + mem = dma_alloc_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL); + if (!mem) { + ata_pad_free(ap, dev); + kfree(pp); + return -ENOMEM; + } + memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ); + + /* + * First item in chunk of DMA memory: 32-slot command table, + * 32 bytes each in size + */ + pp->cmd_slot = mem; + pp->cmd_slot_dma = mem_dma; + + mem += AHCI_CMD_SLOT_SZ; + mem_dma += AHCI_CMD_SLOT_SZ; + + /* + * Second item: Received-FIS area + */ + pp->rx_fis = mem; + pp->rx_fis_dma = mem_dma; + + mem += AHCI_RX_FIS_SZ; + mem_dma += AHCI_RX_FIS_SZ; + + /* + * Third item: data area for storing a single command + * and its scatter-gather table + */ + pp->cmd_tbl = mem; + pp->cmd_tbl_dma = mem_dma; + + 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 */ + + 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); + + ap->private_data = NULL; + dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, + pp->cmd_slot, pp->cmd_slot_dma); + ata_pad_free(ap, dev); + kfree(pp); +} + static void ahci_setup_port(struct ata_ioports *port, unsigned long base, unsigned int port_idx) { * fascist-format comments according to comment style used in libata core layer. * if() -> if () Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com> 51d2cea03da874cd41cd7a29d1314ddbe46b5314 diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 92e2b95..ee00aed 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -410,30 +410,23 @@ static int ahci_start_engine(void __iome { u32 tmp; - /* - * Get current status - */ + /* get current status */ tmp = readl(port_mmio + PORT_CMD); - /* - * AHCI rev 1.1 section 10.3.1: + /* 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. - */ + /* wait for engine to become idle */ tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1,500); - if(tmp & PORT_CMD_LIST_ON) + if (tmp & PORT_CMD_LIST_ON) return -EBUSY; - /* - * Start DMA - */ + /* start DMA */ tmp |= PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); readl(port_mmio + PORT_CMD); /* flush */ @@ -447,20 +440,18 @@ static int ahci_stop_engine(void __iomem tmp = readl(port_mmio + PORT_CMD); - /* Check if the HBA is idle */ + /* check if the HBA is idle */ if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) return 0; - /* Setting HBA to idle */ + /* setting HBA to idle */ tmp &= ~PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); - /* wait for engine to stop. This could be - * as long as 500 msec - */ + /* wait for engine to stop. This could be as long as 500 msec */ tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); - if(tmp & PORT_CMD_LIST_ON) + if (tmp & PORT_CMD_LIST_ON) return -EIO; return 0; Simplify ahci_start_engine() by killing prerequisite condition checks. Rationales are.. * No user checks error return from ahci_start_engine() * Code flow guarantees the prerequisite conditions unless the controller is malfunctioning. In such cases, the driver had chances to learn about the problem _before_ calling this function. * Closely related to the above two, driver calls into this function even when prerequisites fail hoping for the best. Basically, ahci_start_engine() should only do the operation itself. It isn't the right place to check for prerequisites. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com> e98289381268b565aadcda0e32e0bdd5fad5f9f4 diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index ee00aed..e02b9c6 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -406,32 +406,15 @@ static void ahci_scr_write (struct ata_p writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } -static int ahci_start_engine(void __iomem *port_mmio) +static void ahci_start_engine(void __iomem *port_mmio) { u32 tmp; - /* 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 */ - tmp = ata_wait_register(port_mmio + PORT_CMD, - PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1,500); - if (tmp & PORT_CMD_LIST_ON) - return -EBUSY; - /* start DMA */ + tmp = readl(port_mmio + PORT_CMD); tmp |= PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); readl(port_mmio + PORT_CMD); /* flush */ - - return 0; } static int ahci_stop_engine(void __iomem *port_mmio) Implement ahci_[de]init_port() and use it during initialization and de-initialization. ahci_[de]init_port() are supersets of what used to be done during driver [de-]initialization. This patch makes the following behavior changes. * Per-port IRQ mask is cleared on driver load as done in other drivers. The mask will be configured properly during probe. * During init_one(), HOST_IRQ_STAT is cleared after masking port IRQs such that there is no race window. * CMD_SPIN_UP is cleared during init_one() instead of being set. It is set in port_start(). This is more consistent with overall structure of initialization. Note that CMD_SPIN_UP simply controls PHY activation. * Slumber and staggered spin-up are handled properly. * All init/deinit operations are done in step-by-step manner as described in the spec instead of issued as single merged command. Original implementation is from Zhao, Forrest <forrest.zhao@intel.com> Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com> d773d83a6f3c558226b24a09131b0afa83e0ac02 diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index e02b9c6..fb71fa7 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -92,7 +92,9 @@ enum { HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ /* HOST_CAP bits */ + HOST_CAP_SSC = (1 << 14), /* Slumber capable */ HOST_CAP_CLO = (1 << 24), /* Command List Override support */ + HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ @@ -155,6 +157,7 @@ enum { 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 */ @@ -440,6 +443,135 @@ static int ahci_stop_engine(void __iomem return 0; } +static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap, + dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) +{ + u32 tmp; + + /* set FIS registers */ + if (cap & HOST_CAP_64) + writel((cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); + writel(cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); + + if (cap & HOST_CAP_64) + writel((rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); + writel(rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); + + /* enable FIS reception */ + tmp = readl(port_mmio + PORT_CMD); + tmp |= PORT_CMD_FIS_RX; + writel(tmp, port_mmio + PORT_CMD); + + /* flush */ + readl(port_mmio + PORT_CMD); +} + +static int ahci_stop_fis_rx(void __iomem *port_mmio) +{ + u32 tmp; + + /* disable FIS reception */ + tmp = readl(port_mmio + PORT_CMD); + tmp &= ~PORT_CMD_FIS_RX; + writel(tmp, port_mmio + PORT_CMD); + + /* wait for completion, spec says 500ms, give it 1000 */ + tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_FIS_ON, + PORT_CMD_FIS_ON, 10, 1000); + if (tmp & PORT_CMD_FIS_ON) + return -EBUSY; + + return 0; +} + +static void ahci_power_up(void __iomem *port_mmio, u32 cap) +{ + u32 cmd; + + cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; + + /* spin up device */ + if (cap & HOST_CAP_SSS) { + cmd |= PORT_CMD_SPIN_UP; + writel(cmd, port_mmio + PORT_CMD); + } + + /* wake up link */ + writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD); +} + +static void ahci_power_down(void __iomem *port_mmio, u32 cap) +{ + u32 cmd, scontrol; + + cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; + + 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 */ + writel(cmd | PORT_CMD_ICC_SLUMBER, port_mmio + PORT_CMD); + + /* wait for the transition to complete */ + ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_ICC_SLUMBER, + PORT_CMD_ICC_SLUMBER, 1, 50); + } + + /* put device into listen mode */ + if (cap & HOST_CAP_SSS) { + /* first set PxSCTL.DET to 0 */ + scontrol = readl(port_mmio + PORT_SCR_CTL); + scontrol &= ~0xf; + writel(scontrol, port_mmio + PORT_SCR_CTL); + + /* then set PxCMD.SUD to 0 */ + cmd &= ~PORT_CMD_SPIN_UP; + writel(cmd, port_mmio + PORT_CMD); + } +} + +static void ahci_init_port(void __iomem *port_mmio, u32 cap, + dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) +{ + /* power up */ + ahci_power_up(port_mmio, cap); + + /* enable FIS reception */ + ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma); + + /* enable DMA */ + ahci_start_engine(port_mmio); +} + +static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg) +{ + int rc; + + /* disable DMA */ + rc = ahci_stop_engine(port_mmio); + if (rc) { + *emsg = "failed to stop engine"; + return rc; + } + + /* disable FIS reception */ + rc = ahci_stop_fis_rx(port_mmio); + if (rc) { + *emsg = "failed stop FIS RX"; + return rc; + } + + /* put device into slumber mode */ + ahci_power_down(port_mmio, cap); + + return 0; +} + static unsigned int ahci_dev_classify(struct ata_port *ap) { void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; @@ -1037,20 +1169,8 @@ static int ahci_port_start(struct ata_po 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 */ + /* initialize port */ + ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); return 0; } @@ -1058,20 +1178,17 @@ static int ahci_port_start(struct ata_po static void ahci_port_stop(struct ata_port *ap) { struct device *dev = ap->host_set->dev; + struct ahci_host_priv *hpriv = ap->host_set->private_data; 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 */ + const char *emsg = NULL; + int rc; - /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so - * this is slightly incorrect. - */ - msleep(500); + /* de-initialize port */ + rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + if (rc) + ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc); ap->private_data = NULL; dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, @@ -1099,7 +1216,7 @@ static int ahci_host_init(struct ata_pro struct pci_dev *pdev = to_pci_dev(probe_ent->dev); void __iomem *mmio = probe_ent->mmio_base; u32 tmp, cap_save; - unsigned int i, j, using_dac; + unsigned int i, using_dac; int rc; void __iomem *port_mmio; @@ -1175,6 +1292,8 @@ static int ahci_host_init(struct ata_pro } for (i = 0; i < probe_ent->n_ports; i++) { + const char *emsg = NULL; + #if 0 /* BIOSen initialize this incorrectly */ if (!(hpriv->port_map & (1 << i))) continue; @@ -1187,43 +1306,24 @@ #endif (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); - } - - writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD); - - j = 0; - while (j < 100) { - msleep(10); - tmp = readl(port_mmio + PORT_SCR_STAT); - if ((tmp & 0xf) == 0x3) - break; - j++; - } + rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + if (rc) + dev_printk(KERN_WARNING, &pdev->dev, + "%s (%d)\n", emsg, rc); + /* clear SError */ tmp = readl(port_mmio + PORT_SCR_ERR); VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); writel(tmp, port_mmio + PORT_SCR_ERR); - /* ack any pending irq events for this port */ + /* clear & turn off port IRQ */ tmp = readl(port_mmio + PORT_IRQ_STAT); VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); if (tmp) writel(tmp, port_mmio + PORT_IRQ_STAT); writel(1 << i, mmio + HOST_IRQ_STAT); + writel(0, port_mmio + PORT_IRQ_MASK); } tmp = readl(mmio + HOST_CTL); Separate out ahci_reset_controller() and ahci_init_controller() from ata_host_init(). These will be used by PM callbacks. This patch doesn't introduce any behavior change. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com> b2e525899eb80c1433cc21af33430574e15c4d94 diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index fb71fa7..a9e0c5f 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -572,6 +572,94 @@ static int ahci_deinit_port(void __iomem return 0; } +static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) +{ + u32 cap_save, tmp; + + cap_save = readl(mmio + HOST_CAP); + cap_save &= ( (1<<28) | (1<<17) ); + cap_save |= (1 << 27); + + /* global controller reset */ + tmp = readl(mmio + HOST_CTL); + if ((tmp & HOST_RESET) == 0) { + writel(tmp | HOST_RESET, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + } + + /* reset must complete within 1 second, or + * the hardware should be considered fried. + */ + ssleep(1); + + tmp = readl(mmio + HOST_CTL); + if (tmp & HOST_RESET) { + dev_printk(KERN_ERR, &pdev->dev, + "controller reset failed (0x%x)\n", tmp); + return -EIO; + } + + writel(HOST_AHCI_EN, mmio + HOST_CTL); + (void) readl(mmio + HOST_CTL); /* flush */ + writel(cap_save, mmio + HOST_CAP); + writel(0xf, mmio + HOST_PORTS_IMPL); + (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ + + if (pdev->vendor == PCI_VENDOR_ID_INTEL) { + u16 tmp16; + + /* configure PCS */ + pci_read_config_word(pdev, 0x92, &tmp16); + tmp16 |= 0xf; + pci_write_config_word(pdev, 0x92, tmp16); + } + + return 0; +} + +static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev, + int n_ports, u32 cap) +{ + int i, rc; + u32 tmp; + + for (i = 0; i < n_ports; i++) { + void __iomem *port_mmio = ahci_port_base(mmio, i); + const char *emsg = NULL; + +#if 0 /* BIOSen initialize this incorrectly */ + if (!(hpriv->port_map & (1 << i))) + continue; +#endif + + /* make sure port is not active */ + rc = ahci_deinit_port(port_mmio, cap, &emsg); + if (rc) + dev_printk(KERN_WARNING, &pdev->dev, + "%s (%d)\n", emsg, rc); + + /* clear SError */ + tmp = readl(port_mmio + PORT_SCR_ERR); + VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* clear & turn off port IRQ */ + tmp = readl(port_mmio + PORT_IRQ_STAT); + VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); + if (tmp) + writel(tmp, port_mmio + PORT_IRQ_STAT); + + writel(1 << i, mmio + HOST_IRQ_STAT); + writel(0, port_mmio + PORT_IRQ_MASK); + } + + tmp = readl(mmio + HOST_CTL); + VPRINTK("HOST_CTL 0x%x\n", tmp); + writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); + tmp = readl(mmio + HOST_CTL); + VPRINTK("HOST_CTL 0x%x\n", tmp); +} + static unsigned int ahci_dev_classify(struct ata_port *ap) { void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; @@ -1215,47 +1303,12 @@ static int ahci_host_init(struct ata_pro struct ahci_host_priv *hpriv = probe_ent->private_data; struct pci_dev *pdev = to_pci_dev(probe_ent->dev); void __iomem *mmio = probe_ent->mmio_base; - u32 tmp, cap_save; unsigned int i, using_dac; int rc; - void __iomem *port_mmio; - - cap_save = readl(mmio + HOST_CAP); - cap_save &= ( (1<<28) | (1<<17) ); - cap_save |= (1 << 27); - - /* global controller reset */ - tmp = readl(mmio + HOST_CTL); - if ((tmp & HOST_RESET) == 0) { - writel(tmp | HOST_RESET, mmio + HOST_CTL); - readl(mmio + HOST_CTL); /* flush */ - } - - /* reset must complete within 1 second, or - * the hardware should be considered fried. - */ - ssleep(1); - - tmp = readl(mmio + HOST_CTL); - if (tmp & HOST_RESET) { - dev_printk(KERN_ERR, &pdev->dev, - "controller reset failed (0x%x)\n", tmp); - return -EIO; - } - writel(HOST_AHCI_EN, mmio + HOST_CTL); - (void) readl(mmio + HOST_CTL); /* flush */ - writel(cap_save, mmio + HOST_CAP); - writel(0xf, mmio + HOST_PORTS_IMPL); - (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ - - if (pdev->vendor == PCI_VENDOR_ID_INTEL) { - u16 tmp16; - - pci_read_config_word(pdev, 0x92, &tmp16); - tmp16 |= 0xf; - pci_write_config_word(pdev, 0x92, tmp16); - } + rc = ahci_reset_controller(mmio, pdev); + if (rc) + return rc; hpriv->cap = readl(mmio + HOST_CAP); hpriv->port_map = readl(mmio + HOST_PORTS_IMPL); @@ -1291,46 +1344,10 @@ static int ahci_host_init(struct ata_pro } } - for (i = 0; i < probe_ent->n_ports; i++) { - const char *emsg = NULL; - -#if 0 /* BIOSen initialize this incorrectly */ - if (!(hpriv->port_map & (1 << i))) - continue; -#endif + for (i = 0; i < probe_ent->n_ports; i++) + ahci_setup_port(&probe_ent->port[i], (unsigned long) mmio, i); - port_mmio = ahci_port_base(mmio, i); - VPRINTK("mmio %p port_mmio %p\n", mmio, port_mmio); - - ahci_setup_port(&probe_ent->port[i], - (unsigned long) mmio, i); - - /* make sure port is not active */ - rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); - if (rc) - dev_printk(KERN_WARNING, &pdev->dev, - "%s (%d)\n", emsg, rc); - - /* clear SError */ - tmp = readl(port_mmio + PORT_SCR_ERR); - VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); - writel(tmp, port_mmio + PORT_SCR_ERR); - - /* clear & turn off port IRQ */ - tmp = readl(port_mmio + PORT_IRQ_STAT); - VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); - if (tmp) - writel(tmp, port_mmio + PORT_IRQ_STAT); - - writel(1 << i, mmio + HOST_IRQ_STAT); - writel(0, port_mmio + PORT_IRQ_MASK); - } - - tmp = readl(mmio + HOST_CTL); - VPRINTK("HOST_CTL 0x%x\n", tmp); - writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); - tmp = readl(mmio + HOST_CTL); - VPRINTK("HOST_CTL 0x%x\n", tmp); + ahci_init_controller(mmio, pdev, probe_ent->n_ports, hpriv->cap); pci_set_master(pdev); Implement power management support. Original implementation is from Zhao, Forrest <forrest.zhao@intel.com> Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com> 4513d4761b48bca3025af0832d517f6b913061f9 diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index a9e0c5f..909a4dc 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -215,6 +215,10 @@ static void ahci_freeze(struct ata_port static void ahci_thaw(struct ata_port *ap); static void ahci_error_handler(struct ata_port *ap); static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); +static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); +static int ahci_port_resume(struct ata_port *ap); +static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); +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 = { @@ -234,6 +238,8 @@ static struct scsi_host_template ahci_sh .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, + .suspend = ata_scsi_device_suspend, + .resume = ata_scsi_device_resume, }; static const struct ata_port_operations ahci_ops = { @@ -260,6 +266,9 @@ static const struct ata_port_operations .error_handler = ahci_error_handler, .post_internal_cmd = ahci_post_internal_cmd, + .port_suspend = ahci_port_suspend, + .port_resume = ahci_port_resume, + .port_start = ahci_port_start, .port_stop = ahci_port_stop, }; @@ -361,6 +370,8 @@ static struct pci_driver ahci_pci_driver .name = DRV_NAME, .id_table = ahci_pci_tbl, .probe = ahci_init_one, + .suspend = ahci_pci_device_suspend, + .resume = ahci_pci_device_resume, .remove = ahci_remove_one, }; @@ -1199,6 +1210,79 @@ static void ahci_post_internal_cmd(struc } } +static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) +{ + struct ahci_host_priv *hpriv = ap->host_set->private_data; + 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); + const char *emsg = NULL; + int rc; + + rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + if (rc) { + ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); + ahci_init_port(port_mmio, hpriv->cap, + pp->cmd_slot_dma, pp->rx_fis_dma); + } + + return rc; +} + +static int ahci_port_resume(struct ata_port *ap) +{ + struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host_set->private_data; + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + + ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); + + return 0; +} + +static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) +{ + struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); + void __iomem *mmio = host_set->mmio_base; + u32 ctl; + + if (mesg.event == PM_EVENT_SUSPEND) { + /* AHCI spec rev1.1 section 8.3.3: + * Software must disable interrupts prior to requesting a + * transition of the HBA to D3 state. + */ + ctl = readl(mmio + HOST_CTL); + ctl &= ~HOST_IRQ_EN; + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + } + + return ata_pci_device_suspend(pdev, mesg); +} + +static int ahci_pci_device_resume(struct pci_dev *pdev) +{ + struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); + struct ahci_host_priv *hpriv = host_set->private_data; + void __iomem *mmio = host_set->mmio_base; + int rc; + + ata_pci_device_do_resume(pdev); + + if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(mmio, pdev); + if (rc) + return rc; + + ahci_init_controller(mmio, pdev, host_set->n_ports, hpriv->cap); + } + + ata_host_set_resume(host_set); + + return 0; +} + static int ahci_port_start(struct ata_port *ap) { struct device *dev = ap->host_set->dev;