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>
char **next,
unsigned *size)
{
char **next,
unsigned *size)
{
temp = roothub_a (controller);
if (temp == ~(u32)0)
return;
temp = roothub_a (controller);
if (temp == ~(u32)0)
return;
- ndp = (temp & RH_A_NDP);
if (verbose) {
ohci_dbg_sw (controller, next, size,
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" : "",
((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" : "",
+ (temp & RH_A_NDP), controller->num_ports
);
temp = roothub_b (controller);
ohci_dbg_sw (controller, next, size,
);
temp = roothub_b (controller);
ohci_dbg_sw (controller, next, size,
- 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);
}
temp = roothub_portstatus (controller, i);
dbg_port_sw (controller, i, temp, next, size);
}
// flush the writes
(void) ohci_readl (ohci, &ohci->regs->control);
// 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;
if (ohci->hcca)
return 0;
msleep(temp);
temp = roothub_a (ohci);
if (!(temp & RH_A_NPS)) {
msleep(temp);
temp = roothub_a (ohci);
if (!(temp & RH_A_NPS)) {
- unsigned ports = temp & RH_A_NDP;
-
/* power down each port */
/* 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]);
}
ohci_writel (ohci, RH_PS_LSDA,
&ohci->regs->roothub.portstatus [temp]);
}
* and that if we try to turn them back on the root hub
* will respond to CSC processing.
*/
* 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;
while (i--)
ohci_writel (ohci, RH_PS_PSS,
&ohci->regs->roothub.portstatus [temp]);
while (i--)
ohci_writel (ohci, RH_PS_PSS,
&ohci->regs->roothub.portstatus [temp]);
if (status != -EINPROGRESS)
return status;
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,
enables = 0;
while (temp--) {
u32 stat = ohci_readl (ohci,
ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
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;
int can_suspend = hcd->can_wakeup;
unsigned long flags;
- 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;
ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP);
/* retry later; "should not happen" */
goto done;
buf [0] = changed = 1;
else
buf [0] = 0;
buf [0] = changed = 1;
else
buf [0] = 0;
+ if (ohci->num_ports > 7) {
buf [1] = 0;
length++;
}
/* look at each port */
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
u32 status = roothub_portstatus (ohci, i);
if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
struct usb_hub_descriptor *desc
) {
u32 rh = roothub_a (ohci);
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;
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;
desc->bDescLength = 7 + 2 * temp;
temp = 0;
rh = roothub_b (ohci);
memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
desc->bitmap [0] = rh & RH_B_DR;
rh = roothub_b (ohci);
memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
desc->bitmap [0] = rh & RH_B_DR;
+ if (ohci->num_ports > 7) {
desc->bitmap [1] = (rh & RH_B_DR) >> 8;
desc->bitmap [2] = 0xff;
} else
desc->bitmap [1] = (rh & RH_B_DR) >> 8;
desc->bitmap [2] = 0xff;
} else
ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
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;
if ((ret = ohci_init(ohci)) < 0)
return ret;
int load [NUM_INTS];
u32 hc_control; /* copy of hc control reg */
unsigned long next_statechange; /* suspend/resume */
int load [NUM_INTS];
u32 hc_control; /* copy of hc control reg */
unsigned long next_statechange; /* suspend/resume */