[PATCH] USB: OHCI relies less on NDP register

Some OHCI implementations have differences in the way the NDP register
(in roothub_a) reports the number of ports present. This patch allows the
platform specific code to optionally supply the number of ports. The
driver just reads the value at init (if not supplied) instead of reading
it every time its needed (except for an AMD756 bug workaround).

It also sets the value correctly for the ARM pxa27x architecture.

Signed-Off-By: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 447f488..7924c74 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -228,23 +228,22 @@
 	char **next,
 	unsigned *size)
 {
-	u32			temp, ndp, i;
+	u32			temp, i;
 
 	temp = roothub_a (controller);
 	if (temp == ~(u32)0)
 		return;
-	ndp = (temp & RH_A_NDP);
 
 	if (verbose) {
 		ohci_dbg_sw (controller, next, size,
-			"roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp,
+			"roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d(%d)\n", temp,
 			((temp & RH_A_POTPGT) >> 24) & 0xff,
 			(temp & RH_A_NOCP) ? " NOCP" : "",
 			(temp & RH_A_OCPM) ? " OCPM" : "",
 			(temp & RH_A_DT) ? " DT" : "",
 			(temp & RH_A_NPS) ? " NPS" : "",
 			(temp & RH_A_PSM) ? " PSM" : "",
-			ndp
+			(temp & RH_A_NDP), controller->num_ports
 			);
 		temp = roothub_b (controller);
 		ohci_dbg_sw (controller, next, size,
@@ -266,7 +265,7 @@
 			);
 	}
 
-	for (i = 0; i < ndp; i++) {
+	for (i = 0; i < controller->num_ports; i++) {
 		temp = roothub_portstatus (controller, i);
 		dbg_port_sw (controller, i, temp, next, size);
 	}
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 6efb69f..67c1aa5 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -484,6 +484,10 @@
 	// flush the writes
 	(void) ohci_readl (ohci, &ohci->regs->control);
 
+	/* Read the number of ports unless overridden */
+	if (ohci->num_ports == 0)
+		ohci->num_ports = roothub_a(ohci) & RH_A_NDP;
+
 	if (ohci->hcca)
 		return 0;
 
@@ -560,10 +564,8 @@
 	msleep(temp);
 	temp = roothub_a (ohci);
 	if (!(temp & RH_A_NPS)) {
-		unsigned ports = temp & RH_A_NDP; 
-
 		/* power down each port */
-		for (temp = 0; temp < ports; temp++)
+		for (temp = 0; temp < ohci->num_ports; temp++)
 			ohci_writel (ohci, RH_PS_LSDA,
 				&ohci->regs->roothub.portstatus [temp]);
 	}
@@ -861,7 +863,7 @@
 		 * and that if we try to turn them back on the root hub
 		 * will respond to CSC processing.
 		 */
-		i = roothub_a (ohci) & RH_A_NDP;
+		i = ohci->num_ports;
 		while (i--)
 			ohci_writel (ohci, RH_PS_PSS,
 				&ohci->regs->roothub.portstatus [temp]);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 83ca454..ce7b28d 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -184,7 +184,7 @@
 	if (status != -EINPROGRESS)
 		return status;
 
-	temp = roothub_a (ohci) & RH_A_NDP;
+	temp = ohci->num_ports;
 	enables = 0;
 	while (temp--) {
 		u32 stat = ohci_readl (ohci,
@@ -304,7 +304,7 @@
 ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
 {
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
-	int		ports, i, changed = 0, length = 1;
+	int		i, changed = 0, length = 1;
 	int		can_suspend = hcd->can_wakeup;
 	unsigned long	flags;
 
@@ -319,9 +319,10 @@
 		goto done;
 	}
 
-	ports = roothub_a (ohci) & RH_A_NDP; 
-	if (ports > MAX_ROOT_PORTS) {
-		ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", ports,
+	/* undocumented erratum seen on at least rev D */
+	if ((ohci->flags & OHCI_QUIRK_AMD756)
+			&& (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) {
+		ohci_warn (ohci, "bogus NDP, rereads as NDP=%d\n",
 			  ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP);
 		/* retry later; "should not happen" */
 		goto done;
@@ -332,13 +333,13 @@
 		buf [0] = changed = 1;
 	else
 		buf [0] = 0;
-	if (ports > 7) {
+	if (ohci->num_ports > 7) {
 		buf [1] = 0;
 		length++;
 	}
 
 	/* look at each port */
-	for (i = 0; i < ports; i++) {
+	for (i = 0; i < ohci->num_ports; i++) {
 		u32	status = roothub_portstatus (ohci, i);
 
 		if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
@@ -395,15 +396,14 @@
 	struct usb_hub_descriptor	*desc
 ) {
 	u32		rh = roothub_a (ohci);
-	int		ports = rh & RH_A_NDP; 
 	u16		temp;
 
 	desc->bDescriptorType = 0x29;
 	desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
 	desc->bHubContrCurrent = 0;
 
-	desc->bNbrPorts = ports;
-	temp = 1 + (ports / 8);
+	desc->bNbrPorts = ohci->num_ports;
+	temp = 1 + (ohci->num_ports / 8);
 	desc->bDescLength = 7 + 2 * temp;
 
 	temp = 0;
@@ -421,7 +421,7 @@
 	rh = roothub_b (ohci);
 	memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
 	desc->bitmap [0] = rh & RH_B_DR;
-	if (ports > 7) {
+	if (ohci->num_ports > 7) {
 		desc->bitmap [1] = (rh & RH_B_DR) >> 8;
 		desc->bitmap [2] = 0xff;
 	} else
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index e5bc178..5dd20db 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -258,6 +258,9 @@
 
 	ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
 
+	/* The value of NDP in roothub_a is incorrect on this hardware */
+	ohci->num_ports = 3;
+
 	if ((ret = ohci_init(ohci)) < 0)
 		return ret;
 
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 71cdd22..8a9b9d9 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -383,6 +383,7 @@
 	/*
 	 * driver state
 	 */
+	int			num_ports;
 	int			load [NUM_INTS];
 	u32 			hc_control;	/* copy of hc control reg */
 	unsigned long		next_statechange;	/* suspend/resume */