ata: Fixes related to edge trigger latch for the ahci_xgene_driver.
v3 cover ata: Fixes related to edge trigger latch for the ahci_xgene_driver.
From: Suman Tripathi <[email protected]>
This patch set implements a workaround for an errate in the APM X-Gene SATA host controller with edge interrupt. The HOST_IRQ_STAT misses the edge interrupt from the PORT_IRQ_STAT when clearing the HOST_IRQ_STAT and reporting the PORT_IRQ_STAT happens in same clock cycle. It also implements the capability to override generic interrupt handler.
v1 change:
* Implement the capability for LDD to override interrupt handler.
* ahci_xgene driver implements the edge trigger interrupt handler.
v2 change:
* Remove the AHCI_HFLAG_EDGE_IRQ frim libahci.
v3 change:
* Reorder the patch sequence
Suman Tripathi (3):
libahci: Implement the capability to override the generic ahci
interrupt handler.
libahci: ata: Remove the AHCI_HFLAG_EDGE_IRQ support from libahci.
ahci_xgene: Implement the workaround to fix the missing of edge
interrupt for HOST_IRQ_STAT.
drivers/ata/ahci.h | 5 ++- drivers/ata/ahci_xgene.c | 85 +++++++++++++++++++++++++++++++++++++++++++++- drivers/ata/libahci.c | 66 +++++++++++------------------------- 3 files changed, 107 insertions(+), 49 deletions(-)
PATCH v3 1/3 libahci: Implement the capability to override the generic ahci interrupt handler.
From: Suman Tripathi <[email protected]>
This patch implements the capability to override the generic ahci interrupt handler so that the LDD drivers can implement there own custom interrupt handler routines.
--- drivers/ata/ahci.h | 2 ++ drivers/ata/libahci.c | 32 +++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-)
drivers/ata/ahci.h +2 -0
@@ -360,6 +360,7 @@ struct ahci_host_priv {* be overridden anytime before the host is activated. */ void (*start_engine)(struct ata_port *ap); +irqreturn_t (*ahci_irq_intr)(int irq,void *dev_instance); };#ifdef CONFIG_PCI_MSI
@@ -423,6 +424,7 @@ int ahci_reset_em(struct ata_host *host);void ahci_print_info (struct ata_host *host,const char *scc_s);int ahci_host_activate (struct ata_host *host,struct scsi_host_template *sht);void ahci_error_handler (struct ata_port *ap); +u32 ahci_handle_port_intr (struct ata_host *host,u32 irq_masked);static inline void __iomem *__ahci_port_base (struct ata_host *host,unsigned int port_no)
drivers/ata/libahci.c +23 -9
@@ -1816,7 +1816,7 @@ static irqreturn_t ahci_multi_irqs_intr_hard(int irq, void *dev_instance)return IRQ_HANDLED ; } -static u32 ahci_handle_port_intr (struct ata_host *host,u32 irq_masked) +u32 ahci_handle_port_intr (struct ata_host *host,u32 irq_masked) {unsigned int i, handled =0 ;
@@ -1842,6 +1842,7 @@ static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)return handled; } +EXPORT_SYMBOL_GPL (ahci_handle_port_intr);static irqreturn_t ahci_single_edge_irq_intr (int irq,void *dev_instance) {
@@ -2504,15 +2505,28 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)struct ahci_host_priv *hpriv = host->private_data;int irq = hpriv->irq;int rc; +irqreturn_t (*ahci_irq_handler)(int irq,void *dev_instance); -if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI |AHCI_HFLAG_MULTI_MSIX )) - rc =ahci_host_activate_multi_irqs (host, sht); -else if (hpriv->flags &AHCI_HFLAG_EDGE_IRQ ) - rc =ata_host_activate (host, irq, ahci_single_edge_irq_intr, -IRQF_SHARED , sht); -else - rc =ata_host_activate (host, irq, ahci_single_level_irq_intr, -IRQF_SHARED , sht); + ahci_irq_handler = hpriv->ahci_irq_intr; + +if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI |AHCI_HFLAG_MULTI_MSIX )) { +if (!ahci_irq_handler) + rc =ahci_host_activate_multi_irqs (host, sht); +else +dev_warn (host->dev,"both AHCI_HFLAG_MULTI_MSI flag set \ +and custom irq handler implemented\n" ); + + }else { +if (!ahci_irq_handler) { + ahci_irq_handler = (hpriv->flags &AHCI_HFLAG_EDGE_IRQ ? + ahci_single_edge_irq_intr : + ahci_single_level_irq_intr); + } + } + + rc =ata_host_activate (host, irq, ahci_irq_handler, +IRQF_SHARED , sht); +return rc; }EXPORT_SYMBOL_GPL (ahci_host_activate);
--
1.7.1
PATCH v3 1/3 libahci: Implement the capability to override the generic ahci interrupt handler.
Hello.
Quoted 4 lines · suman Tripathi
From: Suman Tripathi <[email protected]>
This patch implements the capability to override the generic ahci interrupt handler so that the LDD drivers can implement
AHCI.
Quoted 1 line
there own custom interrupt handler routines.
s/there/their/.
Quoted 11 lines
Signed-off-by Suman Tripathi <[email protected]>--- drivers/ata/ahci.h | 2 ++ drivers/ata/libahci.c | 32 +++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-)drivers/ata/ahci.h +0
-0
drivers/ata/ahci.h struct ahci_host_priv { · line 363
Quoted 4 lines
drivers/ata/ahci.h +1
-0@@ -360,6 +360,7 @@ struct ahci_host_priv {* be overridden anytime before the host is activated. */ void (*start_engine)(struct ata_port *ap); +irqreturn_t (*ahci_irq_intr)(int irq,void *dev_instance);
The 'ahci_irq_intr' name is somewhat tautological.
[...]
Quoted 4 lines
drivers/ata/libahci.c +0
-0
[...]
Quoted 15 lines
drivers/ata/libahci.c +2
-8@@ -2504,15 +2505,28 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)struct ahci_host_priv *hpriv = host->private_data;int irq = hpriv->irq;int rc; +irqreturn_t (*ahci_irq_handler)(int irq,void *dev_instance); -if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI |AHCI_HFLAG_MULTI_MSIX )) - rc =ahci_host_activate_multi_irqs (host, sht); -else if (hpriv->flags &AHCI_HFLAG_EDGE_IRQ ) - rc =ata_host_activate (host, irq, ahci_single_edge_irq_intr, -IRQF_SHARED , sht); -else - rc =ata_host_activate (host, irq, ahci_single_level_irq_intr, -IRQF_SHARED , sht); + ahci_irq_handler = hpriv->ahci_irq_intr;
drivers/ata/libahci.c int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht) · line 2520
Quoted 10 lines
drivers/ata/libahci.c +10
-0+ +if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI |AHCI_HFLAG_MULTI_MSIX )) { +if (!ahci_irq_handler) + rc =ahci_host_activate_multi_irqs (host, sht); +else +dev_warn (host->dev,"both AHCI_HFLAG_MULTI_MSI flag set \ +and custom irq handler implemented\n" ); + + }else { +if (!ahci_irq_handler) {
Why not *else* *if* on a single line?
[...]
MBR, Sergei
PATCH v3 1/3 libahci: Implement the capability to override the generic ahci interrupt handler.
In reply to suman TripathiHello,
On Fri, Feb 05, 2016 at 02:50:24AM +0530, suman Tripathi wrote:
...
Quoted 12 lines
drivers/ata/libahci.c +7
-0@@ -2504,15 +2505,28 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)struct ahci_host_priv *hpriv = host->private_data;int irq = hpriv->irq;int rc; +irqreturn_t (*ahci_irq_handler)(int irq,void *dev_instance); + ahci_irq_handler = hpriv->ahci_irq_intr; + +if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI |AHCI_HFLAG_MULTI_MSIX )) { +if (!ahci_irq_handler) + rc =ahci_host_activate_multi_irqs (host, sht); +else
drivers/ata/libahci.c int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht) · line 2525
Quoted 10 lines
drivers/ata/libahci.c +10
-0+dev_warn (host->dev,"both AHCI_HFLAG_MULTI_MSI flag set \ +and custom irq handler implemented\n" ); + + }else { +if (!ahci_irq_handler) { + ahci_irq_handler = (hpriv->flags &AHCI_HFLAG_EDGE_IRQ ? + ahci_single_edge_irq_intr : + ahci_single_level_irq_intr); + } + }
I wrote this before but can't we do this in save_initial_config the same way ->start_engine override is handled?
Signature · 1 line
tejun
PATCH v3 1/3 libahci: Implement the capability to override the generic ahci interrupt handler.
Quoted 21 lines · Tejun Heo
Hello,
On Fri, Feb 05, 2016 at 02:50:24AM +0530, suman Tripathi wrote:
...Quoted 17 lines
drivers/ata/libahci.c +12
-0@@ -2504,15 +2505,28 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)struct ahci_host_priv *hpriv = host->private_data;int irq = hpriv->irq;int rc; +irqreturn_t (*ahci_irq_handler)(int irq,void *dev_instance); + ahci_irq_handler = hpriv->ahci_irq_intr; + +if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI |AHCI_HFLAG_MULTI_MSIX )) { +if (!ahci_irq_handler) + rc =ahci_host_activate_multi_irqs (host, sht); +else +dev_warn (host->dev,"both AHCI_HFLAG_MULTI_MSI flag set \ +and custom irq handler implemented\n" ); + + }else { +if (!ahci_irq_handler) {
Quoted 8 lines
drivers/ata/libahci.c int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht) · line 2525
Quoted 5 lines
drivers/ata/libahci.c +5
-0+ ahci_irq_handler = (hpriv->flags &AHCI_HFLAG_EDGE_IRQ ? + ahci_single_edge_irq_intr : + ahci_single_level_irq_intr); + } + }I wrote this before but can't we do this in
save_initial_configthe same way ->start_engine override is handled?
Sorry missed that out.
Quoted 3 lines
--
tejun
Signature · 3 lines
Thanks, with regards, Suman Tripathi
A message that is not in the archive (sent off-list or not yet received): 1454620826-22554-1-git-send-email-stripathi-qTEPVZfXA3Y@public.gmane.org
PATCH v3 2/3 libahci: ata: Remove the AHCI_HFLAG_EDGE_IRQ support from libahci.
From: Suman Tripathi <[email protected]>
This patch removes the AHCI_HFLAG_EDGE_IRQ support from libahci. The flexibility to override the irq handlers in the LLD's are already present, so controller's implementing a EDGE triger latch can implement their own interrupt handler inside the driver.
--- drivers/ata/ahci.h | 3 +-- drivers/ata/ahci_xgene.c | 40 +++++++++++++++++++++++++++++++++++++++- drivers/ata/libahci.c | 44 ++------------------------------------------ 3 files changed, 42 insertions(+), 45 deletions(-)
drivers/ata/ahci.h +1 -2
@@ -240,8 +240,7 @@ enum {error-handling stage) */ AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */ AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */ -AHCI_HFLAG_EDGE_IRQ = (1 <<19 ),/* HOST_IRQ_STAT behaves as -Edge Triggered */ +#ifdef CONFIG_PCI_MSI AHCI_HFLAG_MULTI_MSI = (1 <<20 ),/* multiple PCI MSIs */ AHCI_HFLAG_MULTI_MSIX = (1 << 21), /* per-port MSI-X */
drivers/ata/ahci_xgene.c +39 -1
@@ -548,6 +548,43 @@ softreset_retry:return rc; } +static irqreturn_t xgene_ahci_irq_intr (int irq,void *dev_instance) +{ +struct ata_host *host = dev_instance; +struct ahci_host_priv *hpriv; +unsigned int rc =0 ; +void __iomem *mmio; +u32 irq_stat, irq_masked; + +VPRINTK ("ENTER\n" ); + + hpriv = host->private_data; + mmio = hpriv->mmio; + +/* sigh. 0xffffffff is a valid return from h/w */ + irq_stat =readl (mmio +HOST_IRQ_STAT ); +if (!irq_stat) +return IRQ_NONE ; + + irq_masked = irq_stat & hpriv->port_map; + +spin_lock (&host->lock); + +/* +* HOST_IRQ_STAT behaves as edge triggered latch meaning that +* it should be cleared before all the port events are cleared. +*/ +writel (irq_stat, mmio +HOST_IRQ_STAT ); + + rc =ahci_handle_port_intr (host, irq_masked); + +spin_unlock (&host->lock); + +VPRINTK ("EXIT\n" ); + +return IRQ_RETVAL (rc); +} +static struct ata_port_operations xgene_ahci_v1_ops = { .inherits = &ahci_ops, .host_stop = xgene_ahci_host_stop,
@@ -779,7 +816,8 @@ skip_clk_phy: hpriv->flags =AHCI_HFLAG_NO_NCQ ;break ;case XGENE_AHCI_V2 : - hpriv->flags |=AHCI_HFLAG_YES_FBS |AHCI_HFLAG_EDGE_IRQ ; + hpriv->flags |=AHCI_HFLAG_YES_FBS ; + hpriv->ahci_irq_intr = xgene_ahci_irq_intr;break ;default :break ;
drivers/ata/libahci.c +2 -42
56 lines not on this page · raw message
--
1.7.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v3 3/3 ahci_xgene: Implement the workaround to fix the missing of edge interrupt for HOST_IRQ_STAT.
In reply to suman TripathiFrom: Suman Tripathi <[email protected]>
Due to H/W errata, the HOST_IRQ_STAT register misses the edge interrupt when clearing the HOST_IRQ_STAT register and hardware reporting the PORT_IRQ_STAT register at the same clock cycle.
--- drivers/ata/ahci_xgene.c | 47 +++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 46 insertions(+), 1 deletions(-)
drivers/ata/ahci_xgene.c +46 -1
@@ -548,6 +548,51 @@ softreset_retry:return rc; } +/** +* xgene_ahci_handle_broken_edge_irq - Handle the broken irq. +* @ata_host: Host that recieved the irq +* @irq_masked: HOST_IRQ_STAT value +* +* For hardware with broken edge trigger latch +* the HOST_IRQ_STAT register misses the edge interrupt +* when clearing of HOST_IRQ_STAT register and hardware +* reporting the PORT_IRQ_STAT register at the +* same clock cycle. +* As such, the algorithm below outlines the workaround. +* +* 1. Read HOST_IRQ_STAT register and save the state. +* 2. Clear the HOST_IRQ_STAT register. +* 3. Read back the HOST_IRQ_STAT register. +* 4. If HOST_IRQ_STAT register equals to zero, then +* traverse the rest of port's PORT_IRQ_STAT register +* to check if an interrupt is triggered at that point else +* go to step 6. +* 5. If PORT_IRQ_STAT register of rest ports is not equal to zero +* then update the state of HOST_IRQ_STAT saved in step 1. +* 6. Handle port interrupts. +* 7. Exit +*/ +static int xgene_ahci_handle_broken_edge_irq (struct ata_host *host, +u32 irq_masked) +{ +struct ahci_host_priv *hpriv = host->private_data; +void __iomem *port_mmio; +int i; + +if (!readl (hpriv->mmio +HOST_IRQ_STAT )) { +for (i =0 ; i < host->n_ports; i++) { +if (irq_masked & (1 << i)) +continue ; + + port_mmio =ahci_port_base (host->ports[i]); +if (readl (port_mmio +PORT_IRQ_STAT )) + irq_masked |= (1 << i); + } + } + +return ahci_handle_port_intr (host, irq_masked); +} +static irqreturn_t xgene_ahci_irq_intr (int irq,void *dev_instance) {struct ata_host *host = dev_instance;
@@ -576,7 +621,7 @@ static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)*/ writel (irq_stat, mmio +HOST_IRQ_STAT ); - rc =ahci_handle_port_intr (host, irq_masked); + rc =xgene_ahci_handle_broken_edge_irq (host, irq_masked);spin_unlock (&host->lock);
--
1.7.1