diff options
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 165 |
1 files changed, 116 insertions, 49 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a78f2ebd11b7..e3ddc6a95afe 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
@@ -50,7 +50,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, | |||
50 | temp |= 0x0008; | 50 | temp |= 0x0008; |
51 | /* Bits 6:5 - no TTs in root ports */ | 51 | /* Bits 6:5 - no TTs in root ports */ |
52 | /* Bit 7 - no port indicators */ | 52 | /* Bit 7 - no port indicators */ |
53 | desc->wHubCharacteristics = (__force __u16) cpu_to_le16(temp); | 53 | desc->wHubCharacteristics = cpu_to_le16(temp); |
54 | } | 54 | } |
55 | 55 | ||
56 | /* Fill in the USB 2.0 roothub descriptor */ | 56 | /* Fill in the USB 2.0 roothub descriptor */ |
@@ -314,7 +314,7 @@ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) | |||
314 | } | 314 | } |
315 | 315 | ||
316 | static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, | 316 | static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, |
317 | u16 wIndex, u32 __iomem *addr, u32 port_status) | 317 | u16 wIndex, __le32 __iomem *addr, u32 port_status) |
318 | { | 318 | { |
319 | /* Don't allow the USB core to disable SuperSpeed ports. */ | 319 | /* Don't allow the USB core to disable SuperSpeed ports. */ |
320 | if (hcd->speed == HCD_USB3) { | 320 | if (hcd->speed == HCD_USB3) { |
@@ -331,7 +331,7 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, | |||
331 | } | 331 | } |
332 | 332 | ||
333 | static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, | 333 | static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, |
334 | u16 wIndex, u32 __iomem *addr, u32 port_status) | 334 | u16 wIndex, __le32 __iomem *addr, u32 port_status) |
335 | { | 335 | { |
336 | char *port_change_bit; | 336 | char *port_change_bit; |
337 | u32 status; | 337 | u32 status; |
@@ -341,6 +341,10 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, | |||
341 | status = PORT_RC; | 341 | status = PORT_RC; |
342 | port_change_bit = "reset"; | 342 | port_change_bit = "reset"; |
343 | break; | 343 | break; |
344 | case USB_PORT_FEAT_C_BH_PORT_RESET: | ||
345 | status = PORT_WRC; | ||
346 | port_change_bit = "warm(BH) reset"; | ||
347 | break; | ||
344 | case USB_PORT_FEAT_C_CONNECTION: | 348 | case USB_PORT_FEAT_C_CONNECTION: |
345 | status = PORT_CSC; | 349 | status = PORT_CSC; |
346 | port_change_bit = "connect"; | 350 | port_change_bit = "connect"; |
@@ -357,6 +361,10 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, | |||
357 | status = PORT_PLC; | 361 | status = PORT_PLC; |
358 | port_change_bit = "suspend/resume"; | 362 | port_change_bit = "suspend/resume"; |
359 | break; | 363 | break; |
364 | case USB_PORT_FEAT_C_PORT_LINK_STATE: | ||
365 | status = PORT_PLC; | ||
366 | port_change_bit = "link state"; | ||
367 | break; | ||
360 | default: | 368 | default: |
361 | /* Should never happen */ | 369 | /* Should never happen */ |
362 | return; | 370 | return; |
@@ -376,9 +384,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
376 | unsigned long flags; | 384 | unsigned long flags; |
377 | u32 temp, temp1, status; | 385 | u32 temp, temp1, status; |
378 | int retval = 0; | 386 | int retval = 0; |
379 | u32 __iomem **port_array; | 387 | __le32 __iomem **port_array; |
380 | int slot_id; | 388 | int slot_id; |
381 | struct xhci_bus_state *bus_state; | 389 | struct xhci_bus_state *bus_state; |
390 | u16 link_state = 0; | ||
382 | 391 | ||
383 | if (hcd->speed == HCD_USB3) { | 392 | if (hcd->speed == HCD_USB3) { |
384 | ports = xhci->num_usb3_ports; | 393 | ports = xhci->num_usb3_ports; |
@@ -422,9 +431,6 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
422 | } | 431 | } |
423 | xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); | 432 | xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); |
424 | 433 | ||
425 | /* FIXME - should we return a port status value like the USB | ||
426 | * 3.0 external hubs do? | ||
427 | */ | ||
428 | /* wPortChange bits */ | 434 | /* wPortChange bits */ |
429 | if (temp & PORT_CSC) | 435 | if (temp & PORT_CSC) |
430 | status |= USB_PORT_STAT_C_CONNECTION << 16; | 436 | status |= USB_PORT_STAT_C_CONNECTION << 16; |
@@ -432,13 +438,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
432 | status |= USB_PORT_STAT_C_ENABLE << 16; | 438 | status |= USB_PORT_STAT_C_ENABLE << 16; |
433 | if ((temp & PORT_OCC)) | 439 | if ((temp & PORT_OCC)) |
434 | status |= USB_PORT_STAT_C_OVERCURRENT << 16; | 440 | status |= USB_PORT_STAT_C_OVERCURRENT << 16; |
435 | /* | 441 | if ((temp & PORT_RC)) |
436 | * FIXME ignoring reset and USB 2.1/3.0 specific | 442 | status |= USB_PORT_STAT_C_RESET << 16; |
437 | * changes | 443 | /* USB3.0 only */ |
438 | */ | 444 | if (hcd->speed == HCD_USB3) { |
439 | if ((temp & PORT_PLS_MASK) == XDEV_U3 | 445 | if ((temp & PORT_PLC)) |
440 | && (temp & PORT_POWER)) | 446 | status |= USB_PORT_STAT_C_LINK_STATE << 16; |
441 | status |= 1 << USB_PORT_FEAT_SUSPEND; | 447 | if ((temp & PORT_WRC)) |
448 | status |= USB_PORT_STAT_C_BH_RESET << 16; | ||
449 | } | ||
450 | |||
451 | if (hcd->speed != HCD_USB3) { | ||
452 | if ((temp & PORT_PLS_MASK) == XDEV_U3 | ||
453 | && (temp & PORT_POWER)) | ||
454 | status |= USB_PORT_STAT_SUSPEND; | ||
455 | } | ||
442 | if ((temp & PORT_PLS_MASK) == XDEV_RESUME) { | 456 | if ((temp & PORT_PLS_MASK) == XDEV_RESUME) { |
443 | if ((temp & PORT_RESET) || !(temp & PORT_PE)) | 457 | if ((temp & PORT_RESET) || !(temp & PORT_PE)) |
444 | goto error; | 458 | goto error; |
@@ -469,7 +483,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
469 | && (temp & PORT_POWER) | 483 | && (temp & PORT_POWER) |
470 | && (bus_state->suspended_ports & (1 << wIndex))) { | 484 | && (bus_state->suspended_ports & (1 << wIndex))) { |
471 | bus_state->suspended_ports &= ~(1 << wIndex); | 485 | bus_state->suspended_ports &= ~(1 << wIndex); |
472 | bus_state->port_c_suspend |= 1 << wIndex; | 486 | if (hcd->speed != HCD_USB3) |
487 | bus_state->port_c_suspend |= 1 << wIndex; | ||
473 | } | 488 | } |
474 | if (temp & PORT_CONNECT) { | 489 | if (temp & PORT_CONNECT) { |
475 | status |= USB_PORT_STAT_CONNECTION; | 490 | status |= USB_PORT_STAT_CONNECTION; |
@@ -481,14 +496,28 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
481 | status |= USB_PORT_STAT_OVERCURRENT; | 496 | status |= USB_PORT_STAT_OVERCURRENT; |
482 | if (temp & PORT_RESET) | 497 | if (temp & PORT_RESET) |
483 | status |= USB_PORT_STAT_RESET; | 498 | status |= USB_PORT_STAT_RESET; |
484 | if (temp & PORT_POWER) | 499 | if (temp & PORT_POWER) { |
485 | status |= USB_PORT_STAT_POWER; | 500 | if (hcd->speed == HCD_USB3) |
501 | status |= USB_SS_PORT_STAT_POWER; | ||
502 | else | ||
503 | status |= USB_PORT_STAT_POWER; | ||
504 | } | ||
505 | /* Port Link State */ | ||
506 | if (hcd->speed == HCD_USB3) { | ||
507 | /* resume state is a xHCI internal state. | ||
508 | * Do not report it to usb core. | ||
509 | */ | ||
510 | if ((temp & PORT_PLS_MASK) != XDEV_RESUME) | ||
511 | status |= (temp & PORT_PLS_MASK); | ||
512 | } | ||
486 | if (bus_state->port_c_suspend & (1 << wIndex)) | 513 | if (bus_state->port_c_suspend & (1 << wIndex)) |
487 | status |= 1 << USB_PORT_FEAT_C_SUSPEND; | 514 | status |= 1 << USB_PORT_FEAT_C_SUSPEND; |
488 | xhci_dbg(xhci, "Get port status returned 0x%x\n", status); | 515 | xhci_dbg(xhci, "Get port status returned 0x%x\n", status); |
489 | put_unaligned(cpu_to_le32(status), (__le32 *) buf); | 516 | put_unaligned(cpu_to_le32(status), (__le32 *) buf); |
490 | break; | 517 | break; |
491 | case SetPortFeature: | 518 | case SetPortFeature: |
519 | if (wValue == USB_PORT_FEAT_LINK_STATE) | ||
520 | link_state = (wIndex & 0xff00) >> 3; | ||
492 | wIndex &= 0xff; | 521 | wIndex &= 0xff; |
493 | if (!wIndex || wIndex > ports) | 522 | if (!wIndex || wIndex > ports) |
494 | goto error; | 523 | goto error; |
@@ -537,6 +566,44 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
537 | temp = xhci_readl(xhci, port_array[wIndex]); | 566 | temp = xhci_readl(xhci, port_array[wIndex]); |
538 | bus_state->suspended_ports |= 1 << wIndex; | 567 | bus_state->suspended_ports |= 1 << wIndex; |
539 | break; | 568 | break; |
569 | case USB_PORT_FEAT_LINK_STATE: | ||
570 | temp = xhci_readl(xhci, port_array[wIndex]); | ||
571 | /* Software should not attempt to set | ||
572 | * port link state above '5' (Rx.Detect) and the port | ||
573 | * must be enabled. | ||
574 | */ | ||
575 | if ((temp & PORT_PE) == 0 || | ||
576 | (link_state > USB_SS_PORT_LS_RX_DETECT)) { | ||
577 | xhci_warn(xhci, "Cannot set link state.\n"); | ||
578 | goto error; | ||
579 | } | ||
580 | |||
581 | if (link_state == USB_SS_PORT_LS_U3) { | ||
582 | slot_id = xhci_find_slot_id_by_port(hcd, xhci, | ||
583 | wIndex + 1); | ||
584 | if (slot_id) { | ||
585 | /* unlock to execute stop endpoint | ||
586 | * commands */ | ||
587 | spin_unlock_irqrestore(&xhci->lock, | ||
588 | flags); | ||
589 | xhci_stop_device(xhci, slot_id, 1); | ||
590 | spin_lock_irqsave(&xhci->lock, flags); | ||
591 | } | ||
592 | } | ||
593 | |||
594 | temp = xhci_port_state_to_neutral(temp); | ||
595 | temp &= ~PORT_PLS_MASK; | ||
596 | temp |= PORT_LINK_STROBE | link_state; | ||
597 | xhci_writel(xhci, temp, port_array[wIndex]); | ||
598 | |||
599 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
600 | msleep(20); /* wait device to enter */ | ||
601 | spin_lock_irqsave(&xhci->lock, flags); | ||
602 | |||
603 | temp = xhci_readl(xhci, port_array[wIndex]); | ||
604 | if (link_state == USB_SS_PORT_LS_U3) | ||
605 | bus_state->suspended_ports |= 1 << wIndex; | ||
606 | break; | ||
540 | case USB_PORT_FEAT_POWER: | 607 | case USB_PORT_FEAT_POWER: |
541 | /* | 608 | /* |
542 | * Turn on ports, even if there isn't per-port switching. | 609 | * Turn on ports, even if there isn't per-port switching. |
@@ -557,6 +624,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
557 | temp = xhci_readl(xhci, port_array[wIndex]); | 624 | temp = xhci_readl(xhci, port_array[wIndex]); |
558 | xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); | 625 | xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); |
559 | break; | 626 | break; |
627 | case USB_PORT_FEAT_BH_PORT_RESET: | ||
628 | temp |= PORT_WR; | ||
629 | xhci_writel(xhci, temp, port_array[wIndex]); | ||
630 | |||
631 | temp = xhci_readl(xhci, port_array[wIndex]); | ||
632 | break; | ||
560 | default: | 633 | default: |
561 | goto error; | 634 | goto error; |
562 | } | 635 | } |
@@ -584,35 +657,27 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
584 | if (temp & XDEV_U3) { | 657 | if (temp & XDEV_U3) { |
585 | if ((temp & PORT_PE) == 0) | 658 | if ((temp & PORT_PE) == 0) |
586 | goto error; | 659 | goto error; |
587 | if (DEV_SUPERSPEED(temp)) { | ||
588 | temp = xhci_port_state_to_neutral(temp); | ||
589 | temp &= ~PORT_PLS_MASK; | ||
590 | temp |= PORT_LINK_STROBE | XDEV_U0; | ||
591 | xhci_writel(xhci, temp, | ||
592 | port_array[wIndex]); | ||
593 | xhci_readl(xhci, port_array[wIndex]); | ||
594 | } else { | ||
595 | temp = xhci_port_state_to_neutral(temp); | ||
596 | temp &= ~PORT_PLS_MASK; | ||
597 | temp |= PORT_LINK_STROBE | XDEV_RESUME; | ||
598 | xhci_writel(xhci, temp, | ||
599 | port_array[wIndex]); | ||
600 | 660 | ||
601 | spin_unlock_irqrestore(&xhci->lock, | 661 | temp = xhci_port_state_to_neutral(temp); |
602 | flags); | 662 | temp &= ~PORT_PLS_MASK; |
603 | msleep(20); | 663 | temp |= PORT_LINK_STROBE | XDEV_RESUME; |
604 | spin_lock_irqsave(&xhci->lock, flags); | 664 | xhci_writel(xhci, temp, |
665 | port_array[wIndex]); | ||
605 | 666 | ||
606 | temp = xhci_readl(xhci, | 667 | spin_unlock_irqrestore(&xhci->lock, |
607 | port_array[wIndex]); | 668 | flags); |
608 | temp = xhci_port_state_to_neutral(temp); | 669 | msleep(20); |
609 | temp &= ~PORT_PLS_MASK; | 670 | spin_lock_irqsave(&xhci->lock, flags); |
610 | temp |= PORT_LINK_STROBE | XDEV_U0; | 671 | |
611 | xhci_writel(xhci, temp, | 672 | temp = xhci_readl(xhci, |
612 | port_array[wIndex]); | 673 | port_array[wIndex]); |
613 | } | 674 | temp = xhci_port_state_to_neutral(temp); |
614 | bus_state->port_c_suspend |= 1 << wIndex; | 675 | temp &= ~PORT_PLS_MASK; |
676 | temp |= PORT_LINK_STROBE | XDEV_U0; | ||
677 | xhci_writel(xhci, temp, | ||
678 | port_array[wIndex]); | ||
615 | } | 679 | } |
680 | bus_state->port_c_suspend |= 1 << wIndex; | ||
616 | 681 | ||
617 | slot_id = xhci_find_slot_id_by_port(hcd, xhci, | 682 | slot_id = xhci_find_slot_id_by_port(hcd, xhci, |
618 | wIndex + 1); | 683 | wIndex + 1); |
@@ -625,9 +690,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
625 | case USB_PORT_FEAT_C_SUSPEND: | 690 | case USB_PORT_FEAT_C_SUSPEND: |
626 | bus_state->port_c_suspend &= ~(1 << wIndex); | 691 | bus_state->port_c_suspend &= ~(1 << wIndex); |
627 | case USB_PORT_FEAT_C_RESET: | 692 | case USB_PORT_FEAT_C_RESET: |
693 | case USB_PORT_FEAT_C_BH_PORT_RESET: | ||
628 | case USB_PORT_FEAT_C_CONNECTION: | 694 | case USB_PORT_FEAT_C_CONNECTION: |
629 | case USB_PORT_FEAT_C_OVER_CURRENT: | 695 | case USB_PORT_FEAT_C_OVER_CURRENT: |
630 | case USB_PORT_FEAT_C_ENABLE: | 696 | case USB_PORT_FEAT_C_ENABLE: |
697 | case USB_PORT_FEAT_C_PORT_LINK_STATE: | ||
631 | xhci_clear_port_change_bit(xhci, wValue, wIndex, | 698 | xhci_clear_port_change_bit(xhci, wValue, wIndex, |
632 | port_array[wIndex], temp); | 699 | port_array[wIndex], temp); |
633 | break; | 700 | break; |
@@ -664,7 +731,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) | |||
664 | int i, retval; | 731 | int i, retval; |
665 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | 732 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
666 | int ports; | 733 | int ports; |
667 | u32 __iomem **port_array; | 734 | __le32 __iomem **port_array; |
668 | struct xhci_bus_state *bus_state; | 735 | struct xhci_bus_state *bus_state; |
669 | 736 | ||
670 | if (hcd->speed == HCD_USB3) { | 737 | if (hcd->speed == HCD_USB3) { |
@@ -681,7 +748,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) | |||
681 | memset(buf, 0, retval); | 748 | memset(buf, 0, retval); |
682 | status = 0; | 749 | status = 0; |
683 | 750 | ||
684 | mask = PORT_CSC | PORT_PEC | PORT_OCC; | 751 | mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC; |
685 | 752 | ||
686 | spin_lock_irqsave(&xhci->lock, flags); | 753 | spin_lock_irqsave(&xhci->lock, flags); |
687 | /* For each port, did anything change? If so, set that bit in buf. */ | 754 | /* For each port, did anything change? If so, set that bit in buf. */ |
@@ -709,7 +776,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) | |||
709 | { | 776 | { |
710 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | 777 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
711 | int max_ports, port_index; | 778 | int max_ports, port_index; |
712 | u32 __iomem **port_array; | 779 | __le32 __iomem **port_array; |
713 | struct xhci_bus_state *bus_state; | 780 | struct xhci_bus_state *bus_state; |
714 | unsigned long flags; | 781 | unsigned long flags; |
715 | 782 | ||
@@ -779,7 +846,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) | |||
779 | 846 | ||
780 | if (DEV_HIGHSPEED(t1)) { | 847 | if (DEV_HIGHSPEED(t1)) { |
781 | /* enable remote wake up for USB 2.0 */ | 848 | /* enable remote wake up for USB 2.0 */ |
782 | u32 __iomem *addr; | 849 | __le32 __iomem *addr; |
783 | u32 tmp; | 850 | u32 tmp; |
784 | 851 | ||
785 | /* Add one to the port status register address to get | 852 | /* Add one to the port status register address to get |
@@ -801,7 +868,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) | |||
801 | { | 868 | { |
802 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | 869 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
803 | int max_ports, port_index; | 870 | int max_ports, port_index; |
804 | u32 __iomem **port_array; | 871 | __le32 __iomem **port_array; |
805 | struct xhci_bus_state *bus_state; | 872 | struct xhci_bus_state *bus_state; |
806 | u32 temp; | 873 | u32 temp; |
807 | unsigned long flags; | 874 | unsigned long flags; |
@@ -875,7 +942,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) | |||
875 | 942 | ||
876 | if (DEV_HIGHSPEED(temp)) { | 943 | if (DEV_HIGHSPEED(temp)) { |
877 | /* disable remote wake up for USB 2.0 */ | 944 | /* disable remote wake up for USB 2.0 */ |
878 | u32 __iomem *addr; | 945 | __le32 __iomem *addr; |
879 | u32 tmp; | 946 | u32 tmp; |
880 | 947 | ||
881 | /* Add one to the port status register address to get | 948 | /* Add one to the port status register address to get |