Patchwork 4xx: Add check_link to struct ppc4xx_pciex_hwops

login
register
mail settings
Submitter Tony Breeds
Date July 1, 2011, 6:44 a.m.
Message ID <20110701064424.GE13483@ozlabs.org>
Download mbox | patch
Permalink /patch/102872/
State Accepted
Commit 112d1fe9f7715db423ffeec5ac1beccff6093dc4
Delegated to: Josh Boyer
Headers show

Comments

Tony Breeds - July 1, 2011, 6:44 a.m.
All current pcie controllers unconditionally use SDR to check the link and
poll for reset.

Refactor the code to include device reset in the port_init_hw() op and add a new
check_link() op.

This will make room fro new controllers that do not use SDR for these
operations.

Tested on 460ex.

Signed-off-by: Tony Breeds <tony@bakeyournoodle.com>




Yours Tony
Ayman El-Khashab - July 12, 2011, 5:40 p.m.
On Fri, Jul 01, 2011 at 04:44:24PM +1000, Tony Breeds wrote:
> All current pcie controllers unconditionally use SDR to check the link and
> poll for reset.

I was able to apply this patch and then modify the 460SX to
work correctly, so I think it is fine.  There is only 1
comment below.  So how does one supply a patch atop another
patch?

Best,
Ayman

