forked from Minki/linux
usb: xhci: Add port test modes support for usb2.
For usb2 ports, the port test mode Test_J_State, Test_K_State, Test_Packet, Test_SE0_NAK and Test_Force_En can be enabled as described in usb2 spec. USB2 test mode is a required hardware feature for system integrators validating their hardware according to USB spec, regarding signal strength and stuff. It is purely a hardware test feature. Usually you need an oscilloscope and have to enable those test modes on the hardware. This will send some specific test patterns on D+/D-. There is no report available (in Linux itself) as it is purely externally visible. Regular USB usage is not possible at that time. Anyone (well access to e.g. /dev/bus/usb/001/001 provided) can use it by sending appropriate USB_PORT_FEAT_TEST requests to the hub. [Add better commit message by Alexander Stein -Mathias] Signed-off-by: Guoqing Zhang <guoqing.zhang@intel.com> Cc: Alexander Stein <alexander.stein@systec-electronic.com> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
26bba5c767
commit
0f1d832ed1
@ -583,6 +583,77 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
}
|
||||
|
||||
static void xhci_port_set_test_mode(struct xhci_hcd *xhci,
|
||||
u16 test_mode, u16 wIndex)
|
||||
{
|
||||
u32 temp;
|
||||
__le32 __iomem *addr;
|
||||
|
||||
/* xhci only supports test mode for usb2 ports, i.e. xhci->main_hcd */
|
||||
addr = xhci_get_port_io_addr(xhci->main_hcd, wIndex);
|
||||
temp = readl(addr + PORTPMSC);
|
||||
temp |= test_mode << PORT_TEST_MODE_SHIFT;
|
||||
writel(temp, addr + PORTPMSC);
|
||||
xhci->test_mode = test_mode;
|
||||
if (test_mode == TEST_FORCE_EN)
|
||||
xhci_start(xhci);
|
||||
}
|
||||
|
||||
static int xhci_enter_test_mode(struct xhci_hcd *xhci,
|
||||
u16 test_mode, u16 wIndex)
|
||||
{
|
||||
int i, retval;
|
||||
|
||||
/* Disable all Device Slots */
|
||||
xhci_dbg(xhci, "Disable all slots\n");
|
||||
for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
|
||||
retval = xhci_disable_slot(xhci, NULL, i);
|
||||
if (retval)
|
||||
xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n",
|
||||
i, retval);
|
||||
}
|
||||
/* Put all ports to the Disable state by clear PP */
|
||||
xhci_dbg(xhci, "Disable all port (PP = 0)\n");
|
||||
/* Power off USB3 ports*/
|
||||
for (i = 0; i < xhci->num_usb3_ports; i++)
|
||||
xhci_set_port_power(xhci, xhci->shared_hcd, i, false);
|
||||
/* Power off USB2 ports*/
|
||||
for (i = 0; i < xhci->num_usb2_ports; i++)
|
||||
xhci_set_port_power(xhci, xhci->main_hcd, i, false);
|
||||
/* Stop the controller */
|
||||
xhci_dbg(xhci, "Stop controller\n");
|
||||
retval = xhci_halt(xhci);
|
||||
if (retval)
|
||||
return retval;
|
||||
/* Disable runtime PM for test mode */
|
||||
pm_runtime_forbid(xhci_to_hcd(xhci)->self.controller);
|
||||
/* Set PORTPMSC.PTC field to enter selected test mode */
|
||||
/* Port is selected by wIndex. port_id = wIndex + 1 */
|
||||
xhci_dbg(xhci, "Enter Test Mode: %d, Port_id=%d\n",
|
||||
test_mode, wIndex + 1);
|
||||
xhci_port_set_test_mode(xhci, test_mode, wIndex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int xhci_exit_test_mode(struct xhci_hcd *xhci)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (!xhci->test_mode) {
|
||||
xhci_err(xhci, "Not in test mode, do nothing.\n");
|
||||
return 0;
|
||||
}
|
||||
if (xhci->test_mode == TEST_FORCE_EN &&
|
||||
!(xhci->xhc_state & XHCI_STATE_HALTED)) {
|
||||
retval = xhci_halt(xhci);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
pm_runtime_allow(xhci_to_hcd(xhci)->self.controller);
|
||||
xhci->test_mode = 0;
|
||||
return xhci_reset(xhci);
|
||||
}
|
||||
|
||||
void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
||||
int port_id, u32 link_state)
|
||||
{
|
||||
@ -938,6 +1009,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
u16 link_state = 0;
|
||||
u16 wake_mask = 0;
|
||||
u16 timeout = 0;
|
||||
u16 test_mode = 0;
|
||||
|
||||
max_ports = xhci_get_ports(hcd, &port_array);
|
||||
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
||||
@ -1011,6 +1083,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
link_state = (wIndex & 0xff00) >> 3;
|
||||
if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
|
||||
wake_mask = wIndex & 0xff00;
|
||||
if (wValue == USB_PORT_FEAT_TEST)
|
||||
test_mode = (wIndex & 0xff00) >> 8;
|
||||
/* The MSB of wIndex is the U1/U2 timeout */
|
||||
timeout = (wIndex & 0xff00) >> 8;
|
||||
wIndex &= 0xff;
|
||||
@ -1174,6 +1248,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
temp |= PORT_U2_TIMEOUT(timeout);
|
||||
writel(temp, port_array[wIndex] + PORTPMSC);
|
||||
break;
|
||||
case USB_PORT_FEAT_TEST:
|
||||
/* 4.19.6 Port Test Modes (USB2 Test Mode) */
|
||||
if (hcd->speed != HCD_USB2)
|
||||
goto error;
|
||||
if (test_mode > TEST_FORCE_EN || test_mode < TEST_J)
|
||||
goto error;
|
||||
retval = xhci_enter_test_mode(xhci, test_mode, wIndex);
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
@ -1241,6 +1323,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
case USB_PORT_FEAT_POWER:
|
||||
xhci_set_port_power(xhci, hcd, wIndex, false);
|
||||
break;
|
||||
case USB_PORT_FEAT_TEST:
|
||||
retval = xhci_exit_test_mode(xhci);
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
@ -425,6 +425,7 @@ struct xhci_op_regs {
|
||||
#define PORT_L1DS_MASK (0xff << 8)
|
||||
#define PORT_L1DS(p) (((p) & 0xff) << 8)
|
||||
#define PORT_HLE (1 << 16)
|
||||
#define PORT_TEST_MODE_SHIFT 28
|
||||
|
||||
/* USB3 Protocol PORTLI Port Link Information */
|
||||
#define PORT_RX_LANES(p) (((p) >> 16) & 0xf)
|
||||
@ -1843,6 +1844,7 @@ struct xhci_hcd {
|
||||
/* Compliance Mode Recovery Data */
|
||||
struct timer_list comp_mode_recovery_timer;
|
||||
u32 port_status_u0;
|
||||
u16 test_mode;
|
||||
/* Compliance Mode Timer Triggered every 2 seconds */
|
||||
#define COMP_MODE_RCVRY_MSECS 2000
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user