Patch implements TEST_MODE feature that puts the device port into test mode. This feature is used only when device works in HS mode Signed-off-by: Pawel Laszczak <pawell@xxxxxxxxxxx> --- drivers/usb/usbssp/gadget-ep0.c | 39 ++++++++++++++ drivers/usb/usbssp/gadget-port.c | 91 ++++++++++++++++++++++++++++++++ drivers/usb/usbssp/gadget.h | 6 +++ 3 files changed, 136 insertions(+) diff --git a/drivers/usb/usbssp/gadget-ep0.c b/drivers/usb/usbssp/gadget-ep0.c index 6ded0c1b0e70..565fb410e0b4 100644 --- a/drivers/usb/usbssp/gadget-ep0.c +++ b/drivers/usb/usbssp/gadget-ep0.c @@ -242,6 +242,41 @@ static int usbssp_ep0_handle_feature_u2(struct usbssp_udc *usbssp_data, return 0; } +static int usbssp_ep0_handle_feature_test(struct usbssp_udc *usbssp_data, + enum usb_device_state state, + u32 wIndex, int set) +{ + int test_mode; + __le32 __iomem *port_regs; + u32 temp; + unsigned long flags; + int retval; + + if (usbssp_data->port_major_revision == 0x03) + return -EINVAL; + + usbssp_info(usbssp_data, "Test mode; %d\n", wIndex); + + port_regs = usbssp_get_port_io_addr(usbssp_data); + + + test_mode = (wIndex & 0xff00) >> 8; + + temp = readl(port_regs); + temp = usbssp_port_state_to_neutral(temp); + + if (test_mode > TEST_FORCE_EN || test_mode < TEST_J) { + /* "stall" on error */ + retval = -EPIPE; + } + + usbssp_status_stage(usbssp_data); + retval = usbssp_enter_test_mode(usbssp_data, test_mode, &flags); + usbssp_exit_test_mode(usbssp_data); + + return 0; +} + static int usbssp_ep0_handle_feature_device(struct usbssp_udc *usbssp_data, struct usb_ctrlrequest *ctrl, int set) { @@ -271,6 +306,10 @@ static int usbssp_ep0_handle_feature_device(struct usbssp_udc *usbssp_data, case USB_DEVICE_LTM_ENABLE: ret = -EINVAL; break; + case USB_DEVICE_TEST_MODE: + ret = usbssp_ep0_handle_feature_test(usbssp_data, state, + wIndex, set); + break; default: usbssp_err(usbssp_data, "%s Feature Request not supported\n", (set) ? "Set" : "Clear"); diff --git a/drivers/usb/usbssp/gadget-port.c b/drivers/usb/usbssp/gadget-port.c index fc76139468d5..509c76489a1b 100644 --- a/drivers/usb/usbssp/gadget-port.c +++ b/drivers/usb/usbssp/gadget-port.c @@ -191,3 +191,94 @@ void usbssp_test_and_clear_bit(struct usbssp_udc *usbssp_data, writel(temp, port_regs); } } + +static void usbssp_set_port_power(struct usbssp_udc *usbssp_data, + bool on, unsigned long *flags) +{ + __le32 __iomem *addr; + u32 temp; + + addr = usbssp_get_port_io_addr(usbssp_data); + temp = readl(addr); + temp = usbssp_port_state_to_neutral(temp); + if (on) { + /* Power on */ + writel(temp | PORT_POWER, addr); + temp = readl(addr); + usbssp_dbg(usbssp_data, + "set port power, actual port status = 0x%x\n", + temp); + } else { + /* Power off */ + writel(temp & ~PORT_POWER, addr); + usbssp_dbg(usbssp_data, + "clear port power, actual port status = 0x%x\n", + temp); + } +} + +static void usbssp_port_set_test_mode(struct usbssp_udc *usbssp_data, + u16 test_mode) +{ + u32 temp; + __le32 __iomem *addr; + + /* USBSSP only supports test mode for usb2 ports, */ + addr = usbssp_get_port_io_addr(usbssp_data); + temp = readl(addr + PORTPMSC); + temp |= test_mode << PORT_TEST_MODE_SHIFT; + writel(temp, addr + PORTPMSC); + usbssp_data->test_mode = test_mode; + if (test_mode == TEST_FORCE_EN) + usbssp_start(usbssp_data); +} + +int usbssp_enter_test_mode(struct usbssp_udc *usbssp_data, + u16 test_mode, unsigned long *flags) +{ + int retval; + + retval = usbssp_disable_slot(usbssp_data); + if (retval) { + usbssp_err(usbssp_data, + "Failed to disable slot %d, %d. Enter test mode anyway\n", + usbssp_data->slot_id, retval); + return retval; + } + /* Put port to the Disable state by clear PP */ + usbssp_set_port_power(usbssp_data, false, flags); + + /* Stop the controller */ + retval = usbssp_halt(usbssp_data); + if (retval) + return retval; + + /* Disable runtime PM for test mode */ + pm_runtime_forbid(usbssp_data->dev); + /* Set PORTPMSC.PTC field to enter selected test mode */ + /* Port is selected by wIndex. port_id = wIndex + 1 */ + usbssp_dbg(usbssp_data, "Enter Test Mode: _id=%d\n", + test_mode); + usbssp_port_set_test_mode(usbssp_data, test_mode); + + return retval; +} + +int usbssp_exit_test_mode(struct usbssp_udc *usbssp_data) +{ + int retval; + + if (!usbssp_data->test_mode) { + usbssp_err(usbssp_data, "Not in test mode, do nothing.\n"); + return 0; + } + if (usbssp_data->test_mode == TEST_FORCE_EN && + !(usbssp_data->usbssp_state & USBSSP_STATE_HALTED)) { + retval = usbssp_halt(usbssp_data); + if (retval) + return retval; + } + pm_runtime_allow(usbssp_data->dev); + usbssp_data->test_mode = 0; + return usbssp_reset(usbssp_data); +} diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h index ff10b70b3906..3ad32cf5f2a5 100644 --- a/drivers/usb/usbssp/gadget.h +++ b/drivers/usb/usbssp/gadget.h @@ -1728,6 +1728,7 @@ void usbssp_stop(struct usbssp_udc *usbssp_data); int usbssp_handshake(void __iomem *ptr, u32 mask, u32 done, int usec); void usbssp_quiesce(struct usbssp_udc *usbssp_data); int usbssp_halt(struct usbssp_udc *usbssp_data); +int usbssp_start(struct usbssp_udc *usbssp_data); extern int usbssp_reset(struct usbssp_udc *usbssp_data); int usbssp_disable_slot(struct usbssp_udc *usbssp_data); @@ -1810,6 +1811,8 @@ void usbssp_test_and_clear_bit(struct usbssp_udc *usbssp_data, __le32 __iomem *port_regs, u32 port_bit); void usbssp_udc_died(struct usbssp_udc *usbssp_data); +u32 usbssp_port_state_to_neutral(u32 state); + /* USBSSP DC contexts */ struct usbssp_input_control_ctx *usbssp_get_input_control_ctx( struct usbssp_container_ctx *ctx); @@ -1838,6 +1841,9 @@ int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep, int value); int usbssp_cmd_stop_ep(struct usbssp_udc *usbssp_data, struct usb_gadget *g, struct usbssp_ep *ep_priv); +int usbssp_enter_test_mode(struct usbssp_udc *usbssp_data, + u16 test_mode, unsigned long *flags); +int usbssp_exit_test_mode(struct usbssp_udc *usbssp_data); int usbssp_setup_analyze(struct usbssp_udc *usbssp_data); int usbssp_status_stage(struct usbssp_udc *usbssp_data); -- 2.17.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html