ahci: updated power management support
PATCH 1/1 ahci: updated power management support
Hello, all.
This is updated version of AHCI power management patchset[1] from Forrest Zhao. A lot of implementation is taken verbatim but code has been simplified and reorganized.
Major changes are...
* Initialization functions are best-effort. They don't check for prerequisites. This is for both simplicity & robustness and simplifies code a lot. More explanation on this in patch #3.
* Both port suspend/resume() and pci_device suspend/resume() share most of code with driver init/de-init paths as other drivers do.
* Patches are splitted according to logical steps instead of define X / use X.
Thanks.
--
tejun
PATCH 2/6 ahci: cosmetic changes to ahci_start/stop_engine()
* fascist-format comments according to comment style used in libata core layer.
* if() -> if ()
---
drivers/scsi/ahci.c | 27 +++++++++------------------ 1 files changed, 9 insertions(+), 18 deletions(-)
51d2cea03da874cd41cd7a29d1314ddbe46b5314
drivers/scsi/ahci.c +9 -18
@@ -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 ;
Signature · 1 line
1.3.2
PATCH 1/6 ahci: relocate several internal functions
In reply to Tejun Heo* 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.
---
drivers/scsi/ahci.c | 255 +++++++++++++++++++++++++-------------------------- 1 files changed, 126 insertions(+), 129 deletions(-)
0353fed85301f2f2af7fdce02f6b9c1f55160462
drivers/scsi/ahci.c +126 -129
@@ -205,8 +205,6 @@ static irqreturn_t ahci_interrupt (int istatic 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_bareturn (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_pwritel (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 __iomereturn 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) {
Signature · 1 line
1.3.2
PATCH 3/6 ahci: simplify ahci_start_engine()
In reply to Tejun HeoSimplify 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.
---
drivers/scsi/ahci.c | 21 ++------------------- 1 files changed, 2 insertions(+), 19 deletions(-)
e98289381268b565aadcda0e32e0bdd5fad5f9f4
drivers/scsi/ahci.c +2 -19
@@ -406,32 +406,15 @@ static void ahci_scr_write (struct ata_pwritel (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)
Signature · 1 line
1.3.2
PATCH 4/6 libata: improve driver initialization and deinitialization
In reply to Tejun HeoImplement 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 <[email protected]>
---
drivers/scsi/ahci.c | 202 ++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 151 insertions(+), 51 deletions(-)
d773d83a6f3c558226b24a09131b0afa83e0ac02
drivers/scsi/ahci.c +151 -51
@@ -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 __iomemreturn 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_postatic 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_prostruct 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 );
Signature · 1 line
1.3.2
PATCH 5/6 ahci: separate out ahci_reset_controller() and ahci_init_controller()
In reply to Tejun HeoSeparate 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.
---
drivers/scsi/ahci.c | 171 ++++++++++++++++++++++++++++----------------------- 1 files changed, 94 insertions(+), 77 deletions(-)
b2e525899eb80c1433cc21af33430574e15c4d94
drivers/scsi/ahci.c +94 -77
@@ -572,6 +572,94 @@ static int ahci_deinit_port(void __iomemreturn 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_prostruct 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);
Signature · 1 line
1.3.2
PATCH 6/6 ahci: implement Power Management support
In reply to Tejun HeoImplement power management support.
Original implementation is from Zhao, Forrest <[email protected]>
---
drivers/scsi/ahci.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 84 insertions(+), 0 deletions(-)
4513d4761b48bca3025af0832d517f6b913061f9
drivers/scsi/ahci.c +84 -0
@@ -215,6 +215,10 @@ static void ahci_freeze(struct ata_portstatic 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;
Signature · 1 line
1.3.2
Jeff Garzik
AppliedRe: [PATCH 1/6] ahci: relocate several internal functions
In reply to Tejun HeoApplied patches 1-6 to #upstream