> +static int __init ppc4xx_pciex_port_reset_sdr(struct ppc4xx_pciex_port *port)
> +{
> +	printk(KERN_INFO "PCIE%d: Checking link...\n",
> +	       port->index);

Its not a functional problem, but this printk belongs in the
check link if anywhere rather than the reset.

> +
> +	/* Wait for reset to complete */
> +	if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 1 << 20, 0, 10)) {
> +		printk(KERN_WARNING "PCIE%d: PGRST failed\n",
> +		       port->index);
> +		return -1;
> +	}
> +	return 0;
> +}
Josh Boyer - July 12, 2011, 6:04 p.m.
On Tue, Jul 12, 2011 at 12:40:07PM -0500, Ayman El-Khashab wrote:
>On Fri, Jul 01, 2011 at 04:44:24PM +1000, Tony Breeds wrote:
>> All current pcie controllers unconditionally use SDR to check the link and
>> poll for reset.
>
>I was able to apply this patch and then modify the 460SX to
>work correctly, so I think it is fine.  There is only 1
>comment below.  So how does one supply a patch atop another
>patch?
>
>Best,
>Ayman
>
>> +static int __init ppc4xx_pciex_port_reset_sdr(struct ppc4xx_pciex_port *port)
>> +{
>> +	printk(KERN_INFO "PCIE%d: Checking link...\n",
>> +	       port->index);
>
>Its not a functional problem, but this printk belongs in the
>check link if anywhere rather than the reset.

I've got this queued in my tree locally.  I can make that change before
I push it out.

josh
Ayman El-Khashab - July 12, 2011, 10:13 p.m.
On Tue, Jul 12, 2011 at 02:04:04PM -0400, Josh Boyer wrote:
> On Tue, Jul 12, 2011 at 12:40:07PM -0500, Ayman El-Khashab wrote:
> >On Fri, Jul 01, 2011 at 04:44:24PM +1000, Tony Breeds wrote:
> >> All current pcie controllers unconditionally use SDR to check the link and
> >> poll for reset.
> >
> >I was able to apply this patch and then modify the 460SX to
> >work correctly, so I think it is fine.  There is only 1
> >comment below.  So how does one supply a patch atop another
> >patch?
> >
> >Best,
> >Ayman
> >
> >> +static int __init ppc4xx_pciex_port_reset_sdr(struct ppc4xx_pciex_port *port)
> >> +{
> >> +	printk(KERN_INFO "PCIE%d: Checking link...\n",
> >> +	       port->index);
> >
> >Its not a functional problem, but this printk belongs in the
> >check link if anywhere rather than the reset.
> 
> I've got this queued in my tree locally.  I can make that change before
> I push it out.
> 

Ok, so let me ask the following ... will it cause trouble if
I swap the sequence of the calls to the following in xxx_port_init

ppc4xx_pciex_port_init_mapping(...)

and

if (ppc4xx_pciex_hwops->check_link)...

The reason is that at least on the 460SX, the link check is
done via registers in the config space.  But the init_mapping is 
needed to setup some of the DCRs to make the config space work.  
In my check_link, i map the config space do the link checks
and then unmap since a superset of the space could be mapped
later.

Thanks,
ayman
Tony Breeds - July 13, 2011, 12:41 a.m.
On Tue, Jul 12, 2011 at 05:13:38PM -0500, Ayman El-Khashab wrote:

> Ok, so let me ask the following ... will it cause trouble if
> I swap the sequence of the calls to the following in xxx_port_init
> 
> ppc4xx_pciex_port_init_mapping(...)
> 
> and
> 
> if (ppc4xx_pciex_hwops->check_link)...
> 
> The reason is that at least on the 460SX, the link check is
> done via registers in the config space.  But the init_mapping is 
> needed to setup some of the DCRs to make the config space work.  
> In my check_link, i map the config space do the link checks
> and then unmap since a superset of the space could be mapped
> later.

This is also what I do.  IIRC ppc4xx_pciex_port_init_mapping() required
things that are setup between the 2 calls.

The double Mapping is fugly but I think it should be safe.

Yours Tony
Tony Breeds - July 13, 2011, 12:42 a.m.
On Tue, Jul 12, 2011 at 02:04:04PM -0400, Josh Boyer wrote:
> On Tue, Jul 12, 2011 at 12:40:07PM -0500, Ayman El-Khashab wrote:
> >On Fri, Jul 01, 2011 at 04:44:24PM +1000, Tony Breeds wrote:
> >> All current pcie controllers unconditionally use SDR to check the link and
> >> poll for reset.
> >
> >I was able to apply this patch and then modify the 460SX to
> >work correctly, so I think it is fine.  There is only 1
> >comment below.  So how does one supply a patch atop another
> >patch?
> >
> >Best,
> >Ayman
> >
> >> +static int __init ppc4xx_pciex_port_reset_sdr(struct ppc4xx_pciex_port *port)
> >> +{
> >> +	printk(KERN_INFO "PCIE%d: Checking link...\n",
> >> +	       port->index);
> >
> >Its not a functional problem, but this printk belongs in the
> >check link if anywhere rather than the reset.
> 
> I've got this queued in my tree locally.  I can make that change before
> I push it out.

Thanks Josh.  I really thought I'd fixed that before posting.

Yours Tony

Patch

diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index 156aa7d..ad330fe 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -650,12 +650,75 @@  struct ppc4xx_pciex_hwops
 	int (*core_init)(struct device_node *np);
 	int (*port_init_hw)(struct ppc4xx_pciex_port *port);
 	int (*setup_utl)(struct ppc4xx_pciex_port *port);
+	void (*check_link)(struct ppc4xx_pciex_port *port);
 };
 
 static struct ppc4xx_pciex_hwops *ppc4xx_pciex_hwops;
 
 #ifdef CONFIG_44x
 
+static int __init ppc4xx_pciex_wait_on_sdr(struct ppc4xx_pciex_port *port,
+					   unsigned int sdr_offset,
+					   unsigned int mask,
+					   unsigned int value,
+					   int timeout_ms)
+{
+	u32 val;
+
+	while(timeout_ms--) {
+		val = mfdcri(SDR0, port->sdr_base + sdr_offset);
+		if ((val & mask) == value) {
+			pr_debug("PCIE%d: Wait on SDR %x success with tm %d (%08x)\n",
+				 port->index, sdr_offset, timeout_ms, val);
+			return 0;
+		}
+		msleep(1);
+	}
+	return -1;
+}
+
+static int __init ppc4xx_pciex_port_reset_sdr(struct ppc4xx_pciex_port *port)
+{
+	printk(KERN_INFO "PCIE%d: Checking link...\n",
+	       port->index);
+
+	/* Wait for reset to complete */
+	if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 1 << 20, 0, 10)) {
+		printk(KERN_WARNING "PCIE%d: PGRST failed\n",
+		       port->index);
+		return -1;
+	}
+	return 0;
+}
+
+static void __init ppc4xx_pciex_check_link_sdr(struct ppc4xx_pciex_port *port)
+{
+	/* Check for card presence detect if supported, if not, just wait for
+	 * link unconditionally.
+	 *
+	 * note that we don't fail if there is no link, we just filter out
+	 * config space accesses. That way, it will be easier to implement
+	 * hotplug later on.
+	 */
+	if (!port->has_ibpre ||
+	    !ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP,
+				      1 << 28, 1 << 28, 100)) {
+		printk(KERN_INFO
+		       "PCIE%d: Device detected, waiting for link...\n",
+		       port->index);
+		if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP,
+					     0x1000, 0x1000, 2000))
+			printk(KERN_WARNING
+			       "PCIE%d: Link up failed\n", port->index);
+		else {
+			printk(KERN_INFO
+			       "PCIE%d: link is up !\n", port->index);
+			port->link = 1;
+		}
+	} else
+		printk(KERN_INFO "PCIE%d: No device detected.\n", port->index);
+}
+
 /* Check various reset bits of the 440SPe PCIe core */
 static int __init ppc440spe_pciex_check_reset(struct device_node *np)
 {
@@ -806,7 +869,7 @@  static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
 	dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET,
 			(1 << 24) | (1 << 16), 1 << 12);
 
-	return 0;
+	return ppc4xx_pciex_port_reset_sdr(port);
 }
 
 static int ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
@@ -856,6 +919,7 @@  static struct ppc4xx_pciex_hwops ppc440speA_pcie_hwops __initdata =
 	.core_init	= ppc440spe_pciex_core_init,
 	.port_init_hw	= ppc440speA_pciex_init_port_hw,
 	.setup_utl	= ppc440speA_pciex_init_utl,
+	.check_link	= ppc4xx_pciex_check_link_sdr,
 };
 
 static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata =
@@ -863,6 +927,7 @@  static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata =
 	.core_init	= ppc440spe_pciex_core_init,
 	.port_init_hw	= ppc440speB_pciex_init_port_hw,
 	.setup_utl	= ppc440speB_pciex_init_utl,
+	.check_link	= ppc4xx_pciex_check_link_sdr,
 };
 
 static int __init ppc460ex_pciex_core_init(struct device_node *np)
@@ -944,7 +1009,7 @@  static int ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
 
 	port->has_ibpre = 1;
 
-	return 0;
+	return ppc4xx_pciex_port_reset_sdr(port);
 }
 
 static int ppc460ex_pciex_init_utl(struct ppc4xx_pciex_port *port)
@@ -972,6 +1037,7 @@  static struct ppc4xx_pciex_hwops ppc460ex_pcie_hwops __initdata =
 	.core_init	= ppc460ex_pciex_core_init,
 	.port_init_hw	= ppc460ex_pciex_init_port_hw,
 	.setup_utl	= ppc460ex_pciex_init_utl,
+	.check_link	= ppc4xx_pciex_check_link_sdr,
 };
 
 static int __init ppc460sx_pciex_core_init(struct device_node *np)
@@ -1075,7 +1141,7 @@  static int ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
 
 	port->has_ibpre = 1;
 
-	return 0;
+	return ppc4xx_pciex_port_reset_sdr(port);
 }
 
 static int ppc460sx_pciex_init_utl(struct ppc4xx_pciex_port *port)
@@ -1089,6 +1155,7 @@  static struct ppc4xx_pciex_hwops ppc460sx_pcie_hwops __initdata = {
 	.core_init	= ppc460sx_pciex_core_init,
 	.port_init_hw	= ppc460sx_pciex_init_port_hw,
 	.setup_utl	= ppc460sx_pciex_init_utl,
+	.check_link	= ppc4xx_pciex_check_link_sdr,
 };
 
 #endif /* CONFIG_44x */
@@ -1154,7 +1221,7 @@  static int ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
 
 	port->has_ibpre = 1;
 
-	return 0;
+	return ppc4xx_pciex_port_reset_sdr(port);
 }
 
 static int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port)
@@ -1183,11 +1250,11 @@  static struct ppc4xx_pciex_hwops ppc405ex_pcie_hwops __initdata =
 	.core_init	= ppc405ex_pciex_core_init,
 	.port_init_hw	= ppc405ex_pciex_init_port_hw,
 	.setup_utl	= ppc405ex_pciex_init_utl,
+	.check_link	= ppc4xx_pciex_check_link_sdr,
 };
 
 #endif /* CONFIG_40x */
 
-
 /* Check that the core has been initied and if not, do it */
 static int __init ppc4xx_pciex_check_core_init(struct device_node *np)
 {
@@ -1261,26 +1328,6 @@  static void __init ppc4xx_pciex_port_init_mapping(struct ppc4xx_pciex_port *port
 	dcr_write(port->dcrs, DCRO_PEGPL_MSGMSK, 0);
 }
 
-static int __init ppc4xx_pciex_wait_on_sdr(struct ppc4xx_pciex_port *port,
-					   unsigned int sdr_offset,
-					   unsigned int mask,
-					   unsigned int value,
-					   int timeout_ms)
-{
-	u32 val;
-
-	while(timeout_ms--) {
-		val = mfdcri(SDR0, port->sdr_base + sdr_offset);
-		if ((val & mask) == value) {
-			pr_debug("PCIE%d: Wait on SDR %x success with tm %d (%08x)\n",
-				 port->index, sdr_offset, timeout_ms, val);
-			return 0;
-		}
-		msleep(1);
-	}
-	return -1;
-}
-
 static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
 {
 	int rc = 0;
@@ -1291,40 +1338,8 @@  static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
 	if (rc != 0)
 		return rc;
 
-	printk(KERN_INFO "PCIE%d: Checking link...\n",
-	       port->index);
-
-	/* Wait for reset to complete */
-	if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 1 << 20, 0, 10)) {
-		printk(KERN_WARNING "PCIE%d: PGRST failed\n",
-		       port->index);
-		return -1;
-	}
-
-	/* Check for card presence detect if supported, if not, just wait for
-	 * link unconditionally.
-	 *
-	 * note that we don't fail if there is no link, we just filter out
-	 * config space accesses. That way, it will be easier to implement
-	 * hotplug later on.
-	 */
-	if (!port->has_ibpre ||
-	    !ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP,
-				      1 << 28, 1 << 28, 100)) {
-		printk(KERN_INFO
-		       "PCIE%d: Device detected, waiting for link...\n",
-		       port->index);
-		if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP,
-					     0x1000, 0x1000, 2000))
-			printk(KERN_WARNING
-			       "PCIE%d: Link up failed\n", port->index);
-		else {
-			printk(KERN_INFO
-			       "PCIE%d: link is up !\n", port->index);
-			port->link = 1;
-		}
-	} else
-		printk(KERN_INFO "PCIE%d: No device detected.\n", port->index);
+	if (ppc4xx_pciex_hwops->check_link)
+		ppc4xx_pciex_hwops->check_link(port);
 
 	/*
 	 * Initialize mapping: disable all regions and configure
@@ -1347,14 +1362,17 @@  static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
 	/*
 	 * Check for VC0 active and assert RDY.
 	 */
-	if (port->link &&
-	    ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS,
-				     1 << 16, 1 << 16, 5000)) {
-		printk(KERN_INFO "PCIE%d: VC0 not active\n", port->index);
-		port->link = 0;
+	if (port->sdr_base) {
+		if (port->link &&
+		    ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS,
+					     1 << 16, 1 << 16, 5000)) {
+			printk(KERN_INFO "PCIE%d: VC0 not active\n", port->index);
+			port->link = 0;
+		}
+
+		dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, 0, 1 << 20);
 	}
 
-	dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, 0, 1 << 20);
 	msleep(100);
 
 	return 0;