From 23b5f73266e59a598c1e5dd435d87651b5a7626b Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 1 Oct 2018 12:45:00 -0700 Subject: usb: typec: tcpm: Do not disconnect link for self powered devices During HARD_RESET the data link is disconnected. For self powered device, the spec is advising against doing that. >From USB_PD_R3_0 7.1.5 Response to Hard Resets Device operation during and after a Hard Reset is defined as follows: Self-powered devices Should Not disconnect from USB during a Hard Reset (see Section 9.1.2). Bus powered devices will disconnect from USB during a Hard Reset due to the loss of their power source. Tackle this by letting TCPM know whether the device is self or bus powered. This overcomes unnecessary port disconnections from hard reset. Also, speeds up the enumeration time when connected to Type-A ports. Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Heikki Krogerus --------- Version history: V3: Rebase on top of usb-next V2: Based on feedback from heikki.krogerus@linux.intel.com - self_powered added to the struct tcpm_port which is populated from a. "connector" node of the device tree in tcpm_fw_get_caps() b. "self_powered" node of the tcpc_config in tcpm_copy_caps Based on feedbase from linux@roeck-us.net - Code was refactored - SRC_HARD_RESET_VBUS_OFF sets the link state to false based on self_powered flag V1 located here: https://lkml.org/lkml/2018/9/13/94 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index dbbd71f754d0..ba6e5cdaed2c 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -317,6 +317,9 @@ struct tcpm_port { /* Deadline in jiffies to exit src_try_wait state */ unsigned long max_wait; + /* port belongs to a self powered device */ + bool self_powered; + #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -3254,7 +3257,8 @@ static void run_state_machine(struct tcpm_port *port) case SRC_HARD_RESET_VBUS_OFF: tcpm_set_vconn(port, true); tcpm_set_vbus(port, false); - tcpm_set_roles(port, false, TYPEC_SOURCE, TYPEC_HOST); + tcpm_set_roles(port, port->self_powered, TYPEC_SOURCE, + TYPEC_HOST); tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER); break; case SRC_HARD_RESET_VBUS_ON: @@ -3267,7 +3271,8 @@ static void run_state_machine(struct tcpm_port *port) memset(&port->pps_data, 0, sizeof(port->pps_data)); tcpm_set_vconn(port, false); tcpm_set_charge(port, false); - tcpm_set_roles(port, false, TYPEC_SINK, TYPEC_DEVICE); + tcpm_set_roles(port, port->self_powered, TYPEC_SINK, + TYPEC_DEVICE); /* * VBUS may or may not toggle, depending on the adapter. * If it doesn't toggle, transition to SNK_HARD_RESET_SINK_ON @@ -4412,6 +4417,8 @@ sink: return -EINVAL; port->operating_snk_mw = mw / 1000; + port->self_powered = fwnode_property_read_bool(fwnode, "self-powered"); + return 0; } @@ -4720,6 +4727,7 @@ static int tcpm_copy_caps(struct tcpm_port *port, port->typec_caps.prefer_role = tcfg->default_role; port->typec_caps.type = tcfg->type; port->typec_caps.data = tcfg->data; + port->self_powered = port->tcpc->config->self_powered; return 0; } -- cgit v1.2.3-59-g8ed1b From 157c0f2f641a9938382b092c64548ebdabfe25e0 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 1 Oct 2018 12:45:01 -0700 Subject: usb: typec: tcpm: charge current handling for sink during hard reset During the initial connect to a non-pd port, sink would hard reset twice before deeming that the port partner is non-pd. TCPM sets the the charge path to false during the hard reset. This causes unnecessary connects/disconnects of charge path and makes port take longer to charge from the non-pd ports. Avoid this by not setting the charge path to false unless the partner has already identified to be pd capable. When partner is a pd port, set the charge path to false in SNK_HARD_RESET_SINK_OFF. Set the current limits to default value based of CC pull up and resume the charge path when port enters SNK_HARD_RESET_SINK_ON. Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Rob Herring Reviewed-by: Heikki Krogerus -------- Changes in V3: Rebase on top of usb-next Changes in V2: Based on feedback of jackp@codeaurora.org - vsafe_5v_hard_reset flag from tcpc_config is removed - Patch only differentiates between pd port partner and non-pd port partner V1 version of the patch is here: https://lkml.org/lkml/2018/9/14/11 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index ba6e5cdaed2c..3620efee2688 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -3270,7 +3270,8 @@ static void run_state_machine(struct tcpm_port *port) case SNK_HARD_RESET_SINK_OFF: memset(&port->pps_data, 0, sizeof(port->pps_data)); tcpm_set_vconn(port, false); - tcpm_set_charge(port, false); + if (port->pd_capable) + tcpm_set_charge(port, false); tcpm_set_roles(port, port->self_powered, TYPEC_SINK, TYPEC_DEVICE); /* @@ -3302,6 +3303,12 @@ static void run_state_machine(struct tcpm_port *port) * Similar, dual-mode ports in source mode should transition * to PE_SNK_Transition_to_default. */ + if (port->pd_capable) { + tcpm_set_current_limit(port, + tcpm_get_current_limit(port), + 5000); + tcpm_set_charge(port, true); + } tcpm_set_attached_state(port, true); tcpm_set_state(port, SNK_STARTUP, 0); break; -- cgit v1.2.3-59-g8ed1b From 1d3e773ae0a65a1b6b5d2aa21250c96f3e975aba Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Fri, 2 Nov 2018 22:14:27 +0800 Subject: usb: host: remove unnecessary condition check dma_pool_destroy() can handle NULL pointer correctly, so there is no need to check NULL pointer before calling dma_pool_destroy(). Signed-off-by: Chengguang Xu Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-mem.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index b3da3f12e5b1..3965ac0341eb 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c @@ -57,14 +57,10 @@ static int ohci_mem_init (struct ohci_hcd *ohci) static void ohci_mem_cleanup (struct ohci_hcd *ohci) { - if (ohci->td_cache) { - dma_pool_destroy (ohci->td_cache); - ohci->td_cache = NULL; - } - if (ohci->ed_cache) { - dma_pool_destroy (ohci->ed_cache); - ohci->ed_cache = NULL; - } + dma_pool_destroy(ohci->td_cache); + ohci->td_cache = NULL; + dma_pool_destroy(ohci->ed_cache); + ohci->ed_cache = NULL; } /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-59-g8ed1b From 8ec23b90119a5a64dbb84baa3cc55e5d252b1f2f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 30 Oct 2018 14:15:43 +0000 Subject: uwb: clean an indentation issue, remove extraneous tab Trivial fix to clean up an indentation issue, remove tab Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/uwb/i1480/dfu/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c index c0430a41e24b..b1b466cbc33e 100644 --- a/drivers/uwb/i1480/dfu/usb.c +++ b/drivers/uwb/i1480/dfu/usb.c @@ -309,7 +309,7 @@ int i1480_usb_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size) if (result < 0) { dev_err(dev, "%s: cannot submit NEEP read: %d\n", cmd_name, result); - goto error_submit_ep1; + goto error_submit_ep1; } /* Now post the command on EP0 */ result = usb_control_msg( -- cgit v1.2.3-59-g8ed1b From 15f6f7f48db9b1f89175185c6dafc1eaa16f7a66 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 29 Oct 2018 22:49:45 +0000 Subject: USB: gadget: udc: fix spelling mistake "intrerrupt" -> "interrupt" Trivial fix to spelling mistake in comment Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/pch_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index afaea11ec771..55c8c8abeacd 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -1330,7 +1330,7 @@ static void pch_vbus_gpio_work_rise(struct work_struct *irq_work) } /** - * pch_vbus_gpio_irq() - IRQ handler for GPIO intrerrupt for changing VBUS + * pch_vbus_gpio_irq() - IRQ handler for GPIO interrupt for changing VBUS * @irq: Interrupt request number * @dev: Reference to the device structure * -- cgit v1.2.3-59-g8ed1b From 548f32f59456e71379c61d4e88727ee6c5f6ccd6 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 11 Oct 2018 07:44:33 +0000 Subject: USB: serial: quatech2: remove set but not used variable 'port_priv' Fixes gcc '-Wunused-but-set-variable' warning: drivers/usb/serial/quatech2.c: In function 'qt2_process_read_urb': drivers/usb/serial/quatech2.c:503:27: warning: variable 'port_priv' set but not used [-Wunused-but-set-variable] It not used any more after commit 2be818a116b2 ('Revert "USB: quatech2: only write to the tty if the port is open."') Signed-off-by: YueHaibing Signed-off-by: Johan Hovold --- drivers/usb/serial/quatech2.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index f2fbe1ec9701..a62981ca7a73 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -500,7 +500,6 @@ static void qt2_process_read_urb(struct urb *urb) struct usb_serial *serial; struct qt2_serial_private *serial_priv; struct usb_serial_port *port; - struct qt2_port_private *port_priv; bool escapeflag; unsigned char *ch; int i; @@ -514,7 +513,6 @@ static void qt2_process_read_urb(struct urb *urb) serial = urb->context; serial_priv = usb_get_serial_data(serial); port = serial->port[serial_priv->current_port]; - port_priv = usb_get_serial_port_data(port); for (i = 0; i < urb->actual_length; i++) { ch = (unsigned char *)urb->transfer_buffer + i; @@ -566,7 +564,6 @@ static void qt2_process_read_urb(struct urb *urb) serial_priv->current_port = newport; port = serial->port[serial_priv->current_port]; - port_priv = usb_get_serial_port_data(port); i += 3; escapeflag = true; break; -- cgit v1.2.3-59-g8ed1b From 32d8a6fc5bd62769c7fa60818e0a32bd5126a565 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 26 Oct 2018 20:25:49 +0800 Subject: USB: serial: mos7840: remove set but not used variables 'st, data1, iflag' Fixes gcc '-Wunused-but-set-variable' warning: drivers/usb/serial/mos7840.c: In function 'mos7840_interrupt_callback': drivers/usb/serial/mos7840.c:604:14: warning: variable 'st' set but not used [-Wunused-but-set-variable] drivers/usb/serial/mos7840.c: In function 'mos7840_write': drivers/usb/serial/mos7840.c:1303:17: warning: variable 'data1' set but not used [-Wunused-but-set-variable] drivers/usb/serial/mos7840.c:1700:11: warning: variable 'iflag' set but not used [-Wunused-but-set-variable] They are never used since introduction in commit 3f5429746d91 ("USB: Moschip 7840 USB-Serial Driver") Signed-off-by: YueHaibing Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 88828b4b8c44..bfbf75b36349 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -601,7 +601,7 @@ static void mos7840_interrupt_callback(struct urb *urb) struct usb_serial *serial; __u16 Data; unsigned char *data; - __u8 sp[5], st; + __u8 sp[5]; int i, rv = 0; __u16 wval, wreg = 0; int status = urb->status; @@ -644,7 +644,6 @@ static void mos7840_interrupt_callback(struct urb *urb) sp[1] = (__u8) data[1]; sp[2] = (__u8) data[2]; sp[3] = (__u8) data[3]; - st = (__u8) data[4]; for (i = 0; i < serial->num_ports; i++) { mos7840_port = mos7840_get_port_private(serial->port[i]); @@ -1300,7 +1299,6 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, struct urb *urb; /* __u16 Data; */ const unsigned char *current_position = data; - unsigned char *data1; if (mos7840_port_paranoia_check(port, __func__)) return -1; @@ -1361,7 +1359,6 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, mos7840_bulk_out_data_callback, mos7840_port); } - data1 = urb->transfer_buffer; dev_dbg(&port->dev, "bulkout endpoint is %d\n", port->bulk_out_endpointAddress); if (mos7840_port->has_led) @@ -1697,7 +1694,6 @@ static void mos7840_change_port_settings(struct tty_struct *tty, { int baud; unsigned cflag; - unsigned iflag; __u8 lData; __u8 lParity; __u8 lStop; @@ -1729,7 +1725,6 @@ static void mos7840_change_port_settings(struct tty_struct *tty, lParity = LCR_PAR_NONE; cflag = tty->termios.c_cflag; - iflag = tty->termios.c_iflag; /* Change the number of bits */ switch (cflag & CSIZE) { -- cgit v1.2.3-59-g8ed1b From ab60075f2a4eebca1abb04f712569963fb4d9d6c Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 15 Nov 2018 10:58:44 +0800 Subject: USB: serial: f81534: fix reading old/new IC config The F81532/534 had a internal configuration space to save & control IC state with address F81534_CUSTOM_ADDRESS_START (0x2f00). Layout as following: +00h: to indicate the section is valid +01h~04h: UART Mode & port availability +05h~08h: Output pin control on IC power on +09h~12h: Output pin control on working <-- New added Old driver will use +05~08h as default on working, but newer IC will configed with shutdown mode(7) in 05h~08h and working mode with RS232(1) in 09h~12h. It'll make mainstream driver not working. This patch will make mainstream driver compatible older and newer IC. If using a old IC, the +05h~08h will be 00h~06h, we'll direct apply it. If using a new IC, the +05h~08h will be 07h or larger, we'll read +09h~12h to apply newer configuration. Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Johan Hovold --- drivers/usb/serial/f81534.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 380933db34dd..2b39bda035c7 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -45,14 +45,17 @@ #define F81534_CONFIG1_REG (0x09 + F81534_UART_BASE_ADDRESS) #define F81534_DEF_CONF_ADDRESS_START 0x3000 -#define F81534_DEF_CONF_SIZE 8 +#define F81534_DEF_CONF_SIZE 12 #define F81534_CUSTOM_ADDRESS_START 0x2f00 #define F81534_CUSTOM_DATA_SIZE 0x10 #define F81534_CUSTOM_NO_CUSTOM_DATA 0xff #define F81534_CUSTOM_VALID_TOKEN 0xf0 #define F81534_CONF_OFFSET 1 -#define F81534_CONF_GPIO_OFFSET 4 +#define F81534_CONF_INIT_GPIO_OFFSET 4 +#define F81534_CONF_WORK_GPIO_OFFSET 8 +#define F81534_CONF_GPIO_SHUTDOWN 7 +#define F81534_CONF_GPIO_RS232 1 #define F81534_MAX_DATA_BLOCK 64 #define F81534_MAX_BUS_RETRY 20 @@ -1337,8 +1340,19 @@ static int f81534_set_port_output_pin(struct usb_serial_port *port) serial_priv = usb_get_serial_data(serial); port_priv = usb_get_serial_port_data(port); - idx = F81534_CONF_GPIO_OFFSET + port_priv->phy_num; + idx = F81534_CONF_INIT_GPIO_OFFSET + port_priv->phy_num; value = serial_priv->conf_data[idx]; + if (value >= F81534_CONF_GPIO_SHUTDOWN) { + /* + * Newer IC configure will make transceiver in shutdown mode on + * initial power on. We need enable it before using UARTs. + */ + idx = F81534_CONF_WORK_GPIO_OFFSET + port_priv->phy_num; + value = serial_priv->conf_data[idx]; + if (value >= F81534_CONF_GPIO_SHUTDOWN) + value = F81534_CONF_GPIO_RS232; + } + pins = &f81534_port_out_pins[port_priv->phy_num]; for (i = 0; i < ARRAY_SIZE(pins->pin); ++i) { -- cgit v1.2.3-59-g8ed1b From 6abd837104a3a8e1cda64fc4d7675f6c3ece9d8b Mon Sep 17 00:00:00 2001 From: Nikolaj Fogh Date: Thu, 22 Nov 2018 21:27:46 +0100 Subject: USB: serial: ftdi_sio: use rounding when calculating baud rate divisors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve baud-rate generation by using rounding-to-closest instead of truncation in divisor calculation. Results have been verified by logic analyzer on an FT232RT (232BM) chip. The following table shows the wanted baud rate, the baud rate obtained with the old method (truncation), with the new method (rounding) and the baud rate generated by the windows 10 driver. The numbers in parentheses is the error. +- Wanted --+------ Old -------+------ New -------+------ Win -------+ |   9600  |   9600 (0.00%)  |   9604 (0.05%)  |   9605 (0.05%)  | |   19200   |   19200 (0.00%)  |   19199 (0.01%)  |   19198 (0.01%)  | |   38400   |   38395 (0.01%)  |   38431 (0.08%)  |   38394 (0.02%)  | |   57600   |   57725 (0.22%)  |   57540 (0.10%)  |   57673 (0.13%)  | |  115200   |  115307 (0.09%)  |  115330 (0.11%)  |  115320 (0.10%)  | |  921600   |  919963 (0.18%)  |  920386 (0.13%)  |  920810 (0.09%)  | |  961200   |  996512 (3.67%)  |  956480 (0.49%)  |  956937 (0.44%)  | +-----------+------------------+------------------+------------------+ The error due to noise in the measurements is in the order of a few tenths of a %. As can be seen, the baud rate is significantly improved for some rates (e.g. 961200), and corresponds to the output given by the windows driver. The theoretical baud rate has been calculated for all baud rates from 1 to 3M, and as expected, the error is centered around 0, with a triangle shape instead of a sawtooth, so the maximum error is decreased to half. Signed-off-by: Nikolaj Fogh [ johan: edit commit message slightly ] Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 609198d9594c..1ab2a6191013 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1130,7 +1130,7 @@ static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base) { unsigned short int divisor; /* divisor shifted 3 bits to the left */ - int divisor3 = base / 2 / baud; + int divisor3 = DIV_ROUND_CLOSEST(base, 2 * baud); if ((divisor3 & 0x7) == 7) divisor3++; /* round x.7/8 up to x+1 */ divisor = divisor3 >> 3; @@ -1156,7 +1156,7 @@ static u32 ftdi_232bm_baud_base_to_divisor(int baud, int base) static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; u32 divisor; /* divisor shifted 3 bits to the left */ - int divisor3 = base / 2 / baud; + int divisor3 = DIV_ROUND_CLOSEST(base, 2 * baud); divisor = divisor3 >> 3; divisor |= (u32)divfrac[divisor3 & 0x7] << 14; /* Deal with special cases for highest baud rates. */ @@ -1179,7 +1179,7 @@ static u32 ftdi_2232h_baud_base_to_divisor(int baud, int base) int divisor3; /* hi-speed baud rate is 10-bit sampling instead of 16-bit */ - divisor3 = base * 8 / (baud * 10); + divisor3 = DIV_ROUND_CLOSEST(8 * base, 10 * baud); divisor = divisor3 >> 3; divisor |= (u32)divfrac[divisor3 & 0x7] << 14; -- cgit v1.2.3-59-g8ed1b From 18557feccfbf96dd6d4a62723904927064ad3592 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 19 Nov 2018 16:43:54 +0000 Subject: USB: ene_usb6250: add missing indentation There is a missing indentation before the return statement. Add it. Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/ene_ub6250.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 4d261e4de9ad..c26129d5b943 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -1131,7 +1131,7 @@ static int ms_lib_alloc_writebuf(struct us_data *us) ms_lib_clear_writebuf(us); -return 0; + return 0; } static int ms_lib_force_setlogical_pair(struct us_data *us, u16 logblk, u16 phyblk) -- cgit v1.2.3-59-g8ed1b From f4b614adbf3aceaa4dab034665c379352777de39 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 19 Nov 2018 16:34:00 +0000 Subject: drivers: usb: early: clean up indentation, remove extraneous tabs There is a hunk of code that is indented too much by one level, fix this by removing the extraneous tabs. Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/early/ehci-dbgp.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c index d633c2abe5a4..ea0d531c63e2 100644 --- a/drivers/usb/early/ehci-dbgp.c +++ b/drivers/usb/early/ehci-dbgp.c @@ -631,28 +631,28 @@ static int ehci_reset_port(int port) if (!(portsc & PORT_RESET)) break; } - if (portsc & PORT_RESET) { - /* force reset to complete */ - loop = 100 * 1000; - writel(portsc & ~(PORT_RWC_BITS | PORT_RESET), - &ehci_regs->port_status[port - 1]); - do { - udelay(1); - portsc = readl(&ehci_regs->port_status[port-1]); - } while ((portsc & PORT_RESET) && (--loop > 0)); - } + if (portsc & PORT_RESET) { + /* force reset to complete */ + loop = 100 * 1000; + writel(portsc & ~(PORT_RWC_BITS | PORT_RESET), + &ehci_regs->port_status[port - 1]); + do { + udelay(1); + portsc = readl(&ehci_regs->port_status[port-1]); + } while ((portsc & PORT_RESET) && (--loop > 0)); + } - /* Device went away? */ - if (!(portsc & PORT_CONNECT)) - return -ENOTCONN; + /* Device went away? */ + if (!(portsc & PORT_CONNECT)) + return -ENOTCONN; - /* bomb out completely if something weird happened */ - if ((portsc & PORT_CSC)) - return -EINVAL; + /* bomb out completely if something weird happened */ + if ((portsc & PORT_CSC)) + return -EINVAL; - /* If we've finished resetting, then break out of the loop */ - if (!(portsc & PORT_RESET) && (portsc & PORT_PE)) - return 0; + /* If we've finished resetting, then break out of the loop */ + if (!(portsc & PORT_RESET) && (portsc & PORT_PE)) + return 0; return -EBUSY; } -- cgit v1.2.3-59-g8ed1b From 2c85a1817e4ba09592964226b46305a7b9599884 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 7 Nov 2018 17:55:00 -0800 Subject: usb: dwc3: debugfs: Properly name Tx/RxFIFO The Tx/RxFIFO types in the GDBGFIFOSPACE.FIFO_QUEUE_SELECT are not queue. Properly rename them. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 4 ++-- drivers/usb/dwc3/debugfs.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5bfb62533e0f..2ba034b5da07 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -179,8 +179,8 @@ #define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0) #define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff) -#define DWC3_TXFIFOQ 0 -#define DWC3_RXFIFOQ 1 +#define DWC3_TXFIFO 0 +#define DWC3_RXFIFO 1 #define DWC3_TXREQQ 2 #define DWC3_RXREQQ 3 #define DWC3_RXINFOQ 4 diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index df8e73ec3342..17238bbe9733 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -496,7 +496,7 @@ struct dwc3_ep_file_map { const struct file_operations *const fops; }; -static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused) +static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused) { struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; @@ -504,14 +504,14 @@ static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused) u32 val; spin_lock_irqsave(&dwc->lock, flags); - val = dwc3_core_fifo_space(dep, DWC3_TXFIFOQ); + val = dwc3_core_fifo_space(dep, DWC3_TXFIFO); seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); return 0; } -static int dwc3_rx_fifo_queue_show(struct seq_file *s, void *unused) +static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused) { struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; @@ -519,7 +519,7 @@ static int dwc3_rx_fifo_queue_show(struct seq_file *s, void *unused) u32 val; spin_lock_irqsave(&dwc->lock, flags); - val = dwc3_core_fifo_space(dep, DWC3_RXFIFOQ); + val = dwc3_core_fifo_space(dep, DWC3_RXFIFO); seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); @@ -675,8 +675,8 @@ out: return 0; } -DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_queue); -DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_size); +DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_size); DEFINE_SHOW_ATTRIBUTE(dwc3_tx_request_queue); DEFINE_SHOW_ATTRIBUTE(dwc3_rx_request_queue); DEFINE_SHOW_ATTRIBUTE(dwc3_rx_info_queue); @@ -686,8 +686,8 @@ DEFINE_SHOW_ATTRIBUTE(dwc3_transfer_type); DEFINE_SHOW_ATTRIBUTE(dwc3_trb_ring); static const struct dwc3_ep_file_map dwc3_ep_file_map[] = { - { "tx_fifo_queue", &dwc3_tx_fifo_queue_fops, }, - { "rx_fifo_queue", &dwc3_rx_fifo_queue_fops, }, + { "tx_fifo_size", &dwc3_tx_fifo_size_fops, }, + { "rx_fifo_size", &dwc3_rx_fifo_size_fops, }, { "tx_request_queue", &dwc3_tx_request_queue_fops, }, { "rx_request_queue", &dwc3_rx_request_queue_fops, }, { "rx_info_queue", &dwc3_rx_info_queue_fops, }, -- cgit v1.2.3-59-g8ed1b From 0f874f79dc81aec0229babebd6ef04e591a548d2 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 7 Nov 2018 17:55:06 -0800 Subject: usb: dwc3: debugfs: Print eps Tx/RxFIFO in bytes TxFIFO and RxFIFO from GDBGFIFOSPACE are fifo depths in MDWIDTH. Convert them into bytes for easier read. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debugfs.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 17238bbe9733..bd3d75b2f8bc 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -505,6 +505,10 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused) spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_TXFIFO); + + /* Convert to bytes */ + val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0); + val >>= 3; seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); @@ -520,6 +524,10 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused) spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_RXFIFO); + + /* Convert to bytes */ + val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0); + val >>= 3; seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); -- cgit v1.2.3-59-g8ed1b From 62ba09d6bb6330d8a70e40e23891d8764663d469 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 7 Nov 2018 17:55:13 -0800 Subject: usb: dwc3: debugfs: Dump internal LSP and ep registers To dump internal LSP and endpoint state debug registers, we must write to GDBGLSPMUX register. This patch correctly dump LSP and endpoint states from the debug registers. If the controller is in device mode, all LSP and endpoint state registers will be dumped via the debugfs attribute "lsp_dump". In host mode, the user has to write the LSP number to "lsp_dump" to dump a specific LSP selection. Fixes: 80b776340c78 ("usb: dwc3: Dump LSP and BMU debug info") Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 13 ++++ drivers/usb/dwc3/debugfs.c | 145 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 154 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 2ba034b5da07..7b17bb6a353c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -174,6 +174,12 @@ #define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */ #define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff +/* Global Debug LSP MUX Select */ +#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */ +#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff) +#define DWC3_GDBGLSPMUX_DEVSELECT(n) (((n) & 0xf) << 4) +#define DWC3_GDBGLSPMUX_EPSELECT(n) ((n) & 0xf) + /* Global Debug Queue/FIFO Space Available Register */ #define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f) #define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0) @@ -253,6 +259,9 @@ #define DWC3_GSTS_DEVICE_IP BIT(6) #define DWC3_GSTS_CSR_TIMEOUT BIT(5) #define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4) +#define DWC3_GSTS_CURMOD(n) ((n) & 0x3) +#define DWC3_GSTS_CURMOD_DEVICE 0 +#define DWC3_GSTS_CURMOD_HOST 1 /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31) @@ -321,6 +330,7 @@ #define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2 #define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) #define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) +#define DWC3_GHWPARAMS1_ENDBC BIT(31) /* Global HWPARAMS3 Register */ #define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3) @@ -945,6 +955,7 @@ struct dwc3_scratchpad_array { * @hwparams: copy of hwparams registers * @root: debugfs root folder pointer * @regset: debugfs pointer to regdump file + * @dbg_lsp_select: current debug lsp mux register selection * @test_mode: true when we're entering a USB test mode * @test_mode_nr: test feature selector * @lpm_nyet_threshold: LPM NYET response threshold @@ -1121,6 +1132,8 @@ struct dwc3 { struct dentry *root; struct debugfs_regset32 *regset; + u32 dbg_lsp_select; + u8 test_mode; u8 test_mode_nr; u8 lpm_nyet_threshold; diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index bd3d75b2f8bc..1da012f105d7 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -25,6 +25,8 @@ #include "io.h" #include "debug.h" +#define DWC3_LSP_MUX_UNSELECTED 0xfffff + #define dump_register(nm) \ { \ .name = __stringify(nm), \ @@ -82,10 +84,6 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(GDBGFIFOSPACE), dump_register(GDBGLTSSM), dump_register(GDBGBMU), - dump_register(GDBGLSPMUX), - dump_register(GDBGLSP), - dump_register(GDBGEPINFO0), - dump_register(GDBGEPINFO1), dump_register(GPRTBIMAP_HS0), dump_register(GPRTBIMAP_HS1), dump_register(GPRTBIMAP_FS0), @@ -279,6 +277,114 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(OSTS), }; +static void dwc3_host_lsp(struct seq_file *s) +{ + struct dwc3 *dwc = s->private; + bool dbc_enabled; + u32 sel; + u32 reg; + u32 val; + + dbc_enabled = !!(dwc->hwparams.hwparams1 & DWC3_GHWPARAMS1_ENDBC); + + sel = dwc->dbg_lsp_select; + if (sel == DWC3_LSP_MUX_UNSELECTED) { + seq_puts(s, "Write LSP selection to print for host\n"); + return; + } + + reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel); + + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + val = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val); + + if (dbc_enabled && sel < 256) { + reg |= DWC3_GDBGLSPMUX_ENDBC; + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + val = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, val); + } +} + +static void dwc3_gadget_lsp(struct seq_file *s) +{ + struct dwc3 *dwc = s->private; + int i; + u32 reg; + + for (i = 0; i < 16; i++) { + reg = DWC3_GDBGLSPMUX_DEVSELECT(i); + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg); + } +} + +static int dwc3_lsp_show(struct seq_file *s, void *unused) +{ + struct dwc3 *dwc = s->private; + unsigned int current_mode; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&dwc->lock, flags); + reg = dwc3_readl(dwc->regs, DWC3_GSTS); + current_mode = DWC3_GSTS_CURMOD(reg); + + switch (current_mode) { + case DWC3_GSTS_CURMOD_HOST: + dwc3_host_lsp(s); + break; + case DWC3_GSTS_CURMOD_DEVICE: + dwc3_gadget_lsp(s); + break; + default: + seq_puts(s, "Mode is unknown, no LSP register printed\n"); + break; + } + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +static int dwc3_lsp_open(struct inode *inode, struct file *file) +{ + return single_open(file, dwc3_lsp_show, inode->i_private); +} + +static ssize_t dwc3_lsp_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct dwc3 *dwc = s->private; + unsigned long flags; + char buf[32] = { 0 }; + u32 sel; + int ret; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + ret = kstrtouint(buf, 0, &sel); + if (ret) + return ret; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->dbg_lsp_select = sel; + spin_unlock_irqrestore(&dwc->lock, flags); + + return count; +} + +static const struct file_operations dwc3_lsp_fops = { + .open = dwc3_lsp_open, + .write = dwc3_lsp_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int dwc3_mode_show(struct seq_file *s, void *unused) { struct dwc3 *dwc = s->private; @@ -683,6 +789,30 @@ out: return 0; } +static int dwc3_ep_info_register_show(struct seq_file *s, void *unused) +{ + struct dwc3_ep *dep = s->private; + struct dwc3 *dwc = dep->dwc; + unsigned long flags; + u64 ep_info; + u32 lower_32_bits; + u32 upper_32_bits; + u32 reg; + + spin_lock_irqsave(&dwc->lock, flags); + reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number); + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + + lower_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO0); + upper_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO1); + + ep_info = ((u64)upper_32_bits << 32) | lower_32_bits; + seq_printf(s, "0x%016llx\n", ep_info); + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_size); DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_size); DEFINE_SHOW_ATTRIBUTE(dwc3_tx_request_queue); @@ -692,6 +822,7 @@ DEFINE_SHOW_ATTRIBUTE(dwc3_descriptor_fetch_queue); DEFINE_SHOW_ATTRIBUTE(dwc3_event_queue); DEFINE_SHOW_ATTRIBUTE(dwc3_transfer_type); DEFINE_SHOW_ATTRIBUTE(dwc3_trb_ring); +DEFINE_SHOW_ATTRIBUTE(dwc3_ep_info_register); static const struct dwc3_ep_file_map dwc3_ep_file_map[] = { { "tx_fifo_size", &dwc3_tx_fifo_size_fops, }, @@ -703,6 +834,7 @@ static const struct dwc3_ep_file_map dwc3_ep_file_map[] = { { "event_queue", &dwc3_event_queue_fops, }, { "transfer_type", &dwc3_transfer_type_fops, }, { "trb_ring", &dwc3_trb_ring_fops, }, + { "GDBGEPINFO", &dwc3_ep_info_register_fops, }, }; static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep, @@ -750,6 +882,8 @@ void dwc3_debugfs_init(struct dwc3 *dwc) if (!dwc->regset) return; + dwc->dbg_lsp_select = DWC3_LSP_MUX_UNSELECTED; + dwc->regset->regs = dwc3_regs; dwc->regset->nregs = ARRAY_SIZE(dwc3_regs); dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START; @@ -759,6 +893,9 @@ void dwc3_debugfs_init(struct dwc3 *dwc) debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset); + debugfs_create_file("lsp_dump", S_IRUGO | S_IWUSR, root, dwc, + &dwc3_lsp_fops); + if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) { debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc, &dwc3_mode_fops); -- cgit v1.2.3-59-g8ed1b From 0d36dede457873404becd7c9cb9d0f2bcfd0dcd9 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 7 Nov 2018 17:55:19 -0800 Subject: usb: dwc3: debugfs: Properly print/set link state for HS Highspeed device and below has different state names than superspeed and higher. Add proper checks and printouts of link states for highspeed and below. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debug.h | 29 +++++++++++++++++++++++++++++ drivers/usb/dwc3/debugfs.c | 19 +++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index c66d216dcc30..4f75ab3505b7 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -116,6 +116,35 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state) } } +/** + * dwc3_gadget_hs_link_string - returns highspeed and below link name + * @link_state: link state code + */ +static inline const char * +dwc3_gadget_hs_link_string(enum dwc3_link_state link_state) +{ + switch (link_state) { + case DWC3_LINK_STATE_U0: + return "On"; + case DWC3_LINK_STATE_U2: + return "Sleep"; + case DWC3_LINK_STATE_U3: + return "Suspend"; + case DWC3_LINK_STATE_SS_DIS: + return "Disconnected"; + case DWC3_LINK_STATE_RX_DET: + return "Early Suspend"; + case DWC3_LINK_STATE_RECOV: + return "Recovery"; + case DWC3_LINK_STATE_RESET: + return "Reset"; + case DWC3_LINK_STATE_RESUME: + return "Resume"; + default: + return "UNKNOWN link state\n"; + } +} + /** * dwc3_trb_type_string - returns TRB type as a string * @type: the type of the TRB diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 1da012f105d7..e613a61ae58a 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -539,13 +539,17 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) unsigned long flags; enum dwc3_link_state state; u32 reg; + u8 speed; spin_lock_irqsave(&dwc->lock, flags); reg = dwc3_readl(dwc->regs, DWC3_DSTS); state = DWC3_DSTS_USBLNKST(reg); - spin_unlock_irqrestore(&dwc->lock, flags); + speed = reg & DWC3_DSTS_CONNECTSPD; - seq_printf(s, "%s\n", dwc3_gadget_link_string(state)); + seq_printf(s, "%s\n", (speed >= DWC3_DSTS_SUPERSPEED) ? + dwc3_gadget_link_string(state) : + dwc3_gadget_hs_link_string(state)); + spin_unlock_irqrestore(&dwc->lock, flags); return 0; } @@ -563,6 +567,8 @@ static ssize_t dwc3_link_state_write(struct file *file, unsigned long flags; enum dwc3_link_state state = 0; char buf[32]; + u32 reg; + u8 speed; if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; @@ -583,6 +589,15 @@ static ssize_t dwc3_link_state_write(struct file *file, return -EINVAL; spin_lock_irqsave(&dwc->lock, flags); + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + speed = reg & DWC3_DSTS_CONNECTSPD; + + if (speed < DWC3_DSTS_SUPERSPEED && + state != DWC3_LINK_STATE_RECOV) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -EINVAL; + } + dwc3_gadget_set_link_state(dwc, state); spin_unlock_irqrestore(&dwc->lock, flags); -- cgit v1.2.3-59-g8ed1b From d102444cac156425e1f154089eb4400ddb581629 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 7 Nov 2018 17:56:23 -0800 Subject: usb: dwc3: debugfs: Print/set link state for peripheral mode Current implementation only prints/sets the link state for peripheral mode only. Check and prevent printing bogus link state if the current mode of operation is not peripheral. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debugfs.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index e613a61ae58a..1c792710348f 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -542,6 +542,13 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) u8 speed; spin_lock_irqsave(&dwc->lock, flags); + reg = dwc3_readl(dwc->regs, DWC3_GSTS); + if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) { + seq_puts(s, "Not available\n"); + spin_unlock_irqrestore(&dwc->lock, flags); + return 0; + } + reg = dwc3_readl(dwc->regs, DWC3_DSTS); state = DWC3_DSTS_USBLNKST(reg); speed = reg & DWC3_DSTS_CONNECTSPD; @@ -589,6 +596,12 @@ static ssize_t dwc3_link_state_write(struct file *file, return -EINVAL; spin_lock_irqsave(&dwc->lock, flags); + reg = dwc3_readl(dwc->regs, DWC3_GSTS); + if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -EINVAL; + } + reg = dwc3_readl(dwc->regs, DWC3_DSTS); speed = reg & DWC3_DSTS_CONNECTSPD; -- cgit v1.2.3-59-g8ed1b From eafeacf1196447dac0b8c40e77e96a81b74b8f7f Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 7 Nov 2018 18:10:30 -0800 Subject: usb: dwc3: Set GUSB2PHYCFG.ENBLSLPM GUSB2PHYCFG.ENBLSLPM enables the controller to assert low power signals to the PHY. Unless disabled via device property, explicitly set GUSB2PHYCFG.ENBLSLPM as it may not be set by default. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 2f2048aa5fde..077f60baa12d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -661,6 +661,8 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->dis_enblslpm_quirk) reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; + else + reg |= DWC3_GUSB2PHYCFG_ENBLSLPM; if (dwc->dis_u2_freeclk_exists_quirk) reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS; -- cgit v1.2.3-59-g8ed1b From 022a0208c0ff038f8970a71cb298f85722b4a0ef Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 7 Nov 2018 18:10:42 -0800 Subject: usb: dwc3: Support option to disable USB2 LPM Support the option to disable USB2 LPM. Set xhci "usb2-lpm-disable" property via "snps,usb2-lpm-disable" property. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 2 ++ drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/host.c | 5 ++++- 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 077f60baa12d..0807353f3b07 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1248,6 +1248,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) &hird_threshold); dwc->usb3_lpm_capable = device_property_read_bool(dev, "snps,usb3_lpm_capable"); + dwc->usb2_lpm_disable = device_property_read_bool(dev, + "snps,usb2-lpm-disable"); device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd", &rx_thr_num_pkt_prd); device_property_read_u8(dev, "snps,rx-max-burst-prd", diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 7b17bb6a353c..6a77cd0a0b01 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -982,6 +982,7 @@ struct dwc3_scratchpad_array { * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @three_stage_setup: set if we perform a three phase setup * @usb3_lpm_capable: set if hadrware supports Link Power Management + * @usb2_lpm_disable: set to disable usb2 lpm * @disable_scramble_quirk: set if we enable the disable scramble quirk * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk @@ -1159,6 +1160,7 @@ struct dwc3 { unsigned setup_packet_pending:1; unsigned three_stage_setup:1; unsigned usb3_lpm_capable:1; + unsigned usb2_lpm_disable:1; unsigned disable_scramble_quirk:1; unsigned u2exit_lfps_quirk:1; diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 1a3878a3be78..f55947294f7c 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -46,7 +46,7 @@ out: int dwc3_host_init(struct dwc3 *dwc) { - struct property_entry props[3]; + struct property_entry props[4]; struct platform_device *xhci; int ret, irq; struct resource *res; @@ -93,6 +93,9 @@ int dwc3_host_init(struct dwc3 *dwc) if (dwc->usb3_lpm_capable) props[prop_idx++].name = "usb3-lpm-capable"; + if (dwc->usb2_lpm_disable) + props[prop_idx++].name = "usb2-lpm-disable"; + /** * WORKAROUND: dwc3 revisions <=3.00a have a limitation * where Port Disable command doesn't work. -- cgit v1.2.3-59-g8ed1b From 1808bd2132d1a53a401459474213bdcdbc94d215 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 30 Oct 2018 17:19:59 +0100 Subject: usb: gadget: aspeed-vhub: constify usb_gadget_ops structure The usb_gadget_ops structure can be const as it is only stored in the ops field of a usb_gadget structure and this field is const. Done with the help of Coccinelle. Reviewed-by: Andrew Jeffery Signed-off-by: Julia Lawall Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/aspeed-vhub/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index f0233912bace..6b1b16b17d7d 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -438,7 +438,7 @@ static int ast_vhub_udc_stop(struct usb_gadget *gadget) return 0; } -static struct usb_gadget_ops ast_vhub_udc_ops = { +static const struct usb_gadget_ops ast_vhub_udc_ops = { .get_frame = ast_vhub_udc_get_frame, .wakeup = ast_vhub_udc_wakeup, .pullup = ast_vhub_udc_pullup, -- cgit v1.2.3-59-g8ed1b From 408d3ba006af57380fa48858b39f72fde6405031 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 7 Nov 2018 12:40:29 -0800 Subject: usb: dwc3: don't log probe deferrals; but do log other error codes It's not very useful to repeat a bunch of probe deferral errors. And it's also not very useful to log "failed" without telling the error code. Signed-off-by: Brian Norris Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 0807353f3b07..6acadd647dc3 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1486,7 +1486,8 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_core_init(dwc); if (ret) { - dev_err(dev, "failed to initialize core\n"); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize core: %d\n", ret); goto err4; } -- cgit v1.2.3-59-g8ed1b From 85383756ae34db2ee46daa779f869598a3443651 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 10 Nov 2018 20:11:00 +0200 Subject: usb: dwc3: drd: Switch to device property for 'extcon' handling Switch to device property for 'extcon' handling. No functional change intended. Signed-off-by: Andy Shevchenko Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/drd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 218371f985ca..2401bd504891 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "debug.h" #include "core.h" @@ -446,8 +447,8 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) struct device_node *np_phy, *np_conn; struct extcon_dev *edev; - if (of_property_read_bool(dev->of_node, "extcon")) - return extcon_get_edev_by_phandle(dwc->dev, 0); + if (device_property_read_bool(dev, "extcon")) + return extcon_get_edev_by_phandle(dev, 0); np_phy = of_parse_phandle(dev->of_node, "phys", 0); np_conn = of_graph_get_remote_node(np_phy, -1, -1); -- cgit v1.2.3-59-g8ed1b From 268784ba14a7bff23bc80531d6db31986eafd54b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 10 Nov 2018 20:11:01 +0200 Subject: usb: dwc3: drd: Add support for DR detection through extcon Allow extcon device, found by name, to provide DR status for USB. This is needed, for example, in case of Intel Merrifield platform, where the Intel Basin Cove PMIC provides an extcon device to communicate the detected role. Note, that the "linux,extcon-name" property name is only for kernel internal use by X86/ACPI platform code and as such is not documented in the device tree bindings. Signed-off-by: Andy Shevchenko Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/drd.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 2401bd504891..869725d15c74 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -446,10 +446,20 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) struct device *dev = dwc->dev; struct device_node *np_phy, *np_conn; struct extcon_dev *edev; + const char *name; if (device_property_read_bool(dev, "extcon")) return extcon_get_edev_by_phandle(dev, 0); + /* + * Device tree platforms should get extcon via phandle. + * On ACPI platforms, we get the name from a device property. + * This device property is for kernel internal use only and + * is expected to be set by the glue code. + */ + if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) + return extcon_get_extcon_dev(name); + np_phy = of_parse_phandle(dev->of_node, "phys", 0); np_conn = of_graph_get_remote_node(np_phy, -1, -1); -- cgit v1.2.3-59-g8ed1b From ceb94bc52c437463f0903e61060a94a2226fb672 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 9 Nov 2018 20:44:36 +0900 Subject: usb: gadget: udc: renesas_usb3: add a safety connection way for forced_b_device This patch adds a safety connection way for "forced_b_device" with "workaround_for_vbus" like below: < Example for R-Car E3 Ebisu > # modprobe # echo 1 > /sys/kernel/debug/ee020000.usb/b_device (connect a usb cable to host side.) # echo 2 > /sys/kernel/debug/ee020000.usb/b_device Previous code should have connected a usb cable before the "b_device" is set to 1 on the Ebisu board. However, if xHCI driver on the board is probed, it causes some troubles: - Conflicts USB VBUS/signals between the board and another host. - "Cannot enable. Maybe the USB cable is bad?" might happen on both the board and another host with a usb hub. - Cannot enumerate a usb gadget correctly because an interruption of VBUS change happens unexpectedly. Reported-by: Kazuya Mizuguchi Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/renesas_usb3.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index cdffbd1e0316..6e34f9594159 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -358,6 +358,7 @@ struct renesas_usb3 { bool extcon_host; /* check id and set EXTCON_USB_HOST */ bool extcon_usb; /* check vbus and set EXTCON_USB */ bool forced_b_device; + bool start_to_connect; }; #define gadget_to_renesas_usb3(_gadget) \ @@ -476,7 +477,8 @@ static void usb3_init_axi_bridge(struct renesas_usb3 *usb3) static void usb3_init_epc_registers(struct renesas_usb3 *usb3) { usb3_write(usb3, ~0, USB3_USB_INT_STA_1); - usb3_enable_irq_1(usb3, USB_INT_1_VBUS_CNG); + if (!usb3->workaround_for_vbus) + usb3_enable_irq_1(usb3, USB_INT_1_VBUS_CNG); } static bool usb3_wakeup_usb2_phy(struct renesas_usb3 *usb3) @@ -700,8 +702,7 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev) usb3_set_mode_by_role_sw(usb3, host); usb3_vbus_out(usb3, a_dev); /* for A-Peripheral or forced B-device mode */ - if ((!host && a_dev) || - (usb3->workaround_for_vbus && usb3->forced_b_device)) + if ((!host && a_dev) || usb3->start_to_connect) usb3_connect(usb3); spin_unlock_irqrestore(&usb3->lock, flags); } @@ -2432,7 +2433,11 @@ static ssize_t renesas_usb3_b_device_write(struct file *file, if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; - if (!strncmp(buf, "1", 1)) + usb3->start_to_connect = false; + if (usb3->workaround_for_vbus && usb3->forced_b_device && + !strncmp(buf, "2", 1)) + usb3->start_to_connect = true; + else if (!strncmp(buf, "1", 1)) usb3->forced_b_device = true; else usb3->forced_b_device = false; @@ -2440,7 +2445,7 @@ static ssize_t renesas_usb3_b_device_write(struct file *file, if (usb3->workaround_for_vbus) usb3_disconnect(usb3); - /* Let this driver call usb3_connect() anyway */ + /* Let this driver call usb3_connect() if needed */ usb3_check_id(usb3); return count; -- cgit v1.2.3-59-g8ed1b From 89a9cc47513e91bc91ba2c438d5e422a0c8d05e7 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Fri, 2 Nov 2018 18:41:42 -0700 Subject: usb: dwc3: Set default mode for DWC_usb3 v3.30a and higher DWC_usb31 and DWC_usb3 v3.30a and higher do not support OTG mode. If the controller supports DRD but the dr_mode is not specified or set to OTG, then set the mode to peripheral. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 9 +++++---- drivers/usb/dwc3/core.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 6acadd647dc3..8f6d9c6f016e 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -80,11 +80,12 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) mode = USB_DR_MODE_PERIPHERAL; /* - * dwc_usb31 does not support OTG mode. If the controller - * supports DRD but the dr_mode is not specified or set to OTG, - * then set the mode to peripheral. + * DWC_usb31 and DWC_usb3 v3.30a and higher do not support OTG + * mode. If the controller supports DRD but the dr_mode is not + * specified or set to OTG, then set the mode to peripheral. */ - if (mode == USB_DR_MODE_OTG && dwc3_is_usb31(dwc)) + if (mode == USB_DR_MODE_OTG && + dwc->revision >= DWC3_REVISION_330A) mode = USB_DR_MODE_PERIPHERAL; } diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 6a77cd0a0b01..eec4b9735fb7 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1107,6 +1107,7 @@ struct dwc3 { #define DWC3_REVISION_290A 0x5533290a #define DWC3_REVISION_300A 0x5533300a #define DWC3_REVISION_310A 0x5533310a +#define DWC3_REVISION_330A 0x5533330a /* * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really -- cgit v1.2.3-59-g8ed1b From d64bc8ee92856e39b3150d93e244ca8239ae6ada Mon Sep 17 00:00:00 2001 From: Artur Petrosyan Date: Fri, 2 Nov 2018 11:29:48 -0400 Subject: usb: dwc2: gadget: Fix WkupAlert interrupt handler. According to the databook DCTL_RMTWKUPSIG bit is defined in DCTL register not in DCFG. Updated setting DCTL_RMTWKUPSIG bit to DCTL register. Fixes: 187c5298a122 ("usb: dwc2: gadget: Add handler for WkupAlert interrupt") Signed-off-by: Artur Petrosyan Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2d6d2c8244de..6bd4054e894d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -262,7 +262,7 @@ static void dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg *hsotg) if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) { dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__); dwc2_clear_bit(hsotg, GINTSTS2, GINTSTS2_WKUP_ALERT_INT); - dwc2_set_bit(hsotg, DCFG, DCTL_RMTWKUPSIG); + dwc2_set_bit(hsotg, DCTL, DCTL_RMTWKUPSIG); } } -- cgit v1.2.3-59-g8ed1b From 9aed8c08c82d8498769119b73358d070a7cbb54c Mon Sep 17 00:00:00 2001 From: Artur Petrosyan Date: Fri, 2 Nov 2018 11:29:55 -0400 Subject: usb: dwc2: gadget: Accept LPM token when TxFIFO is not empty Set GLPMCFG_LPM_ACCEPT_CTRL_ISOC bit in GLPMCFG register to accept LPM token during ISOC transfers when TxFIFO is not empty. - Added two definitions. #define GLPMCFG_LPM_ACCEPT_CTRL_CONTROL BIT(21) #define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22) This patch uses GLPMCFG_LPM_ACCEPT_CTRL_ISOC. GLPMCFG_LPM_ACCEPT_CTRL_CONTROL is defined for further use. - Added setting GLPMCFG_LPM_ACCEPT_CTRL_ISOC bit in GLPMCFG register in dwc2_gadget_init_lpm function. Signed-off-by: Artur Petrosyan Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 1 + drivers/usb/dwc2/hw.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 6bd4054e894d..94f3ba995580 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -5026,6 +5026,7 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0; val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT; val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0; + val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC; dwc2_writel(hsotg, val, GLPMCFG); dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG)); diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 2b1ea441b7d4..98af924a9a5c 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -333,6 +333,8 @@ #define GLPMCFG_SNDLPM BIT(24) #define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) #define GLPMCFG_RETRY_CNT_SHIFT 21 +#define GLPMCFG_LPM_ACCEPT_CTRL_CONTROL BIT(21) +#define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22) #define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) #define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 #define GLPMCFG_L1RESUMEOK BIT(16) -- cgit v1.2.3-59-g8ed1b From e89428381080b73740e1fb1fa9b08f1173723b80 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 30 Oct 2018 16:31:21 +0100 Subject: usb: gadget: uvc: constify vb2_ops structure The vb2_ops structure can be const as it is only stored in the ops field of a vb2_queue structure and this field is const. Done with the help of Coccinelle. Reviewed-by: Laurent Pinchart Signed-off-by: Julia Lawall Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index f2497cb96abb..61e2c94cc0b0 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -102,7 +102,7 @@ static void uvc_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&queue->irqlock, flags); } -static struct vb2_ops uvc_queue_qops = { +static const struct vb2_ops uvc_queue_qops = { .queue_setup = uvc_queue_setup, .buf_prepare = uvc_buffer_prepare, .buf_queue = uvc_buffer_queue, -- cgit v1.2.3-59-g8ed1b From 4ab9c39f038d929845f70aade2c2eb64174bba87 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 29 Oct 2018 22:49:45 +0000 Subject: usb: gadget: udc: fix spelling mistake "intrerrupt" -> "interrupt" Trivial fix to spelling mistake in comment Signed-off-by: Colin Ian King Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pch_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index afaea11ec771..55c8c8abeacd 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -1330,7 +1330,7 @@ static void pch_vbus_gpio_work_rise(struct work_struct *irq_work) } /** - * pch_vbus_gpio_irq() - IRQ handler for GPIO intrerrupt for changing VBUS + * pch_vbus_gpio_irq() - IRQ handler for GPIO interrupt for changing VBUS * @irq: Interrupt request number * @dev: Reference to the device structure * -- cgit v1.2.3-59-g8ed1b From 7f7c548c5f652375a61c1072bac3db11f7a48326 Mon Sep 17 00:00:00 2001 From: Vincent Pelletier Date: Tue, 9 Oct 2018 14:43:18 +0000 Subject: usb: gadget: f_fs: Add support for CCID descriptors. Nothing to remap, only check length. Define a minimal structure for CCID descriptor only used to check length. As this descriptor shares the same value as HID descriptors, keep track and compare current interface's class to expected HID and CCID standard values. Signed-off-by: Vincent Pelletier Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 29 ++++++++++++++++------ include/linux/usb/ccid.h | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 include/linux/usb/ccid.h (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 31e8bf3578c8..65b72e5c4605 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -1926,7 +1927,7 @@ typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity, static int __must_check ffs_do_single_desc(char *data, unsigned len, ffs_entity_callback entity, - void *priv) + void *priv, int *current_class) { struct usb_descriptor_header *_ds = (void *)data; u8 length; @@ -1984,6 +1985,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len, __entity(INTERFACE, ds->bInterfaceNumber); if (ds->iInterface) __entity(STRING, ds->iInterface); + *current_class = ds->bInterfaceClass; } break; @@ -1997,11 +1999,22 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len, } break; - case HID_DT_HID: - pr_vdebug("hid descriptor\n"); - if (length != sizeof(struct hid_descriptor)) - goto inv_length; - break; + case USB_TYPE_CLASS | 0x01: + if (*current_class == USB_INTERFACE_CLASS_HID) { + pr_vdebug("hid descriptor\n"); + if (length != sizeof(struct hid_descriptor)) + goto inv_length; + break; + } else if (*current_class == USB_INTERFACE_CLASS_CCID) { + pr_vdebug("ccid descriptor\n"); + if (length != sizeof(struct ccid_descriptor)) + goto inv_length; + break; + } else { + pr_vdebug("unknown descriptor: %d for class %d\n", + _ds->bDescriptorType, *current_class); + return -EINVAL; + } case USB_DT_OTG: if (length != sizeof(struct usb_otg_descriptor)) @@ -2058,6 +2071,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, { const unsigned _len = len; unsigned long num = 0; + int current_class = -1; ENTER(); @@ -2078,7 +2092,8 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, if (!data) return _len - len; - ret = ffs_do_single_desc(data, len, entity, priv); + ret = ffs_do_single_desc(data, len, entity, priv, + ¤t_class); if (unlikely(ret < 0)) { pr_debug("%s returns %d\n", __func__, ret); return ret; diff --git a/include/linux/usb/ccid.h b/include/linux/usb/ccid.h new file mode 100644 index 000000000000..3431446d6864 --- /dev/null +++ b/include/linux/usb/ccid.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 Vincent Pelletier + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __CCID_H +#define __CCID_H + +#include + +#define USB_INTERFACE_CLASS_CCID 0x0b + +struct ccid_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __le16 bcdCCID; + __u8 bMaxSlotIndex; + __u8 bVoltageSupport; + __le32 dwProtocols; + __le32 dwDefaultClock; + __le32 dwMaximumClock; + __u8 bNumClockSupported; + __le32 dwDataRate; + __le32 dwMaxDataRate; + __u8 bNumDataRatesSupported; + __le32 dwMaxIFSD; + __le32 dwSynchProtocols; + __le32 dwMechanical; + __le32 dwFeatures; + __le32 dwMaxCCIDMessageLength; + __u8 bClassGetResponse; + __u8 bClassEnvelope; + __le16 wLcdLayout; + __u8 bPINSupport; + __u8 bMaxCCIDBusySlots; +} __attribute__ ((packed)); + +#endif /* __CCID_H */ -- cgit v1.2.3-59-g8ed1b From 772a7a724f69d258025fedd87dde1aafe4171aef Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Wed, 14 Nov 2018 10:47:48 +0100 Subject: usb: gadget: f_fs: Allow scatter-gather buffers Some protocols implemented in userspace with FunctionFS might require large buffers, e.g. 64kB or more. Currently the said memory is allocated with kmalloc, which might fail should system memory be highly fragmented. On the other hand, some UDC hardware allows scatter-gather operation and this patch takes advantage of this capability: if the requested buffer is larger than PAGE_SIZE and the UDC allows scatter-gather operation, then the buffer is allocated with vmalloc and a scatterlist describing it is created and passed to usb request. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 93 +++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 65b72e5c4605..1e5430438703 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -18,9 +18,12 @@ #include #include #include +#include #include +#include #include #include +#include #include #include @@ -219,6 +222,8 @@ struct ffs_io_data { struct usb_ep *ep; struct usb_request *req; + struct sg_table sgt; + bool use_sg; struct ffs_data *ffs; }; @@ -750,6 +755,65 @@ static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter) return ret; } +/* + * allocate a virtually contiguous buffer and create a scatterlist describing it + * @sg_table - pointer to a place to be filled with sg_table contents + * @size - required buffer size + */ +static void *ffs_build_sg_list(struct sg_table *sgt, size_t sz) +{ + struct page **pages; + void *vaddr, *ptr; + unsigned int n_pages; + int i; + + vaddr = vmalloc(sz); + if (!vaddr) + return NULL; + + n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT; + pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL); + if (!pages) { + vfree(vaddr); + + return NULL; + } + for (i = 0, ptr = vaddr; i < n_pages; ++i, ptr += PAGE_SIZE) + pages[i] = vmalloc_to_page(ptr); + + if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) { + kvfree(pages); + vfree(vaddr); + + return NULL; + } + kvfree(pages); + + return vaddr; +} + +static inline void *ffs_alloc_buffer(struct ffs_io_data *io_data, + size_t data_len) +{ + if (io_data->use_sg) + return ffs_build_sg_list(&io_data->sgt, data_len); + + return kmalloc(data_len, GFP_KERNEL); +} + +static inline void ffs_free_buffer(struct ffs_io_data *io_data) +{ + if (!io_data->buf) + return; + + if (io_data->use_sg) { + sg_free_table(&io_data->sgt); + vfree(io_data->buf); + } else { + kfree(io_data->buf); + } +} + static void ffs_user_copy_worker(struct work_struct *work) { struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, @@ -777,7 +841,7 @@ static void ffs_user_copy_worker(struct work_struct *work) if (io_data->read) kfree(io_data->to_free); - kfree(io_data->buf); + ffs_free_buffer(io_data); kfree(io_data); } @@ -933,6 +997,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) * earlier */ gadget = epfile->ffs->gadget; + io_data->use_sg = gadget->sg_supported && data_len > PAGE_SIZE; spin_lock_irq(&epfile->ffs->eps_lock); /* In the meantime, endpoint got disabled or changed. */ @@ -949,7 +1014,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) data_len = usb_ep_align_maybe(gadget, ep->ep, data_len); spin_unlock_irq(&epfile->ffs->eps_lock); - data = kmalloc(data_len, GFP_KERNEL); + data = ffs_alloc_buffer(io_data, data_len); if (unlikely(!data)) { ret = -ENOMEM; goto error_mutex; @@ -989,8 +1054,16 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) bool interrupted = false; req = ep->req; - req->buf = data; - req->length = data_len; + if (io_data->use_sg) { + req->buf = NULL; + req->sg = io_data->sgt.sgl; + req->num_sgs = io_data->sgt.nents; + } else { + req->buf = data; + } + req->length = data_len; + + io_data->buf = data; req->context = &done; req->complete = ffs_epfile_io_complete; @@ -1023,8 +1096,14 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) } else if (!(req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC))) { ret = -ENOMEM; } else { - req->buf = data; - req->length = data_len; + if (io_data->use_sg) { + req->buf = NULL; + req->sg = io_data->sgt.sgl; + req->num_sgs = io_data->sgt.nents; + } else { + req->buf = data; + } + req->length = data_len; io_data->buf = data; io_data->ep = ep->ep; @@ -1053,7 +1132,7 @@ error_lock: error_mutex: mutex_unlock(&epfile->mutex); error: - kfree(data); + ffs_free_buffer(io_data); return ret; } -- cgit v1.2.3-59-g8ed1b From 475d8e0197f1bcf4647041f46206ffc1a16d15dd Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 8 Nov 2018 12:06:48 -0800 Subject: usb: dwc3: Track DWC_usb31 VERSIONTYPE Add a new field to dwc3 structure to track VERSIONTYPE. The VERSIONTYPE is represented in ASCII in the 32-bit VERSIONTYPE register. In DWC_usb31, sub releases for each version are tracked with VERSIONTYPE such as "ea01" and "ea02". Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 1 + drivers/usb/dwc3/core.h | 10 ++++++++++ 2 files changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 8f6d9c6f016e..eb064a65363c 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -705,6 +705,7 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc) /* Detected DWC_usb31 IP */ dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); dwc->revision |= DWC3_REVISION_IS_DWC31; + dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE); } else { return false; } diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index eec4b9735fb7..709e8dd356ee 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -928,6 +928,7 @@ struct dwc3_scratchpad_array { * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) * @revision: revision register contents + * @version_type: VERSIONTYPE register contents, a sub release of a revision * @dr_mode: requested mode of operation * @current_dr_role: current role of operation when in dual-role mode * @desired_dr_role: desired role of operation when in dual-role mode @@ -1117,6 +1118,15 @@ struct dwc3 { #define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31) #define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31) + u32 version_type; + +#define DWC31_VERSIONTYPE_EA01 0x65613031 +#define DWC31_VERSIONTYPE_EA02 0x65613032 +#define DWC31_VERSIONTYPE_EA03 0x65613033 +#define DWC31_VERSIONTYPE_EA04 0x65613034 +#define DWC31_VERSIONTYPE_EA05 0x65613035 +#define DWC31_VERSIONTYPE_EA06 0x65613036 + enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; -- cgit v1.2.3-59-g8ed1b From d92021f66063b30910255d70dc95e0d7b57f018f Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 14 Nov 2018 22:56:54 -0800 Subject: usb: dwc3: Add workaround for isoc start transfer failure In DWC_usb31 version 1.70a-ea06 and prior, for highspeed and fullspeed isochronous IN, BIT[15:14] of the 16-bit microframe number reported by the XferNotReady event are invalid. The driver uses this number to schedule the isochronous transfer and passes it to the START TRANSFER command. Because this number is invalid, the command may fail. If BIT[15:14] matches the internal 16-bit microframe, the START TRANSFER command will pass and the transfer will start at the scheduled time, if it is off by 1, the command will still pass, but the transfer will start 2 seconds in the future. For all other conditions, the START TRANSFER command will fail with bus-expiry. In order to workaround this issue, we can test for the correct combination of BIT[15:14] by sending START TRANSFER commands with different values of BIT[15:14]: 'b00, 'b01, 'b10, and 'b11. Each combination is 2^14 uframe apart (or 2 seconds). 4 seconds into the future will result in a bus-expiry status. As the result, within the 4 possible combinations for BIT[15:14], there will be 2 successful and 2 failure START COMMAND status. One of the 2 successful command status will result in a 2-second delay start. The smaller BIT[15:14] value is the correct combination. Since there are only 4 outcomes and the results are ordered, we can simply test 2 START TRANSFER commands with BIT[15:14] combinations 'b00 and 'b01 to deduce the smaller successful combination. Let test0 = test status for combination 'b00 and test1 = test status for 'b01 of BIT[15:14]. The correct combination is as follow: if test0 fails and test1 passes, BIT[15:14] is 'b01 if test0 fails and test1 fails, BIT[15:14] is 'b10 if test0 passes and test1 fails, BIT[15:14] is 'b11 if test0 passes and test1 passes, BIT[15:14] is 'b00 Synopsys STAR 9001202023: Wrong microframe number for isochronous IN endpoints. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 2 + drivers/usb/dwc3/core.h | 13 +++++ drivers/usb/dwc3/gadget.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index eb064a65363c..a1b126f90261 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1248,6 +1248,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) "snps,is-utmi-l1-suspend"); device_property_read_u8(dev, "snps,hird-threshold", &hird_threshold); + dwc->dis_start_transfer_quirk = device_property_read_bool(dev, + "snps,dis-start-transfer-quirk"); dwc->usb3_lpm_capable = device_property_read_bool(dev, "snps,usb3_lpm_capable"); dwc->usb2_lpm_disable = device_property_read_bool(dev, diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 709e8dd356ee..0136aa7766e1 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -666,6 +666,10 @@ struct dwc3_event_buffer { * @name: a human readable name e.g. ep1out-bulk * @direction: true for TX, false for RX * @stream_capable: true when streams are enabled + * @combo_num: the test combination BIT[15:14] of the frame number to test + * isochronous START TRANSFER command failure workaround + * @start_cmd_status: the status of testing START TRANSFER command with + * combo_num = 'b00 */ struct dwc3_ep { struct usb_ep endpoint; @@ -715,6 +719,10 @@ struct dwc3_ep { unsigned direction:1; unsigned stream_capable:1; + + /* For isochronous START TRANSFER workaround only */ + u8 combo_num; + int start_cmd_status; }; enum dwc3_phy { @@ -982,6 +990,8 @@ struct dwc3_scratchpad_array { * @pullups_connected: true when Run/Stop bit is set * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @three_stage_setup: set if we perform a three phase setup + * @dis_start_transfer_quirk: set if start_transfer failure SW workaround is + * not needed for DWC_usb31 version 1.70a-ea06 and below * @usb3_lpm_capable: set if hadrware supports Link Power Management * @usb2_lpm_disable: set to disable usb2 lpm * @disable_scramble_quirk: set if we enable the disable scramble quirk @@ -1117,6 +1127,8 @@ struct dwc3 { #define DWC3_REVISION_IS_DWC31 0x80000000 #define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31) #define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31) +#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31) +#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31) u32 version_type; @@ -1170,6 +1182,7 @@ struct dwc3 { unsigned pullups_connected:1; unsigned setup_packet_pending:1; unsigned three_stage_setup:1; + unsigned dis_start_transfer_quirk:1; unsigned usb3_lpm_capable:1; unsigned usb2_lpm_disable:1; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9faad896b3a1..c48ea78341a4 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1263,8 +1263,125 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc) return DWC3_DSTS_SOFFN(reg); } +/** + * dwc3_gadget_start_isoc_quirk - workaround invalid frame number + * @dep: isoc endpoint + * + * This function tests for the correct combination of BIT[15:14] from the 16-bit + * microframe number reported by the XferNotReady event for the future frame + * number to start the isoc transfer. + * + * In DWC_usb31 version 1.70a-ea06 and prior, for highspeed and fullspeed + * isochronous IN, BIT[15:14] of the 16-bit microframe number reported by the + * XferNotReady event are invalid. The driver uses this number to schedule the + * isochronous transfer and passes it to the START TRANSFER command. Because + * this number is invalid, the command may fail. If BIT[15:14] matches the + * internal 16-bit microframe, the START TRANSFER command will pass and the + * transfer will start at the scheduled time, if it is off by 1, the command + * will still pass, but the transfer will start 2 seconds in the future. For all + * other conditions, the START TRANSFER command will fail with bus-expiry. + * + * In order to workaround this issue, we can test for the correct combination of + * BIT[15:14] by sending START TRANSFER commands with different values of + * BIT[15:14]: 'b00, 'b01, 'b10, and 'b11. Each combination is 2^14 uframe apart + * (or 2 seconds). 4 seconds into the future will result in a bus-expiry status. + * As the result, within the 4 possible combinations for BIT[15:14], there will + * be 2 successful and 2 failure START COMMAND status. One of the 2 successful + * command status will result in a 2-second delay start. The smaller BIT[15:14] + * value is the correct combination. + * + * Since there are only 4 outcomes and the results are ordered, we can simply + * test 2 START TRANSFER commands with BIT[15:14] combinations 'b00 and 'b01 to + * deduce the smaller successful combination. + * + * Let test0 = test status for combination 'b00 and test1 = test status for 'b01 + * of BIT[15:14]. The correct combination is as follow: + * + * if test0 fails and test1 passes, BIT[15:14] is 'b01 + * if test0 fails and test1 fails, BIT[15:14] is 'b10 + * if test0 passes and test1 fails, BIT[15:14] is 'b11 + * if test0 passes and test1 passes, BIT[15:14] is 'b00 + * + * Synopsys STAR 9001202023: Wrong microframe number for isochronous IN + * endpoints. + */ +static void dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) +{ + int cmd_status = 0; + bool test0; + bool test1; + + while (dep->combo_num < 2) { + struct dwc3_gadget_ep_cmd_params params; + u32 test_frame_number; + u32 cmd; + + /* + * Check if we can start isoc transfer on the next interval or + * 4 uframes in the future with BIT[15:14] as dep->combo_num + */ + test_frame_number = dep->frame_number & 0x3fff; + test_frame_number |= dep->combo_num << 14; + test_frame_number += max_t(u32, 4, dep->interval); + + params.param0 = upper_32_bits(dep->dwc->bounce_addr); + params.param1 = lower_32_bits(dep->dwc->bounce_addr); + + cmd = DWC3_DEPCMD_STARTTRANSFER; + cmd |= DWC3_DEPCMD_PARAM(test_frame_number); + cmd_status = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); + + /* Redo if some other failure beside bus-expiry is received */ + if (cmd_status && cmd_status != -EAGAIN) { + dep->start_cmd_status = 0; + dep->combo_num = 0; + return; + } + + /* Store the first test status */ + if (dep->combo_num == 0) + dep->start_cmd_status = cmd_status; + + dep->combo_num++; + + /* + * End the transfer if the START_TRANSFER command is successful + * to wait for the next XferNotReady to test the command again + */ + if (cmd_status == 0) { + dwc3_stop_active_transfer(dep, true); + return; + } + } + + /* test0 and test1 are both completed at this point */ + test0 = (dep->start_cmd_status == 0); + test1 = (cmd_status == 0); + + if (!test0 && test1) + dep->combo_num = 1; + else if (!test0 && !test1) + dep->combo_num = 2; + else if (test0 && !test1) + dep->combo_num = 3; + else if (test0 && test1) + dep->combo_num = 0; + + dep->frame_number &= 0x3fff; + dep->frame_number |= dep->combo_num << 14; + dep->frame_number += max_t(u32, 4, dep->interval); + + /* Reinitialize test variables */ + dep->start_cmd_status = 0; + dep->combo_num = 0; + + __dwc3_gadget_kick_transfer(dep); +} + static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep) { + struct dwc3 *dwc = dep->dwc; + if (list_empty(&dep->pending_list)) { dev_info(dep->dwc->dev, "%s: ran out of requests\n", dep->name); @@ -1272,6 +1389,18 @@ static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep) return; } + if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) && + (dwc->revision <= DWC3_USB31_REVISION_160A || + (dwc->revision == DWC3_USB31_REVISION_170A && + dwc->version_type >= DWC31_VERSIONTYPE_EA01 && + dwc->version_type <= DWC31_VERSIONTYPE_EA06))) { + + if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction) { + dwc3_gadget_start_isoc_quirk(dep); + return; + } + } + dep->frame_number = DWC3_ALIGN_FRAME(dep); __dwc3_gadget_kick_transfer(dep); } @@ -2153,6 +2282,8 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) dep->direction = direction; dep->regs = dwc->regs + DWC3_DEP_BASE(epnum); dwc->eps[epnum] = dep; + dep->combo_num = 0; + dep->start_cmd_status = 0; snprintf(dep->name, sizeof(dep->name), "ep%u%s", num, direction ? "in" : "out"); -- cgit v1.2.3-59-g8ed1b From 1a22ec643580626f439c8583edafdcc73798f2fb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:15:05 +0300 Subject: usb: dwc3: gadget: combine unaligned and zero flags Both flags are used for the same purpose in dwc3: appending an extra TRB at the end to deal with controller requirements. By combining both flags into one, we make it clear that the situation is the same and that they should be treated equally. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 7 +++---- drivers/usb/dwc3/gadget.c | 18 +++++++++--------- 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 0136aa7766e1..b89d31232028 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -865,11 +865,11 @@ struct dwc3_hwparams { * @epnum: endpoint number to which this request refers * @trb: pointer to struct dwc3_trb * @trb_dma: DMA address of @trb - * @unaligned: true for OUT endpoints with length not divisible by maxp + * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP + * or unaligned OUT) * @direction: IN or OUT direction flag * @mapped: true when request has been dma-mapped * @started: request is started - * @zero: wants a ZLP */ struct dwc3_request { struct usb_request request; @@ -885,11 +885,10 @@ struct dwc3_request { struct dwc3_trb *trb; dma_addr_t trb_dma; - unsigned unaligned:1; + unsigned needs_extra_trb:1; unsigned direction:1; unsigned mapped:1; unsigned started:1; - unsigned zero:1; }; /* diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c48ea78341a4..3d5bd2a4c07f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1073,7 +1073,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; - req->unaligned = true; + req->needs_extra_trb = true; /* prepare normal TRB */ dwc3_prepare_one_trb(dep, req, true, i); @@ -1117,7 +1117,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; - req->unaligned = true; + req->needs_extra_trb = true; /* prepare normal TRB */ dwc3_prepare_one_trb(dep, req, true, 0); @@ -1133,7 +1133,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; - req->zero = true; + req->needs_extra_trb = true; /* prepare normal TRB */ dwc3_prepare_one_trb(dep, req, true, 0); @@ -1544,7 +1544,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, dwc3_ep_inc_deq(dep); } - if (r->unaligned || r->zero) { + if (r->needs_extra_trb) { trb = r->trb + r->num_pending_sgs + 1; trb->ctrl &= ~DWC3_TRB_CTRL_HWO; dwc3_ep_inc_deq(dep); @@ -1555,7 +1555,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, trb->ctrl &= ~DWC3_TRB_CTRL_HWO; dwc3_ep_inc_deq(dep); - if (r->unaligned || r->zero) { + if (r->needs_extra_trb) { trb = r->trb + 1; trb->ctrl &= ~DWC3_TRB_CTRL_HWO; dwc3_ep_inc_deq(dep); @@ -2390,7 +2390,8 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, * with one TRB pending in the ring. We need to manually clear HWO bit * from that TRB. */ - if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { + + if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { trb->ctrl &= ~DWC3_TRB_CTRL_HWO; return 1; } @@ -2467,11 +2468,10 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status); - if (req->unaligned || req->zero) { + if (req->needs_extra_trb) { ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status); - req->unaligned = false; - req->zero = false; + req->needs_extra_trb = false; } req->request.actual = req->request.length - req->remaining; -- cgit v1.2.3-59-g8ed1b From 09fe1f8d7e2f461275b1cdd832f2cfa5e9be346d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:32:07 +0300 Subject: usb: dwc3: gadget: track number of TRBs per request This will help us remove the wait_event() from our ->dequeue(). Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/gadget.c | 6 ++++++ 2 files changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index b89d31232028..8405519413a4 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -865,6 +865,7 @@ struct dwc3_hwparams { * @epnum: endpoint number to which this request refers * @trb: pointer to struct dwc3_trb * @trb_dma: DMA address of @trb + * @num_trbs: number of TRBs used by this request * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP * or unaligned OUT) * @direction: IN or OUT direction flag @@ -885,6 +886,8 @@ struct dwc3_request { struct dwc3_trb *trb; dma_addr_t trb_dma; + unsigned num_trbs; + unsigned needs_extra_trb:1; unsigned direction:1; unsigned mapped:1; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3d5bd2a4c07f..70a8ff1112bb 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1046,6 +1046,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, req->trb_dma = dwc3_trb_dma_offset(dep, trb); } + req->num_trbs++; + __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node, stream_id, short_not_ok, no_interrupt); } @@ -1080,6 +1082,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, /* Now prepare one extra TRB to align transfer size */ trb = &dep->trb_pool[dep->trb_enqueue]; + req->num_trbs++; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem, false, 1, req->request.stream_id, @@ -1124,6 +1127,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, /* Now prepare one extra TRB to align transfer size */ trb = &dep->trb_pool[dep->trb_enqueue]; + req->num_trbs++; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem, false, 1, req->request.stream_id, req->request.short_not_ok, @@ -1140,6 +1144,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, /* Now prepare one extra TRB to handle ZLP */ trb = &dep->trb_pool[dep->trb_enqueue]; + req->num_trbs++; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0, false, 1, req->request.stream_id, req->request.short_not_ok, @@ -2371,6 +2376,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, dwc3_ep_inc_deq(dep); trace_dwc3_complete_trb(dep, trb); + req->num_trbs--; /* * If we're in the middle of series of chained TRBs and we -- cgit v1.2.3-59-g8ed1b From c3acd59014148470dc58519870fbc779785b4bf7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:37:53 +0300 Subject: usb: dwc3: gadget: use num_trbs when skipping TRBs on ->dequeue() Now that we track how many TRBs a request uses, it's easier to skip over them in case of a call to usb_ep_dequeue(). Let's do so and simplify the code a bit. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 70a8ff1112bb..1510490acfa5 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1502,6 +1502,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, break; } if (r == req) { + int i; + /* wait until it is processed */ dwc3_stop_active_transfer(dep, true); @@ -1539,32 +1541,12 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, if (!r->trb) goto out0; - if (r->num_pending_sgs) { + for (i = 0; i < r->num_trbs; i++) { struct dwc3_trb *trb; - int i = 0; - - for (i = 0; i < r->num_pending_sgs; i++) { - trb = r->trb + i; - trb->ctrl &= ~DWC3_TRB_CTRL_HWO; - dwc3_ep_inc_deq(dep); - } - - if (r->needs_extra_trb) { - trb = r->trb + r->num_pending_sgs + 1; - trb->ctrl &= ~DWC3_TRB_CTRL_HWO; - dwc3_ep_inc_deq(dep); - } - } else { - struct dwc3_trb *trb = r->trb; + trb = r->trb + i; trb->ctrl &= ~DWC3_TRB_CTRL_HWO; dwc3_ep_inc_deq(dep); - - if (r->needs_extra_trb) { - trb = r->trb + 1; - trb->ctrl &= ~DWC3_TRB_CTRL_HWO; - dwc3_ep_inc_deq(dep); - } } goto out1; } @@ -1575,8 +1557,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, } out1: - /* giveback the request */ - dwc3_gadget_giveback(dep, req, -ECONNRESET); out0: -- cgit v1.2.3-59-g8ed1b From 7746a8dfb3f9c91b3a0b63a1d5c2664410e6498d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:42:29 +0300 Subject: usb: dwc3: gadget: extract dwc3_gadget_ep_skip_trbs() Extract the logic for skipping over TRBs to its own function. This makes the code slightly more readable and makes it easier to move this call to its final resting place as a following patch. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 61 +++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1510490acfa5..9728978070b7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1475,6 +1475,29 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, return ret; } +static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req) +{ + int i; + + /* + * If request was already started, this means we had to + * stop the transfer. With that we also need to ignore + * all TRBs used by the request, however TRBs can only + * be modified after completion of END_TRANSFER + * command. So what we do here is that we wait for + * END_TRANSFER completion and only after that, we jump + * over TRBs by clearing HWO and incrementing dequeue + * pointer. + */ + for (i = 0; i < req->num_trbs; i++) { + struct dwc3_trb *trb; + + trb = req->trb + i; + trb->ctrl &= ~DWC3_TRB_CTRL_HWO; + dwc3_ep_inc_deq(dep); + } +} + static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request) { @@ -1502,38 +1525,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, break; } if (r == req) { - int i; - /* wait until it is processed */ dwc3_stop_active_transfer(dep, true); - - /* - * If request was already started, this means we had to - * stop the transfer. With that we also need to ignore - * all TRBs used by the request, however TRBs can only - * be modified after completion of END_TRANSFER - * command. So what we do here is that we wait for - * END_TRANSFER completion and only after that, we jump - * over TRBs by clearing HWO and incrementing dequeue - * pointer. - * - * Note that we have 2 possible types of transfers here: - * - * i) Linear buffer request - * ii) SG-list based request - * - * SG-list based requests will have r->num_pending_sgs - * set to a valid number (> 0). Linear requests, - * normally use a single TRB. - * - * For each of these two cases, if r->unaligned flag is - * set, one extra TRB has been used to align transfer - * size to wMaxPacketSize. - * - * All of these cases need to be taken into - * consideration so we don't mess up our TRB ring - * pointers. - */ wait_event_lock_irq(dep->wait_end_transfer, !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), dwc->lock); @@ -1541,13 +1534,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, if (!r->trb) goto out0; - for (i = 0; i < r->num_trbs; i++) { - struct dwc3_trb *trb; - - trb = r->trb + i; - trb->ctrl &= ~DWC3_TRB_CTRL_HWO; - dwc3_ep_inc_deq(dep); - } + dwc3_gadget_ep_skip_trbs(dep, r); goto out1; } dev_err(dwc->dev, "request %pK was not queued to %s\n", -- cgit v1.2.3-59-g8ed1b From d5443bbf5fc8f8389cce146b1fc2987cdd229d12 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:53:29 +0300 Subject: usb: dwc3: gadget: introduce cancelled_list This list will host cancelled requests who still have TRBs being processed. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/gadget.c | 1 + drivers/usb/dwc3/gadget.h | 15 +++++++++++++++ 3 files changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 8405519413a4..9798c73c09ce 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -646,6 +646,7 @@ struct dwc3_event_buffer { /** * struct dwc3_ep - device side endpoint representation * @endpoint: usb endpoint + * @cancelled_list: list of cancelled requests for this endpoint * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint * @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete @@ -673,6 +674,7 @@ struct dwc3_event_buffer { */ struct dwc3_ep { struct usb_ep endpoint; + struct list_head cancelled_list; struct list_head pending_list; struct list_head started_list; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9728978070b7..17203944d77f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2284,6 +2284,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) INIT_LIST_HEAD(&dep->pending_list); INIT_LIST_HEAD(&dep->started_list); + INIT_LIST_HEAD(&dep->cancelled_list); return 0; } diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 2aacd1afd9ff..023a473648eb 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -79,6 +79,21 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req) list_move_tail(&req->list, &dep->started_list); } +/** + * dwc3_gadget_move_cancelled_request - move @req to the cancelled_list + * @req: the request to be moved + * + * Caller should take care of locking. This function will move @req from its + * current list to the endpoint's cancelled_list. + */ +static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req) +{ + struct dwc3_ep *dep = req->dep; + + req->started = false; + list_move_tail(&req->list, &dep->cancelled_list); +} + void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status); -- cgit v1.2.3-59-g8ed1b From d4f1afe5e896c18ae01099a85dab5e1a198bd2a8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:54:25 +0300 Subject: usb: dwc3: gadget: move requests to cancelled_list Whenever we have a request in flight, we can move it to the cancelled list and later simply iterate over that list and skip over any TRBs we find. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 17203944d77f..d5b9db90ca1c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1498,6 +1498,17 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r } } +static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) +{ + struct dwc3_request *req; + struct dwc3_request *tmp; + + list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) { + dwc3_gadget_ep_skip_trbs(dep, req); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + } +} + static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request) { @@ -1534,8 +1545,9 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, if (!r->trb) goto out0; - dwc3_gadget_ep_skip_trbs(dep, r); - goto out1; + dwc3_gadget_move_cancelled_request(req); + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + goto out0; } dev_err(dwc->dev, "request %pK was not queued to %s\n", request, ep->name); @@ -1543,7 +1555,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, goto out0; } -out1: dwc3_gadget_giveback(dep, req, -ECONNRESET); out0: -- cgit v1.2.3-59-g8ed1b From fec9095bdef4e7c988adb603d0d4f92ee735d4a1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:56:50 +0300 Subject: usb: dwc3: gadget: remove wait_end_transfer Now that we have a list of cancelled requests, we can skip over TRBs when END_TRANSFER command completes. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 3 --- drivers/usb/dwc3/gadget.c | 40 +--------------------------------------- 2 files changed, 1 insertion(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 9798c73c09ce..58f4aa5e3443 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -649,7 +649,6 @@ struct dwc3_event_buffer { * @cancelled_list: list of cancelled requests for this endpoint * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint - * @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete * @lock: spinlock for endpoint request queue traversal * @regs: pointer to first endpoint register * @trb_pool: array of transaction buffers @@ -678,8 +677,6 @@ struct dwc3_ep { struct list_head pending_list; struct list_head started_list; - wait_queue_head_t wait_end_transfer; - spinlock_t lock; void __iomem *regs; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d5b9db90ca1c..a61bc9928a1a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -647,8 +647,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) reg |= DWC3_DALEPENA_EP(dep->number); dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); - init_waitqueue_head(&dep->wait_end_transfer); - if (usb_endpoint_xfer_control(desc)) goto out; @@ -1538,15 +1536,11 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, if (r == req) { /* wait until it is processed */ dwc3_stop_active_transfer(dep, true); - wait_event_lock_irq(dep->wait_end_transfer, - !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), - dwc->lock); if (!r->trb) goto out0; dwc3_gadget_move_cancelled_request(req); - dwc3_gadget_ep_cleanup_cancelled_requests(dep); goto out0; } dev_err(dwc->dev, "request %pK was not queued to %s\n", @@ -2051,8 +2045,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g) { struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; - int epnum; - u32 tmo_eps = 0; spin_lock_irqsave(&dwc->lock, flags); @@ -2061,36 +2053,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g) __dwc3_gadget_stop(dwc); - for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) { - struct dwc3_ep *dep = dwc->eps[epnum]; - int ret; - - if (!dep) - continue; - - if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) - continue; - - ret = wait_event_interruptible_lock_irq_timeout(dep->wait_end_transfer, - !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), - dwc->lock, msecs_to_jiffies(5)); - - if (ret <= 0) { - /* Timed out or interrupted! There's nothing much - * we can do so we just log here and print which - * endpoints timed out at the end. - */ - tmo_eps |= 1 << epnum; - dep->flags &= DWC3_EP_END_TRANSFER_PENDING; - } - } - - if (tmo_eps) { - dev_err(dwc->dev, - "end transfer timed out on endpoints 0x%x [bitmap]\n", - tmo_eps); - } - out: dwc->gadget_driver = NULL; spin_unlock_irqrestore(&dwc->lock, flags); @@ -2589,7 +2551,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, if (cmd == DWC3_DEPCMD_ENDTRANSFER) { dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; - wake_up(&dep->wait_end_transfer); + dwc3_gadget_ep_cleanup_cancelled_requests(dep); } break; case DWC3_DEPEVT_STREAMEVT: -- cgit v1.2.3-59-g8ed1b From 25abad6a0584c3c08e2859738d23cbc53597179d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 14 Aug 2018 10:41:19 +0300 Subject: usb: dwc3: gadget: return errors from __dwc3_gadget_start_isoc() Sometimes, errors happen when kicking transfers from __dwc3_gadget_start_isoc(). In those cases, we need to pass along the error so gadget driver can make informed decisions. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a61bc9928a1a..7e0f8ff5946d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1308,7 +1308,7 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc) * Synopsys STAR 9001202023: Wrong microframe number for isochronous IN * endpoints. */ -static void dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) +static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) { int cmd_status = 0; bool test0; @@ -1338,7 +1338,7 @@ static void dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) if (cmd_status && cmd_status != -EAGAIN) { dep->start_cmd_status = 0; dep->combo_num = 0; - return; + return 0; } /* Store the first test status */ @@ -1353,7 +1353,7 @@ static void dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) */ if (cmd_status == 0) { dwc3_stop_active_transfer(dep, true); - return; + return 0; } } @@ -1378,10 +1378,10 @@ static void dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) dep->start_cmd_status = 0; dep->combo_num = 0; - __dwc3_gadget_kick_transfer(dep); + return __dwc3_gadget_kick_transfer(dep); } -static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep) +static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; @@ -1389,7 +1389,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep) dev_info(dep->dwc->dev, "%s: ran out of requests\n", dep->name); dep->flags |= DWC3_EP_PENDING_REQUEST; - return; + return -EAGAIN; } if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) && @@ -1398,14 +1398,12 @@ static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep) dwc->version_type >= DWC31_VERSIONTYPE_EA01 && dwc->version_type <= DWC31_VERSIONTYPE_EA06))) { - if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction) { - dwc3_gadget_start_isoc_quirk(dep); - return; - } + if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction) + return dwc3_gadget_start_isoc_quirk(dep); } dep->frame_number = DWC3_ALIGN_FRAME(dep); - __dwc3_gadget_kick_transfer(dep); + return __dwc3_gadget_kick_transfer(dep); } static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) @@ -1446,8 +1444,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) if ((dep->flags & DWC3_EP_PENDING_REQUEST)) { if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) { - __dwc3_gadget_start_isoc(dep); - return 0; + return __dwc3_gadget_start_isoc(dep); } } } @@ -2513,7 +2510,7 @@ static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep, const struct dwc3_event_depevt *event) { dwc3_gadget_endpoint_frame_from_event(dep, event); - __dwc3_gadget_start_isoc(dep); + (void) __dwc3_gadget_start_isoc(dep); } static void dwc3_endpoint_interrupt(struct dwc3 *dwc, -- cgit v1.2.3-59-g8ed1b From 1517265228b4ea6b89379fa8e134e62f75ea1dfe Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 15 Aug 2018 08:30:59 +0300 Subject: usb: dwc3: trace: log ep commands in hex They are much more useful in hexadecimal than in decimal. Moreover, generic commands are already logged in hex. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/trace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index f22714cce070..50fb6f2d92dd 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -199,7 +199,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, __entry->param2 = params->param2; __entry->cmd_status = cmd_status; ), - TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x --> status: %s", + TP_printk("%s: cmd '%s' [%x] params %08x %08x %08x --> status: %s", __get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd), __entry->cmd, __entry->param0, __entry->param1, __entry->param2, -- cgit v1.2.3-59-g8ed1b From 3451f6affaef8c2a0a7a6a5960b86eac9d2ff2f7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 15 Aug 2018 08:34:44 +0300 Subject: usb: dwc3: gadget: remove unnecessary dev_info() Running out of requests on isochronous endpoints is part of normal operation. We don't really need to know about it every time it happens. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7e0f8ff5946d..e94971a7f468 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1386,8 +1386,6 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) struct dwc3 *dwc = dep->dwc; if (list_empty(&dep->pending_list)) { - dev_info(dep->dwc->dev, "%s: ran out of requests\n", - dep->name); dep->flags |= DWC3_EP_PENDING_REQUEST; return -EAGAIN; } -- cgit v1.2.3-59-g8ed1b From d53701067f048b8b11635e964b6d3bd9a248c622 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 14 Aug 2018 10:42:43 +0300 Subject: usb: dwc3: gadget: check if dep->frame_number is still valid Gadget driver may take an unbounded amount of time to queue requests after XferNotReady. This is important for isochronous endpoints which need to be started for a specific (micro-)frame. If we fail to start a transfer for isochronous endpoint, let's try queueing to a future interval and see if that helps. We will stop trying if we fail a start transfer for 5 intervals in the future. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/gadget.c | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 58f4aa5e3443..15c07b2b5866 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -37,6 +37,7 @@ #define DWC3_EP0_SETUP_SIZE 512 #define DWC3_ENDPOINTS_NUM 32 #define DWC3_XHCI_RESOURCES_NUM 2 +#define DWC3_ISOC_MAX_RETRIES 5 #define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ #define DWC3_EVENT_BUFFERS_SIZE 4096 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e94971a7f468..c4e91c6d4fce 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -27,7 +27,7 @@ #include "gadget.h" #include "io.h" -#define DWC3_ALIGN_FRAME(d) (((d)->frame_number + (d)->interval) \ +#define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \ & ~((d)->interval - 1)) /** @@ -1384,6 +1384,8 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; + int ret; + int i; if (list_empty(&dep->pending_list)) { dep->flags |= DWC3_EP_PENDING_REQUEST; @@ -1400,8 +1402,15 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) return dwc3_gadget_start_isoc_quirk(dep); } - dep->frame_number = DWC3_ALIGN_FRAME(dep); - return __dwc3_gadget_kick_transfer(dep); + for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) { + dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1); + + ret = __dwc3_gadget_kick_transfer(dep); + if (ret != -EAGAIN) + break; + } + + return ret; } static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) -- cgit v1.2.3-59-g8ed1b From e8603076f5404cc103869a62f319bc8a4797cc50 Mon Sep 17 00:00:00 2001 From: JackyChou Date: Fri, 30 Nov 2018 14:31:21 +0800 Subject: USB: serial: mos7840: clean up register handling In the read/write function, set port 2 independently in the 2-port case. When setting the offset of port registers, the offset between port 1 and other ports is different, so port 1 is set independently. Then in the rest of ports, the port 2 between 2-ports case and 4-ports case is different, so port 2 in 2-ports case is set independently. Specifically, port 2 in the 2-port case maps to the registers used by port 3 in the 4-port case. Signed-off-by: JackyChou [ johan: simplify register-offset handling at port probe, add a comment and amend commit message ] Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 48 +++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index bfbf75b36349..4159d3195388 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -298,15 +298,10 @@ static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg, val = val & 0x00ff; /* For the UART control registers, the application number need to be Or'ed */ - if (port->serial->num_ports == 4) { + if (port->serial->num_ports == 2 && port->port_number != 0) + val |= ((__u16)port->port_number + 2) << 8; + else val |= ((__u16)port->port_number + 1) << 8; - } else { - if (port->port_number == 0) { - val |= ((__u16)port->port_number + 1) << 8; - } else { - val |= ((__u16)port->port_number + 2) << 8; - } - } dev_dbg(&port->dev, "%s application number is %x\n", __func__, val); return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, MCS_WR_RTYPE, val, reg, NULL, 0, @@ -332,15 +327,10 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg, return -ENOMEM; /* Wval is same as application number */ - if (port->serial->num_ports == 4) { + if (port->serial->num_ports == 2 && port->port_number != 0) + Wval = ((__u16)port->port_number + 2) << 8; + else Wval = ((__u16)port->port_number + 1) << 8; - } else { - if (port->port_number == 0) { - Wval = ((__u16)port->port_number + 1) << 8; - } else { - Wval = ((__u16)port->port_number + 2) << 8; - } - } dev_dbg(&port->dev, "%s application number is %x\n", __func__, Wval); ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH, @@ -2127,22 +2117,16 @@ static int mos7840_port_probe(struct usb_serial_port *port) mos7840_port->SpRegOffset = 0x0; mos7840_port->ControlRegOffset = 0x1; mos7840_port->DcrRegOffset = 0x4; - } else if ((mos7840_port->port_num == 2) && (serial->num_ports == 4)) { - mos7840_port->SpRegOffset = 0x8; - mos7840_port->ControlRegOffset = 0x9; - mos7840_port->DcrRegOffset = 0x16; - } else if ((mos7840_port->port_num == 2) && (serial->num_ports == 2)) { - mos7840_port->SpRegOffset = 0xa; - mos7840_port->ControlRegOffset = 0xb; - mos7840_port->DcrRegOffset = 0x19; - } else if ((mos7840_port->port_num == 3) && (serial->num_ports == 4)) { - mos7840_port->SpRegOffset = 0xa; - mos7840_port->ControlRegOffset = 0xb; - mos7840_port->DcrRegOffset = 0x19; - } else if ((mos7840_port->port_num == 4) && (serial->num_ports == 4)) { - mos7840_port->SpRegOffset = 0xc; - mos7840_port->ControlRegOffset = 0xd; - mos7840_port->DcrRegOffset = 0x1c; + } else { + u8 phy_num = mos7840_port->port_num; + + /* Port 2 in the 2-port case uses registers of port 3 */ + if (serial->num_ports == 2) + phy_num = 3; + + mos7840_port->SpRegOffset = 0x8 + 2 * (phy_num - 2); + mos7840_port->ControlRegOffset = 0x9 + 2 * (phy_num - 2); + mos7840_port->DcrRegOffset = 0x16 + 3 * (phy_num - 2); } mos7840_dump_serial_port(port, mos7840_port); mos7840_set_port_private(port, mos7840_port); -- cgit v1.2.3-59-g8ed1b From 32899682cf457de5c091ee7dbbee34ad9bf2f992 Mon Sep 17 00:00:00 2001 From: JackyChou Date: Fri, 30 Nov 2018 14:31:22 +0800 Subject: USB: serial: mos7840: add a product ID for the new product Add a new PID 0x7843 to the driver. Let the new products be able to set up 3 serial ports with the driver. Note that this depends on e8603076f540 ("USB: serial: mos7840: clean up register handling"). Signed-off-by: JackyChou [ johan: mention dependency in case anyone wants to backport this ] Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 4159d3195388..6ff6d67b6d5a 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -94,6 +94,7 @@ /* The native mos7840/7820 component */ #define USB_VENDOR_ID_MOSCHIP 0x9710 #define MOSCHIP_DEVICE_ID_7840 0x7840 +#define MOSCHIP_DEVICE_ID_7843 0x7843 #define MOSCHIP_DEVICE_ID_7820 0x7820 #define MOSCHIP_DEVICE_ID_7810 0x7810 /* The native component can have its vendor/device id's overridden @@ -176,6 +177,7 @@ enum mos7840_flag { static const struct usb_device_id id_table[] = { {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)}, + {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7843)}, {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)}, {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7810)}, {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)}, @@ -2028,7 +2030,8 @@ static int mos7840_probe(struct usb_serial *serial, int device_type; if (product == MOSCHIP_DEVICE_ID_7810 || - product == MOSCHIP_DEVICE_ID_7820) { + product == MOSCHIP_DEVICE_ID_7820 || + product == MOSCHIP_DEVICE_ID_7843) { device_type = product; goto out; } @@ -2062,7 +2065,10 @@ static int mos7840_calc_num_ports(struct usb_serial *serial, int device_type = (unsigned long)usb_get_serial_data(serial); int num_ports; - num_ports = (device_type >> 4) & 0x000F; + if (device_type == MOSCHIP_DEVICE_ID_7843) + num_ports = 3; + else + num_ports = (device_type >> 4) & 0x000F; /* * num_ports is currently never zero as device_type is one of -- cgit v1.2.3-59-g8ed1b From a7351807bd8b5db0306ccd6977f8c13722731dd5 Mon Sep 17 00:00:00 2001 From: Anurag Kumar Vulisha Date: Sat, 1 Dec 2018 16:43:25 +0530 Subject: usb: dwc3: update stream id in depcmd For stream capable endpoints, stream id related information needs to be updated into DEPCMD while issuing START TRANSFER. This patch does the same. Signed-off-by: Anurag Kumar Vulisha Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c4e91c6d4fce..d54d434dc986 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1235,6 +1235,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) params.param1 = lower_32_bits(req->trb_dma); cmd = DWC3_DEPCMD_STARTTRANSFER; + if (dep->stream_capable) + cmd |= DWC3_DEPCMD_PARAM(req->request.stream_id); + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) cmd |= DWC3_DEPCMD_PARAM(dep->frame_number); } else { -- cgit v1.2.3-59-g8ed1b From 26d62b4d10ad54622cd16b4871ee635f0ee287b6 Mon Sep 17 00:00:00 2001 From: Anurag Kumar Vulisha Date: Sat, 1 Dec 2018 16:43:27 +0530 Subject: usb: dwc3: don't issue no-op trb for stream capable endpoints The stream capable endpoints require stream id to be given when issuing START TRANSFER. While issuing no-op trb the stream id is not yet known, so don't issue no-op trb's on stream capable endpoints. Signed-off-by: Anurag Kumar Vulisha Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d54d434dc986..08dea6fdb4a8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -670,7 +670,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) * Issue StartTransfer here with no-op TRB so we can always rely on No * Response Update Transfer command. */ - if (usb_endpoint_xfer_bulk(desc) || + if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) || usb_endpoint_xfer_int(desc)) { struct dwc3_gadget_ep_cmd_params params; struct dwc3_trb *trb; -- cgit v1.2.3-59-g8ed1b From b7a4fbe2300a8965ea760c7e871507b84aea17f6 Mon Sep 17 00:00:00 2001 From: Anurag Kumar Vulisha Date: Sat, 1 Dec 2018 16:43:29 +0530 Subject: usb: dwc3: Correct the logic for checking TRB full in __dwc3_prepare_one_trb() Availability of TRB's is calculated using dwc3_calc_trbs_left(), which determines total available TRB's based on the HWO bit set in a TRB. In the present code, __dwc3_prepare_one_trb() is called with a TRB which needs to be prepared for transfer. This __dwc3_prepare_one_trb() calls dwc3_calc_trbs_left() to determine total available TRBs and set IOC bit if the total available TRBs are zero. Since the present working TRB (which is passed as an argument to __dwc3_prepare_one_trb() ) doesn't yet have the HWO bit set before calling dwc3_calc_trbs_left(), there are chances that dwc3_calc_trbs_left() wrongly calculates this present working TRB as free(since the HWO bit is not yet set) and returns the total available TRBs as greater than zero (including the present working TRB). This could be a problem. This patch corrects the above mentioned problem in __dwc3_prepare_one_trb() by increementing the dep->trb_enqueue at the last (after preparing the TRB) instead of increementing at the start and setting the IOC bit only if the total available TRBs returned by dwc3_calc_trbs_left() is 1 . Since we are increementing the dep->trb_enqueue at the last, the present working TRB is also considered as available by dwc3_calc_trbs_left() and non zero value is returned . So, according to the modified logic, when the total available TRBs is equal to 1 that means the total available TRBs in the pool are 0. Signed-off-by: Anurag Kumar Vulisha Reviewed-by: Thinh Nguyen Tested-by: Tejas Joglekar Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 08dea6fdb4a8..38d6df98e9ce 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -917,8 +917,6 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, struct usb_gadget *gadget = &dwc->gadget; enum usb_device_speed speed = gadget->speed; - dwc3_ep_inc_enq(dep); - trb->size = DWC3_TRB_SIZE_LENGTH(length); trb->bpl = lower_32_bits(dma); trb->bph = upper_32_bits(dma); @@ -997,7 +995,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, } if ((!no_interrupt && !chain) || - (dwc3_calc_trbs_left(dep) == 0)) + (dwc3_calc_trbs_left(dep) == 1)) trb->ctrl |= DWC3_TRB_CTRL_IOC; if (chain) @@ -1008,6 +1006,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, trb->ctrl |= DWC3_TRB_CTRL_HWO; + dwc3_ep_inc_enq(dep); + trace_dwc3_prepare_trb(dep, trb); } -- cgit v1.2.3-59-g8ed1b From 35a6054132286a4ab92b536595093b82e6bdfcbc Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 20 Nov 2018 16:38:15 +0100 Subject: usb: dwc2: Disable power down feature on Samsung SoCs Power down feature of DWC2 module integrated in Samsung SoCs doesn't work properly or needs some additional handling in PHY or SoC glue layer, so disable it for now. Without disabling power down, DWC2 causes random memory trashes and fails enumeration if there is no USB link to host on driver probe. Fixes: 03ea6d6e9e1ff1 ("usb: dwc2: Enable power down") Acked-by: Minas Harutyunyan Signed-off-by: Marek Szyprowski Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 7c1b6938f212..266157ae179a 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -71,6 +71,13 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg) p->power_down = false; } +static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->power_down = 0; +} + static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg) { struct dwc2_core_params *p = &hsotg->params; @@ -151,7 +158,8 @@ const struct of_device_id dwc2_of_match_table[] = { { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params }, { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params }, { .compatible = "snps,dwc2" }, - { .compatible = "samsung,s3c6400-hsotg" }, + { .compatible = "samsung,s3c6400-hsotg", + .data = dwc2_set_s3c6400_params }, { .compatible = "amlogic,meson8-usb", .data = dwc2_set_amlogic_params }, { .compatible = "amlogic,meson8b-usb", -- cgit v1.2.3-59-g8ed1b From 36b25b69c2c13ebe3d6f80c11de537cb7d1c43f0 Mon Sep 17 00:00:00 2001 From: "Hsin-Yi, Wang" Date: Thu, 29 Nov 2018 11:16:27 +0800 Subject: usb/mtu3: power down device ip at setup Originally, when dr_mode is USB_DR_MODE_HOST, it didn't power down device ip, so host ip sleep will fail at ssusb_host_disable. Power down device ip at ssusb_host_setup. Acked-by: Chunfeng Yun Signed-off-by: Hsin-Yi, Wang Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_plat.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 46551f6d16fd..e086630e41a9 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -200,6 +200,14 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb) mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST); udelay(1); mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST); + + /* + * device ip may be powered on in firmware/BROM stage before entering + * kernel stage; + * power down device ip, otherwise ip-sleep will fail when working as + * host only mode + */ + mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); } /* ignore the error if the clock does not exist */ -- cgit v1.2.3-59-g8ed1b From 4f7371314e57b21725ef208e9a37dd58d3f5d974 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Nov 2018 10:34:32 +0800 Subject: usb: mtu3: remove QMU checksum The QMU checksum calculation is redundant, mostly used by debug, so remove it here. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_core.c | 2 -- drivers/usb/mtu3/mtu3_qmu.c | 26 -------------------------- 2 files changed, 28 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index ae70b9bfd797..3dce5fd9887d 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -592,8 +592,6 @@ static void mtu3_regs_init(struct mtu3 *mtu) mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK); /* U2/U3 detected by HW */ mtu3_writel(mbase, U3D_DEVICE_CONF, 0); - /* enable QMU 16B checksum */ - mtu3_setbits(mbase, U3D_QCR0, QMU_CS16B_EN); /* vbus detected by HW */ mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON); } diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index ff62ba232177..73ac042c45a8 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -154,27 +154,6 @@ void mtu3_gpd_ring_free(struct mtu3_ep *mep) memset(ring, 0, sizeof(*ring)); } -/* - * calculate check sum of a gpd or bd - * add "noinline" and "mb" to prevent wrong calculation - */ -static noinline u8 qmu_calc_checksum(u8 *data) -{ - u8 chksum = 0; - int i; - - data[1] = 0x0; /* set checksum to 0 */ - - mb(); /* ensure the gpd/bd is really up-to-date */ - for (i = 0; i < QMU_CHECKSUM_LEN; i++) - chksum += data[i]; - - /* Default: HWO=1, @flag[bit0] */ - chksum += 1; - - return 0xFF - chksum; -} - void mtu3_qmu_resume(struct mtu3_ep *mep) { struct mtu3 *mtu = mep->mtu; @@ -260,7 +239,6 @@ static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) if (req->zero) gpd->ext_flag |= GPD_EXT_FLAG_ZLP; - gpd->chksum = qmu_calc_checksum((u8 *)gpd); gpd->flag |= GPD_FLAGS_HWO; mreq->gpd = gpd; @@ -295,7 +273,6 @@ static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma)); ext_addr |= GPD_EXT_NGP(upper_32_bits(enq_dma)); gpd->rx_ext_addr = cpu_to_le16(ext_addr); - gpd->chksum = qmu_calc_checksum((u8 *)gpd); gpd->flag |= GPD_FLAGS_HWO; mreq->gpd = gpd; @@ -323,7 +300,6 @@ int mtu3_qmu_start(struct mtu3_ep *mep) /* set QMU start address */ write_txq_start_addr(mbase, epnum, ring->dma); mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_DMAREQEN); - mtu3_setbits(mbase, U3D_QCR0, QMU_TX_CS_EN(epnum)); /* send zero length packet according to ZLP flag in GPD */ mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum)); mtu3_writel(mbase, U3D_TQERRIESR0, @@ -338,7 +314,6 @@ int mtu3_qmu_start(struct mtu3_ep *mep) } else { write_rxq_start_addr(mbase, epnum, ring->dma); mtu3_setbits(mbase, MU3D_EP_RXCR0(epnum), RX_DMAREQEN); - mtu3_setbits(mbase, U3D_QCR0, QMU_RX_CS_EN(epnum)); /* don't expect ZLP */ mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum)); /* move to next GPD when receive ZLP */ @@ -441,7 +416,6 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) /* by pass the current GDP */ gpd_current->flag |= GPD_FLAGS_BPS; - gpd_current->chksum = qmu_calc_checksum((u8 *)gpd_current); gpd_current->flag |= GPD_FLAGS_HWO; /*enable DMAREQEN, switch back to QMU mode */ -- cgit v1.2.3-59-g8ed1b From 68c750cf4504f8179188db36057834ba00afb183 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Nov 2018 10:34:33 +0800 Subject: usb: mtu3: enable hardware remote wakeup from L1 automatically Enable hardware remote wakeup from L1 automatically based on the FIFO status, instead of manual way. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_core.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 3dce5fd9887d..981e4e8c5c13 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -180,7 +180,7 @@ static void mtu3_intr_enable(struct mtu3 *mtu) mtu3_writel(mbase, U3D_LV1IESR, value); /* Enable U2 common USB interrupts */ - value = SUSPEND_INTR | RESUME_INTR | RESET_INTR | LPM_RESUME_INTR; + value = SUSPEND_INTR | RESUME_INTR | RESET_INTR; mtu3_writel(mbase, U3D_COMMON_USB_INTR_ENABLE, value); if (mtu->is_u3_ip) { @@ -594,6 +594,8 @@ static void mtu3_regs_init(struct mtu3 *mtu) mtu3_writel(mbase, U3D_DEVICE_CONF, 0); /* vbus detected by HW */ mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON); + /* enable automatical HWRW from L1 */ + mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE); } static irqreturn_t mtu3_link_isr(struct mtu3 *mtu) @@ -706,12 +708,6 @@ static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu) if (u2comm & RESET_INTR) mtu3_gadget_reset(mtu); - if (u2comm & LPM_RESUME_INTR) { - if (!(mtu3_readl(mbase, U3D_POWER_MANAGEMENT) & LPM_HRWE)) - mtu3_setbits(mbase, U3D_USB20_MISC_CONTROL, - LPM_U3_ACK_EN); - } - return IRQ_HANDLED; } -- cgit v1.2.3-59-g8ed1b From a0678e2eed41e81004308693ac84ea95614b0920 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Nov 2018 10:34:34 +0800 Subject: usb: mtu3: fix the issue about SetFeature(U1/U2_Enable) Fix the issue: device doesn't accept LGO_U1/U2: 1. set SW_U1/U2_ACCEPT_ENABLE to eanble controller to accept LGO_U1/U2 by default; 2. enable/disable controller to initiate requests for transition into U1/U2 by SW_U1/U2_REQUEST_ENABLE instead of SW_U1/U2_ACCEPT_ENABLE; Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_core.c | 4 +++- drivers/usb/mtu3/mtu3_gadget_ep0.c | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 981e4e8c5c13..1ffc0bc31c1d 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -578,8 +578,10 @@ static void mtu3_regs_init(struct mtu3 *mtu) if (mtu->is_u3_ip) { /* disable LGO_U1/U2 by default */ mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL, - SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE | SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE); + /* enable accept LGO_U1/U2 link command from host */ + mtu3_setbits(mbase, U3D_LINK_POWER_CONTROL, + SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE); /* device responses to u3_exit from host automatically */ mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN); /* automatically build U2 link when U3 detect fail */ diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 25216e79cd6e..3c464d8ae023 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -336,9 +336,9 @@ static int ep0_handle_feature_dev(struct mtu3 *mtu, lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); if (set) - lpc |= SW_U1_ACCEPT_ENABLE; + lpc |= SW_U1_REQUEST_ENABLE; else - lpc &= ~SW_U1_ACCEPT_ENABLE; + lpc &= ~SW_U1_REQUEST_ENABLE; mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc); mtu->u1_enable = !!set; @@ -351,9 +351,9 @@ static int ep0_handle_feature_dev(struct mtu3 *mtu, lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); if (set) - lpc |= SW_U2_ACCEPT_ENABLE; + lpc |= SW_U2_REQUEST_ENABLE; else - lpc &= ~SW_U2_ACCEPT_ENABLE; + lpc &= ~SW_U2_REQUEST_ENABLE; mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc); mtu->u2_enable = !!set; -- cgit v1.2.3-59-g8ed1b From 49187dd14cc84f0ff7db2876c43ad510eeec04b0 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Nov 2018 10:34:35 +0800 Subject: usb: mtu3: enable SETUPENDISR interrupt If the controller receives a new SETUP during SETUP data stage, and will generate SETUPENDISR interrupt, the driver should abort the current SETUP command and process the new one. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_core.c | 2 +- drivers/usb/mtu3/mtu3_gadget_ep0.c | 6 +++++- drivers/usb/mtu3/mtu3_hw_regs.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 1ffc0bc31c1d..b6b20949d63a 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -484,7 +484,7 @@ void mtu3_ep0_setup(struct mtu3 *mtu) mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr); /* Enable EP0 interrupt */ - mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR); + mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR | SETUPENDISR); } static int mtu3_mem_alloc(struct mtu3 *mtu) diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 3c464d8ae023..7cb7ac980446 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -692,9 +692,13 @@ irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu) mtu3_writel(mbase, U3D_EPISR, int_status); /* W1C */ /* only handle ep0's */ - if (!(int_status & EP0ISR)) + if (!(int_status & (EP0ISR | SETUPENDISR))) return IRQ_NONE; + /* abort current SETUP, and process new one */ + if (int_status & SETUPENDISR) + mtu->ep0_state = MU3D_EP0_STATE_SETUP; + csr = mtu3_readl(mbase, U3D_EP0CSR); dev_dbg(mtu->dev, "%s csr=0x%x\n", __func__, csr); diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index a45bb253939f..d11fcd64c19d 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -104,6 +104,7 @@ /* U3D_EPISR */ #define EPRISR(x) (BIT(16) << (x)) +#define SETUPENDISR BIT(16) #define EPTISR(x) (BIT(0) << (x)) #define EP0ISR BIT(0) -- cgit v1.2.3-59-g8ed1b From 47b6f8bf870035d420614844de5e308abe505e8a Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Nov 2018 10:34:36 +0800 Subject: usb: mtu3: clear SOFTCONN when clear USB3_EN if work as HS mode When the controller supports SS mode, but works as HS mode, the SOFTCONN will not be cleared automatically when clear USB3_EN by default, this cause an issue that can't disconnect from host, so clear SOFTCONN when clear USB3_EN when the class driver want to disable the D+ pullup. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_core.c | 2 ++ drivers/usb/mtu3/mtu3_hw_regs.h | 5 +++++ 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index b6b20949d63a..4fee200795a5 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -586,6 +586,8 @@ static void mtu3_regs_init(struct mtu3 *mtu) mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN); /* automatically build U2 link when U3 detect fail */ mtu3_setbits(mbase, U3D_USB2_TEST_MODE, U2U3_AUTO_SWITCH); + /* auto clear SOFT_CONN when clear USB3_EN if work as HS */ + mtu3_setbits(mbase, U3D_U3U2_SWITCH_CTRL, SOFTCON_CLR_AUTO_EN); } mtu3_set_speed(mtu); diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index d11fcd64c19d..1d65b7476f23 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -268,6 +268,8 @@ #define U3D_LTSSM_INTR_ENABLE (SSUSB_USB3_MAC_CSR_BASE + 0x013C) #define U3D_LTSSM_INTR (SSUSB_USB3_MAC_CSR_BASE + 0x0140) +#define U3D_U3U2_SWITCH_CTRL (SSUSB_USB3_MAC_CSR_BASE + 0x0170) + /*---------------- SSUSB_USB3_MAC_CSR FIELD DEFINITION ----------------*/ /* U3D_LTSSM_CTRL */ @@ -302,6 +304,9 @@ #define SS_DISABLE_INTR BIT(1) #define SS_INACTIVE_INTR BIT(0) +/* U3D_U3U2_SWITCH_CTRL */ +#define SOFTCON_CLR_AUTO_EN BIT(0) + /*---------------- SSUSB_USB3_SYS_CSR REGISTER DEFINITION ----------------*/ #define U3D_LINK_UX_INACT_TIMER (SSUSB_USB3_SYS_CSR_BASE + 0x020C) -- cgit v1.2.3-59-g8ed1b From e86108940e541febf35813402ff29fa6f4a9ac0b Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 28 Nov 2018 15:55:21 +0200 Subject: usb: hub: delay hub autosuspend if USB3 port is still link training When initializing a hub we want to give a USB3 port in link training the same debounce delay time before autosuspening the hub as already trained, connected enabled ports. USB3 ports won't reach the enabled state with "current connect status" and "connect status change" bits set until the USB3 link training finishes. Catching the port in link training (polling) and adding the debounce delay prevents unnecessary failed attempts to autosuspend the hub. Signed-off-by: Mathias Nyman Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0f9381b69a3b..009f92800e03 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1112,6 +1112,16 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) USB_PORT_FEAT_ENABLE); } + /* + * Add debounce if USB3 link is in polling/link training state. + * Link will automatically transition to Enabled state after + * link training completes. + */ + if (hub_is_superspeed(hdev) && + ((portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_POLLING)) + need_debounce_delay = true; + /* Clear status-change flags; we'll debounce later */ if (portchange & USB_PORT_STAT_C_CONNECTION) { need_debounce_delay = true; -- cgit v1.2.3-59-g8ed1b From 6ed30a7d8ec29d3aba46e47aa8b4a44f077dda4e Mon Sep 17 00:00:00 2001 From: Terin Stock Date: Sun, 9 Sep 2018 21:24:31 -0700 Subject: usb: dwc2: host: use hrtimer for NAK retries Modify the wait delay utilize the high resolution timer API to allow for more precisely scheduled callbacks. A previous commit added a 1ms retry delay after multiple consecutive NAKed transactions using jiffies. On systems with a low timer interrupt frequency, this delay may be significantly longer than specified, resulting in misbehavior with some USB devices. This scenario was reached on a Raspberry Pi 3B with a Macally FDD-USB floppy drive (identified as 0424:0fdc Standard Microsystems Corp. Floppy, based on the USB97CFDC USB FDC). With the relay delay, the drive would be unable to mount a disk, replying with NAKs until the device was reset. Using ktime, the delta between starting the timer (in dwc2_hcd_qh_add) and the callback function can be determined. With the original delay implementation, this value was consistently approximately 12ms. (output in us). -0 [000] ..s. 1600.559974: dwc2_wait_timer_fn: wait_timer delta: 11976 -0 [000] ..s. 1600.571974: dwc2_wait_timer_fn: wait_timer delta: 11977 -0 [000] ..s. 1600.583974: dwc2_wait_timer_fn: wait_timer delta: 11976 -0 [000] ..s. 1600.595974: dwc2_wait_timer_fn: wait_timer delta: 11977 After converting the relay delay to using a higher resolution timer, the delay was much closer to 1ms. -0 [000] d.h. 1956.553017: dwc2_wait_timer_fn: wait_timer delta: 1002 -0 [000] d.h. 1956.554114: dwc2_wait_timer_fn: wait_timer delta: 1002 -0 [000] d.h. 1957.542660: dwc2_wait_timer_fn: wait_timer delta: 1004 -0 [000] d.h. 1957.543701: dwc2_wait_timer_fn: wait_timer delta: 1002 The floppy drive operates properly with delays up to approximately 5ms, and sends NAKs for any delays that are longer. Fixes: 38d2b5fb75c1 ("usb: dwc2: host: Don't retry NAKed transactions right away") Cc: Reviewed-by: Douglas Anderson Acked-by: Minas Harutyunyan Signed-off-by: Terin Stock Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.h | 2 +- drivers/usb/dwc2/hcd_queue.c | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 3f9bccc95add..c089ffa1f0a8 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -366,7 +366,7 @@ struct dwc2_qh { u32 desc_list_sz; u32 *n_bytes; struct timer_list unreserve_timer; - struct timer_list wait_timer; + struct hrtimer wait_timer; struct dwc2_tt *dwc_tt; int ttport; unsigned tt_buffer_dirty:1; diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 40839591d2ec..ea3aa640c15c 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -59,7 +59,7 @@ #define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5)) /* If we get a NAK, wait this long before retrying */ -#define DWC2_RETRY_WAIT_DELAY (msecs_to_jiffies(1)) +#define DWC2_RETRY_WAIT_DELAY 1*1E6L /** * dwc2_periodic_channel_available() - Checks that a channel is available for a @@ -1464,10 +1464,12 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg, * qh back to the "inactive" list, then queues transactions. * * @t: Pointer to wait_timer in a qh. + * + * Return: HRTIMER_NORESTART to not automatically restart this timer. */ -static void dwc2_wait_timer_fn(struct timer_list *t) +static enum hrtimer_restart dwc2_wait_timer_fn(struct hrtimer *t) { - struct dwc2_qh *qh = from_timer(qh, t, wait_timer); + struct dwc2_qh *qh = container_of(t, struct dwc2_qh, wait_timer); struct dwc2_hsotg *hsotg = qh->hsotg; unsigned long flags; @@ -1491,6 +1493,7 @@ static void dwc2_wait_timer_fn(struct timer_list *t) } spin_unlock_irqrestore(&hsotg->lock, flags); + return HRTIMER_NORESTART; } /** @@ -1521,7 +1524,8 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, /* Initialize QH */ qh->hsotg = hsotg; timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0); - timer_setup(&qh->wait_timer, dwc2_wait_timer_fn, 0); + hrtimer_init(&qh->wait_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + qh->wait_timer.function = &dwc2_wait_timer_fn; qh->ep_type = ep_type; qh->ep_is_in = ep_is_in; @@ -1690,7 +1694,7 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) * won't do anything anyway, but we want it to finish before we free * memory. */ - del_timer_sync(&qh->wait_timer); + hrtimer_cancel(&qh->wait_timer); dwc2_host_put_tt_info(hsotg, qh->dwc_tt); @@ -1716,6 +1720,7 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { int status; u32 intr_mask; + ktime_t delay; if (dbg_qh(qh)) dev_vdbg(hsotg->dev, "%s()\n", __func__); @@ -1734,8 +1739,8 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) list_add_tail(&qh->qh_list_entry, &hsotg->non_periodic_sched_waiting); qh->wait_timer_cancel = false; - mod_timer(&qh->wait_timer, - jiffies + DWC2_RETRY_WAIT_DELAY + 1); + delay = ktime_set(0, DWC2_RETRY_WAIT_DELAY); + hrtimer_start(&qh->wait_timer, delay, HRTIMER_MODE_REL); } else { list_add_tail(&qh->qh_list_entry, &hsotg->non_periodic_sched_inactive); -- cgit v1.2.3-59-g8ed1b From 1e3af5dfd05c53b3dfd367af4c78ebbf60f6fb41 Mon Sep 17 00:00:00 2001 From: "Hsin-Yi, Wang" Date: Thu, 29 Nov 2018 11:16:27 +0800 Subject: usb/mtu3: power down device ip at setup Originally, when dr_mode is USB_DR_MODE_HOST, it didn't power down device ip, so host ip sleep will fail at ssusb_host_disable. Power down device ip at ssusb_host_setup. Signed-off-by: Hsin-Yi, Wang Acked-by: Chunfeng Yun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_plat.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 46551f6d16fd..e086630e41a9 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -200,6 +200,14 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb) mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST); udelay(1); mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST); + + /* + * device ip may be powered on in firmware/BROM stage before entering + * kernel stage; + * power down device ip, otherwise ip-sleep will fail when working as + * host only mode + */ + mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); } /* ignore the error if the clock does not exist */ -- cgit v1.2.3-59-g8ed1b From b01828e26048f43dc4cea5759f200ec9e8332039 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 21 Nov 2018 06:31:23 +0000 Subject: usb: renesas_usbhs: Remove dummy runtime PM callbacks Platform drivers don't need dummy runtime PM callbacks that just return success in order to have runtime PM happening. This has changed since following commits: commit 05aa55dddb9e ("PM / Runtime: Lenient generic runtime pm callbacks") commit 543f2503a956 ("PM / platform_bus: Allow runtime PM by default") commit 8b313a38ecff ("PM / Platform: Use generic runtime PM callbacks directly") Signed-off-by: Jarkko Nikula Reviewed-by: Yoshihiro Shimoda Acked-by: Wolfram Sang [shimoda: revise git commit description style] Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index a3e1290d682d..0e760f228dd8 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -874,23 +874,9 @@ static int usbhsc_resume(struct device *dev) return 0; } -static int usbhsc_runtime_nop(struct device *dev) -{ - /* Runtime PM callback shared between ->runtime_suspend() - * and ->runtime_resume(). Simply returns success. - * - * This driver re-initializes all registers after - * pm_runtime_get_sync() anyway so there is no need - * to save and restore registers here. - */ - return 0; -} - static const struct dev_pm_ops usbhsc_pm_ops = { .suspend = usbhsc_suspend, .resume = usbhsc_resume, - .runtime_suspend = usbhsc_runtime_nop, - .runtime_resume = usbhsc_runtime_nop, }; static struct platform_driver renesas_usbhs_driver = { -- cgit v1.2.3-59-g8ed1b From d54d334e75b93f3a372d1cbb7e805380b3281482 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 21 Nov 2018 06:31:23 +0000 Subject: usb: renesas_usbhs: Use SIMPLE_DEV_PM_OPS macro This patch uses SIMPLE_DEV_PM_OPS macro instead of struct dev_pm_ops directly. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 0e760f228dd8..02c1d2bf4f86 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -874,10 +874,7 @@ static int usbhsc_resume(struct device *dev) return 0; } -static const struct dev_pm_ops usbhsc_pm_ops = { - .suspend = usbhsc_suspend, - .resume = usbhsc_resume, -}; +static SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume); static struct platform_driver renesas_usbhs_driver = { .driver = { -- cgit v1.2.3-59-g8ed1b From aef34b48d084c42d765c912583c51811e853fbce Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Nov 2018 10:34:32 +0800 Subject: usb: mtu3: remove QMU checksum The QMU checksum calculation is redundant, mostly used by debug, so remove it here. Signed-off-by: Chunfeng Yun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_core.c | 2 -- drivers/usb/mtu3/mtu3_qmu.c | 26 -------------------------- 2 files changed, 28 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index ae70b9bfd797..3dce5fd9887d 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -592,8 +592,6 @@ static void mtu3_regs_init(struct mtu3 *mtu) mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK); /* U2/U3 detected by HW */ mtu3_writel(mbase, U3D_DEVICE_CONF, 0); - /* enable QMU 16B checksum */ - mtu3_setbits(mbase, U3D_QCR0, QMU_CS16B_EN); /* vbus detected by HW */ mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON); } diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index ff62ba232177..73ac042c45a8 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -154,27 +154,6 @@ void mtu3_gpd_ring_free(struct mtu3_ep *mep) memset(ring, 0, sizeof(*ring)); } -/* - * calculate check sum of a gpd or bd - * add "noinline" and "mb" to prevent wrong calculation - */ -static noinline u8 qmu_calc_checksum(u8 *data) -{ - u8 chksum = 0; - int i; - - data[1] = 0x0; /* set checksum to 0 */ - - mb(); /* ensure the gpd/bd is really up-to-date */ - for (i = 0; i < QMU_CHECKSUM_LEN; i++) - chksum += data[i]; - - /* Default: HWO=1, @flag[bit0] */ - chksum += 1; - - return 0xFF - chksum; -} - void mtu3_qmu_resume(struct mtu3_ep *mep) { struct mtu3 *mtu = mep->mtu; @@ -260,7 +239,6 @@ static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) if (req->zero) gpd->ext_flag |= GPD_EXT_FLAG_ZLP; - gpd->chksum = qmu_calc_checksum((u8 *)gpd); gpd->flag |= GPD_FLAGS_HWO; mreq->gpd = gpd; @@ -295,7 +273,6 @@ static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma)); ext_addr |= GPD_EXT_NGP(upper_32_bits(enq_dma)); gpd->rx_ext_addr = cpu_to_le16(ext_addr); - gpd->chksum = qmu_calc_checksum((u8 *)gpd); gpd->flag |= GPD_FLAGS_HWO; mreq->gpd = gpd; @@ -323,7 +300,6 @@ int mtu3_qmu_start(struct mtu3_ep *mep) /* set QMU start address */ write_txq_start_addr(mbase, epnum, ring->dma); mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_DMAREQEN); - mtu3_setbits(mbase, U3D_QCR0, QMU_TX_CS_EN(epnum)); /* send zero length packet according to ZLP flag in GPD */ mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum)); mtu3_writel(mbase, U3D_TQERRIESR0, @@ -338,7 +314,6 @@ int mtu3_qmu_start(struct mtu3_ep *mep) } else { write_rxq_start_addr(mbase, epnum, ring->dma); mtu3_setbits(mbase, MU3D_EP_RXCR0(epnum), RX_DMAREQEN); - mtu3_setbits(mbase, U3D_QCR0, QMU_RX_CS_EN(epnum)); /* don't expect ZLP */ mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum)); /* move to next GPD when receive ZLP */ @@ -441,7 +416,6 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) /* by pass the current GDP */ gpd_current->flag |= GPD_FLAGS_BPS; - gpd_current->chksum = qmu_calc_checksum((u8 *)gpd_current); gpd_current->flag |= GPD_FLAGS_HWO; /*enable DMAREQEN, switch back to QMU mode */ -- cgit v1.2.3-59-g8ed1b From 29ae096ef93a9df78a4b86e49ef916afa8e7c34b Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Nov 2018 10:34:33 +0800 Subject: usb: mtu3: enable hardware remote wakeup from L1 automatically Enable hardware remote wakeup from L1 automatically based on the FIFO status, instead of manual way. Signed-off-by: Chunfeng Yun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_core.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 3dce5fd9887d..981e4e8c5c13 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -180,7 +180,7 @@ static void mtu3_intr_enable(struct mtu3 *mtu) mtu3_writel(mbase, U3D_LV1IESR, value); /* Enable U2 common USB interrupts */ - value = SUSPEND_INTR | RESUME_INTR | RESET_INTR | LPM_RESUME_INTR; + value = SUSPEND_INTR | RESUME_INTR | RESET_INTR; mtu3_writel(mbase, U3D_COMMON_USB_INTR_ENABLE, value); if (mtu->is_u3_ip) { @@ -594,6 +594,8 @@ static void mtu3_regs_init(struct mtu3 *mtu) mtu3_writel(mbase, U3D_DEVICE_CONF, 0); /* vbus detected by HW */ mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON); + /* enable automatical HWRW from L1 */ + mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE); } static irqreturn_t mtu3_link_isr(struct mtu3 *mtu) @@ -706,12 +708,6 @@ static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu) if (u2comm & RESET_INTR) mtu3_gadget_reset(mtu); - if (u2comm & LPM_RESUME_INTR) { - if (!(mtu3_readl(mbase, U3D_POWER_MANAGEMENT) & LPM_HRWE)) - mtu3_setbits(mbase, U3D_USB20_MISC_CONTROL, - LPM_U3_ACK_EN); - } - return IRQ_HANDLED; } -- cgit v1.2.3-59-g8ed1b From e802972433f7cee173ec3ffe470d51b39029de9b Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Nov 2018 10:34:34 +0800 Subject: usb: mtu3: fix the issue about SetFeature(U1/U2_Enable) Fix the issue: device doesn't accept LGO_U1/U2: 1. set SW_U1/U2_ACCEPT_ENABLE to eanble controller to accept LGO_U1/U2 by default; 2. enable/disable controller to initiate requests for transition into U1/U2 by SW_U1/U2_REQUEST_ENABLE instead of SW_U1/U2_ACCEPT_ENABLE; Signed-off-by: Chunfeng Yun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_core.c | 4 +++- drivers/usb/mtu3/mtu3_gadget_ep0.c | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 981e4e8c5c13..1ffc0bc31c1d 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -578,8 +578,10 @@ static void mtu3_regs_init(struct mtu3 *mtu) if (mtu->is_u3_ip) { /* disable LGO_U1/U2 by default */ mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL, - SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE | SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE); + /* enable accept LGO_U1/U2 link command from host */ + mtu3_setbits(mbase, U3D_LINK_POWER_CONTROL, + SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE); /* device responses to u3_exit from host automatically */ mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN); /* automatically build U2 link when U3 detect fail */ diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 25216e79cd6e..3c464d8ae023 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -336,9 +336,9 @@ static int ep0_handle_feature_dev(struct mtu3 *mtu, lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); if (set) - lpc |= SW_U1_ACCEPT_ENABLE; + lpc |= SW_U1_REQUEST_ENABLE; else - lpc &= ~SW_U1_ACCEPT_ENABLE; + lpc &= ~SW_U1_REQUEST_ENABLE; mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc); mtu->u1_enable = !!set; @@ -351,9 +351,9 @@ static int ep0_handle_feature_dev(struct mtu3 *mtu, lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); if (set) - lpc |= SW_U2_ACCEPT_ENABLE; + lpc |= SW_U2_REQUEST_ENABLE; else - lpc &= ~SW_U2_ACCEPT_ENABLE; + lpc &= ~SW_U2_REQUEST_ENABLE; mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc); mtu->u2_enable = !!set; -- cgit v1.2.3-59-g8ed1b From 94552090cd188751afdc58a311aa2f3456f5fac0 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Nov 2018 10:34:35 +0800 Subject: usb: mtu3: enable SETUPENDISR interrupt If the controller receives a new SETUP during SETUP data stage, and will generate SETUPENDISR interrupt, the driver should abort the current SETUP command and process the new one. Signed-off-by: Chunfeng Yun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_core.c | 2 +- drivers/usb/mtu3/mtu3_gadget_ep0.c | 6 +++++- drivers/usb/mtu3/mtu3_hw_regs.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 1ffc0bc31c1d..b6b20949d63a 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -484,7 +484,7 @@ void mtu3_ep0_setup(struct mtu3 *mtu) mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr); /* Enable EP0 interrupt */ - mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR); + mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR | SETUPENDISR); } static int mtu3_mem_alloc(struct mtu3 *mtu) diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 3c464d8ae023..7cb7ac980446 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -692,9 +692,13 @@ irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu) mtu3_writel(mbase, U3D_EPISR, int_status); /* W1C */ /* only handle ep0's */ - if (!(int_status & EP0ISR)) + if (!(int_status & (EP0ISR | SETUPENDISR))) return IRQ_NONE; + /* abort current SETUP, and process new one */ + if (int_status & SETUPENDISR) + mtu->ep0_state = MU3D_EP0_STATE_SETUP; + csr = mtu3_readl(mbase, U3D_EP0CSR); dev_dbg(mtu->dev, "%s csr=0x%x\n", __func__, csr); diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index a45bb253939f..d11fcd64c19d 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -104,6 +104,7 @@ /* U3D_EPISR */ #define EPRISR(x) (BIT(16) << (x)) +#define SETUPENDISR BIT(16) #define EPTISR(x) (BIT(0) << (x)) #define EP0ISR BIT(0) -- cgit v1.2.3-59-g8ed1b From 1fab219e65c45cf6e294c757b10e514a15f55f8d Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 29 Nov 2018 10:34:36 +0800 Subject: usb: mtu3: clear SOFTCONN when clear USB3_EN if work as HS mode When the controller supports SS mode, but works as HS mode, the SOFTCONN will not be cleared automatically when clear USB3_EN by default, this cause an issue that can't disconnect from host, so clear SOFTCONN when clear USB3_EN when the class driver want to disable the D+ pullup. Signed-off-by: Chunfeng Yun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_core.c | 2 ++ drivers/usb/mtu3/mtu3_hw_regs.h | 5 +++++ 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index b6b20949d63a..4fee200795a5 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -586,6 +586,8 @@ static void mtu3_regs_init(struct mtu3 *mtu) mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN); /* automatically build U2 link when U3 detect fail */ mtu3_setbits(mbase, U3D_USB2_TEST_MODE, U2U3_AUTO_SWITCH); + /* auto clear SOFT_CONN when clear USB3_EN if work as HS */ + mtu3_setbits(mbase, U3D_U3U2_SWITCH_CTRL, SOFTCON_CLR_AUTO_EN); } mtu3_set_speed(mtu); diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index d11fcd64c19d..1d65b7476f23 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -268,6 +268,8 @@ #define U3D_LTSSM_INTR_ENABLE (SSUSB_USB3_MAC_CSR_BASE + 0x013C) #define U3D_LTSSM_INTR (SSUSB_USB3_MAC_CSR_BASE + 0x0140) +#define U3D_U3U2_SWITCH_CTRL (SSUSB_USB3_MAC_CSR_BASE + 0x0170) + /*---------------- SSUSB_USB3_MAC_CSR FIELD DEFINITION ----------------*/ /* U3D_LTSSM_CTRL */ @@ -302,6 +304,9 @@ #define SS_DISABLE_INTR BIT(1) #define SS_INACTIVE_INTR BIT(0) +/* U3D_U3U2_SWITCH_CTRL */ +#define SOFTCON_CLR_AUTO_EN BIT(0) + /*---------------- SSUSB_USB3_SYS_CSR REGISTER DEFINITION ----------------*/ #define U3D_LINK_UX_INACT_TIMER (SSUSB_USB3_SYS_CSR_BASE + 0x020C) -- cgit v1.2.3-59-g8ed1b From 6abfa0f5bb7cce98c89e2c28fcea31c17200890e Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 15 Nov 2018 19:03:27 -0800 Subject: usb: dwc3: gadget: Report isoc transfer frame number Implement the new frame_number API to report the isochronous interval frame number. This patch checks and reports the interval in which the isoc transfer was transmitted or received via the Isoc-First TRB SOF number field. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/gadget.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 15c07b2b5866..df876418cb78 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -784,6 +784,7 @@ enum dwc3_link_state { #define DWC3_TRB_CTRL_ISP_IMI BIT(10) #define DWC3_TRB_CTRL_IOC BIT(11) #define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14) +#define DWC3_TRB_CTRL_GET_SID_SOFN(n) (((n) & (0xffff << 14)) >> 14) #define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4)) #define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 38d6df98e9ce..e2caf9eec30a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2339,6 +2339,19 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO)) trb->ctrl &= ~DWC3_TRB_CTRL_HWO; + /* + * For isochronous transfers, the first TRB in a service interval must + * have the Isoc-First type. Track and report its interval frame number. + */ + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + (trb->ctrl & DWC3_TRBCTL_ISOCHRONOUS_FIRST)) { + unsigned int frame_number; + + frame_number = DWC3_TRB_CTRL_GET_SID_SOFN(trb->ctrl); + frame_number &= ~(dep->interval - 1); + req->request.frame_number = frame_number; + } + /* * If we're dealing with unaligned size OUT transfer, we will be left * with one TRB pending in the ring. We need to manually clear HWO bit -- cgit v1.2.3-59-g8ed1b From 440da5a30e54deda4ded2a1dc61baf3ee3891f5d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 15 Nov 2018 15:16:19 +0200 Subject: staging: typec: fusb302: Rename fcs,extcon-name to linux,extcon-name Since we are going to use the same in Designware USB 3 driver, rename the property to be consistent across the drivers. No functional change intended. Signed-off-by: Andy Shevchenko Cc: Hans de Goede Cc: Guenter Roeck Acked-by: Hans de Goede Acked-by: Guenter Roeck Reviewed-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel_cht_int33fe.c | 2 +- drivers/usb/typec/tcpm/fusb302.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index 464fe93657b5..87cbf18d0305 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -79,7 +79,7 @@ static const struct property_entry max17047_props[] = { }; static const struct property_entry fusb302_props[] = { - PROPERTY_ENTRY_STRING("fcs,extcon-name", "cht_wcove_pwrsrc"), + PROPERTY_ENTRY_STRING("linux,extcon-name", "cht_wcove_pwrsrc"), PROPERTY_ENTRY_U32("fcs,max-sink-microvolt", 12000000), PROPERTY_ENTRY_U32("fcs,max-sink-microamp", 3000000), PROPERTY_ENTRY_U32("fcs,max-sink-microwatt", 36000000), diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 43b64d9309d0..e9344997329c 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1765,7 +1765,7 @@ static int fusb302_probe(struct i2c_client *client, * to be set by the platform code which also registers the i2c client * for the fusb302. */ - if (device_property_read_string(dev, "fcs,extcon-name", &name) == 0) { + if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) { chip->extcon = extcon_get_extcon_dev(name); if (!chip->extcon) return -EPROBE_DEFER; -- cgit v1.2.3-59-g8ed1b From 67f3a0d0ad7220b9c37b5e6722a821cd7f389b39 Mon Sep 17 00:00:00 2001 From: Alexander Theissen Date: Tue, 4 Dec 2018 23:43:36 +0100 Subject: usb: appledisplay: Set urb transfer_flags to URB_NO_TRANSFER_DMA_MAP The driver does allocate a DMA address with usb_alloc_coherent but did not set the appropriate flag to signal that transfer_dma is set to a valid value. Signed-off-by: Alexander Theissen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/appledisplay.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index 85b48c6ddc7e..730a266cd7b9 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -260,6 +260,7 @@ static int appledisplay_probe(struct usb_interface *iface, usb_rcvintpipe(udev, int_in_endpointAddr), pdata->urbdata, ACD_URB_BUFFER_LEN, appledisplay_complete, pdata, 1); + pdata->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; if (usb_submit_urb(pdata->urb, GFP_KERNEL)) { retval = -EIO; dev_err(&iface->dev, "Submitting URB failed\n"); -- cgit v1.2.3-59-g8ed1b From 3ea5eb139f43360ae2d471e975b82d3fa38929c8 Mon Sep 17 00:00:00 2001 From: Alexander Theissen Date: Tue, 4 Dec 2018 23:43:37 +0100 Subject: usb: appledisplay: Remove unnecessary spinlock The spinlock was inside the urb completion function which is only called once per display and is then resubmitted from this function. There was no other place where this lock was used. Signed-off-by: Alexander Theissen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/appledisplay.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index 730a266cd7b9..8859cd968be9 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -68,7 +68,6 @@ struct appledisplay { struct delayed_work work; int button_pressed; - spinlock_t lock; struct mutex sysfslock; /* concurrent read and write */ }; @@ -78,7 +77,6 @@ static void appledisplay_complete(struct urb *urb) { struct appledisplay *pdata = urb->context; struct device *dev = &pdata->udev->dev; - unsigned long flags; int status = urb->status; int retval; @@ -104,8 +102,6 @@ static void appledisplay_complete(struct urb *urb) goto exit; } - spin_lock_irqsave(&pdata->lock, flags); - switch(pdata->urbdata[1]) { case ACD_BTN_BRIGHT_UP: case ACD_BTN_BRIGHT_DOWN: @@ -118,8 +114,6 @@ static void appledisplay_complete(struct urb *urb) break; } - spin_unlock_irqrestore(&pdata->lock, flags); - exit: retval = usb_submit_urb(pdata->urb, GFP_ATOMIC); if (retval) { @@ -228,7 +222,6 @@ static int appledisplay_probe(struct usb_interface *iface, pdata->udev = udev; - spin_lock_init(&pdata->lock); INIT_DELAYED_WORK(&pdata->work, appledisplay_work); mutex_init(&pdata->sysfslock); -- cgit v1.2.3-59-g8ed1b From 54d48183d21e03f780053d7129312049cb5dd591 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 3 Dec 2018 11:28:47 +0200 Subject: usb: dwc3: trace: add missing break statement to make compiler happy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The missed break statement in the outer switch makes the code fall through always and thus always same value will be printed. Besides that, compiler warns about missed fall through marker: drivers/usb/dwc3/./trace.h: In function ‘trace_raw_output_dwc3_log_trb’: drivers/usb/dwc3/./trace.h:246:4: warning: this statement may fall through [-Wimplicit-fallthrough=] switch (pcm) { ^~~~~~ Add the missing break statement to work correctly without compilation warnings. Fixes: fa8d965d736b ("usb: dwc3: trace: pretty print high-bandwidth transfers too") Cc: Felipe Balbi Signed-off-by: Andy Shevchenko Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/trace.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 50fb6f2d92dd..e97a00593dda 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -251,9 +251,11 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, s = "2x "; break; case 3: + default: s = "3x "; break; } + break; default: s = ""; } s; }), -- cgit v1.2.3-59-g8ed1b From f770e3bc236ee954a3b4052bdf55739e26ee25db Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 7 Dec 2018 03:52:43 +0000 Subject: usb: mtu3: fix dbginfo in qmu_tx_zlp_error_handler Fixes gcc '-Wunused-but-set-variable' warning: drivers/usb/mtu3/mtu3_qmu.c: In function 'qmu_tx_zlp_error_handler': drivers/usb/mtu3/mtu3_qmu.c:385:22: warning: variable 'req' set but not used [-Wunused-but-set-variable] It seems dbginfo original intention is print 'req' other than 'mreq' Acked-by: Chunfeng Yun Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_qmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index 73ac042c45a8..09f19f70fe8f 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -402,7 +402,7 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) return; } - dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq); + dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, req); mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN); -- cgit v1.2.3-59-g8ed1b From d9d1dc817020773c010498f5f81bb49439f7c962 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 5 Dec 2018 11:26:39 -0500 Subject: USB: gadget: udc: s3c2410_udc: convert to DEFINE_SHOW_ATTRIBUTE Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yangtao Li Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/s3c2410_udc.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 8bf5ad7a59ad..af3e63316ace 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -119,7 +119,7 @@ static void dprintk(int level, const char *fmt, ...) } #endif -static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) +static int s3c2410_udc_debugfs_show(struct seq_file *m, void *p) { u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg; u32 ep_int_en_reg, usb_int_en_reg, ep0_csr; @@ -168,20 +168,7 @@ static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) return 0; } - -static int s3c2410_udc_debugfs_fops_open(struct inode *inode, - struct file *file) -{ - return single_open(file, s3c2410_udc_debugfs_seq_show, NULL); -} - -static const struct file_operations s3c2410_udc_debugfs_fops = { - .open = s3c2410_udc_debugfs_fops_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(s3c2410_udc_debugfs); /* io macros */ -- cgit v1.2.3-59-g8ed1b From 3004cfd6204927c1294060b849029cf0c2651074 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 6 Dec 2018 19:42:28 +0100 Subject: Revert "usb: dwc3: pci: Use devm functions to get the phy GPIOs" Commit 211f658b7b40 ("usb: dwc3: pci: Use devm functions to get the phy GPIOs") changed the code to claim the PHY GPIOs permanently for Intel Baytrail devices. This causes issues when the actual PHY driver attempts to claim the same GPIO descriptors. For example, tusb1210 now fails to probe with: tusb1210: probe of dwc3.0.auto.ulpi failed with error -16 (EBUSY) dwc3-pci needs to turn on the PHY once before dwc3 is loaded, but usually the PHY driver will then hold the GPIOs to turn off the PHY when requested (e.g. during suspend). To fix the problem, this reverts the commit to restore the old behavior to put the GPIOs immediately after usage. Link: https://www.spinics.net/lists/linux-usb/msg174681.html Cc: stable@vger.kernel.org Signed-off-by: Stephan Gerhold Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 842795856bf4..fdc6e4e403e8 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -170,20 +170,20 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc) * put the gpio descriptors again here because the phy driver * might want to grab them, too. */ - gpio = devm_gpiod_get_optional(&pdev->dev, "cs", - GPIOD_OUT_LOW); + gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW); if (IS_ERR(gpio)) return PTR_ERR(gpio); gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); - gpio = devm_gpiod_get_optional(&pdev->dev, "reset", - GPIOD_OUT_LOW); + gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(gpio)) return PTR_ERR(gpio); if (gpio) { gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); usleep_range(10000, 11000); } } -- cgit v1.2.3-59-g8ed1b From f1fd62a6b6c62e10ceb97432e9616b575538b699 Mon Sep 17 00:00:00 2001 From: Zeng Tao Date: Fri, 7 Dec 2018 16:19:29 +0200 Subject: xhci: remove the unused sw_lpm_support It is introduced for the pre-0.96 xHC controllers, and the driver only support HW LPM for 1.0 and later controllers.It's not actually used now and is thought not to be used in the future any more, so just remove it. Signed-off-by: Zeng Tao Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 20 ++++---------------- drivers/usb/host/xhci.c | 3 +-- drivers/usb/host/xhci.h | 2 -- 3 files changed, 5 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index b1f27aa38b10..791c5d844d60 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2181,23 +2181,11 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, if (major_revision < 0x03 && xhci->num_ext_caps < max_caps) xhci->ext_caps[xhci->num_ext_caps++] = temp; - /* Check the host's USB2 LPM capability */ - if ((xhci->hci_version == 0x96) && (major_revision != 0x03) && - (temp & XHCI_L1C)) { + if ((xhci->hci_version >= 0x100) && (major_revision != 0x03) && + (temp & XHCI_HLC)) { xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "xHCI 0.96: support USB2 software lpm"); - xhci->sw_lpm_support = 1; - } - - if ((xhci->hci_version >= 0x100) && (major_revision != 0x03)) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "xHCI 1.0: support USB2 software lpm"); - xhci->sw_lpm_support = 1; - if (temp & XHCI_HLC) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "xHCI 1.0: support USB2 hardware lpm"); - xhci->hw_lpm_support = 1; - } + "xHCI 1.0: support USB2 hardware lpm"); + xhci->hw_lpm_support = 1; } port_offset--; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index c928dbbff881..553e97442824 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4370,8 +4370,7 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_hcd *xhci = hcd_to_xhci(hcd); int portnum = udev->portnum - 1; - if (hcd->speed >= HCD_USB3 || !xhci->sw_lpm_support || - !udev->lpm_capable) + if (hcd->speed >= HCD_USB3 || !udev->lpm_capable) return 0; /* we only support lpm for non-hub device connected to root hub yet */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 260b259b72bc..59b8562a2ffe 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1858,8 +1858,6 @@ struct xhci_hcd { struct xhci_port *hw_ports; struct xhci_hub usb2_rhub; struct xhci_hub usb3_rhub; - /* support xHCI 0.96 spec USB2 software LPM */ - unsigned sw_lpm_support:1; /* support xHCI 1.0 spec USB2 hardware LPM */ unsigned hw_lpm_support:1; /* cached usb2 extened protocol capabilites */ -- cgit v1.2.3-59-g8ed1b From f6187f424c10210e8e9917d4b7035ddc935010f6 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 7 Dec 2018 16:19:30 +0200 Subject: xhci: move bus_state structure under the xhci_hub structure. Move the bus_state structure under struct usb_hub. We need a bus_state strucure for each roothub to keep track of suspend related info for each port. Instead of keeping an array of two bus_state structures right under struct xhci, it makes more sense move them to the xhci_hub structure. No functional changes. Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 15 ++++++--------- drivers/usb/host/xhci-mem.c | 10 +++++----- drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.c | 19 ++++++++++--------- drivers/usb/host/xhci.h | 4 ++-- 5 files changed, 24 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 94aca1b5ac8a..5dba0a40acad 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1031,7 +1031,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, rhub = xhci_get_rhub(hcd); ports = rhub->ports; max_ports = rhub->num_ports; - bus_state = &xhci->bus_state[hcd_index(hcd)]; + bus_state = &rhub->bus_state; spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { @@ -1421,7 +1421,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) rhub = xhci_get_rhub(hcd); ports = rhub->ports; max_ports = rhub->num_ports; - bus_state = &xhci->bus_state[hcd_index(hcd)]; + bus_state = &rhub->bus_state; /* Initial status is no changes */ retval = (max_ports + 8) / 8; @@ -1480,7 +1480,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) rhub = xhci_get_rhub(hcd); ports = rhub->ports; max_ports = rhub->num_ports; - bus_state = &xhci->bus_state[hcd_index(hcd)]; + bus_state = &rhub->bus_state; wake_enabled = hcd->self.root_hub->do_remote_wakeup; spin_lock_irqsave(&xhci->lock, flags); @@ -1622,7 +1622,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) rhub = xhci_get_rhub(hcd); ports = rhub->ports; max_ports = rhub->num_ports; - bus_state = &xhci->bus_state[hcd_index(hcd)]; + bus_state = &rhub->bus_state; if (time_before(jiffies, bus_state->next_statechange)) msleep(5); @@ -1723,13 +1723,10 @@ int xhci_bus_resume(struct usb_hcd *hcd) unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd) { - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct xhci_bus_state *bus_state; - - bus_state = &xhci->bus_state[hcd_index(hcd)]; + struct xhci_hub *rhub = xhci_get_rhub(hcd); /* USB3 port wakeups are reported via usb_wakeup_notification() */ - return bus_state->resuming_ports; /* USB2 ports only */ + return rhub->bus_state.resuming_ports; /* USB2 ports only */ } #endif /* CONFIG_PM */ diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 791c5d844d60..36a3eb8849f1 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1922,8 +1922,8 @@ no_bw: xhci->page_size = 0; xhci->page_shift = 0; - xhci->bus_state[0].bus_suspended = 0; - xhci->bus_state[1].bus_suspended = 0; + xhci->usb2_rhub.bus_state.bus_suspended = 0; + xhci->usb3_rhub.bus_state.bus_suspended = 0; } static int xhci_test_trb_in_td(struct xhci_hcd *xhci, @@ -2524,10 +2524,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) for (i = 0; i < MAX_HC_SLOTS; i++) xhci->devs[i] = NULL; for (i = 0; i < USB_MAXCHILDREN; i++) { - xhci->bus_state[0].resume_done[i] = 0; - xhci->bus_state[1].resume_done[i] = 0; + xhci->usb2_rhub.bus_state.resume_done[i] = 0; + xhci->usb3_rhub.bus_state.resume_done[i] = 0; /* Only the USB 2.0 completions will ever be used. */ - init_completion(&xhci->bus_state[1].rexit_done[i]); + init_completion(&xhci->usb2_rhub.bus_state.rexit_done[i]); } if (scratchpad_alloc(xhci, flags)) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 65750582133f..40fa25c4d041 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1593,7 +1593,7 @@ static void handle_port_status(struct xhci_hcd *xhci, } hcd = port->rhub->hcd; - bus_state = &xhci->bus_state[hcd_index(hcd)]; + bus_state = &port->rhub->bus_state; hcd_portnum = port->hcd_portnum; portsc = readl(port->addr); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 553e97442824..6631e7f363b3 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -169,7 +169,7 @@ int xhci_reset(struct xhci_hcd *xhci) { u32 command; u32 state; - int ret, i; + int ret; state = readl(&xhci->op_regs->status); @@ -215,11 +215,12 @@ int xhci_reset(struct xhci_hcd *xhci) ret = xhci_handshake(&xhci->op_regs->status, STS_CNR, 0, 10 * 1000 * 1000); - for (i = 0; i < 2; i++) { - xhci->bus_state[i].port_c_suspend = 0; - xhci->bus_state[i].suspended_ports = 0; - xhci->bus_state[i].resuming_ports = 0; - } + xhci->usb2_rhub.bus_state.port_c_suspend = 0; + xhci->usb2_rhub.bus_state.suspended_ports = 0; + xhci->usb2_rhub.bus_state.resuming_ports = 0; + xhci->usb3_rhub.bus_state.port_c_suspend = 0; + xhci->usb3_rhub.bus_state.suspended_ports = 0; + xhci->usb3_rhub.bus_state.resuming_ports = 0; return ret; } @@ -1069,9 +1070,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) /* Wait a bit if either of the roothubs need to settle from the * transition into bus suspend. */ - if (time_before(jiffies, xhci->bus_state[0].next_statechange) || - time_before(jiffies, - xhci->bus_state[1].next_statechange)) + + if (time_before(jiffies, xhci->usb2_rhub.bus_state.next_statechange) || + time_before(jiffies, xhci->usb3_rhub.bus_state.next_statechange)) msleep(100); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 59b8562a2ffe..b57b7934fae1 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1700,6 +1700,8 @@ struct xhci_hub { struct xhci_port **ports; unsigned int num_ports; struct usb_hcd *hcd; + /* keep track of bus suspend info */ + struct xhci_bus_state bus_state; /* supported prococol extended capabiliy values */ u8 maj_rev; u8 min_rev; @@ -1853,8 +1855,6 @@ struct xhci_hcd { unsigned int num_active_eps; unsigned int limit_active_eps; - /* There are two roothubs to keep track of bus suspend info for */ - struct xhci_bus_state bus_state[2]; struct xhci_port *hw_ports; struct xhci_hub usb2_rhub; struct xhci_hub usb3_rhub; -- cgit v1.2.3-59-g8ed1b From 1c2d81cc287c59161b19c5810f0091fe33448e07 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 7 Dec 2018 16:19:31 +0200 Subject: xhci: remove unused hcd_index() Now that each root hub has their own bus_state strucure the hcd_undex() used to get the correct bus_state strucure is no longer needed. No functional changes Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index b57b7934fae1..3c6b5049464a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1682,13 +1682,6 @@ struct xhci_bus_state { */ #define XHCI_MAX_REXIT_TIMEOUT_MS 20 -static inline unsigned int hcd_index(struct usb_hcd *hcd) -{ - if (hcd->speed >= HCD_USB3) - return 0; - else - return 1; -} struct xhci_port { __le32 __iomem *addr; int hw_portnum; -- cgit v1.2.3-59-g8ed1b From 5f78a54f8d31e86aedd50f5b4d148dfeabafe88a Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 7 Dec 2018 16:19:32 +0200 Subject: xhci: move usb3 speficic bits to own function in get_port_status call refactoring, no functional changes Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 68 ++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 5dba0a40acad..60aeff3e154b 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -818,6 +818,41 @@ static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li) return ext_stat; } +static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status, + u32 portsc) +{ + struct xhci_hcd *xhci; + u32 link_state; + u32 portnum; + + xhci = hcd_to_xhci(port->rhub->hcd); + link_state = portsc & PORT_PLS_MASK; + portnum = port->hcd_portnum; + + /* USB3 specific wPortChange bits + * + * Port link change with port in resume state should not be + * reported to usbcore, as this is an internal state to be + * handled by xhci driver. Reporting PLC to usbcore may + * cause usbcore clearing PLC first and port change event + * irq won't be generated. + */ + + if (portsc & PORT_PLC && (link_state != XDEV_RESUME)) + *status |= USB_PORT_STAT_C_LINK_STATE << 16; + if (portsc & PORT_WRC) + *status |= USB_PORT_STAT_C_BH_RESET << 16; + if (portsc & PORT_CEC) + *status |= USB_PORT_STAT_C_CONFIG_ERROR << 16; + + /* USB3 specific wPortStatus bits */ + if (portsc & PORT_POWER) + *status |= USB_SS_PORT_STAT_POWER; + + xhci_hub_report_usb3_link_state(xhci, status, portsc); + xhci_del_comp_mod_timer(xhci, portsc, portnum); +} + /* * Converts a raw xHCI port status into the format that external USB 2.0 or USB * 3.0 hubs use. @@ -854,22 +889,8 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, if ((raw_port_status & PORT_RC)) status |= USB_PORT_STAT_C_RESET << 16; /* USB3.0 only */ - if (hcd->speed >= HCD_USB3) { - /* Port link change with port in resume state should not be - * reported to usbcore, as this is an internal state to be - * handled by xhci driver. Reporting PLC to usbcore may - * cause usbcore clearing PLC first and port change event - * irq won't be generated. - */ - if ((raw_port_status & PORT_PLC) && - (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) - status |= USB_PORT_STAT_C_LINK_STATE << 16; - if ((raw_port_status & PORT_WRC)) - status |= USB_PORT_STAT_C_BH_RESET << 16; - if ((raw_port_status & PORT_CEC)) - status |= USB_PORT_STAT_C_CONFIG_ERROR << 16; - } - + if (hcd->speed >= HCD_USB3) + xhci_get_usb3_port_status(port, &status, raw_port_status); if (hcd->speed < HCD_USB3) { if ((raw_port_status & PORT_PLS_MASK) == XDEV_U3 && (raw_port_status & PORT_POWER)) @@ -989,22 +1010,13 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, if (raw_port_status & PORT_RESET) status |= USB_PORT_STAT_RESET; if (raw_port_status & PORT_POWER) { - if (hcd->speed >= HCD_USB3) - status |= USB_SS_PORT_STAT_POWER; - else + if (hcd->speed < HCD_USB3) status |= USB_PORT_STAT_POWER; } /* Update Port Link State */ - if (hcd->speed >= HCD_USB3) { - xhci_hub_report_usb3_link_state(xhci, &status, raw_port_status); - /* - * Verify if all USB3 Ports Have entered U0 already. - * Delete Compliance Mode Timer if so. - */ - xhci_del_comp_mod_timer(xhci, raw_port_status, wIndex); - } else { + if (hcd->speed < HCD_USB3) xhci_hub_report_usb2_link_state(&status, raw_port_status); - } + if (bus_state->port_c_suspend & (1 << wIndex)) status |= USB_PORT_STAT_C_SUSPEND << 16; -- cgit v1.2.3-59-g8ed1b From 70e9b53dfedced674d054166aae6e0366489eb86 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 7 Dec 2018 16:19:33 +0200 Subject: xhci: move usb2 speficic bits to own function in get_port_status call Mostly refactoring, with the exception that USB_PORT_STAT_L1 link state is reported if xhci port link is in U2 AND port is powered. Previously we did not check if the port was powered, but according to xhci spec 4.19.1.1.6 All the 'Enabled' states, including USB_PORT_STAT_L1 (U2), U1, U0 and U3 must have Port power bit set. Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 60aeff3e154b..5af4f90489a0 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -714,13 +714,6 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port, } } -/* Updates Link Status for USB 2.1 port */ -static void xhci_hub_report_usb2_link_state(u32 *status, u32 status_reg) -{ - if ((status_reg & PORT_PLS_MASK) == XDEV_U2) - *status |= USB_PORT_STAT_L1; -} - /* Updates Link Status for super Speed port */ static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, u32 *status, u32 status_reg) @@ -853,6 +846,25 @@ static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status, xhci_del_comp_mod_timer(xhci, portsc, portnum); } +static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, + u32 portsc) +{ + u32 link_state; + + link_state = portsc & PORT_PLS_MASK; + + /* USB2 wPortStatus bits */ + if (portsc & PORT_POWER) { + *status |= USB_PORT_STAT_POWER; + + /* link state is only valid if port is powered */ + if (link_state == XDEV_U3) + *status |= USB_PORT_STAT_SUSPEND; + if (link_state == XDEV_U2) + *status |= USB_PORT_STAT_L1; + } +} + /* * Converts a raw xHCI port status into the format that external USB 2.0 or USB * 3.0 hubs use. @@ -888,14 +900,13 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, status |= USB_PORT_STAT_C_OVERCURRENT << 16; if ((raw_port_status & PORT_RC)) status |= USB_PORT_STAT_C_RESET << 16; - /* USB3.0 only */ + + /* USB2 and USB3 specific bits including Port Link State */ if (hcd->speed >= HCD_USB3) xhci_get_usb3_port_status(port, &status, raw_port_status); - if (hcd->speed < HCD_USB3) { - if ((raw_port_status & PORT_PLS_MASK) == XDEV_U3 - && (raw_port_status & PORT_POWER)) - status |= USB_PORT_STAT_SUSPEND; - } + else + xhci_get_usb2_port_status(port, &status, raw_port_status); + if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME && !DEV_SUPERSPEED_ANY(raw_port_status) && hcd->speed < HCD_USB3) { if ((raw_port_status & PORT_RESET) || @@ -1009,13 +1020,6 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, status |= USB_PORT_STAT_OVERCURRENT; if (raw_port_status & PORT_RESET) status |= USB_PORT_STAT_RESET; - if (raw_port_status & PORT_POWER) { - if (hcd->speed < HCD_USB3) - status |= USB_PORT_STAT_POWER; - } - /* Update Port Link State */ - if (hcd->speed < HCD_USB3) - xhci_hub_report_usb2_link_state(&status, raw_port_status); if (bus_state->port_c_suspend & (1 << wIndex)) status |= USB_PORT_STAT_C_SUSPEND << 16; -- cgit v1.2.3-59-g8ed1b From 3c2ddb449a91df849b65be7509a575930c7eb5eb Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 7 Dec 2018 16:19:34 +0200 Subject: xhci: cleanup code that sets portstatus and portchange bits Group the code where the wPortstatus and wPortChange bits are set into one place. No functional changes Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 5af4f90489a0..c0de2d017d39 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -891,7 +891,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, rhub = xhci_get_rhub(hcd); port = rhub->ports[wIndex]; - /* wPortChange bits */ + /* common wPortChange bits */ if (raw_port_status & PORT_CSC) status |= USB_PORT_STAT_C_CONNECTION << 16; if (raw_port_status & PORT_PEC) @@ -901,7 +901,19 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, if ((raw_port_status & PORT_RC)) status |= USB_PORT_STAT_C_RESET << 16; - /* USB2 and USB3 specific bits including Port Link State */ + /* common wPortStatus bits */ + if (raw_port_status & PORT_CONNECT) { + status |= USB_PORT_STAT_CONNECTION; + status |= xhci_port_speed(raw_port_status); + } + if (raw_port_status & PORT_PE) + status |= USB_PORT_STAT_ENABLE; + if (raw_port_status & PORT_OC) + status |= USB_PORT_STAT_OVERCURRENT; + if (raw_port_status & PORT_RESET) + status |= USB_PORT_STAT_RESET; + + /* USB2 and USB3 specific bits, including Port Link State */ if (hcd->speed >= HCD_USB3) xhci_get_usb3_port_status(port, &status, raw_port_status); else @@ -1010,16 +1022,6 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, bus_state->resume_done[wIndex] = 0; clear_bit(wIndex, &bus_state->resuming_ports); } - if (raw_port_status & PORT_CONNECT) { - status |= USB_PORT_STAT_CONNECTION; - status |= xhci_port_speed(raw_port_status); - } - if (raw_port_status & PORT_PE) - status |= USB_PORT_STAT_ENABLE; - if (raw_port_status & PORT_OC) - status |= USB_PORT_STAT_OVERCURRENT; - if (raw_port_status & PORT_RESET) - status |= USB_PORT_STAT_RESET; if (bus_state->port_c_suspend & (1 << wIndex)) status |= USB_PORT_STAT_C_SUSPEND << 16; -- cgit v1.2.3-59-g8ed1b From a231ec41e6f6433daf4c693f169f6c5cfda8cb9d Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 7 Dec 2018 16:19:35 +0200 Subject: xhci: refactor U0 link state handling in get_port_status Move U0 link state handing to USB3 and USB2 specific functions Note that bus_state->resuming_ports: bus_state->resume_done[]: are only used for USB2, and don't need to cleared for USB3 ports No functional changes Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index c0de2d017d39..d86d1d50bfd2 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -814,10 +814,12 @@ static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li) static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status, u32 portsc) { + struct xhci_bus_state *bus_state; struct xhci_hcd *xhci; u32 link_state; u32 portnum; + bus_state = &port->rhub->bus_state; xhci = hcd_to_xhci(port->rhub->hcd); link_state = portsc & PORT_PLS_MASK; portnum = port->hcd_portnum; @@ -839,8 +841,12 @@ static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status, *status |= USB_PORT_STAT_C_CONFIG_ERROR << 16; /* USB3 specific wPortStatus bits */ - if (portsc & PORT_POWER) + if (portsc & PORT_POWER) { *status |= USB_SS_PORT_STAT_POWER; + /* link state handling */ + if (link_state == XDEV_U0) + bus_state->suspended_ports &= ~(1 << portnum); + } xhci_hub_report_usb3_link_state(xhci, status, portsc); xhci_del_comp_mod_timer(xhci, portsc, portnum); @@ -849,9 +855,13 @@ static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status, static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, u32 portsc) { + struct xhci_bus_state *bus_state; u32 link_state; + u32 portnum; + bus_state = &port->rhub->bus_state; link_state = portsc & PORT_PLS_MASK; + portnum = port->hcd_portnum; /* USB2 wPortStatus bits */ if (portsc & PORT_POWER) { @@ -862,6 +872,14 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, *status |= USB_PORT_STAT_SUSPEND; if (link_state == XDEV_U2) *status |= USB_PORT_STAT_L1; + if (link_state == XDEV_U0) { + bus_state->resume_done[portnum] = 0; + clear_bit(portnum, &bus_state->resuming_ports); + if (bus_state->suspended_ports & (1 << portnum)) { + bus_state->suspended_ports &= ~(1 << portnum); + bus_state->port_c_suspend |= 1 << portnum; + } + } } } @@ -1011,18 +1029,6 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, usb_hcd_end_port_resume(&hcd->self, wIndex); } - - if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 && - (raw_port_status & PORT_POWER)) { - if (bus_state->suspended_ports & (1 << wIndex)) { - bus_state->suspended_ports &= ~(1 << wIndex); - if (hcd->speed < HCD_USB3) - bus_state->port_c_suspend |= 1 << wIndex; - } - bus_state->resume_done[wIndex] = 0; - clear_bit(wIndex, &bus_state->resuming_ports); - } - if (bus_state->port_c_suspend & (1 << wIndex)) status |= USB_PORT_STAT_C_SUSPEND << 16; -- cgit v1.2.3-59-g8ed1b From e67ebf1b3815b2d1fc505dba182761c0be6c179d Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 7 Dec 2018 16:19:36 +0200 Subject: xhci: move usb2 get port status link resume handling to its own function Refactoring, no functional changes. But worth mentioning that checking for port link resume state is now behind a additional port power check. This is fine as ports can't be in resume state if port power bit is not set. xhci spec section 4.19.1.1.6 figure 34 shows that port power bit must be set for all 'Enable' substates, including U0,U1,U2,U3 (suspended), Resume, and RExit states. Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 188 ++++++++++++++++++++++++-------------------- 1 file changed, 104 insertions(+), 84 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index d86d1d50bfd2..c7601750c37f 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -795,6 +795,100 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, } } +static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, + u32 *status, u32 portsc, + unsigned long flags) +{ + struct xhci_bus_state *bus_state; + struct xhci_hcd *xhci; + struct usb_hcd *hcd; + int slot_id; + u32 wIndex; + + hcd = port->rhub->hcd; + bus_state = &port->rhub->bus_state; + xhci = hcd_to_xhci(hcd); + wIndex = port->hcd_portnum; + + if ((portsc & PORT_RESET) || !(portsc & PORT_PE)) { + *status = 0xffffffff; + return -EINVAL; + } + /* did port event handler already start resume timing? */ + if (!bus_state->resume_done[wIndex]) { + /* If not, maybe we are in a host initated resume? */ + if (test_bit(wIndex, &bus_state->resuming_ports)) { + /* Host initated resume doesn't time the resume + * signalling using resume_done[]. + * It manually sets RESUME state, sleeps 20ms + * and sets U0 state. This should probably be + * changed, but not right now. + */ + } else { + /* port resume was discovered now and here, + * start resume timing + */ + unsigned long timeout = jiffies + + msecs_to_jiffies(USB_RESUME_TIMEOUT); + + set_bit(wIndex, &bus_state->resuming_ports); + bus_state->resume_done[wIndex] = timeout; + mod_timer(&hcd->rh_timer, timeout); + usb_hcd_start_port_resume(&hcd->self, wIndex); + } + /* Has resume been signalled for USB_RESUME_TIME yet? */ + } else if (time_after_eq(jiffies, bus_state->resume_done[wIndex])) { + int time_left; + + xhci_dbg(xhci, "Resume USB2 port %d\n", wIndex + 1); + bus_state->resume_done[wIndex] = 0; + clear_bit(wIndex, &bus_state->resuming_ports); + + set_bit(wIndex, &bus_state->rexit_ports); + + xhci_test_and_clear_bit(xhci, port, PORT_PLC); + xhci_set_link_state(xhci, port, XDEV_U0); + + spin_unlock_irqrestore(&xhci->lock, flags); + time_left = wait_for_completion_timeout( + &bus_state->rexit_done[wIndex], + msecs_to_jiffies(XHCI_MAX_REXIT_TIMEOUT_MS)); + spin_lock_irqsave(&xhci->lock, flags); + + if (time_left) { + slot_id = xhci_find_slot_id_by_port(hcd, xhci, + wIndex + 1); + if (!slot_id) { + xhci_dbg(xhci, "slot_id is zero\n"); + *status = 0xffffffff; + return -ENODEV; + } + xhci_ring_device(xhci, slot_id); + } else { + int port_status = readl(port->addr); + + xhci_warn(xhci, "Port resume %i msec timed out, portsc = 0x%x\n", + XHCI_MAX_REXIT_TIMEOUT_MS, + port_status); + *status |= USB_PORT_STAT_SUSPEND; + clear_bit(wIndex, &bus_state->rexit_ports); + } + + usb_hcd_end_port_resume(&hcd->self, wIndex); + bus_state->port_c_suspend |= 1 << wIndex; + bus_state->suspended_ports &= ~(1 << wIndex); + } else { + /* + * The resume has been signaling for less than + * USB_RESUME_TIME. Report the port status as SUSPEND, + * let the usbcore check port status again and clear + * resume signaling later. + */ + *status |= USB_PORT_STAT_SUSPEND; + } + return 0; +} + static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li) { u32 ext_stat = 0; @@ -853,11 +947,12 @@ static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status, } static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, - u32 portsc) + u32 portsc, unsigned long flags) { struct xhci_bus_state *bus_state; u32 link_state; u32 portnum; + int ret; bus_state = &port->rhub->bus_state; link_state = portsc & PORT_PLS_MASK; @@ -880,6 +975,12 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, bus_state->port_c_suspend |= 1 << portnum; } } + if (link_state == XDEV_RESUME) { + ret = xhci_handle_usb2_port_link_resume(port, status, + portsc, flags); + if (ret) + return; + } } } @@ -900,9 +1001,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, __releases(&xhci->lock) __acquires(&xhci->lock) { - struct xhci_hcd *xhci = hcd_to_xhci(hcd); u32 status = 0; - int slot_id; struct xhci_hub *rhub; struct xhci_port *port; @@ -935,87 +1034,8 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, if (hcd->speed >= HCD_USB3) xhci_get_usb3_port_status(port, &status, raw_port_status); else - xhci_get_usb2_port_status(port, &status, raw_port_status); - - if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME && - !DEV_SUPERSPEED_ANY(raw_port_status) && hcd->speed < HCD_USB3) { - if ((raw_port_status & PORT_RESET) || - !(raw_port_status & PORT_PE)) - return 0xffffffff; - /* did port event handler already start resume timing? */ - if (!bus_state->resume_done[wIndex]) { - /* If not, maybe we are in a host initated resume? */ - if (test_bit(wIndex, &bus_state->resuming_ports)) { - /* Host initated resume doesn't time the resume - * signalling using resume_done[]. - * It manually sets RESUME state, sleeps 20ms - * and sets U0 state. This should probably be - * changed, but not right now. - */ - } else { - /* port resume was discovered now and here, - * start resume timing - */ - unsigned long timeout = jiffies + - msecs_to_jiffies(USB_RESUME_TIMEOUT); - - set_bit(wIndex, &bus_state->resuming_ports); - bus_state->resume_done[wIndex] = timeout; - mod_timer(&hcd->rh_timer, timeout); - usb_hcd_start_port_resume(&hcd->self, wIndex); - } - /* Has resume been signalled for USB_RESUME_TIME yet? */ - } else if (time_after_eq(jiffies, - bus_state->resume_done[wIndex])) { - int time_left; - - xhci_dbg(xhci, "Resume USB2 port %d\n", - wIndex + 1); - bus_state->resume_done[wIndex] = 0; - clear_bit(wIndex, &bus_state->resuming_ports); - - set_bit(wIndex, &bus_state->rexit_ports); - - xhci_test_and_clear_bit(xhci, port, PORT_PLC); - xhci_set_link_state(xhci, port, XDEV_U0); - - spin_unlock_irqrestore(&xhci->lock, flags); - time_left = wait_for_completion_timeout( - &bus_state->rexit_done[wIndex], - msecs_to_jiffies( - XHCI_MAX_REXIT_TIMEOUT_MS)); - spin_lock_irqsave(&xhci->lock, flags); - - if (time_left) { - slot_id = xhci_find_slot_id_by_port(hcd, - xhci, wIndex + 1); - if (!slot_id) { - xhci_dbg(xhci, "slot_id is zero\n"); - return 0xffffffff; - } - xhci_ring_device(xhci, slot_id); - } else { - int port_status = readl(port->addr); - xhci_warn(xhci, "Port resume took longer than %i msec, port status = 0x%x\n", - XHCI_MAX_REXIT_TIMEOUT_MS, - port_status); - status |= USB_PORT_STAT_SUSPEND; - clear_bit(wIndex, &bus_state->rexit_ports); - } - - usb_hcd_end_port_resume(&hcd->self, wIndex); - bus_state->port_c_suspend |= 1 << wIndex; - bus_state->suspended_ports &= ~(1 << wIndex); - } else { - /* - * The resume has been signaling for less than - * USB_RESUME_TIME. Report the port status as SUSPEND, - * let the usbcore check port status again and clear - * resume signaling later. - */ - status |= USB_PORT_STAT_SUSPEND; - } - } + xhci_get_usb2_port_status(port, &status, raw_port_status, + flags); /* * Clear stale usb2 resume signalling variables in case port changed * state during resume signalling. For example on error -- cgit v1.2.3-59-g8ed1b From cc10ce0c51b13d1566d0ec1dcb472fb86330b391 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 9 Dec 2018 20:01:29 +0100 Subject: usb: dwc2: disable power_down on Amlogic devices Disable power_down by setting the parameter to DWC2_POWER_DOWN_PARAM_NONE. This fixes a problem on various Amlogic Meson SoCs where USB devices are only recognized when plugged in before booting Linux. A hot-plugged USB device was not detected even though the device got power (my USB thumb drive for example has an LED which lit up). A similar fix was implemented for Rockchip SoCs in commit c216765d3a1def ("usb: dwc2: disable power_down on rockchip devices"). That commit suggests that a change in the dwc2 driver is the cause because the default value for the "hibernate" parameter (which then got renamed to "power_down" to support other modes) was changed in the v4.17 merge window with: commit 6d23ee9caa6790 ("Merge tag 'usb-for-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-testing"). Cc: # 4.19 Acked-by: Minas Harutyunyan Suggested-by: Christian Hewitt Signed-off-by: Martin Blumenstingl Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 266157ae179a..24ff5f21cb25 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -118,6 +118,7 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg) p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI; p->ahbcfg = GAHBCFG_HBSTLEN_INCR8 << GAHBCFG_HBSTLEN_SHIFT; + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; } static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg) -- cgit v1.2.3-59-g8ed1b From 01688a6d66b5aaa06f49d4d1336a9ee641fe3c46 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 7 Dec 2018 02:50:42 +0000 Subject: USB: serial: mos7840: remove set but not used variables 'number, serial' Fixes gcc '-Wunused-but-set-variable' warning: drivers/usb/serial/mos7840.c: In function 'mos7840_send_cmd_write_baud_rate': drivers/usb/serial/mos7840.c:1584:16: warning: variable 'number' set but not used [-Wunused-but-set-variable] drivers/usb/serial/mos7840.c: In function 'mos7840_change_port_settings': drivers/usb/serial/mos7840.c:1695:21: warning: variable 'serial' set but not used [-Wunused-but-set-variable] 'number' never used since introduction in commit 3f5429746d91 ("USB: Moschip 7840 USB-Serial Driver") 'serial' not used since commit 5833041f1b13 ("USB: serial: remove unnecessary reinitialisations of urb->dev") Signed-off-by: YueHaibing Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 6ff6d67b6d5a..a698d46ba773 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1581,7 +1581,6 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, int divisor = 0; int status; __u16 Data; - unsigned char number; __u16 clk_sel_val; struct usb_serial_port *port; @@ -1595,8 +1594,6 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, if (mos7840_serial_paranoia_check(port->serial, __func__)) return -1; - number = mos7840_port->port->port_number; - dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudRate); /* reset clk_uart_sel in spregOffset */ if (baudRate > 115200) { @@ -1692,7 +1689,6 @@ static void mos7840_change_port_settings(struct tty_struct *tty, int status; __u16 Data; struct usb_serial_port *port; - struct usb_serial *serial; if (mos7840_port == NULL) return; @@ -1705,8 +1701,6 @@ static void mos7840_change_port_settings(struct tty_struct *tty, if (mos7840_serial_paranoia_check(port->serial, __func__)) return; - serial = port->serial; - if (!mos7840_port->open) { dev_dbg(&port->dev, "%s - port not opened\n", __func__); return; -- cgit v1.2.3-59-g8ed1b From 244add8ebfb231c39db9e33b204bd0ce8f24f782 Mon Sep 17 00:00:00 2001 From: Tejas Joglekar Date: Mon, 10 Dec 2018 16:08:13 +0530 Subject: usb: dwc3: gadget: Disable CSP for stream OUT ep In stream mode, when fast-forwarding TRBs, the stream number is not cleared causing the new stream to not get assigned. So we don't want controller to carry on transfers when short packet is received. So disable the CSP for stream capable endpoint. This is based on the 3.30a Programming guide, where table 3-1 device descriptor structure field definitions says for CSP bit If this bit is 0, the controller generates an XferComplete event and remove the stream. So if we keep CSP as 1 then switching between streams would not happen as in stream mode, when fast-forwarding TRBs, the stream number is not cleared causing the new stream to not get assigned. Signed-off-by: Tejas Joglekar Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e2caf9eec30a..2ecde30ad0b7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -986,9 +986,13 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, usb_endpoint_type(dep->endpoint.desc)); } - /* always enable Continue on Short Packet */ + /* + * Enable Continue on Short Packet + * when endpoint is not a stream capable + */ if (usb_endpoint_dir_out(dep->endpoint.desc)) { - trb->ctrl |= DWC3_TRB_CTRL_CSP; + if (!dep->stream_capable) + trb->ctrl |= DWC3_TRB_CTRL_CSP; if (short_not_ok) trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; -- cgit v1.2.3-59-g8ed1b From 014abe34a9095daaa6cbb2693ee90bbb54674693 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 15 Oct 2018 17:02:57 +0800 Subject: usb: chipidea: add flag for imx hsic implementation NXP (Freecale) imx HSIC design has some special requirements, add some flags at host code to handle them. Reviewed-by: Frieder Schrempf Tested-by: Frieder Schrempf Signed-off-by: Peter Chen --- drivers/usb/chipidea/host.c | 17 +++++++++++++++++ include/linux/usb/chipidea.h | 3 +++ 2 files changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index d858a82c4f44..028a3574266a 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -170,6 +170,11 @@ static int host_start(struct ci_hdrc *ci) otg->host = &hcd->self; hcd->self.otg_port = 1; } + + if (ci->platdata->notify_event && + (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC)) + ci->platdata->notify_event + (ci, CI_HDRC_IMX_HSIC_ACTIVE_EVENT); } return ret; @@ -218,6 +223,8 @@ void ci_hdrc_host_destroy(struct ci_hdrc *ci) static int ci_ehci_bus_suspend(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct device *dev = hcd->self.controller; + struct ci_hdrc *ci = dev_get_drvdata(dev); int port; u32 tmp; @@ -249,6 +256,16 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd) * It needs a short delay between set RS bit and PHCD. */ usleep_range(150, 200); + /* + * Need to clear WKCN and WKOC for imx HSIC, + * otherwise, there will be wakeup event. + */ + if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) { + tmp = ehci_readl(ehci, reg); + tmp &= ~(PORT_WKDISC_E | PORT_WKCONN_E); + ehci_writel(ehci, tmp, reg); + } + break; } } diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index 63758c399e4e..911e05af671e 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -60,9 +60,12 @@ struct ci_hdrc_platform_data { #define CI_HDRC_OVERRIDE_RX_BURST BIT(11) #define CI_HDRC_OVERRIDE_PHY_CONTROL BIT(12) /* Glue layer manages phy */ #define CI_HDRC_REQUIRES_ALIGNED_DMA BIT(13) +#define CI_HDRC_IMX_IS_HSIC BIT(14) enum usb_dr_mode dr_mode; #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 +#define CI_HDRC_IMX_HSIC_ACTIVE_EVENT 2 +#define CI_HDRC_IMX_HSIC_SUSPEND_EVENT 3 int (*notify_event) (struct ci_hdrc *ci, unsigned event); struct regulator *reg_vbus; struct usb_otg_caps ci_otg_caps; -- cgit v1.2.3-59-g8ed1b From 7c8e8909417eb6342ac487dc5ab3076d46718f71 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 16 Oct 2018 09:17:02 +0800 Subject: usb: chipidea: imx: add HSIC support To support imx HSIC, there are some special requirement: - The HSIC pad is 1.2v, it may need to supply from external - The data/strobe pin needs to be pulled down first, and after host mode is initialized, the strobe pin needs to be pulled up - During the USB suspend/resume, special setting is needed Reviewed-by: Frieder Schrempf Tested-by: Frieder Schrempf Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_imx.c | 140 ++++++++++++++++++++++++++++++++----- drivers/usb/chipidea/ci_hdrc_imx.h | 9 ++- drivers/usb/chipidea/usbmisc_imx.c | 140 +++++++++++++++++++++++++++++++++++++ 3 files changed, 270 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 09b37c0d075d..56781c329db0 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "ci.h" #include "ci_hdrc_imx.h" @@ -85,6 +86,9 @@ struct ci_hdrc_imx_data { bool supports_runtime_pm; bool override_phy_control; bool in_lpm; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_hsic_active; + struct regulator *hsic_pad_regulator; /* SoC before i.mx6 (except imx23/imx28) needs three clks */ bool need_three_clks; struct clk *clk_ipg; @@ -245,19 +249,49 @@ static void imx_disable_unprepare_clks(struct device *dev) } } +static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) +{ + struct device *dev = ci->dev->parent; + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + int ret = 0; + + switch (event) { + case CI_HDRC_IMX_HSIC_ACTIVE_EVENT: + ret = pinctrl_select_state(data->pinctrl, + data->pinctrl_hsic_active); + if (ret) + dev_err(dev, "hsic_active select failed, err=%d\n", + ret); + break; + case CI_HDRC_IMX_HSIC_SUSPEND_EVENT: + ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data); + if (ret) + dev_err(dev, + "hsic_set_connect failed, err=%d\n", ret); + break; + default: + break; + } + + return ret; +} + static int ci_hdrc_imx_probe(struct platform_device *pdev) { struct ci_hdrc_imx_data *data; struct ci_hdrc_platform_data pdata = { .name = dev_name(&pdev->dev), .capoffset = DEF_CAPOFFSET, + .notify_event = ci_hdrc_imx_notify_event, }; int ret; const struct of_device_id *of_id; const struct ci_hdrc_imx_platform_flag *imx_platform_flag; struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct pinctrl_state *pinctrl_hsic_idle; - of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); + of_id = of_match_device(ci_hdrc_imx_dt_ids, dev); if (!of_id) return -ENODEV; @@ -268,19 +302,73 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, data); - data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); + data->usbmisc_data = usbmisc_get_init_data(dev); if (IS_ERR(data->usbmisc_data)) return PTR_ERR(data->usbmisc_data); - ret = imx_get_clks(&pdev->dev); + if (of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) { + pdata.flags |= CI_HDRC_IMX_IS_HSIC; + data->usbmisc_data->hsic = 1; + data->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(data->pinctrl)) { + dev_err(dev, "pinctrl get failed, err=%ld\n", + PTR_ERR(data->pinctrl)); + return PTR_ERR(data->pinctrl); + } + + pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle"); + if (IS_ERR(pinctrl_hsic_idle)) { + dev_err(dev, + "pinctrl_hsic_idle lookup failed, err=%ld\n", + PTR_ERR(pinctrl_hsic_idle)); + return PTR_ERR(pinctrl_hsic_idle); + } + + ret = pinctrl_select_state(data->pinctrl, pinctrl_hsic_idle); + if (ret) { + dev_err(dev, "hsic_idle select failed, err=%d\n", ret); + return ret; + } + + data->pinctrl_hsic_active = pinctrl_lookup_state(data->pinctrl, + "active"); + if (IS_ERR(data->pinctrl_hsic_active)) { + dev_err(dev, + "pinctrl_hsic_active lookup failed, err=%ld\n", + PTR_ERR(data->pinctrl_hsic_active)); + return PTR_ERR(data->pinctrl_hsic_active); + } + + data->hsic_pad_regulator = devm_regulator_get(dev, "hsic"); + if (PTR_ERR(data->hsic_pad_regulator) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { + /* no pad regualator is needed */ + data->hsic_pad_regulator = NULL; + } else if (IS_ERR(data->hsic_pad_regulator)) { + dev_err(dev, "Get HSIC pad regulator error: %ld\n", + PTR_ERR(data->hsic_pad_regulator)); + return PTR_ERR(data->hsic_pad_regulator); + } + + if (data->hsic_pad_regulator) { + ret = regulator_enable(data->hsic_pad_regulator); + if (ret) { + dev_err(dev, + "Failed to enable HSIC pad regulator\n"); + return ret; + } + } + } + ret = imx_get_clks(dev); if (ret) - return ret; + goto disable_hsic_regulator; - ret = imx_prepare_enable_clks(&pdev->dev); + ret = imx_prepare_enable_clks(dev); if (ret) - return ret; + goto disable_hsic_regulator; - data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); + data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0); if (IS_ERR(data->phy)) { ret = PTR_ERR(data->phy); /* Return -EINVAL if no usbphy is available */ @@ -305,40 +393,43 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) ret = imx_usbmisc_init(data->usbmisc_data); if (ret) { - dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); + dev_err(dev, "usbmisc init failed, ret=%d\n", ret); goto err_clk; } - data->ci_pdev = ci_hdrc_add_device(&pdev->dev, + data->ci_pdev = ci_hdrc_add_device(dev, pdev->resource, pdev->num_resources, &pdata); if (IS_ERR(data->ci_pdev)) { ret = PTR_ERR(data->ci_pdev); if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "ci_hdrc_add_device failed, err=%d\n", ret); + dev_err(dev, "ci_hdrc_add_device failed, err=%d\n", + ret); goto err_clk; } ret = imx_usbmisc_init_post(data->usbmisc_data); if (ret) { - dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret); + dev_err(dev, "usbmisc post failed, ret=%d\n", ret); goto disable_device; } if (data->supports_runtime_pm) { - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); } - device_set_wakeup_capable(&pdev->dev, true); + device_set_wakeup_capable(dev, true); return 0; disable_device: ci_hdrc_remove_device(data->ci_pdev); err_clk: - imx_disable_unprepare_clks(&pdev->dev); + imx_disable_unprepare_clks(dev); +disable_hsic_regulator: + if (data->hsic_pad_regulator) + ret = regulator_disable(data->hsic_pad_regulator); return ret; } @@ -355,6 +446,8 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) if (data->override_phy_control) usb_phy_shutdown(data->phy); imx_disable_unprepare_clks(&pdev->dev); + if (data->hsic_pad_regulator) + regulator_disable(data->hsic_pad_regulator); return 0; } @@ -367,9 +460,16 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev) static int __maybe_unused imx_controller_suspend(struct device *dev) { struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + int ret = 0; dev_dbg(dev, "at %s\n", __func__); + ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, false); + if (ret) { + dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret); + return ret; + } + imx_disable_unprepare_clks(dev); data->in_lpm = true; @@ -400,8 +500,16 @@ static int __maybe_unused imx_controller_resume(struct device *dev) goto clk_disable; } + ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, true); + if (ret) { + dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret); + goto hsic_set_clk_fail; + } + return 0; +hsic_set_clk_fail: + imx_usbmisc_set_wakeup(data->usbmisc_data, true); clk_disable: imx_disable_unprepare_clks(dev); return ret; diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 204275f47573..fcecab478934 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -14,10 +14,13 @@ struct imx_usbmisc_data { unsigned int oc_polarity:1; /* over current polarity if oc enabled */ unsigned int evdo:1; /* set external vbus divider option */ unsigned int ulpi:1; /* connected to an ULPI phy */ + unsigned int hsic:1; /* HSIC controlller */ }; -int imx_usbmisc_init(struct imx_usbmisc_data *); -int imx_usbmisc_init_post(struct imx_usbmisc_data *); -int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *, bool); +int imx_usbmisc_init(struct imx_usbmisc_data *data); +int imx_usbmisc_init_post(struct imx_usbmisc_data *data); +int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled); +int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data); +int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on); #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */ diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index def80ff547e4..43a15a6e86f5 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -64,10 +64,22 @@ #define MX6_BM_OVER_CUR_DIS BIT(7) #define MX6_BM_OVER_CUR_POLARITY BIT(8) #define MX6_BM_WAKEUP_ENABLE BIT(10) +#define MX6_BM_UTMI_ON_CLOCK BIT(13) #define MX6_BM_ID_WAKEUP BIT(16) #define MX6_BM_VBUS_WAKEUP BIT(17) #define MX6SX_BM_DPDM_WAKEUP_EN BIT(29) #define MX6_BM_WAKEUP_INTR BIT(31) + +#define MX6_USB_HSIC_CTRL_OFFSET 0x10 +/* Send resume signal without 480Mhz PHY clock */ +#define MX6SX_BM_HSIC_AUTO_RESUME BIT(23) +/* set before portsc.suspendM = 1 */ +#define MX6_BM_HSIC_DEV_CONN BIT(21) +/* HSIC enable */ +#define MX6_BM_HSIC_EN BIT(12) +/* Force HSIC module 480M clock on, even when in Host is in suspend mode */ +#define MX6_BM_HSIC_CLK_ON BIT(11) + #define MX6_USB_OTG1_PHY_CTRL 0x18 /* For imx6dql, it is host-only controller, for later imx6, it is otg's */ #define MX6_USB_OTG2_PHY_CTRL 0x1c @@ -94,6 +106,10 @@ struct usbmisc_ops { int (*post)(struct imx_usbmisc_data *data); /* It's called when we need to enable/disable usb wakeup */ int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled); + /* It's called before setting portsc.suspendM */ + int (*hsic_set_connect)(struct imx_usbmisc_data *data); + /* It's called during suspend/resume */ + int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled); }; struct imx_usbmisc { @@ -353,6 +369,18 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base + data->index * 4); + /* For HSIC controller */ + if (data->hsic) { + reg = readl(usbmisc->base + data->index * 4); + writel(reg | MX6_BM_UTMI_ON_CLOCK, + usbmisc->base + data->index * 4); + reg = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + + (data->index - 2) * 4); + reg |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON; + writel(reg, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + + (data->index - 2) * 4); + } + spin_unlock_irqrestore(&usbmisc->lock, flags); usbmisc_imx6q_set_wakeup(data, false); @@ -360,6 +388,79 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) return 0; } +static int usbmisc_imx6_hsic_get_reg_offset(struct imx_usbmisc_data *data) +{ + int offset, ret = 0; + + if (data->index == 2 || data->index == 3) { + offset = (data->index - 2) * 4; + } else if (data->index == 0) { + /* + * For SoCs like i.MX7D and later, each USB controller has + * its own non-core register region. For SoCs before i.MX7D, + * the first two USB controllers are non-HSIC controllers. + */ + offset = 0; + } else { + dev_err(data->dev, "index is error for usbmisc\n"); + ret = -EINVAL; + } + + return ret ? ret : offset; +} + +static int usbmisc_imx6_hsic_set_connect(struct imx_usbmisc_data *data) +{ + unsigned long flags; + u32 val; + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + int offset; + + spin_lock_irqsave(&usbmisc->lock, flags); + offset = usbmisc_imx6_hsic_get_reg_offset(data); + if (offset < 0) { + spin_unlock_irqrestore(&usbmisc->lock, flags); + return offset; + } + + val = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset); + if (!(val & MX6_BM_HSIC_DEV_CONN)) + writel(val | MX6_BM_HSIC_DEV_CONN, + usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset); + + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; +} + +static int usbmisc_imx6_hsic_set_clk(struct imx_usbmisc_data *data, bool on) +{ + unsigned long flags; + u32 val; + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + int offset; + + spin_lock_irqsave(&usbmisc->lock, flags); + offset = usbmisc_imx6_hsic_get_reg_offset(data); + if (offset < 0) { + spin_unlock_irqrestore(&usbmisc->lock, flags); + return offset; + } + + val = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset); + val |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON; + if (on) + val |= MX6_BM_HSIC_CLK_ON; + else + val &= ~MX6_BM_HSIC_CLK_ON; + + writel(val, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; +} + + static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data) { void __iomem *reg = NULL; @@ -385,6 +486,13 @@ static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data) spin_unlock_irqrestore(&usbmisc->lock, flags); } + /* For HSIC controller */ + if (data->hsic) { + val = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET); + val |= MX6SX_BM_HSIC_AUTO_RESUME; + writel(val, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET); + } + return 0; } @@ -454,6 +562,7 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID, usbmisc->base + MX7D_USBNC_USB_CTRL2); + spin_unlock_irqrestore(&usbmisc->lock, flags); usbmisc_imx7d_set_wakeup(data, false); @@ -481,6 +590,8 @@ static const struct usbmisc_ops imx53_usbmisc_ops = { static const struct usbmisc_ops imx6q_usbmisc_ops = { .set_wakeup = usbmisc_imx6q_set_wakeup, .init = usbmisc_imx6q_init, + .hsic_set_connect = usbmisc_imx6_hsic_set_connect, + .hsic_set_clk = usbmisc_imx6_hsic_set_clk, }; static const struct usbmisc_ops vf610_usbmisc_ops = { @@ -490,6 +601,8 @@ static const struct usbmisc_ops vf610_usbmisc_ops = { static const struct usbmisc_ops imx6sx_usbmisc_ops = { .set_wakeup = usbmisc_imx6q_set_wakeup, .init = usbmisc_imx6sx_init, + .hsic_set_connect = usbmisc_imx6_hsic_set_connect, + .hsic_set_clk = usbmisc_imx6_hsic_set_clk, }; static const struct usbmisc_ops imx7d_usbmisc_ops = { @@ -546,6 +659,33 @@ int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled) } EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup); +int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc; + + if (!data) + return 0; + + usbmisc = dev_get_drvdata(data->dev); + if (!usbmisc->ops->hsic_set_connect || !data->hsic) + return 0; + return usbmisc->ops->hsic_set_connect(data); +} +EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_connect); + +int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on) +{ + struct imx_usbmisc *usbmisc; + + if (!data) + return 0; + + usbmisc = dev_get_drvdata(data->dev); + if (!usbmisc->ops->hsic_set_clk || !data->hsic) + return 0; + return usbmisc->ops->hsic_set_clk(data, on); +} +EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk); static const struct of_device_id usbmisc_imx_dt_ids[] = { { .compatible = "fsl,imx25-usbmisc", -- cgit v1.2.3-59-g8ed1b From 2c4593ecc920b6f3c148f2d123a5c6aa2f1f3e73 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 16 Oct 2018 10:11:42 +0800 Subject: usb: chipidea: host: override ehci->hub_control The chipidea controller has some special requirements during suspend/resume, override common ehci->hub_control to implement it. Reviewed-by: Frieder Schrempf Tested-by: Frieder Schrempf Signed-off-by: Peter Chen --- drivers/usb/chipidea/host.c | 75 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 028a3574266a..b45ceb91c735 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -220,6 +220,80 @@ void ci_hdrc_host_destroy(struct ci_hdrc *ci) host_stop(ci); } +/* The below code is based on tegra ehci driver */ +static int ci_ehci_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + u32 __iomem *status_reg; + u32 temp; + unsigned long flags; + int retval = 0; + struct device *dev = hcd->self.controller; + struct ci_hdrc *ci = dev_get_drvdata(dev); + + status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; + + spin_lock_irqsave(&ehci->lock, flags); + + if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { + temp = ehci_readl(ehci, status_reg); + if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) { + retval = -EPIPE; + goto done; + } + + temp &= ~(PORT_RWC_BITS | PORT_WKCONN_E); + temp |= PORT_WKDISC_E | PORT_WKOC_E; + ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); + + /* + * If a transaction is in progress, there may be a delay in + * suspending the port. Poll until the port is suspended. + */ + if (ehci_handshake(ehci, status_reg, PORT_SUSPEND, + PORT_SUSPEND, 5000)) + ehci_err(ehci, "timeout waiting for SUSPEND\n"); + + if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) { + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, + CI_HDRC_IMX_HSIC_SUSPEND_EVENT); + + temp = ehci_readl(ehci, status_reg); + temp &= ~(PORT_WKDISC_E | PORT_WKCONN_E); + ehci_writel(ehci, temp, status_reg); + } + + set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); + goto done; + } + + /* + * After resume has finished, it needs do some post resume + * operation for some SoCs. + */ + else if (typeReq == ClearPortFeature && + wValue == USB_PORT_FEAT_C_SUSPEND) { + /* Make sure the resume has finished, it should be finished */ + if (ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 25000)) + ehci_err(ehci, "timeout waiting for resume\n"); + } + + spin_unlock_irqrestore(&ehci->lock, flags); + + /* Handle the hub control events here */ + return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); +done: + spin_unlock_irqrestore(&ehci->lock, flags); + return retval; +} static int ci_ehci_bus_suspend(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -298,4 +372,5 @@ void ci_hdrc_host_driver_init(void) ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides); orig_bus_suspend = ci_ehci_hc_driver.bus_suspend; ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend; + ci_ehci_hc_driver.hub_control = ci_ehci_hub_control; } -- cgit v1.2.3-59-g8ed1b From a82bf696aa39b08c0dfce5569525e61368c6827f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 4 Dec 2018 09:31:29 +0100 Subject: usb: chipidea: imx: support configuring for active low oc signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The status quo on i.MX6 is that if "over-current-active-high" is specified in the device tree this is configured as expected. If the property is missing polarity isn't changed and so the polarity is kept as setup by the bootloader. Reset default is active high, so active low can only be used with help by the bootloader. On i.MX7 it is similar, but there disabling of over current detection has a similar inconsistency. This patch introduces a new property that allows to explicitly configure for active low over current detection and consistently sets this up. In the absence of an explicit configuration the bit is kept as is. On i.MX7 over current detection is used unless disabled in the device tree. Signed-off-by: Uwe Kleine-König Signed-off-by: Peter Chen --- .../devicetree/bindings/usb/ci-hdrc-usb2.txt | 5 ++-- drivers/usb/chipidea/ci_hdrc_imx.c | 16 +++++++++---- drivers/usb/chipidea/ci_hdrc_imx.h | 8 ++++++- drivers/usb/chipidea/usbmisc_imx.c | 28 ++++++++++++++++------ 4 files changed, 43 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt index 3381b9618b5b..adae82385dd6 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt @@ -90,8 +90,9 @@ i.mx specific properties - fsl,usbmisc: phandler of non-core register device, with one argument that indicate usb controller index - disable-over-current: disable over current detect -- over-current-active-high: over current signal polarity is high active, - typically over current signal polarity is low active. +- over-current-active-low: over current signal polarity is active low. +- over-current-active-high: over current signal polarity is active high. + It's recommended to specify the over current polarity. - external-vbus-divider: enables off-chip resistor divider for Vbus Example: diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 56781c329db0..1951b37aa39d 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -136,11 +136,19 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) data->dev = &misc_pdev->dev; - if (of_find_property(np, "disable-over-current", NULL)) + /* + * Check the various over current related properties. If over current + * detection is disabled we're not interested in the polarity. + */ + if (of_find_property(np, "disable-over-current", NULL)) { data->disable_oc = 1; - - if (of_find_property(np, "over-current-active-high", NULL)) - data->oc_polarity = 1; + } else if (of_find_property(np, "over-current-active-high", NULL)) { + data->oc_pol_active_low = 0; + data->oc_pol_configured = 1; + } else if (of_find_property(np, "over-current-active-low", NULL)) { + data->oc_pol_active_low = 1; + data->oc_pol_configured = 1; + } if (of_find_property(np, "external-vbus-divider", NULL)) data->evdo = 1; diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index fcecab478934..7cc53e2ce564 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -11,7 +11,13 @@ struct imx_usbmisc_data { int index; unsigned int disable_oc:1; /* over current detect disabled */ - unsigned int oc_polarity:1; /* over current polarity if oc enabled */ + + /* true if over-current polarity is active low */ + unsigned int oc_pol_active_low:1; + + /* true if dt specifies polarity */ + unsigned int oc_pol_configured:1; + unsigned int evdo:1; /* set external vbus divider option */ unsigned int ulpi:1; /* connected to an ULPI phy */ unsigned int hsic:1; /* HSIC controlller */ diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 43a15a6e86f5..4c3839d345cd 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -356,11 +356,17 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) reg = readl(usbmisc->base + data->index * 4); if (data->disable_oc) { reg |= MX6_BM_OVER_CUR_DIS; - } else if (data->oc_polarity == 1) { - /* High active */ - reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY); } else { - reg &= ~(MX6_BM_OVER_CUR_DIS); + reg &= ~MX6_BM_OVER_CUR_DIS; + + /* + * If the polarity is not configured keep it as setup by the + * bootloader. + */ + if (data->oc_pol_configured && data->oc_pol_active_low) + reg |= MX6_BM_OVER_CUR_POLARITY; + else if (data->oc_pol_configured) + reg &= ~MX6_BM_OVER_CUR_POLARITY; } writel(reg, usbmisc->base + data->index * 4); @@ -552,9 +558,17 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) reg = readl(usbmisc->base); if (data->disable_oc) { reg |= MX6_BM_OVER_CUR_DIS; - } else if (data->oc_polarity == 1) { - /* High active */ - reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY); + } else { + reg &= ~MX6_BM_OVER_CUR_DIS; + + /* + * If the polarity is not configured keep it as setup by the + * bootloader. + */ + if (data->oc_pol_configured && data->oc_pol_active_low) + reg |= MX6_BM_OVER_CUR_POLARITY; + else if (data->oc_pol_configured) + reg &= ~MX6_BM_OVER_CUR_POLARITY; } writel(reg, usbmisc->base); -- cgit v1.2.3-59-g8ed1b From 1bf4743f641d85f32fe3f3a4d8aa01a387549e3e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 4 Dec 2018 09:31:30 +0100 Subject: usb: chipidea: imx: Warn if oc polarity isn't specified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The polarity of the over current detection pin isn't configured on i.MX6/7 if it's unspecified in the device tree. So the actual configuration depends on bootloader behavior which is bad. So encourage users to fix their device tree by issuing a warning in this case. Signed-off-by: Uwe Kleine-König Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_imx.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 1951b37aa39d..e81de9ca8729 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -148,6 +148,8 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) } else if (of_find_property(np, "over-current-active-low", NULL)) { data->oc_pol_active_low = 1; data->oc_pol_configured = 1; + } else { + dev_warn(dev, "No over current polarity defined\n"); } if (of_find_property(np, "external-vbus-divider", NULL)) -- cgit v1.2.3-59-g8ed1b From 9049fce897edae50835a8f799d4b05f67a538e9a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 4 Dec 2018 09:31:31 +0100 Subject: usb: chipidea: imx: allow to configure oc polarity on i.MX25 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now the polarity of the over current pin was hard coded to active high. Use the already defined device tree properties to configure polarity on i.MX25, too. In difference to i.MX6/7 use active high behavior if the polarity is unspecified to keep compatibility to existing device trees. Signed-off-by: Uwe Kleine-König Signed-off-by: Peter Chen --- drivers/usb/chipidea/usbmisc_imx.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 4c3839d345cd..097ffbca0bd9 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -136,6 +136,14 @@ static int usbmisc_imx25_init(struct imx_usbmisc_data *data) val &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT); val |= (MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT; val |= (MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT); + + /* + * If the polarity is not configured assume active high for + * historical reasons. + */ + if (data->oc_pol_configured && data->oc_pol_active_low) + val &= ~MX25_OTG_OCPOL_BIT; + writel(val, usbmisc->base); break; case 1: @@ -145,6 +153,13 @@ static int usbmisc_imx25_init(struct imx_usbmisc_data *data) val |= (MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT | MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT); + /* + * If the polarity is not configured assume active high for + * historical reasons. + */ + if (data->oc_pol_configured && data->oc_pol_active_low) + val &= ~MX25_H1_OCPOL_BIT; + writel(val, usbmisc->base); break; -- cgit v1.2.3-59-g8ed1b From 4fe4f9fecc36956fd53c8edf96dd0c691ef98ff9 Mon Sep 17 00:00:00 2001 From: Minas Harutyunyan Date: Mon, 10 Dec 2018 18:09:32 +0400 Subject: usb: dwc2: Fix disable all EP's on disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disabling all EP's allow to reset EP's to initial state. Introduced new function dwc2_hsotg_ep_disable_lock() which before calling dwc2_hsotg_ep_disable() function acquire hsotg->lock and release on exiting. From dwc2_hsotg_ep_disable() function removed acquiring hsotg->lock. In dwc2_hsotg_core_init_disconnected() function when USB reset interrupt asserted disabling all ep’s by dwc2_hsotg_ep_disable() function. This updates eliminating sparse imbalance warnings. Reverted changes in dwc2_hostg_disconnect() function. Introduced new function dwc2_hsotg_ep_disable_lock(). Changed dwc2_hsotg_ep_ops. Now disable point to dwc2_hsotg_ep_disable_lock() function. In functions dwc2_hsotg_udc_stop() and dwc2_hsotg_suspend() dwc2_hsotg_ep_disable() function replaced by dwc2_hsotg_ep_disable_lock() function. In dwc2_hsotg_ep_disable() function removed acquiring of hsotg->lock. Fixes: dccf1bad4be7 ("usb: dwc2: Disable all EP's on disconnect") Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 94f3ba995580..68ad75a7460d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3165,8 +3165,6 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg, dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index); } -static int dwc2_hsotg_ep_disable(struct usb_ep *ep); - /** * dwc2_hsotg_disconnect - disconnect service * @hsotg: The device state. @@ -3188,9 +3186,11 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg) /* all endpoints should be shutdown */ for (ep = 0; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) - dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); + kill_all_requests(hsotg, hsotg->eps_in[ep], + -ESHUTDOWN); if (hsotg->eps_out[ep]) - dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); + kill_all_requests(hsotg, hsotg->eps_out[ep], + -ESHUTDOWN); } call_gadget(hsotg, disconnect); @@ -3234,6 +3234,7 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) GINTSTS_PTXFEMP | \ GINTSTS_RXFLVL) +static int dwc2_hsotg_ep_disable(struct usb_ep *ep); /** * dwc2_hsotg_core_init - issue softreset to the core * @hsotg: The device state @@ -4069,10 +4070,8 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) struct dwc2_hsotg *hsotg = hs_ep->parent; int dir_in = hs_ep->dir_in; int index = hs_ep->index; - unsigned long flags; u32 epctrl_reg; u32 ctrl; - int locked; dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep); @@ -4088,10 +4087,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); - locked = spin_is_locked(&hsotg->lock); - if (!locked) - spin_lock_irqsave(&hsotg->lock, flags); - ctrl = dwc2_readl(hsotg, epctrl_reg); if (ctrl & DXEPCTL_EPENA) @@ -4114,12 +4109,22 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) hs_ep->fifo_index = 0; hs_ep->fifo_size = 0; - if (!locked) - spin_unlock_irqrestore(&hsotg->lock, flags); - return 0; } +static int dwc2_hsotg_ep_disable_lock(struct usb_ep *ep) +{ + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg *hsotg = hs_ep->parent; + unsigned long flags; + int ret; + + spin_lock_irqsave(&hsotg->lock, flags); + ret = dwc2_hsotg_ep_disable(ep); + spin_unlock_irqrestore(&hsotg->lock, flags); + return ret; +} + /** * on_list - check request is on the given endpoint * @ep: The endpoint to check. @@ -4267,7 +4272,7 @@ static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) static const struct usb_ep_ops dwc2_hsotg_ep_ops = { .enable = dwc2_hsotg_ep_enable, - .disable = dwc2_hsotg_ep_disable, + .disable = dwc2_hsotg_ep_disable_lock, .alloc_request = dwc2_hsotg_ep_alloc_request, .free_request = dwc2_hsotg_ep_free_request, .queue = dwc2_hsotg_ep_queue_lock, @@ -4407,9 +4412,9 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) /* all endpoints should be shutdown */ for (ep = 1; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) - dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); + dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep); if (hsotg->eps_out[ep]) - dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); + dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep); } spin_lock_irqsave(&hsotg->lock, flags); @@ -4857,9 +4862,9 @@ int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg) for (ep = 0; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) - dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); + dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep); if (hsotg->eps_out[ep]) - dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); + dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep); } } -- cgit v1.2.3-59-g8ed1b From 2659392e5c08dff626e6db1d739adff58a94604d Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 4 Oct 2018 20:28:47 +0800 Subject: phy: sun4i-usb: add support for missing USB PHY index The new Allwinner H6 SoC's USB2 PHY has two holes -- USB1 (which is a 3.0 port with dedicated PHY) and USB2 (which doesn't exist at all). Add support for this kind of missing USB PHY index. Signed-off-by: Icenowy Zheng Reviewed-by: Chen-Yu Tsai Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/allwinner/phy-sun4i-usb.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index d4dcd39b8d76..881078ff73f6 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -126,6 +126,7 @@ struct sun4i_usb_phy_cfg { bool dedicated_clocks; bool enable_pmu_unk1; bool phy0_dual_route; + int missing_phys; }; struct sun4i_usb_phy_data { @@ -646,6 +647,9 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev, if (args->args[0] >= data->cfg->num_phys) return ERR_PTR(-ENODEV); + if (data->cfg->missing_phys & BIT(args->args[0])) + return ERR_PTR(-ENODEV); + return data->phys[args->args[0]].phy; } @@ -741,6 +745,9 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) struct sun4i_usb_phy *phy = data->phys + i; char name[16]; + if (data->cfg->missing_phys & BIT(i)) + continue; + snprintf(name, sizeof(name), "usb%d_vbus", i); phy->vbus = devm_regulator_get_optional(dev, name); if (IS_ERR(phy->vbus)) { -- cgit v1.2.3-59-g8ed1b From ae409cc7c3cdb9ac4a1dba3eae70efec3d6b6c79 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 4 Oct 2018 20:28:48 +0800 Subject: phy: sun4i-usb: add support for H6 USB2 PHY The USB 2.0 PHY on Allwinner H6 SoC is similar to older Allwinner SoCs, with some USB0 quirk like A83T and PHY index 1/2 missing. Add support for it. Signed-off-by: Icenowy Zheng Reviewed-by: Chen-Yu Tsai Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/allwinner/phy-sun4i-usb.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 881078ff73f6..ae16854a770a 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -115,6 +115,7 @@ enum sun4i_usb_phy_type { sun8i_r40_phy, sun8i_v3s_phy, sun50i_a64_phy, + sun50i_h6_phy, }; struct sun4i_usb_phy_cfg { @@ -295,7 +296,8 @@ static int sun4i_usb_phy_init(struct phy *_phy) return ret; } - if (data->cfg->type == sun8i_a83t_phy) { + if (data->cfg->type == sun8i_a83t_phy || + data->cfg->type == sun50i_h6_phy) { if (phy->index == 0) { val = readl(data->base + data->cfg->phyctl_offset); val |= PHY_CTL_VBUSVLDEXT; @@ -344,7 +346,8 @@ static int sun4i_usb_phy_exit(struct phy *_phy) struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); if (phy->index == 0) { - if (data->cfg->type == sun8i_a83t_phy) { + if (data->cfg->type == sun8i_a83t_phy || + data->cfg->type == sun50i_h6_phy) { void __iomem *phyctl = data->base + data->cfg->phyctl_offset; @@ -959,6 +962,17 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { .phy0_dual_route = true, }; +static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = { + .num_phys = 4, + .type = sun50i_h6_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .dedicated_clocks = true, + .enable_pmu_unk1 = true, + .phy0_dual_route = true, + .missing_phys = BIT(1) | BIT(2), +}; + static const struct of_device_id sun4i_usb_phy_of_match[] = { { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg }, { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg }, @@ -972,6 +986,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = { { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, { .compatible = "allwinner,sun50i-a64-usb-phy", .data = &sun50i_a64_cfg}, + { .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg }, { }, }; MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); -- cgit v1.2.3-59-g8ed1b From 79a5a18aa9d1062205cdcfa183d4cd5241d1b8da Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 19 Nov 2018 19:24:20 -0600 Subject: phy: core: rework phy_set_mode to accept phy mode and submode Currently the attempt to add support for Ethernet interface mode PHY (MII/GMII/RGMII) will lead to the necessity of extending enum phy_mode and duplicate there values from phy_interface_t enum (or introduce more PHY callbacks) [1]. Both approaches are ineffective and would lead to fast bloating of enum phy_mode or struct phy_ops in the process of adding more PHYs for different subsystems which will make them unmaintainable. As discussed in [1] the solution could be to introduce dual level PHYs mode configuration - PHY mode and PHY submode. The PHY mode will define generic PHY type (subsystem - PCIE/ETHERNET/USB_) while the PHY submode - subsystem specific interface mode. The last is usually already defined in corresponding subsystem headers (phy_interface_t for Ethernet, enum usb_device_speed for USB). This patch is cumulative change which refactors PHY framework code to support dual level PHYs mode configuration - PHY mode and PHY submode. It extends .set_mode() callback to support additional parameter "int submode" and converts all corresponding PHY drivers to support new .set_mode() callback declaration. The new extended PHY API int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode) is introduced to support dual level PHYs mode configuration and existing phy_set_mode() API is converted to macros, so PHY framework consumers do not need to be changed (~21 matches). [1] http://lkml.kernel.org/r/d63588f6-9ab0-848a-5ad4-8073143bd95d@ti.com Signed-off-by: Grygorii Strashko Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/allwinner/phy-sun4i-usb.c | 3 ++- drivers/phy/amlogic/phy-meson-gxl-usb2.c | 5 +++-- drivers/phy/amlogic/phy-meson-gxl-usb3.c | 5 +++-- drivers/phy/marvell/phy-mvebu-cp110-comphy.c | 3 ++- drivers/phy/mediatek/phy-mtk-tphy.c | 2 +- drivers/phy/mediatek/phy-mtk-xsphy.c | 2 +- drivers/phy/mscc/phy-ocelot-serdes.c | 2 +- drivers/phy/phy-core.c | 6 +++--- drivers/phy/qualcomm/phy-qcom-qmp.c | 3 ++- drivers/phy/qualcomm/phy-qcom-qusb2.c | 3 ++- drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 3 ++- drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 3 ++- drivers/phy/qualcomm/phy-qcom-usb-hs.c | 3 ++- drivers/phy/ti/phy-da8xx-usb.c | 3 ++- drivers/phy/ti/phy-tusb1210.c | 2 +- include/linux/phy/phy.h | 13 ++++++++++--- 16 files changed, 39 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index ae16854a770a..5163097b43df 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -478,7 +478,8 @@ static int sun4i_usb_phy_power_off(struct phy *_phy) return 0; } -static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode) +static int sun4i_usb_phy_set_mode(struct phy *_phy, + enum phy_mode mode, int submode) { struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb2.c b/drivers/phy/amlogic/phy-meson-gxl-usb2.c index 9f9b5414b97a..148ef0bdb9c1 100644 --- a/drivers/phy/amlogic/phy-meson-gxl-usb2.c +++ b/drivers/phy/amlogic/phy-meson-gxl-usb2.c @@ -152,7 +152,8 @@ static int phy_meson_gxl_usb2_reset(struct phy *phy) return 0; } -static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode) +static int phy_meson_gxl_usb2_set_mode(struct phy *phy, + enum phy_mode mode, int submode) { struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy); @@ -209,7 +210,7 @@ static int phy_meson_gxl_usb2_power_on(struct phy *phy) /* power on the PHY by taking it out of reset mode */ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0); - ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode); + ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode, 0); if (ret) { phy_meson_gxl_usb2_power_off(phy); diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb3.c b/drivers/phy/amlogic/phy-meson-gxl-usb3.c index d37d94ddf9c0..c0e9e4c16149 100644 --- a/drivers/phy/amlogic/phy-meson-gxl-usb3.c +++ b/drivers/phy/amlogic/phy-meson-gxl-usb3.c @@ -119,7 +119,8 @@ static int phy_meson_gxl_usb3_power_off(struct phy *phy) return 0; } -static int phy_meson_gxl_usb3_set_mode(struct phy *phy, enum phy_mode mode) +static int phy_meson_gxl_usb3_set_mode(struct phy *phy, + enum phy_mode mode, int submode) { struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); @@ -164,7 +165,7 @@ static int phy_meson_gxl_usb3_init(struct phy *phy) if (ret) goto err_disable_clk_phy; - ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode); + ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode, 0); if (ret) goto err_disable_clk_peripheral; diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c index 86a5f7b9448b..79b52c39c5b4 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c @@ -512,7 +512,8 @@ static int mvebu_comphy_power_on(struct phy *phy) return ret; } -static int mvebu_comphy_set_mode(struct phy *phy, enum phy_mode mode) +static int mvebu_comphy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) { struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c index 3eb8e1bd7b78..5b6a470ca145 100644 --- a/drivers/phy/mediatek/phy-mtk-tphy.c +++ b/drivers/phy/mediatek/phy-mtk-tphy.c @@ -971,7 +971,7 @@ static int mtk_phy_exit(struct phy *phy) return 0; } -static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode) +static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct mtk_phy_instance *instance = phy_get_drvdata(phy); struct mtk_tphy *tphy = dev_get_drvdata(phy->dev.parent); diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c index 020cd0227397..8c51131945c0 100644 --- a/drivers/phy/mediatek/phy-mtk-xsphy.c +++ b/drivers/phy/mediatek/phy-mtk-xsphy.c @@ -426,7 +426,7 @@ static int mtk_phy_exit(struct phy *phy) return 0; } -static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode) +static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct xsphy_instance *inst = phy_get_drvdata(phy); struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); diff --git a/drivers/phy/mscc/phy-ocelot-serdes.c b/drivers/phy/mscc/phy-ocelot-serdes.c index cbb49d9da6f9..c61a98908d36 100644 --- a/drivers/phy/mscc/phy-ocelot-serdes.c +++ b/drivers/phy/mscc/phy-ocelot-serdes.c @@ -158,7 +158,7 @@ static const struct serdes_mux ocelot_serdes_muxes[] = { HSIO_HW_CFG_PCIE_ENA), }; -static int serdes_set_mode(struct phy *phy, enum phy_mode mode) +static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct serdes_macro *macro = phy_get_drvdata(phy); unsigned int i; diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 35fd38c5a4a1..df3d4ba516ab 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -360,7 +360,7 @@ int phy_power_off(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_power_off); -int phy_set_mode(struct phy *phy, enum phy_mode mode) +int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode) { int ret; @@ -368,14 +368,14 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode) return 0; mutex_lock(&phy->mutex); - ret = phy->ops->set_mode(phy, mode); + ret = phy->ops->set_mode(phy, mode, submode); if (!ret) phy->attrs.mode = mode; mutex_unlock(&phy->mutex); return ret; } -EXPORT_SYMBOL_GPL(phy_set_mode); +EXPORT_SYMBOL_GPL(phy_set_mode_ext); int phy_reset(struct phy *phy) { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index a83332411026..514db7248a5d 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -1365,7 +1365,8 @@ static int qcom_qmp_phy_poweron(struct phy *phy) return ret; } -static int qcom_qmp_phy_set_mode(struct phy *phy, enum phy_mode mode) +static int qcom_qmp_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) { struct qmp_phy *qphy = phy_get_drvdata(phy); struct qcom_qmp *qmp = qphy->qmp; diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 6d4b44b569bc..9177989f22d1 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -425,7 +425,8 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) HSTX_TRIM_MASK); } -static int qusb2_phy_set_mode(struct phy *phy, enum phy_mode mode) +static int qusb2_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) { struct qusb2_phy *qphy = phy_get_drvdata(phy); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c index ba1895b76a5d..1e0d4f2046a4 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c @@ -65,7 +65,8 @@ static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) } static -int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, enum phy_mode mode) +int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, + enum phy_mode mode, int submode) { struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c index 49f435c71147..aef40f7a41d4 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c @@ -84,7 +84,8 @@ static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) } static -int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, enum phy_mode mode) +int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, + enum phy_mode mode, int submode) { struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c index abbbe75070da..04934f8dac91 100644 --- a/drivers/phy/qualcomm/phy-qcom-usb-hs.c +++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c @@ -42,7 +42,8 @@ struct qcom_usb_hs_phy { struct notifier_block vbus_notify; }; -static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode) +static int qcom_usb_hs_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) { struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); u8 addr; diff --git a/drivers/phy/ti/phy-da8xx-usb.c b/drivers/phy/ti/phy-da8xx-usb.c index befb886ff121..d5f4fbc32b52 100644 --- a/drivers/phy/ti/phy-da8xx-usb.c +++ b/drivers/phy/ti/phy-da8xx-usb.c @@ -93,7 +93,8 @@ static int da8xx_usb20_phy_power_off(struct phy *phy) return 0; } -static int da8xx_usb20_phy_set_mode(struct phy *phy, enum phy_mode mode) +static int da8xx_usb20_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) { struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); u32 val; diff --git a/drivers/phy/ti/phy-tusb1210.c b/drivers/phy/ti/phy-tusb1210.c index b8ec39ac4dfc..329fb938099a 100644 --- a/drivers/phy/ti/phy-tusb1210.c +++ b/drivers/phy/ti/phy-tusb1210.c @@ -53,7 +53,7 @@ static int tusb1210_power_off(struct phy *phy) return 0; } -static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode) +static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct tusb1210 *tusb = phy_get_drvdata(phy); int ret; diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 03b319f89a34..b17e7709c5dc 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -60,7 +60,7 @@ struct phy_ops { int (*exit)(struct phy *phy); int (*power_on)(struct phy *phy); int (*power_off)(struct phy *phy); - int (*set_mode)(struct phy *phy, enum phy_mode mode); + int (*set_mode)(struct phy *phy, enum phy_mode mode, int submode); int (*reset)(struct phy *phy); int (*calibrate)(struct phy *phy); struct module *owner; @@ -164,7 +164,10 @@ int phy_init(struct phy *phy); int phy_exit(struct phy *phy); int phy_power_on(struct phy *phy); int phy_power_off(struct phy *phy); -int phy_set_mode(struct phy *phy, enum phy_mode mode); +int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode); +#define phy_set_mode(phy, mode) \ + phy_set_mode_ext(phy, mode, 0) + static inline enum phy_mode phy_get_mode(struct phy *phy) { return phy->attrs.mode; @@ -278,13 +281,17 @@ static inline int phy_power_off(struct phy *phy) return -ENOSYS; } -static inline int phy_set_mode(struct phy *phy, enum phy_mode mode) +static inline int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, + int submode) { if (!phy) return 0; return -ENOSYS; } +#define phy_set_mode(phy, mode) \ + phy_set_mode_ext(phy, mode, 0) + static inline enum phy_mode phy_get_mode(struct phy *phy) { return PHY_MODE_INVALID; -- cgit v1.2.3-59-g8ed1b From c8fe6d7f3f8345ba1d4b4f38d3163f6f94412f0a Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 19 Nov 2018 19:24:22 -0600 Subject: phy: ocelot-serdes: convert to use eth phy mode and submode Convert ocelot-serdes PHY driver to use recently introduced PHY_MODE_ETHERNET and phy_set_mode_ext(). Cc: Quentin Schulz Signed-off-by: Grygorii Strashko Reviewed-by: Quentin Schulz Tested-by: Quentin Schulz Signed-off-by: Kishon Vijay Abraham I --- drivers/net/ethernet/mscc/ocelot.c | 9 ++------- drivers/phy/mscc/phy-ocelot-serdes.c | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 3238b9ee42f3..3edb6082c670 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -472,7 +472,6 @@ static int ocelot_port_open(struct net_device *dev) { struct ocelot_port *port = netdev_priv(dev); struct ocelot *ocelot = port->ocelot; - enum phy_mode phy_mode; int err; /* Enable receiving frames on the port, and activate auto-learning of @@ -484,12 +483,8 @@ static int ocelot_port_open(struct net_device *dev) ANA_PORT_PORT_CFG, port->chip_port); if (port->serdes) { - if (port->phy_mode == PHY_INTERFACE_MODE_SGMII) - phy_mode = PHY_MODE_SGMII; - else - phy_mode = PHY_MODE_QSGMII; - - err = phy_set_mode(port->serdes, phy_mode); + err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET, + port->phy_mode); if (err) { netdev_err(dev, "Could not set mode of SerDes\n"); return err; diff --git a/drivers/phy/mscc/phy-ocelot-serdes.c b/drivers/phy/mscc/phy-ocelot-serdes.c index c61a98908d36..77c46f639fbf 100644 --- a/drivers/phy/mscc/phy-ocelot-serdes.c +++ b/drivers/phy/mscc/phy-ocelot-serdes.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -104,20 +105,24 @@ struct serdes_mux { u8 idx; u8 port; enum phy_mode mode; + int submode; u32 mask; u32 mux; }; -#define SERDES_MUX(_idx, _port, _mode, _mask, _mux) { \ +#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \ .idx = _idx, \ .port = _port, \ .mode = _mode, \ + .submode = _submode, \ .mask = _mask, \ .mux = _mux, \ } -#define SERDES_MUX_SGMII(i, p, m, c) SERDES_MUX(i, p, PHY_MODE_SGMII, m, c) -#define SERDES_MUX_QSGMII(i, p, m, c) SERDES_MUX(i, p, PHY_MODE_QSGMII, m, c) +#define SERDES_MUX_SGMII(i, p, m, c) \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c) +#define SERDES_MUX_QSGMII(i, p, m, c) \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c) static const struct serdes_mux ocelot_serdes_muxes[] = { SERDES_MUX_SGMII(SERDES1G(0), 0, 0, 0), @@ -154,7 +159,7 @@ static const struct serdes_mux ocelot_serdes_muxes[] = { SERDES_MUX_SGMII(SERDES6G(1), 8, 0, 0), SERDES_MUX_SGMII(SERDES6G(2), 10, HSIO_HW_CFG_PCIE_ENA | HSIO_HW_CFG_DEV2G5_10_MODE, 0), - SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, HSIO_HW_CFG_PCIE_ENA, + SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, 0, HSIO_HW_CFG_PCIE_ENA, HSIO_HW_CFG_PCIE_ENA), }; @@ -164,12 +169,17 @@ static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode) unsigned int i; int ret; + /* As of now only PHY_MODE_ETHERNET is supported */ + if (mode != PHY_MODE_ETHERNET) + return -EOPNOTSUPP; + for (i = 0; i < ARRAY_SIZE(ocelot_serdes_muxes); i++) { if (macro->idx != ocelot_serdes_muxes[i].idx || - mode != ocelot_serdes_muxes[i].mode) + mode != ocelot_serdes_muxes[i].mode || + submode != ocelot_serdes_muxes[i].submode) continue; - if (mode != PHY_MODE_QSGMII && + if (submode != PHY_INTERFACE_MODE_QSGMII && macro->port != ocelot_serdes_muxes[i].port) continue; -- cgit v1.2.3-59-g8ed1b From cccc43b853df4d2663d3890e3365513d55612794 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 19 Nov 2018 19:24:23 -0600 Subject: phy: mvebu-cp110-comphy: convert to use eth phy mode and submode Convert mvebu-cp110-comphy PHY driver to use recently introduced PHY_MODE_ETHERNET and phy_set_mode_ext(). Cc: Russell King - ARM Linux Cc: Maxime Chevallier Cc: Antoine Tenart Signed-off-by: Grygorii Strashko Acked-by: Antoine Tenart Signed-off-by: Kishon Vijay Abraham I --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 19 +----- drivers/phy/marvell/phy-mvebu-cp110-comphy.c | 90 ++++++++++++++----------- 2 files changed, 53 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 7a37a37e3fb3..731793a87e43 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -1165,28 +1165,13 @@ static void mvpp22_gop_setup_irq(struct mvpp2_port *port) */ static int mvpp22_comphy_init(struct mvpp2_port *port) { - enum phy_mode mode; int ret; if (!port->comphy) return 0; - switch (port->phy_interface) { - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_1000BASEX: - mode = PHY_MODE_SGMII; - break; - case PHY_INTERFACE_MODE_2500BASEX: - mode = PHY_MODE_2500SGMII; - break; - case PHY_INTERFACE_MODE_10GKR: - mode = PHY_MODE_10GKR; - break; - default: - return -EINVAL; - } - - ret = phy_set_mode(port->comphy, mode); + ret = phy_set_mode_ext(port->comphy, PHY_MODE_ETHERNET, + port->phy_interface); if (ret) return ret; diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c index 79b52c39c5b4..2b4462a28a58 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -116,41 +117,43 @@ struct mvebu_comhy_conf { enum phy_mode mode; + int submode; unsigned lane; unsigned port; u32 mux; }; -#define MVEBU_COMPHY_CONF(_lane, _port, _mode, _mux) \ +#define MVEBU_COMPHY_CONF(_lane, _port, _submode, _mux) \ { \ .lane = _lane, \ .port = _port, \ - .mode = _mode, \ + .mode = PHY_MODE_ETHERNET, \ + .submode = _submode, \ .mux = _mux, \ } static const struct mvebu_comhy_conf mvebu_comphy_cp110_modes[] = { /* lane 0 */ - MVEBU_COMPHY_CONF(0, 1, PHY_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(0, 1, PHY_MODE_2500SGMII, 0x1), + MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1), + MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1), /* lane 1 */ - MVEBU_COMPHY_CONF(1, 2, PHY_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(1, 2, PHY_MODE_2500SGMII, 0x1), + MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_SGMII, 0x1), + MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1), /* lane 2 */ - MVEBU_COMPHY_CONF(2, 0, PHY_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(2, 0, PHY_MODE_2500SGMII, 0x1), - MVEBU_COMPHY_CONF(2, 0, PHY_MODE_10GKR, 0x1), + MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_SGMII, 0x1), + MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1), + MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_10GKR, 0x1), /* lane 3 */ - MVEBU_COMPHY_CONF(3, 1, PHY_MODE_SGMII, 0x2), - MVEBU_COMPHY_CONF(3, 1, PHY_MODE_2500SGMII, 0x2), + MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_SGMII, 0x2), + MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2), /* lane 4 */ - MVEBU_COMPHY_CONF(4, 0, PHY_MODE_SGMII, 0x2), - MVEBU_COMPHY_CONF(4, 0, PHY_MODE_2500SGMII, 0x2), - MVEBU_COMPHY_CONF(4, 0, PHY_MODE_10GKR, 0x2), - MVEBU_COMPHY_CONF(4, 1, PHY_MODE_SGMII, 0x1), + MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_SGMII, 0x2), + MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2), + MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_10GKR, 0x2), + MVEBU_COMPHY_CONF(4, 1, PHY_INTERFACE_MODE_SGMII, 0x1), /* lane 5 */ - MVEBU_COMPHY_CONF(5, 2, PHY_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(5, 2, PHY_MODE_2500SGMII, 0x1), + MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_SGMII, 0x1), + MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1), }; struct mvebu_comphy_priv { @@ -163,10 +166,12 @@ struct mvebu_comphy_lane { struct mvebu_comphy_priv *priv; unsigned id; enum phy_mode mode; + int submode; int port; }; -static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode) +static int mvebu_comphy_get_mux(int lane, int port, + enum phy_mode mode, int submode) { int i, n = ARRAY_SIZE(mvebu_comphy_cp110_modes); @@ -177,7 +182,8 @@ static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode) for (i = 0; i < n; i++) { if (mvebu_comphy_cp110_modes[i].lane == lane && mvebu_comphy_cp110_modes[i].port == port && - mvebu_comphy_cp110_modes[i].mode == mode) + mvebu_comphy_cp110_modes[i].mode == mode && + mvebu_comphy_cp110_modes[i].submode == submode) break; } @@ -187,8 +193,7 @@ static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode) return mvebu_comphy_cp110_modes[i].mux; } -static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane, - enum phy_mode mode) +static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane) { struct mvebu_comphy_priv *priv = lane->priv; u32 val; @@ -206,14 +211,14 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane, MVEBU_COMPHY_SERDES_CFG0_HALF_BUS | MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xf) | MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xf)); - if (mode == PHY_MODE_10GKR) + if (lane->submode == PHY_INTERFACE_MODE_10GKR) val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xe) | MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xe); - else if (mode == PHY_MODE_2500SGMII) + else if (lane->submode == PHY_INTERFACE_MODE_2500BASEX) val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x8) | MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x8) | MVEBU_COMPHY_SERDES_CFG0_HALF_BUS; - else if (mode == PHY_MODE_SGMII) + else if (lane->submode == PHY_INTERFACE_MODE_SGMII) val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x6) | MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x6) | MVEBU_COMPHY_SERDES_CFG0_HALF_BUS; @@ -243,7 +248,7 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane, /* refclk selection */ val = readl(priv->base + MVEBU_COMPHY_MISC_CTRL0(lane->id)); val &= ~MVEBU_COMPHY_MISC_CTRL0_REFCLK_SEL; - if (mode == PHY_MODE_10GKR) + if (lane->submode == PHY_INTERFACE_MODE_10GKR) val |= MVEBU_COMPHY_MISC_CTRL0_ICP_FORCE; writel(val, priv->base + MVEBU_COMPHY_MISC_CTRL0(lane->id)); @@ -261,8 +266,7 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane, writel(val, priv->base + MVEBU_COMPHY_LOOPBACK(lane->id)); } -static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane, - enum phy_mode mode) +static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane) { struct mvebu_comphy_priv *priv = lane->priv; u32 val; @@ -303,13 +307,13 @@ static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane, return 0; } -static int mvebu_comphy_set_mode_sgmii(struct phy *phy, enum phy_mode mode) +static int mvebu_comphy_set_mode_sgmii(struct phy *phy) { struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); struct mvebu_comphy_priv *priv = lane->priv; u32 val; - mvebu_comphy_ethernet_init_reset(lane, mode); + mvebu_comphy_ethernet_init_reset(lane); val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id)); val &= ~MVEBU_COMPHY_RX_CTRL1_CLK8T_EN; @@ -330,7 +334,7 @@ static int mvebu_comphy_set_mode_sgmii(struct phy *phy, enum phy_mode mode) val |= MVEBU_COMPHY_GEN1_S0_TX_EMPH(0x1); writel(val, priv->base + MVEBU_COMPHY_GEN1_S0(lane->id)); - return mvebu_comphy_init_plls(lane, PHY_MODE_SGMII); + return mvebu_comphy_init_plls(lane); } static int mvebu_comphy_set_mode_10gkr(struct phy *phy) @@ -339,7 +343,7 @@ static int mvebu_comphy_set_mode_10gkr(struct phy *phy) struct mvebu_comphy_priv *priv = lane->priv; u32 val; - mvebu_comphy_ethernet_init_reset(lane, PHY_MODE_10GKR); + mvebu_comphy_ethernet_init_reset(lane); val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id)); val |= MVEBU_COMPHY_RX_CTRL1_RXCLK2X_SEL | @@ -469,7 +473,7 @@ static int mvebu_comphy_set_mode_10gkr(struct phy *phy) val |= MVEBU_COMPHY_EXT_SELV_RX_SAMPL(0x1a); writel(val, priv->base + MVEBU_COMPHY_EXT_SELV(lane->id)); - return mvebu_comphy_init_plls(lane, PHY_MODE_10GKR); + return mvebu_comphy_init_plls(lane); } static int mvebu_comphy_power_on(struct phy *phy) @@ -479,7 +483,8 @@ static int mvebu_comphy_power_on(struct phy *phy) int ret, mux; u32 val; - mux = mvebu_comphy_get_mux(lane->id, lane->port, lane->mode); + mux = mvebu_comphy_get_mux(lane->id, lane->port, + lane->mode, lane->submode); if (mux < 0) return -ENOTSUPP; @@ -492,12 +497,12 @@ static int mvebu_comphy_power_on(struct phy *phy) val |= mux << MVEBU_COMPHY_SELECTOR_PHY(lane->id); regmap_write(priv->regmap, MVEBU_COMPHY_SELECTOR, val); - switch (lane->mode) { - case PHY_MODE_SGMII: - case PHY_MODE_2500SGMII: - ret = mvebu_comphy_set_mode_sgmii(phy, lane->mode); + switch (lane->submode) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_2500BASEX: + ret = mvebu_comphy_set_mode_sgmii(phy); break; - case PHY_MODE_10GKR: + case PHY_INTERFACE_MODE_10GKR: ret = mvebu_comphy_set_mode_10gkr(phy); break; default: @@ -517,10 +522,17 @@ static int mvebu_comphy_set_mode(struct phy *phy, { struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); - if (mvebu_comphy_get_mux(lane->id, lane->port, mode) < 0) + if (mode != PHY_MODE_ETHERNET) + return -EINVAL; + + if (submode == PHY_INTERFACE_MODE_1000BASEX) + submode = PHY_INTERFACE_MODE_SGMII; + + if (mvebu_comphy_get_mux(lane->id, lane->port, mode, submode) < 0) return -EINVAL; lane->mode = mode; + lane->submode = submode; return 0; } -- cgit v1.2.3-59-g8ed1b From 44d30d622821d3bf7fa74b62e2ea62bde314ec1b Mon Sep 17 00:00:00 2001 From: Alan Douglas Date: Mon, 12 Nov 2018 16:42:16 +0000 Subject: phy: cadence: Add driver for Sierra PHY Add a Sierra PHY driver with PCIe and USB support. The PHY has multiple lanes, which can be configured into groups, and a generic PHY device is created for each group. There are two resets controlling the overall PHY block, one to enable the APB interface for programming registers, and another to enable the PHY itself. Additionally there are resets for each PHY lane. The PHY can be configured in hardware to read register settings from ROM, or they can be written by the driver. The sequence of operation on startup is to enable the APB bus, write the PHY registers (if required) for each lane group, and then enable the PHY. Each group of lanes can then be individually controlled using the power_on()/ power_off() function for that generic PHY Signed-off-by: Alan Douglas Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/Kconfig | 9 +- drivers/phy/cadence/Makefile | 1 + drivers/phy/cadence/phy-cadence-sierra.c | 395 +++++++++++++++++++++++++++++++ 3 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 drivers/phy/cadence/phy-cadence-sierra.c (limited to 'drivers') diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig index 57fff7de4031..2b8c0851ff33 100644 --- a/drivers/phy/cadence/Kconfig +++ b/drivers/phy/cadence/Kconfig @@ -1,5 +1,5 @@ # -# Phy driver for Cadence MHDP DisplayPort controller +# Phy drivers for Cadence PHYs # config PHY_CADENCE_DP tristate "Cadence MHDP DisplayPort PHY driver" @@ -8,3 +8,10 @@ config PHY_CADENCE_DP select GENERIC_PHY help Support for Cadence MHDP DisplayPort PHY. + +config PHY_CADENCE_SIERRA + tristate "Cadence Sierra PHY Driver" + depends on OF && HAS_IOMEM && RESET_CONTROLLER + select GENERIC_PHY + help + Enable this to support the Cadence Sierra PHY driver \ No newline at end of file diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile index e5b0a11cf28a..412349af0492 100644 --- a/drivers/phy/cadence/Makefile +++ b/drivers/phy/cadence/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o +obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c new file mode 100644 index 000000000000..de10402f2931 --- /dev/null +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence Sierra PHY Driver + * + * Copyright (c) 2018 Cadence Design Systems + * Author: Alan Douglas + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PHY register offsets */ +#define SIERRA_PHY_PLL_CFG (0xc00e << 2) +#define SIERRA_DET_STANDEC_A (0x4000 << 2) +#define SIERRA_DET_STANDEC_B (0x4001 << 2) +#define SIERRA_DET_STANDEC_C (0x4002 << 2) +#define SIERRA_DET_STANDEC_D (0x4003 << 2) +#define SIERRA_DET_STANDEC_E (0x4004 << 2) +#define SIERRA_PSM_LANECAL (0x4008 << 2) +#define SIERRA_PSM_DIAG (0x4015 << 2) +#define SIERRA_PSC_TX_A0 (0x4028 << 2) +#define SIERRA_PSC_TX_A1 (0x4029 << 2) +#define SIERRA_PSC_TX_A2 (0x402A << 2) +#define SIERRA_PSC_TX_A3 (0x402B << 2) +#define SIERRA_PSC_RX_A0 (0x4030 << 2) +#define SIERRA_PSC_RX_A1 (0x4031 << 2) +#define SIERRA_PSC_RX_A2 (0x4032 << 2) +#define SIERRA_PSC_RX_A3 (0x4033 << 2) +#define SIERRA_PLLCTRL_SUBRATE (0x403A << 2) +#define SIERRA_PLLCTRL_GEN_D (0x403E << 2) +#define SIERRA_DRVCTRL_ATTEN (0x406A << 2) +#define SIERRA_CLKPATHCTRL_TMR (0x4081 << 2) +#define SIERRA_RX_CREQ_FLTR_A_MODE1 (0x4087 << 2) +#define SIERRA_RX_CREQ_FLTR_A_MODE0 (0x4088 << 2) +#define SIERRA_CREQ_CCLKDET_MODE01 (0x408E << 2) +#define SIERRA_RX_CTLE_MAINTENANCE (0x4091 << 2) +#define SIERRA_CREQ_FSMCLK_SEL (0x4092 << 2) +#define SIERRA_CTLELUT_CTRL (0x4098 << 2) +#define SIERRA_DFE_ECMP_RATESEL (0x40C0 << 2) +#define SIERRA_DFE_SMP_RATESEL (0x40C1 << 2) +#define SIERRA_DEQ_VGATUNE_CTRL (0x40E1 << 2) +#define SIERRA_TMRVAL_MODE3 (0x416E << 2) +#define SIERRA_TMRVAL_MODE2 (0x416F << 2) +#define SIERRA_TMRVAL_MODE1 (0x4170 << 2) +#define SIERRA_TMRVAL_MODE0 (0x4171 << 2) +#define SIERRA_PICNT_MODE1 (0x4174 << 2) +#define SIERRA_CPI_OUTBUF_RATESEL (0x417C << 2) +#define SIERRA_LFPSFILT_NS (0x418A << 2) +#define SIERRA_LFPSFILT_RD (0x418B << 2) +#define SIERRA_LFPSFILT_MP (0x418C << 2) +#define SIERRA_SDFILT_H2L_A (0x4191 << 2) + +#define SIERRA_MACRO_ID 0x00007364 +#define SIERRA_MAX_LANES 4 + +struct cdns_sierra_inst { + struct phy *phy; + u32 phy_type; + u32 num_lanes; + u32 mlane; + struct reset_control *lnk_rst; +}; + +struct cdns_reg_pairs { + u16 val; + u32 off; +}; + +struct cdns_sierra_data { + u32 id_value; + u32 pcie_regs; + u32 usb_regs; + struct cdns_reg_pairs *pcie_vals; + struct cdns_reg_pairs *usb_vals; +}; + +struct cdns_sierra_phy { + struct device *dev; + void __iomem *base; + struct cdns_sierra_data *init_data; + struct cdns_sierra_inst phys[SIERRA_MAX_LANES]; + struct reset_control *phy_rst; + struct reset_control *apb_rst; + struct clk *clk; + int nsubnodes; + bool autoconf; +}; + +static void cdns_sierra_phy_init(struct phy *gphy) +{ + struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); + struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent); + int i, j; + struct cdns_reg_pairs *vals; + u32 num_regs; + + if (ins->phy_type == PHY_TYPE_PCIE) { + num_regs = phy->init_data->pcie_regs; + vals = phy->init_data->pcie_vals; + } else if (ins->phy_type == PHY_TYPE_USB3) { + num_regs = phy->init_data->usb_regs; + vals = phy->init_data->usb_vals; + } else { + return; + } + for (i = 0; i < ins->num_lanes; i++) + for (j = 0; j < num_regs ; j++) + writel(vals[j].val, phy->base + + vals[j].off + (i + ins->mlane) * 0x800); +} + +static int cdns_sierra_phy_on(struct phy *gphy) +{ + struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); + + /* Take the PHY lane group out of reset */ + return reset_control_deassert(ins->lnk_rst); +} + +static int cdns_sierra_phy_off(struct phy *gphy) +{ + struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); + + return reset_control_assert(ins->lnk_rst); +} + +static const struct phy_ops ops = { + .power_on = cdns_sierra_phy_on, + .power_off = cdns_sierra_phy_off, + .owner = THIS_MODULE, +}; + +static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst, + struct device_node *child) +{ + if (of_property_read_u32(child, "reg", &inst->mlane)) + return -EINVAL; + + if (of_property_read_u32(child, "cdns,num-lanes", &inst->num_lanes)) + return -EINVAL; + + if (of_property_read_u32(child, "cdns,phy-type", &inst->phy_type)) + return -EINVAL; + + return 0; +} + +static const struct of_device_id cdns_sierra_id_table[]; + +static int cdns_sierra_phy_probe(struct platform_device *pdev) +{ + struct cdns_sierra_phy *sp; + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct resource *res; + int i, ret, node = 0; + struct device_node *dn = dev->of_node, *child; + + if (of_get_child_count(dn) == 0) + return -ENODEV; + + sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL); + if (!sp) + return -ENOMEM; + dev_set_drvdata(dev, sp); + sp->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sp->base = devm_ioremap_resource(dev, res); + if (IS_ERR(sp->base)) { + dev_err(dev, "missing \"reg\"\n"); + return PTR_ERR(sp->base); + } + + /* Get init data for this PHY */ + match = of_match_device(cdns_sierra_id_table, dev); + if (!match) + return -EINVAL; + sp->init_data = (struct cdns_sierra_data *)match->data; + + platform_set_drvdata(pdev, sp); + + sp->clk = devm_clk_get(dev, "phy_clk"); + if (IS_ERR(sp->clk)) { + dev_err(dev, "failed to get clock phy_clk\n"); + return PTR_ERR(sp->clk); + } + + sp->phy_rst = devm_reset_control_get(dev, "sierra_reset"); + if (IS_ERR(sp->phy_rst)) { + dev_err(dev, "failed to get reset\n"); + return PTR_ERR(sp->phy_rst); + } + + sp->apb_rst = devm_reset_control_get(dev, "sierra_apb"); + if (IS_ERR(sp->apb_rst)) { + dev_err(dev, "failed to get apb reset\n"); + return PTR_ERR(sp->apb_rst); + } + + ret = clk_prepare_enable(sp->clk); + if (ret) + return ret; + + /* Enable APB */ + reset_control_deassert(sp->apb_rst); + + /* Check that PHY is present */ + if (sp->init_data->id_value != readl(sp->base)) { + ret = -EINVAL; + goto clk_disable; + } + + sp->autoconf = of_property_read_bool(dn, "cdns,autoconf"); + + for_each_available_child_of_node(dn, child) { + struct phy *gphy; + + sp->phys[node].lnk_rst = + of_reset_control_get_exclusive_by_index(child, 0); + + if (IS_ERR(sp->phys[node].lnk_rst)) { + dev_err(dev, "failed to get reset %s\n", + child->full_name); + ret = PTR_ERR(sp->phys[node].lnk_rst); + goto put_child2; + } + + if (!sp->autoconf) { + ret = cdns_sierra_get_optional(&sp->phys[node], child); + if (ret) { + dev_err(dev, "missing property in node %s\n", + child->name); + goto put_child; + } + } + + gphy = devm_phy_create(dev, child, &ops); + + if (IS_ERR(gphy)) { + ret = PTR_ERR(gphy); + goto put_child; + } + sp->phys[node].phy = gphy; + phy_set_drvdata(gphy, &sp->phys[node]); + + /* Initialise the PHY registers, unless auto configured */ + if (!sp->autoconf) + cdns_sierra_phy_init(gphy); + + node++; + } + sp->nsubnodes = node; + + /* If more than one subnode, configure the PHY as multilink */ + if (!sp->autoconf && sp->nsubnodes > 1) + writel(2, sp->base + SIERRA_PHY_PLL_CFG); + + pm_runtime_enable(dev); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + reset_control_deassert(sp->phy_rst); + return PTR_ERR_OR_ZERO(phy_provider); + +put_child: + node++; +put_child2: + for (i = 0; i < node; i++) + reset_control_put(sp->phys[i].lnk_rst); + of_node_put(child); +clk_disable: + clk_disable_unprepare(sp->clk); + reset_control_assert(sp->apb_rst); + return ret; +} + +static int cdns_sierra_phy_remove(struct platform_device *pdev) +{ + struct cdns_sierra_phy *phy = dev_get_drvdata(pdev->dev.parent); + int i; + + reset_control_assert(phy->phy_rst); + reset_control_assert(phy->apb_rst); + pm_runtime_disable(&pdev->dev); + + /* + * The device level resets will be put automatically. + * Need to put the subnode resets here though. + */ + for (i = 0; i < phy->nsubnodes; i++) { + reset_control_assert(phy->phys[i].lnk_rst); + reset_control_put(phy->phys[i].lnk_rst); + } + return 0; +} + +static struct cdns_reg_pairs cdns_usb_regs[] = { + /* + * Write USB configuration parameters to the PHY. + * These values are specific to this specific hardware + * configuration. + */ + {0xFE0A, SIERRA_DET_STANDEC_A}, + {0x000F, SIERRA_DET_STANDEC_B}, + {0x55A5, SIERRA_DET_STANDEC_C}, + {0x69AD, SIERRA_DET_STANDEC_D}, + {0x0241, SIERRA_DET_STANDEC_E}, + {0x0110, SIERRA_PSM_LANECAL}, + {0xCF00, SIERRA_PSM_DIAG}, + {0x001F, SIERRA_PSC_TX_A0}, + {0x0007, SIERRA_PSC_TX_A1}, + {0x0003, SIERRA_PSC_TX_A2}, + {0x0003, SIERRA_PSC_TX_A3}, + {0x0FFF, SIERRA_PSC_RX_A0}, + {0x0003, SIERRA_PSC_RX_A1}, + {0x0003, SIERRA_PSC_RX_A2}, + {0x0001, SIERRA_PSC_RX_A3}, + {0x0001, SIERRA_PLLCTRL_SUBRATE}, + {0x0406, SIERRA_PLLCTRL_GEN_D}, + {0x0000, SIERRA_DRVCTRL_ATTEN}, + {0x823E, SIERRA_CLKPATHCTRL_TMR}, + {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1}, + {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0}, + {0x7B3C, SIERRA_CREQ_CCLKDET_MODE01}, + {0x023C, SIERRA_RX_CTLE_MAINTENANCE}, + {0x3232, SIERRA_CREQ_FSMCLK_SEL}, + {0x8452, SIERRA_CTLELUT_CTRL}, + {0x4121, SIERRA_DFE_ECMP_RATESEL}, + {0x4121, SIERRA_DFE_SMP_RATESEL}, + {0x9999, SIERRA_DEQ_VGATUNE_CTRL}, + {0x0330, SIERRA_TMRVAL_MODE0}, + {0x01FF, SIERRA_PICNT_MODE1}, + {0x0009, SIERRA_CPI_OUTBUF_RATESEL}, + {0x000F, SIERRA_LFPSFILT_NS}, + {0x0009, SIERRA_LFPSFILT_RD}, + {0x0001, SIERRA_LFPSFILT_MP}, + {0x8013, SIERRA_SDFILT_H2L_A}, + {0x0400, SIERRA_TMRVAL_MODE1}, +}; + +static struct cdns_reg_pairs cdns_pcie_regs[] = { + /* + * Write PCIe configuration parameters to the PHY. + * These values are specific to this specific hardware + * configuration. + */ + {0x891f, SIERRA_DET_STANDEC_D}, + {0x0053, SIERRA_DET_STANDEC_E}, + {0x0400, SIERRA_TMRVAL_MODE2}, + {0x0200, SIERRA_TMRVAL_MODE3}, +}; + +static const struct cdns_sierra_data cdns_map_sierra = { + SIERRA_MACRO_ID, + ARRAY_SIZE(cdns_pcie_regs), + ARRAY_SIZE(cdns_usb_regs), + cdns_pcie_regs, + cdns_usb_regs +}; + +static const struct of_device_id cdns_sierra_id_table[] = { + { + .compatible = "cdns,sierra-phy-t0", + .data = &cdns_map_sierra, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cdns_sierra_id_table); + +static struct platform_driver cdns_sierra_driver = { + .probe = cdns_sierra_phy_probe, + .remove = cdns_sierra_phy_remove, + .driver = { + .name = "cdns-sierra-phy", + .of_match_table = cdns_sierra_id_table, + }, +}; +module_platform_driver(cdns_sierra_driver); + +MODULE_ALIAS("platform:cdns_sierra"); +MODULE_AUTHOR("Cadence Design Systems"); +MODULE_DESCRIPTION("CDNS sierra phy driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 72c0339c115b31b3c0b22b1809854136cadd49be Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 30 Nov 2018 16:00:57 +0900 Subject: phy: renesas: rcar-gen3-usb2: follow the hardware manual procedure This patch modifies rcar_gen3_init_otg() procedure to follow Figure 73.4 of "R-Car Series, 3rd Generation User's Manual: Hardware Rev.1.00". Signed-off-by: Yoshihiro Shimoda Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index d0f412c25981..0a34782aaaa2 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -307,16 +307,21 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) void __iomem *usb2_base = ch->base; u32 val; + /* Should not use functions of read-modify-write a register */ + val = readl(usb2_base + USB2_LINECTRL1); + val = (val & ~USB2_LINECTRL1_DP_RPD) | USB2_LINECTRL1_DPRPD_EN | + USB2_LINECTRL1_DMRPD_EN | USB2_LINECTRL1_DM_RPD; + writel(val, usb2_base + USB2_LINECTRL1); + val = readl(usb2_base + USB2_VBCTRL); writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); - writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); - rcar_gen3_control_otg_irq(ch, 1); val = readl(usb2_base + USB2_ADPCTRL); writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); - val = readl(usb2_base + USB2_LINECTRL1); - rcar_gen3_set_linectrl(ch, 0, 0); - writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN, - usb2_base + USB2_LINECTRL1); + + msleep(20); + + writel(0xffffffff, usb2_base + USB2_OBINTSTA); + writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); rcar_gen3_device_recognition(ch); } -- cgit v1.2.3-59-g8ed1b From 2ad2af0816221ac9ce9d5c8b979868b58a696c40 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 2 Dec 2018 15:51:35 -0800 Subject: phy: mapphone-mdm6600: Improve phy related runtime PM calls I noticed that phy_pm_runtime_get_sync() and phy_pm_runtime_put() are not currently doing anything for phy-mapphone-mdm6600, only the sysfs interface for works for "auto" and "on". This is because of the shared GPIO pins between mdm6600 USB port and n_gsm port. We have not enabled runtime PM for the phy driver until after we've booted up mdm6600 properly to the USB mode. Otherwise phy_create() would have called pm_runtime_enable() and pm_runtime_no_callbacks() automatically on init. Let's fix this by registering the phy a bit later after we've powered up the mdm6600 USB port. And as the PM runtime support is only needed for the n_gsm mode and not for USB, we can allow the device to idle between phy_mdm6600_power_on() and phy_mdm6600_power_off(). Note that for suspend, runtime_pm is already disabled for the phy so we need to check for pm_runtime_enabled(). Cc: Johan Hovold Cc: Pavel Machek Cc: Sebastian Reichel Signed-off-by: Tony Lindgren Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/motorola/phy-mapphone-mdm6600.c | 71 +++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c index 25d456a323c2..ee184d5607bd 100644 --- a/drivers/phy/motorola/phy-mapphone-mdm6600.c +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -16,6 +16,7 @@ #include #include #include +#include #define PHY_MDM6600_PHY_DELAY_MS 4000 /* PHY enable 2.2s to 3.5s */ #define PHY_MDM6600_ENABLED_DELAY_MS 8000 /* 8s more total for MDM6600 */ @@ -120,12 +121,22 @@ static int phy_mdm6600_power_on(struct phy *x) { struct phy_mdm6600 *ddata = phy_get_drvdata(x); struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE]; + int error; if (!ddata->enabled) return -ENODEV; + error = pinctrl_pm_select_default_state(ddata->dev); + if (error) + dev_warn(ddata->dev, "%s: error with default_state: %i\n", + __func__, error); + gpiod_set_value_cansleep(enable_gpio, 1); + /* Allow aggressive PM for USB, it's only needed for n_gsm port */ + if (pm_runtime_enabled(&x->dev)) + phy_pm_runtime_put(x); + return 0; } @@ -133,12 +144,26 @@ static int phy_mdm6600_power_off(struct phy *x) { struct phy_mdm6600 *ddata = phy_get_drvdata(x); struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE]; + int error; if (!ddata->enabled) return -ENODEV; + /* Paired with phy_pm_runtime_put() in phy_mdm6600_power_on() */ + if (pm_runtime_enabled(&x->dev)) { + error = phy_pm_runtime_get(x); + if (error < 0 && error != -EINPROGRESS) + dev_warn(ddata->dev, "%s: phy_pm_runtime_get: %i\n", + __func__, error); + } + gpiod_set_value_cansleep(enable_gpio, 0); + error = pinctrl_pm_select_sleep_state(ddata->dev); + if (error) + dev_warn(ddata->dev, "%s: error with sleep_state: %i\n", + __func__, error); + return 0; } @@ -529,28 +554,17 @@ static int phy_mdm6600_probe(struct platform_device *pdev) ddata->dev = &pdev->dev; platform_set_drvdata(pdev, ddata); + /* Active state selected in phy_mdm6600_power_on() */ + error = pinctrl_pm_select_sleep_state(ddata->dev); + if (error) + dev_warn(ddata->dev, "%s: error with sleep_state: %i\n", + __func__, error); + error = phy_mdm6600_init_lines(ddata); if (error) return error; phy_mdm6600_init_irq(ddata); - - ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops); - if (IS_ERR(ddata->generic_phy)) { - error = PTR_ERR(ddata->generic_phy); - goto cleanup; - } - - phy_set_drvdata(ddata->generic_phy, ddata); - - ddata->phy_provider = - devm_of_phy_provider_register(ddata->dev, - of_phy_simple_xlate); - if (IS_ERR(ddata->phy_provider)) { - error = PTR_ERR(ddata->phy_provider); - goto cleanup; - } - schedule_delayed_work(&ddata->bootup_work, 0); /* @@ -574,14 +588,31 @@ static int phy_mdm6600_probe(struct platform_device *pdev) if (error < 0) { dev_warn(ddata->dev, "failed to wake modem: %i\n", error); pm_runtime_put_noidle(ddata->dev); + goto cleanup; } + + ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops); + if (IS_ERR(ddata->generic_phy)) { + error = PTR_ERR(ddata->generic_phy); + goto idle; + } + + phy_set_drvdata(ddata->generic_phy, ddata); + + ddata->phy_provider = + devm_of_phy_provider_register(ddata->dev, + of_phy_simple_xlate); + if (IS_ERR(ddata->phy_provider)) + error = PTR_ERR(ddata->phy_provider); + +idle: pm_runtime_mark_last_busy(ddata->dev); pm_runtime_put_autosuspend(ddata->dev); - return 0; - cleanup: - phy_mdm6600_device_power_off(ddata); + if (error < 0) + phy_mdm6600_device_power_off(ddata); + return error; } -- cgit v1.2.3-59-g8ed1b From c5e18b3413b56ac9a8eadb96c44fcda26d1b49ff Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 23 Nov 2018 10:21:15 +0100 Subject: phy: mvebu-cp110-comphy: fix spelling in structure name Rename the mvebu_comhy_conf structure to be mvebu_comphy_conf, which is probably what the original author meant. Signed-off-by: Miquel Raynal Acked-by: Antoine Tenart Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/marvell/phy-mvebu-cp110-comphy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c index 2b4462a28a58..187cccde53b5 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c @@ -115,7 +115,7 @@ #define MVEBU_COMPHY_LANES 6 #define MVEBU_COMPHY_PORTS 3 -struct mvebu_comhy_conf { +struct mvebu_comphy_conf { enum phy_mode mode; int submode; unsigned lane; @@ -132,7 +132,7 @@ struct mvebu_comhy_conf { .mux = _mux, \ } -static const struct mvebu_comhy_conf mvebu_comphy_cp110_modes[] = { +static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = { /* lane 0 */ MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1), MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1), -- cgit v1.2.3-59-g8ed1b From 92b58b34741ff5b9efa583add6e63ca4103f8e29 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Sun, 25 Nov 2018 18:15:23 -0600 Subject: phy: ti: introduce phy-gmii-sel driver TI am335x/am437x/dra7(am5)/dm814x CPSW3G Ethernet Subsystem supports two 10/100/1000 Ethernet ports with selectable G/MII, RMII, and RGMII interfaces. The interface mode is selected by configuring the MII mode selection register(s) (GMII_SEL) in the System Control Module chapter (SCM). GMII_SEL register(s) and bit fields placement in SCM are different between SoCs while fields meaning is the same. Historically CPSW external Port's interface mode selection configuration was introduced using custom API and driver cpsw-phy-sel.c. This leads to unnecessary driver, DT binding and custom API support effort. This patch introduces CPSW Port's PHY Interface Mode selection Driver (phy-gmii-sel) which implements standard Linux PHY interface and used as a replacement for TI's specific driver cpsw-phy-sel.c and corresponding custom API. Cc: Kishon Vijay Abraham I Cc: Tony Lindgren Signed-off-by: Grygorii Strashko Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/ti/Kconfig | 10 ++ drivers/phy/ti/Makefile | 1 + drivers/phy/ti/phy-gmii-sel.c | 349 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 360 insertions(+) create mode 100644 drivers/phy/ti/phy-gmii-sel.c (limited to 'drivers') diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index 20503562666c..f137e0107764 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -76,3 +76,13 @@ config TWL4030_USB family chips (including the TWL5030 and TPS659x0 devices). This transceiver supports high and full speed devices plus, in host mode, low speed. + +config PHY_TI_GMII_SEL + tristate + default y if TI_CPSW=y + depends on TI_CPSW || COMPILE_TEST + select GENERIC_PHY + default m + help + This driver supports configuring of the TI CPSW Port mode depending on + the Ethernet PHY connected to the CPSW Port. diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile index 9f361756eaf2..bea8f25a137a 100644 --- a/drivers/phy/ti/Makefile +++ b/drivers/phy/ti/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o +obj-$(CONFIG_PHY_TI_GMII_SEL) += phy-gmii-sel.o diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c new file mode 100644 index 000000000000..04ebf53d2b32 --- /dev/null +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments CPSW Port's PHY Interface Mode selection Driver + * + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * + * Based on cpsw-phy-sel.c driver created by Mugunthan V N + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* AM33xx SoC specific definitions for the CONTROL port */ +#define AM33XX_GMII_SEL_MODE_MII 0 +#define AM33XX_GMII_SEL_MODE_RMII 1 +#define AM33XX_GMII_SEL_MODE_RGMII 2 + +enum { + PHY_GMII_SEL_PORT_MODE, + PHY_GMII_SEL_RGMII_ID_MODE, + PHY_GMII_SEL_RMII_IO_CLK_EN, + PHY_GMII_SEL_LAST, +}; + +struct phy_gmii_sel_phy_priv { + struct phy_gmii_sel_priv *priv; + u32 id; + struct phy *if_phy; + int rmii_clock_external; + int phy_if_mode; + struct regmap_field *fields[PHY_GMII_SEL_LAST]; +}; + +struct phy_gmii_sel_soc_data { + u32 num_ports; + u32 features; + const struct reg_field (*regfields)[PHY_GMII_SEL_LAST]; +}; + +struct phy_gmii_sel_priv { + struct device *dev; + const struct phy_gmii_sel_soc_data *soc_data; + struct regmap *regmap; + struct phy_provider *phy_provider; + struct phy_gmii_sel_phy_priv *if_phys; +}; + +static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct phy_gmii_sel_phy_priv *if_phy = phy_get_drvdata(phy); + const struct phy_gmii_sel_soc_data *soc_data = if_phy->priv->soc_data; + struct device *dev = if_phy->priv->dev; + struct regmap_field *regfield; + int ret, rgmii_id = 0; + u32 gmii_sel_mode = 0; + + if (mode != PHY_MODE_ETHERNET) + return -EINVAL; + + switch (submode) { + case PHY_INTERFACE_MODE_RMII: + gmii_sel_mode = AM33XX_GMII_SEL_MODE_RMII; + break; + + case PHY_INTERFACE_MODE_RGMII: + gmii_sel_mode = AM33XX_GMII_SEL_MODE_RGMII; + break; + + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + gmii_sel_mode = AM33XX_GMII_SEL_MODE_RGMII; + rgmii_id = 1; + break; + + case PHY_INTERFACE_MODE_MII: + mode = AM33XX_GMII_SEL_MODE_MII; + break; + + default: + dev_warn(dev, + "port%u: unsupported mode: \"%s\". Defaulting to MII.\n", + if_phy->id, phy_modes(rgmii_id)); + return -EINVAL; + }; + + if_phy->phy_if_mode = submode; + + dev_dbg(dev, "%s id:%u mode:%u rgmii_id:%d rmii_clk_ext:%d\n", + __func__, if_phy->id, mode, rgmii_id, + if_phy->rmii_clock_external); + + regfield = if_phy->fields[PHY_GMII_SEL_PORT_MODE]; + ret = regmap_field_write(regfield, gmii_sel_mode); + if (ret) { + dev_err(dev, "port%u: set mode fail %d", if_phy->id, ret); + return ret; + } + + if (soc_data->features & BIT(PHY_GMII_SEL_RGMII_ID_MODE) && + if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE]) { + regfield = if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE]; + ret = regmap_field_write(regfield, rgmii_id); + if (ret) + return ret; + } + + if (soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) && + if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN]) { + regfield = if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN]; + ret = regmap_field_write(regfield, + if_phy->rmii_clock_external); + } + + return 0; +} + +static const +struct reg_field phy_gmii_sel_fields_am33xx[][PHY_GMII_SEL_LAST] = { + { + [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x650, 0, 1), + [PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD(0x650, 4, 4), + [PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD(0x650, 6, 6), + }, + { + [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x650, 2, 3), + [PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD(0x650, 5, 5), + [PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD(0x650, 7, 7), + }, +}; + +static const +struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am33xx = { + .num_ports = 2, + .features = BIT(PHY_GMII_SEL_RGMII_ID_MODE) | + BIT(PHY_GMII_SEL_RMII_IO_CLK_EN), + .regfields = phy_gmii_sel_fields_am33xx, +}; + +static const +struct reg_field phy_gmii_sel_fields_dra7[][PHY_GMII_SEL_LAST] = { + { + [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 0, 1), + [PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0), + [PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0), + }, + { + [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 4, 5), + [PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0), + [PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0), + }, +}; + +static const +struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dra7 = { + .num_ports = 2, + .regfields = phy_gmii_sel_fields_dra7, +}; + +static const +struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814 = { + .num_ports = 2, + .features = BIT(PHY_GMII_SEL_RGMII_ID_MODE), + .regfields = phy_gmii_sel_fields_am33xx, +}; + +static const struct of_device_id phy_gmii_sel_id_table[] = { + { + .compatible = "ti,am3352-phy-gmii-sel", + .data = &phy_gmii_sel_soc_am33xx, + }, + { + .compatible = "ti,dra7xx-phy-gmii-sel", + .data = &phy_gmii_sel_soc_dra7, + }, + { + .compatible = "ti,am43xx-phy-gmii-sel", + .data = &phy_gmii_sel_soc_am33xx, + }, + { + .compatible = "ti,dm814-phy-gmii-sel", + .data = &phy_gmii_sel_soc_dm814, + }, + {} +}; +MODULE_DEVICE_TABLE(of, phy_gmii_sel_id_table); + +static const struct phy_ops phy_gmii_sel_ops = { + .set_mode = phy_gmii_sel_mode, + .owner = THIS_MODULE, +}; + +static struct phy *phy_gmii_sel_of_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct phy_gmii_sel_priv *priv = dev_get_drvdata(dev); + int phy_id = args->args[0]; + + if (args->args_count < 1) + return ERR_PTR(-EINVAL); + if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) && + args->args_count < 2) + return ERR_PTR(-EINVAL); + if (!priv || !priv->if_phys) + return ERR_PTR(-ENODEV); + if (phy_id > priv->soc_data->num_ports) + return ERR_PTR(-EINVAL); + if (phy_id != priv->if_phys[phy_id - 1].id) + return ERR_PTR(-EINVAL); + + phy_id--; + if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN)) + priv->if_phys[phy_id].rmii_clock_external = args->args[1]; + dev_dbg(dev, "%s id:%u ext:%d\n", __func__, + priv->if_phys[phy_id].id, args->args[1]); + + return priv->if_phys[phy_id].if_phy; +} + +static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv) +{ + const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data; + struct device *dev = priv->dev; + struct phy_gmii_sel_phy_priv *if_phys; + int i, num_ports, ret; + + num_ports = priv->soc_data->num_ports; + + if_phys = devm_kcalloc(priv->dev, num_ports, + sizeof(*if_phys), GFP_KERNEL); + if (!if_phys) + return -ENOMEM; + dev_dbg(dev, "%s %d\n", __func__, num_ports); + + for (i = 0; i < num_ports; i++) { + const struct reg_field *field; + struct regmap_field *regfield; + + if_phys[i].id = i + 1; + if_phys[i].priv = priv; + + field = &soc_data->regfields[i][PHY_GMII_SEL_PORT_MODE]; + dev_dbg(dev, "%s field %x %d %d\n", __func__, + field->reg, field->msb, field->lsb); + + regfield = devm_regmap_field_alloc(dev, priv->regmap, *field); + if (IS_ERR(regfield)) + return PTR_ERR(regfield); + if_phys[i].fields[PHY_GMII_SEL_PORT_MODE] = regfield; + + field = &soc_data->regfields[i][PHY_GMII_SEL_RGMII_ID_MODE]; + if (field->reg != (~0)) { + regfield = devm_regmap_field_alloc(dev, + priv->regmap, + *field); + if (IS_ERR(regfield)) + return PTR_ERR(regfield); + if_phys[i].fields[PHY_GMII_SEL_RGMII_ID_MODE] = + regfield; + } + + field = &soc_data->regfields[i][PHY_GMII_SEL_RMII_IO_CLK_EN]; + if (field->reg != (~0)) { + regfield = devm_regmap_field_alloc(dev, + priv->regmap, + *field); + if (IS_ERR(regfield)) + return PTR_ERR(regfield); + if_phys[i].fields[PHY_GMII_SEL_RMII_IO_CLK_EN] = + regfield; + } + + if_phys[i].if_phy = devm_phy_create(dev, + priv->dev->of_node, + &phy_gmii_sel_ops); + if (IS_ERR(if_phys[i].if_phy)) { + ret = PTR_ERR(if_phys[i].if_phy); + dev_err(dev, "Failed to create phy%d %d\n", i, ret); + return ret; + } + phy_set_drvdata(if_phys[i].if_phy, &if_phys[i]); + } + + priv->if_phys = if_phys; + return 0; +} + +static int phy_gmii_sel_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + const struct of_device_id *of_id; + struct phy_gmii_sel_priv *priv; + int ret; + + of_id = of_match_node(phy_gmii_sel_id_table, pdev->dev.of_node); + if (!of_id) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->soc_data = of_id->data; + + priv->regmap = syscon_node_to_regmap(node->parent); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(dev, "Failed to get syscon %d\n", ret); + return ret; + } + + ret = phy_gmii_sel_init_ports(priv); + if (ret) + return ret; + + dev_set_drvdata(&pdev->dev, priv); + + priv->phy_provider = + devm_of_phy_provider_register(dev, + phy_gmii_sel_of_xlate); + if (IS_ERR(priv->phy_provider)) { + ret = PTR_ERR(priv->phy_provider); + dev_err(dev, "Failed to create phy provider %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver phy_gmii_sel_driver = { + .probe = phy_gmii_sel_probe, + .driver = { + .name = "phy-gmii-sel", + .of_match_table = phy_gmii_sel_id_table, + }, +}; +module_platform_driver(phy_gmii_sel_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Grygorii Strashko "); +MODULE_DESCRIPTION("TI CPSW Port's PHY Interface Mode selection Driver"); -- cgit v1.2.3-59-g8ed1b From 3ff18849eb658b16ff4a22da9360bba5dc68a2d2 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Sun, 25 Nov 2018 18:15:25 -0600 Subject: net: ethernet: ti: cpsw: add support for port interface mode selection phy Add support for port interface mode selection phy (phy-gmii-sel): - try to request interface mode selection phy from Port DT node and fail silently if not defined and old CONFIG_TI_CPSW_PHY_SEL driver enabled. - use new phy if requested successfully. Cc: Kishon Vijay Abraham I Cc: Tony Lindgren Signed-off-by: Grygorii Strashko Signed-off-by: Kishon Vijay Abraham I --- drivers/net/ethernet/ti/cpsw.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 500f7ed8c58c..94e5e5b791ec 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -387,6 +388,7 @@ struct cpsw_slave_data { int phy_if; u8 mac_addr[ETH_ALEN]; u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */ + struct phy *ifphy; }; struct cpsw_platform_data { @@ -1510,7 +1512,12 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) phy_start(slave->phy); /* Configure GMII_SEL register */ - cpsw_phy_sel(cpsw->dev, slave->phy->interface, slave->slave_num); + if (!IS_ERR(slave->data->ifphy)) + phy_set_mode_ext(slave->data->ifphy, PHY_MODE_ETHERNET, + slave->data->phy_if); + else + cpsw_phy_sel(cpsw->dev, slave->phy->interface, + slave->slave_num); } static inline void cpsw_add_default_vlan(struct cpsw_priv *priv) @@ -3147,6 +3154,16 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, if (strcmp(slave_node->name, "slave")) continue; + slave_data->ifphy = devm_of_phy_get(&pdev->dev, slave_node, + NULL); + if (!IS_ENABLED(CONFIG_TI_CPSW_PHY_SEL) && + IS_ERR(slave_data->ifphy)) { + ret = PTR_ERR(slave_data->ifphy); + dev_err(&pdev->dev, + "%d: Error retrieving port phy: %d\n", i, ret); + return ret; + } + slave_data->phy_node = of_parse_phandle(slave_node, "phy-handle", 0); parp = of_get_property(slave_node, "phy_id", &lenp); -- cgit v1.2.3-59-g8ed1b From 03e7d002526dc447902d15aba7d24919d9a13f92 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 5 Dec 2018 13:50:35 -0600 Subject: phy: Use of_node_name_eq for node name comparisons Convert string compares of DT node names to use of_node_name_eq helper instead. This removes direct access to the node name pointer. For instances using of_node_cmp, this has the side effect of now using case sensitive comparisons. This should not matter for any FDT based system which all of these are. Cc: Kishon Vijay Abraham I Cc: Heiko Stuebner Cc: linux-arm-kernel@lists.infradead.org Cc: linux-rockchip@lists.infradead.org Signed-off-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 6 +++--- drivers/phy/rockchip/phy-rockchip-typec.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 24bd2717abdb..91fba60267a0 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -1168,8 +1168,8 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) struct phy *phy; /* This driver aims to support both otg-port and host-port */ - if (of_node_cmp(child_np->name, "host-port") && - of_node_cmp(child_np->name, "otg-port")) + if (!of_node_name_eq(child_np, "host-port") && + !of_node_name_eq(child_np, "otg-port")) goto next_child; phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); @@ -1183,7 +1183,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) phy_set_drvdata(rport->phy, rport); /* initialize otg/host port separately */ - if (!of_node_cmp(child_np->name, "host-port")) { + if (of_node_name_eq(child_np, "host-port")) { ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np); if (ret) diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index c57e496f0b0c..e32edeebcd63 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -1176,10 +1176,10 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) for_each_available_child_of_node(np, child_np) { struct phy *phy; - if (!of_node_cmp(child_np->name, "dp-port")) + if (of_node_name_eq(child_np, "dp-port")) phy = devm_phy_create(dev, child_np, &rockchip_dp_phy_ops); - else if (!of_node_cmp(child_np->name, "usb3-port")) + else if (of_node_name_eq(child_np, "usb3-port")) phy = devm_phy_create(dev, child_np, &rockchip_usb3_phy_ops); else -- cgit v1.2.3-59-g8ed1b From efe81bea891586680a928ea5dde40eb1fff34be2 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Thu, 15 Nov 2018 15:12:47 +0100 Subject: phy: add driver for Freescale i.MX8MQ USB3 PHY This is a cleaned up port of the downstream i.MX8MQ USB3 PHY driver. Signed-off-by: Li Jun Signed-off-by: Lucas Stach Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 1 + drivers/phy/freescale/Kconfig | 5 ++ drivers/phy/freescale/Makefile | 1 + drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 127 +++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 drivers/phy/freescale/Kconfig create mode 100644 drivers/phy/freescale/Makefile create mode 100644 drivers/phy/freescale/phy-fsl-imx8mq-usb.c (limited to 'drivers') diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 60f949e2a684..eaf0778a18d4 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -44,6 +44,7 @@ source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/broadcom/Kconfig" source "drivers/phy/cadence/Kconfig" +source "drivers/phy/freescale/Kconfig" source "drivers/phy/hisilicon/Kconfig" source "drivers/phy/lantiq/Kconfig" source "drivers/phy/marvell/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 0301e25d07c1..84acb3761457 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-y += broadcom/ \ cadence/ \ + freescale/ \ hisilicon/ \ marvell/ \ motorola/ \ diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig new file mode 100644 index 000000000000..f050bd4e97e0 --- /dev/null +++ b/drivers/phy/freescale/Kconfig @@ -0,0 +1,5 @@ +config PHY_FSL_IMX8MQ_USB + tristate "Freescale i.MX8M USB3 PHY" + depends on OF && HAS_IOMEM + select GENERIC_PHY + default SOC_IMX8MQ diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile new file mode 100644 index 000000000000..dc2b3f1f2f80 --- /dev/null +++ b/drivers/phy/freescale/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c new file mode 100644 index 000000000000..d6ea5ce8afa5 --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2017 NXP. */ + +#include +#include +#include +#include +#include + +#define PHY_CTRL0 0x0 +#define PHY_CTRL0_REF_SSP_EN BIT(2) + +#define PHY_CTRL1 0x4 +#define PHY_CTRL1_RESET BIT(0) +#define PHY_CTRL1_COMMONONN BIT(1) +#define PHY_CTRL1_ATERESET BIT(3) +#define PHY_CTRL1_VDATSRCENB0 BIT(19) +#define PHY_CTRL1_VDATDETENB0 BIT(20) + +#define PHY_CTRL2 0x8 +#define PHY_CTRL2_TXENABLEN0 BIT(8) + +struct imx8mq_usb_phy { + struct phy *phy; + struct clk *clk; + void __iomem *base; +}; + +static int imx8mq_usb_phy_init(struct phy *phy) +{ + struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + u32 value; + + value = readl(imx_phy->base + PHY_CTRL1); + value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 | + PHY_CTRL1_COMMONONN); + value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; + writel(value, imx_phy->base + PHY_CTRL1); + + value = readl(imx_phy->base + PHY_CTRL0); + value |= PHY_CTRL0_REF_SSP_EN; + writel(value, imx_phy->base + PHY_CTRL0); + + value = readl(imx_phy->base + PHY_CTRL2); + value |= PHY_CTRL2_TXENABLEN0; + writel(value, imx_phy->base + PHY_CTRL2); + + value = readl(imx_phy->base + PHY_CTRL1); + value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); + writel(value, imx_phy->base + PHY_CTRL1); + + return 0; +} + +static int imx8mq_phy_power_on(struct phy *phy) +{ + struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + + return clk_prepare_enable(imx_phy->clk); +} + +static int imx8mq_phy_power_off(struct phy *phy) +{ + struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + + clk_disable_unprepare(imx_phy->clk); + + return 0; +} + +static struct phy_ops imx8mq_usb_phy_ops = { + .init = imx8mq_usb_phy_init, + .power_on = imx8mq_phy_power_on, + .power_off = imx8mq_phy_power_off, + .owner = THIS_MODULE, +}; + +static int imx8mq_usb_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct imx8mq_usb_phy *imx_phy; + struct resource *res; + + imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL); + if (!imx_phy) + return -ENOMEM; + + imx_phy->clk = devm_clk_get(dev, "phy"); + if (IS_ERR(imx_phy->clk)) { + dev_err(dev, "failed to get imx8mq usb phy clock\n"); + return PTR_ERR(imx_phy->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + imx_phy->base = devm_ioremap_resource(dev, res); + if (IS_ERR(imx_phy->base)) + return PTR_ERR(imx_phy->base); + + imx_phy->phy = devm_phy_create(dev, NULL, &imx8mq_usb_phy_ops); + if (IS_ERR(imx_phy->phy)) + return PTR_ERR(imx_phy->phy); + + phy_set_drvdata(imx_phy->phy, imx_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id imx8mq_usb_phy_of_match[] = { + {.compatible = "fsl,imx8mq-usb-phy",}, + { }, +}; +MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match); + +static struct platform_driver imx8mq_usb_phy_driver = { + .probe = imx8mq_usb_phy_probe, + .driver = { + .name = "imx8mq-usb-phy", + .of_match_table = imx8mq_usb_phy_of_match, + } +}; +module_platform_driver(imx8mq_usb_phy_driver); + +MODULE_DESCRIPTION("FSL IMX8MQ USB PHY driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From aeaac93ddb28eeacc0dff9c12cb338eb1de7481d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 7 Dec 2018 14:55:29 +0100 Subject: phy: Add configuration interface The phy framework is only allowing to configure the power state of the PHY using the init and power_on hooks, and their power_off and exit counterparts. While it works for most, simple, PHYs supported so far, some more advanced PHYs need some configuration depending on runtime parameters. These PHYs have been supported by a number of means already, often by using ad-hoc drivers in their consumer drivers. That doesn't work too well however, when a consumer device needs to deal with multiple PHYs, or when multiple consumers need to deal with the same PHY (a DSI driver and a CSI driver for example). So we'll add a new interface, through two funtions, phy_validate and phy_configure. The first one will allow to check that a current configuration, for a given mode, is applicable. It will also allow the PHY driver to tune the settings given as parameters as it sees fit. phy_configure will actually apply that configuration in the phy itself. Signed-off-by: Maxime Ripard Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-core.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/phy/phy.h | 58 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) (limited to 'drivers') diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index df3d4ba516ab..19b05e824ee4 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -407,6 +407,70 @@ int phy_calibrate(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_calibrate); +/** + * phy_configure() - Changes the phy parameters + * @phy: the phy returned by phy_get() + * @opts: New configuration to apply + * + * Used to change the PHY parameters. phy_init() must have been called + * on the phy. The configuration will be applied on the current phy + * mode, that can be changed using phy_set_mode(). + * + * Returns: 0 if successful, an negative error code otherwise + */ +int phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + int ret; + + if (!phy) + return -EINVAL; + + if (!phy->ops->configure) + return -EOPNOTSUPP; + + mutex_lock(&phy->mutex); + ret = phy->ops->configure(phy, opts); + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_configure); + +/** + * phy_validate() - Checks the phy parameters + * @phy: the phy returned by phy_get() + * @mode: phy_mode the configuration is applicable to. + * @submode: PHY submode the configuration is applicable to. + * @opts: Configuration to check + * + * Used to check that the current set of parameters can be handled by + * the phy. Implementations are free to tune the parameters passed as + * arguments if needed by some implementation detail or + * constraints. It will not change any actual configuration of the + * PHY, so calling it as many times as deemed fit will have no side + * effect. + * + * Returns: 0 if successful, an negative error code otherwise + */ +int phy_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts) +{ + int ret; + + if (!phy) + return -EINVAL; + + if (!phy->ops->validate) + return -EOPNOTSUPP; + + mutex_lock(&phy->mutex); + ret = phy->ops->validate(phy, mode, submode, opts); + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_validate); + /** * _of_phy_get() - lookup and obtain a reference to a phy by phandle * @np: device_node for which to get the phy diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 453f21834685..04476c026b5a 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -42,6 +42,12 @@ enum phy_mode { PHY_MODE_MIPI_DPHY, }; +/** + * union phy_configure_opts - Opaque generic phy configuration + */ +union phy_configure_opts { +}; + /** * struct phy_ops - set of function pointers for performing phy operations * @init: operation to be performed for initializing phy @@ -59,6 +65,37 @@ struct phy_ops { int (*power_on)(struct phy *phy); int (*power_off)(struct phy *phy); int (*set_mode)(struct phy *phy, enum phy_mode mode, int submode); + + /** + * @configure: + * + * Optional. + * + * Used to change the PHY parameters. phy_init() must have + * been called on the phy. + * + * Returns: 0 if successful, an negative error code otherwise + */ + int (*configure)(struct phy *phy, union phy_configure_opts *opts); + + /** + * @validate: + * + * Optional. + * + * Used to check that the current set of parameters can be + * handled by the phy. Implementations are free to tune the + * parameters passed as arguments if needed by some + * implementation detail or constraints. It must not change + * any actual configuration of the PHY, so calling it as many + * times as deemed fit by the consumer must have no side + * effect. + * + * Returns: 0 if the configuration can be applied, an negative + * error code otherwise + */ + int (*validate)(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts); int (*reset)(struct phy *phy); int (*calibrate)(struct phy *phy); struct module *owner; @@ -165,6 +202,9 @@ int phy_power_off(struct phy *phy); int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode); #define phy_set_mode(phy, mode) \ phy_set_mode_ext(phy, mode, 0) +int phy_configure(struct phy *phy, union phy_configure_opts *opts); +int phy_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts); static inline enum phy_mode phy_get_mode(struct phy *phy) { @@ -309,6 +349,24 @@ static inline int phy_calibrate(struct phy *phy) return -ENOSYS; } +static inline int phy_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + if (!phy) + return 0; + + return -ENOSYS; +} + +static inline int phy_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts) +{ + if (!phy) + return 0; + + return -ENOSYS; +} + static inline int phy_get_bus_width(struct phy *phy) { return -ENOSYS; -- cgit v1.2.3-59-g8ed1b From dddc97e823033b705bbc06bc08b078200ad736a3 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 7 Dec 2018 14:55:31 +0100 Subject: phy: dphy: Add configuration helpers The MIPI D-PHY spec defines default values and boundaries for most of the parameters it defines. Introduce helpers to help drivers get meaningful values based on their current parameters, and validate the boundaries of these parameters if needed. Signed-off-by: Maxime Ripard Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 8 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-core-mipi-dphy.c | 166 ++++++++++++++++++++++++++++++++++++++ include/linux/phy/phy-mipi-dphy.h | 6 ++ 4 files changed, 181 insertions(+) create mode 100644 drivers/phy/phy-core-mipi-dphy.c (limited to 'drivers') diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index eaf0778a18d4..250abe290ca1 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -15,6 +15,14 @@ config GENERIC_PHY phy users can obtain reference to the PHY. All the users of this framework should select this config. +config GENERIC_PHY_MIPI_DPHY + bool + help + Generic MIPI D-PHY support. + + Provides a number of helpers a core functions for MIPI D-PHY + drivers to us. + config PHY_LPC18XX_USB_OTG tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver" depends on OF && (ARCH_LPC18XX || COMPILE_TEST) diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 84acb3761457..0d9fddc498a6 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_GENERIC_PHY) += phy-core.o +obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c new file mode 100644 index 000000000000..465fa1b91a5f --- /dev/null +++ b/drivers/phy/phy-core-mipi-dphy.c @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2013 NVIDIA Corporation + * Copyright (C) 2018 Cadence Design Systems Inc. + */ + +#include +#include +#include +#include + +#include +#include + +#define PSEC_PER_SEC 1000000000000LL + +/* + * Minimum D-PHY timings based on MIPI D-PHY specification. Derived + * from the valid ranges specified in Section 6.9, Table 14, Page 41 + * of the D-PHY specification (v2.1). + */ +int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, + unsigned int bpp, + unsigned int lanes, + struct phy_configure_opts_mipi_dphy *cfg) +{ + unsigned long long hs_clk_rate; + unsigned long long ui; + + if (!cfg) + return -EINVAL; + + hs_clk_rate = pixel_clock * bpp; + do_div(hs_clk_rate, lanes); + + ui = ALIGN(PSEC_PER_SEC, hs_clk_rate); + do_div(ui, hs_clk_rate); + + cfg->clk_miss = 0; + cfg->clk_post = 60000 + 52 * ui; + cfg->clk_pre = 8000; + cfg->clk_prepare = 38000; + cfg->clk_settle = 95000; + cfg->clk_term_en = 0; + cfg->clk_trail = 60000; + cfg->clk_zero = 262000; + cfg->d_term_en = 0; + cfg->eot = 0; + cfg->hs_exit = 100000; + cfg->hs_prepare = 40000 + 4 * ui; + cfg->hs_zero = 105000 + 6 * ui; + cfg->hs_settle = 85000 + 6 * ui; + cfg->hs_skip = 40000; + + /* + * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40) + * contains this formula as: + * + * T_HS-TRAIL = max(n * 8 * ui, 60 + n * 4 * ui) + * + * where n = 1 for forward-direction HS mode and n = 4 for reverse- + * direction HS mode. There's only one setting and this function does + * not parameterize on anything other that ui, so this code will + * assumes that reverse-direction HS mode is supported and uses n = 4. + */ + cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui); + + cfg->init = 100000000; + cfg->lpx = 60000; + cfg->ta_get = 5 * cfg->lpx; + cfg->ta_go = 4 * cfg->lpx; + cfg->ta_sure = 2 * cfg->lpx; + cfg->wakeup = 1000000000; + + cfg->hs_clk_rate = hs_clk_rate; + cfg->lanes = lanes; + + return 0; +} +EXPORT_SYMBOL(phy_mipi_dphy_get_default_config); + +/* + * Validate D-PHY configuration according to MIPI D-PHY specification + * (v1.2, Section Section 6.9 "Global Operation Timing Parameters"). + */ +int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg) +{ + unsigned long long ui; + + if (!cfg) + return -EINVAL; + + ui = ALIGN(PSEC_PER_SEC, cfg->hs_clk_rate); + do_div(ui, cfg->hs_clk_rate); + + if (cfg->clk_miss > 60000) + return -EINVAL; + + if (cfg->clk_post < (60000 + 52 * ui)) + return -EINVAL; + + if (cfg->clk_pre < 8000) + return -EINVAL; + + if (cfg->clk_prepare < 38000 || cfg->clk_prepare > 95000) + return -EINVAL; + + if (cfg->clk_settle < 95000 || cfg->clk_settle > 300000) + return -EINVAL; + + if (cfg->clk_term_en > 38000) + return -EINVAL; + + if (cfg->clk_trail < 60000) + return -EINVAL; + + if ((cfg->clk_prepare + cfg->clk_zero) < 300000) + return -EINVAL; + + if (cfg->d_term_en > (35000 + 4 * ui)) + return -EINVAL; + + if (cfg->eot > (105000 + 12 * ui)) + return -EINVAL; + + if (cfg->hs_exit < 100000) + return -EINVAL; + + if (cfg->hs_prepare < (40000 + 4 * ui) || + cfg->hs_prepare > (85000 + 6 * ui)) + return -EINVAL; + + if ((cfg->hs_prepare + cfg->hs_zero) < (145000 + 10 * ui)) + return -EINVAL; + + if ((cfg->hs_settle < (85000 + 6 * ui)) || + (cfg->hs_settle > (145000 + 10 * ui))) + return -EINVAL; + + if (cfg->hs_skip < 40000 || cfg->hs_skip > (55000 + 4 * ui)) + return -EINVAL; + + if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui)) + return -EINVAL; + + if (cfg->init < 100000000) + return -EINVAL; + + if (cfg->lpx < 50000) + return -EINVAL; + + if (cfg->ta_get != (5 * cfg->lpx)) + return -EINVAL; + + if (cfg->ta_go != (4 * cfg->lpx)) + return -EINVAL; + + if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx)) + return -EINVAL; + + if (cfg->wakeup < 1000000000) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(phy_mipi_dphy_config_validate); diff --git a/include/linux/phy/phy-mipi-dphy.h b/include/linux/phy/phy-mipi-dphy.h index 29bf94db88ad..c08aacc0ac35 100644 --- a/include/linux/phy/phy-mipi-dphy.h +++ b/include/linux/phy/phy-mipi-dphy.h @@ -276,4 +276,10 @@ struct phy_configure_opts_mipi_dphy { unsigned char lanes; }; +int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, + unsigned int bpp, + unsigned int lanes, + struct phy_configure_opts_mipi_dphy *cfg); +int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg); + #endif /* __PHY_MIPI_DPHY_H_ */ -- cgit v1.2.3-59-g8ed1b From 1a3a09270668d92755cc6efe8e0395bbb3de8629 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sun, 9 Dec 2018 22:20:03 +0800 Subject: phy: ti: fix semicolon.cocci warnings drivers/phy/ti/phy-gmii-sel.c:91:2-3: Unneeded semicolon Remove unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci Fixes: 1811851f4e73 ("phy: ti: introduce phy-gmii-sel driver") CC: Grygorii Strashko Signed-off-by: kbuild test robot Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/ti/phy-gmii-sel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index 04ebf53d2b32..77fdaa551977 100644 --- a/drivers/phy/ti/phy-gmii-sel.c +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -88,7 +88,7 @@ static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode) "port%u: unsupported mode: \"%s\". Defaulting to MII.\n", if_phy->id, phy_modes(rgmii_id)); return -EINVAL; - }; + } if_phy->phy_if_mode = submode; -- cgit v1.2.3-59-g8ed1b From 5e17b95d9893dbbe3366c4d66b0f1677cce99997 Mon Sep 17 00:00:00 2001 From: Evan Green Date: Mon, 10 Dec 2018 11:28:23 -0800 Subject: phy: qcom-qmp: Utilize fully-specified DT registers Utilize the newly fixed up DT bindings to get the tx2 and rx2 register regions for the second lane of dual-lane PHYs. Before this change, the driver was simply using lane one's register region and adding 0x400, which reached well beyond the DT-specified register allocation. This would have been a crash were it not for the page size on ARM64. Fix the driver not to rely on the magic of virtual memory by using the newly specified DT register regions for tx2 and rx2. In order to support existing device trees, this change also contains a fallback mode for when those new register regions don't exist, which reverts to the original behavior of overreaching and prints a complaint. Signed-off-by: Evan Green Reviewed-by: Douglas Anderson Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-qmp.c | 51 +++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 514db7248a5d..8204d55e2d65 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -72,6 +72,9 @@ #define MAX_PROP_NAME 32 +/* Define the assumed distance between lanes for underspecified device trees. */ +#define QMP_PHY_LEGACY_LANE_STRIDE 0x400 + struct qmp_phy_init_tbl { unsigned int offset; unsigned int val; @@ -733,9 +736,6 @@ struct qmp_phy_cfg { bool has_phy_dp_com_ctrl; /* true, if PHY has secondary tx/rx lanes to be configured */ bool is_dual_lane_phy; - /* Register offset of secondary tx/rx lanes for USB DP combo PHY */ - unsigned int tx_b_lane_offset; - unsigned int rx_b_lane_offset; /* true, if PCS block has no separate SW_RESET register */ bool no_pcs_sw_reset; @@ -748,6 +748,8 @@ struct qmp_phy_cfg { * @tx: iomapped memory space for lane's tx * @rx: iomapped memory space for lane's rx * @pcs: iomapped memory space for lane's pcs + * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs) + * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs) * @pcs_misc: iomapped memory space for lane's pcs_misc * @pipe_clk: pipe lock * @index: lane index @@ -759,6 +761,8 @@ struct qmp_phy { void __iomem *tx; void __iomem *rx; void __iomem *pcs; + void __iomem *tx2; + void __iomem *rx2; void __iomem *pcs_misc; struct clk *pipe_clk; unsigned int index; @@ -975,8 +979,6 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .has_phy_dp_com_ctrl = true, .is_dual_lane_phy = true, - .tx_b_lane_offset = 0x400, - .rx_b_lane_offset = 0x400, }; static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = { @@ -1031,9 +1033,6 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = { .mask_pcs_ready = PCS_READY, .is_dual_lane_phy = true, - .tx_b_lane_offset = 0x400, - .rx_b_lane_offset = 0x400, - .no_pcs_sw_reset = true, }; @@ -1238,12 +1237,12 @@ static int qcom_qmp_phy_init(struct phy *phy) qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num); /* Configuration for other LANE for USB-DP combo PHY */ if (cfg->is_dual_lane_phy) - qcom_qmp_phy_configure(tx + cfg->tx_b_lane_offset, cfg->regs, + qcom_qmp_phy_configure(qphy->tx2, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num); qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num); if (cfg->is_dual_lane_phy) - qcom_qmp_phy_configure(rx + cfg->rx_b_lane_offset, cfg->regs, + qcom_qmp_phy_configure(qphy->rx2, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num); qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); @@ -1615,8 +1614,9 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) /* * Get memory resources for each phy lane: - * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2; and - * pcs_misc (optional) -> 3. + * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. + * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5 + * For single lane PHYs: pcs_misc (optional) -> 3. */ qphy->tx = of_iomap(np, 0); if (!qphy->tx) @@ -1630,7 +1630,32 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) if (!qphy->pcs) return -ENOMEM; - qphy->pcs_misc = of_iomap(np, 3); + /* + * If this is a dual-lane PHY, then there should be registers for the + * second lane. Some old device trees did not specify this, so fall + * back to old legacy behavior of assuming they can be reached at an + * offset from the first lane. + */ + if (qmp->cfg->is_dual_lane_phy) { + qphy->tx2 = of_iomap(np, 3); + qphy->rx2 = of_iomap(np, 4); + if (!qphy->tx2 || !qphy->rx2) { + dev_warn(dev, + "Underspecified device tree, falling back to legacy register regions\n"); + + /* In the old version, pcs_misc is at index 3. */ + qphy->pcs_misc = qphy->tx2; + qphy->tx2 = qphy->tx + QMP_PHY_LEGACY_LANE_STRIDE; + qphy->rx2 = qphy->rx + QMP_PHY_LEGACY_LANE_STRIDE; + + } else { + qphy->pcs_misc = of_iomap(np, 5); + } + + } else { + qphy->pcs_misc = of_iomap(np, 3); + } + if (!qphy->pcs_misc) dev_vdbg(dev, "PHY pcs_misc-reg not used\n"); -- cgit v1.2.3-59-g8ed1b From 2e38c2e7026a9846b5bdadf0bf82808ec2c2f446 Mon Sep 17 00:00:00 2001 From: Evan Green Date: Mon, 10 Dec 2018 11:32:07 -0800 Subject: phy: qcom-qmp: Expose provided clocks to DT Register a simple clock provider for the PHY pipe clock sources so that device tree users can point at these clocks via phandles to the lane nodes. Signed-off-by: Evan Green Reviewed-by: Stephen Boyd Tested-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-qmp.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 8204d55e2d65..b4006818e1b6 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -1542,6 +1542,11 @@ static int qcom_qmp_phy_clk_init(struct device *dev) return devm_clk_bulk_get(dev, num, qmp->clks); } +static void phy_pipe_clk_release_provider(void *res) +{ + of_clk_del_provider(res); +} + /* * Register a fixed rate pipe clock. * @@ -1588,7 +1593,23 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) fixed->fixed_rate = 125000000; fixed->hw.init = &init; - return devm_clk_hw_register(qmp->dev, &fixed->hw); + ret = devm_clk_hw_register(qmp->dev, &fixed->hw); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw); + if (ret) + return ret; + + /* + * Roll a devm action because the clock provider is the child node, but + * the child node is not actually a device. + */ + ret = devm_add_action(qmp->dev, phy_pipe_clk_release_provider, np); + if (ret) + phy_pipe_clk_release_provider(np); + + return ret; } static const struct phy_ops qcom_qmp_phy_gen_ops = { -- cgit v1.2.3-59-g8ed1b From 9e412c66a8a24fabad97b706132ee270db2e4c8e Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 5 Dec 2018 11:33:39 -0500 Subject: usb: host: isp1362-hcd: convert to DEFINE_SHOW_ATTRIBUTE Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yangtao Li Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362-hcd.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index b21c386e6a46..28bf8bfb091e 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2159,25 +2159,15 @@ static int isp1362_show(struct seq_file *s, void *unused) return 0; } - -static int isp1362_open(struct inode *inode, struct file *file) -{ - return single_open(file, isp1362_show, inode); -} - -static const struct file_operations debug_ops = { - .open = isp1362_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(isp1362); /* expect just one isp1362_hcd per system */ static void create_debug_file(struct isp1362_hcd *isp1362_hcd) { isp1362_hcd->debug_file = debugfs_create_file("isp1362", S_IRUGO, usb_debug_root, - isp1362_hcd, &debug_ops); + isp1362_hcd, + &isp1362_fops); } static void remove_debug_file(struct isp1362_hcd *isp1362_hcd) -- cgit v1.2.3-59-g8ed1b From c238ec3ef638f87d8d701600d13a185b011fa078 Mon Sep 17 00:00:00 2001 From: Suwan Kim Date: Tue, 4 Dec 2018 23:31:43 +0900 Subject: usb: core: Remove unnecessary memset() register_root_hub() calls memset() setting usb_dev->bus->devmap. devicemap to 0 during hcd probe function (usb_hcd_pci_probe). But in previous function which is also the procedure of usb_hcd_pci_probe(), usb_bus_init() already initialized bus->devmap calling memset(). Furthermore, register_root_hub() is called only once in kernel. So, calling memset() which resets usb_bus->devmap.devicemap in register_root_hub() is redundant. Signed-off-by: Suwan Kim Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 487025d31d44..015b126ce455 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1074,8 +1074,6 @@ static int register_root_hub(struct usb_hcd *hcd) usb_dev->devnum = devnum; usb_dev->bus->devnum_next = devnum + 1; - memset (&usb_dev->bus->devmap.devicemap, 0, - sizeof usb_dev->bus->devmap.devicemap); set_bit (devnum, usb_dev->bus->devmap.devicemap); usb_set_device_state(usb_dev, USB_STATE_ADDRESS); -- cgit v1.2.3-59-g8ed1b From eaf3074e0a8c2a39c4c14aa8ef1c2ec09ace9c79 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 11 Dec 2018 11:06:25 +0100 Subject: usb: renesas_usbhs: mark PM functions as __maybe_unused Without CONFIG_PM, we get a new build warning here: drivers/usb/renesas_usbhs/common.c:860:12: error: 'usbhsc_resume' defined but not used [-Werror=unused-function] static int usbhsc_resume(struct device *dev) ^~~~~~~~~~~~~ drivers/usb/renesas_usbhs/common.c:844:12: error: 'usbhsc_suspend' defined but not used [-Werror=unused-function] static int usbhsc_suspend(struct device *dev) ^~~~~~~~~~~~~~ Fixes: d54d334e75b9 ("usb: renesas_usbhs: Use SIMPLE_DEV_PM_OPS macro") Signed-off-by: Arnd Bergmann Reviewed-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 02c1d2bf4f86..2ff7991f4517 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -841,7 +841,7 @@ static int usbhs_remove(struct platform_device *pdev) return 0; } -static int usbhsc_suspend(struct device *dev) +static __maybe_unused int usbhsc_suspend(struct device *dev) { struct usbhs_priv *priv = dev_get_drvdata(dev); struct usbhs_mod *mod = usbhs_mod_get_current(priv); @@ -857,7 +857,7 @@ static int usbhsc_suspend(struct device *dev) return 0; } -static int usbhsc_resume(struct device *dev) +static __maybe_unused int usbhsc_resume(struct device *dev) { struct usbhs_priv *priv = dev_get_drvdata(dev); struct platform_device *pdev = usbhs_priv_to_pdev(priv); -- cgit v1.2.3-59-g8ed1b From c3788cd9963eb2e77de3c24142fb7c67b61f1a26 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 12 Dec 2018 20:13:55 +0300 Subject: usb: roles: Add a description for the class to Kconfig That makes the USB role switch support option visible and selectable for the user. The class driver is also moved to drivers/usb/roles/ directory. This will fix an issue that we have with the Intel USB role switch driver on systems that don't have USB Type-C connectors: Intel USB role switch driver depends on the USB role switch class as it should, but since there was no way for the user to enable the USB role switch class, there was also no way to select that driver. USB Type-C drivers select the USB role switch class which makes the Intel USB role switch driver available and therefore hides the problem. So in practice Intel USB role switch driver was depending on USB Type-C drivers. Fixes: f6fb9ec02be1 ("usb: roles: Add Intel xHCI USB role switch driver") Cc: Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 4 - drivers/usb/common/Makefile | 1 - drivers/usb/common/roles.c | 314 -------------------------------------------- drivers/usb/roles/Kconfig | 13 ++ drivers/usb/roles/Makefile | 4 +- drivers/usb/roles/class.c | 314 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 330 insertions(+), 320 deletions(-) delete mode 100644 drivers/usb/common/roles.c create mode 100644 drivers/usb/roles/class.c (limited to 'drivers') diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 987fc5ba6321..70e6c956c23c 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -205,8 +205,4 @@ config USB_ULPI_BUS To compile this driver as a module, choose M here: the module will be called ulpi. -config USB_ROLE_SWITCH - tristate - select USB_COMMON - endif # USB_SUPPORT diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index fb4d5ef4165c..0a7c45e85481 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -9,4 +9,3 @@ usb-common-$(CONFIG_USB_LED_TRIG) += led.o obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o -obj-$(CONFIG_USB_ROLE_SWITCH) += roles.o diff --git a/drivers/usb/common/roles.c b/drivers/usb/common/roles.c deleted file mode 100644 index 99116af07f1d..000000000000 --- a/drivers/usb/common/roles.c +++ /dev/null @@ -1,314 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * USB Role Switch Support - * - * Copyright (C) 2018 Intel Corporation - * Author: Heikki Krogerus - * Hans de Goede - */ - -#include -#include -#include -#include -#include - -static struct class *role_class; - -struct usb_role_switch { - struct device dev; - struct mutex lock; /* device lock*/ - enum usb_role role; - - /* From descriptor */ - struct device *usb2_port; - struct device *usb3_port; - struct device *udc; - usb_role_switch_set_t set; - usb_role_switch_get_t get; - bool allow_userspace_control; -}; - -#define to_role_switch(d) container_of(d, struct usb_role_switch, dev) - -/** - * usb_role_switch_set_role - Set USB role for a switch - * @sw: USB role switch - * @role: USB role to be switched to - * - * Set USB role @role for @sw. - */ -int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role) -{ - int ret; - - if (IS_ERR_OR_NULL(sw)) - return 0; - - mutex_lock(&sw->lock); - - ret = sw->set(sw->dev.parent, role); - if (!ret) - sw->role = role; - - mutex_unlock(&sw->lock); - - return ret; -} -EXPORT_SYMBOL_GPL(usb_role_switch_set_role); - -/** - * usb_role_switch_get_role - Get the USB role for a switch - * @sw: USB role switch - * - * Depending on the role-switch-driver this function returns either a cached - * value of the last set role, or reads back the actual value from the hardware. - */ -enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) -{ - enum usb_role role; - - if (IS_ERR_OR_NULL(sw)) - return USB_ROLE_NONE; - - mutex_lock(&sw->lock); - - if (sw->get) - role = sw->get(sw->dev.parent); - else - role = sw->role; - - mutex_unlock(&sw->lock); - - return role; -} -EXPORT_SYMBOL_GPL(usb_role_switch_get_role); - -static int __switch_match(struct device *dev, const void *name) -{ - return !strcmp((const char *)name, dev_name(dev)); -} - -static void *usb_role_switch_match(struct device_connection *con, int ep, - void *data) -{ - struct device *dev; - - dev = class_find_device(role_class, NULL, con->endpoint[ep], - __switch_match); - - return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); -} - -/** - * usb_role_switch_get - Find USB role switch linked with the caller - * @dev: The caller device - * - * Finds and returns role switch linked with @dev. The reference count for the - * found switch is incremented. - */ -struct usb_role_switch *usb_role_switch_get(struct device *dev) -{ - struct usb_role_switch *sw; - - sw = device_connection_find_match(dev, "usb-role-switch", NULL, - usb_role_switch_match); - - if (!IS_ERR_OR_NULL(sw)) - WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); - - return sw; -} -EXPORT_SYMBOL_GPL(usb_role_switch_get); - -/** - * usb_role_switch_put - Release handle to a switch - * @sw: USB Role Switch - * - * Decrement reference count for @sw. - */ -void usb_role_switch_put(struct usb_role_switch *sw) -{ - if (!IS_ERR_OR_NULL(sw)) { - put_device(&sw->dev); - module_put(sw->dev.parent->driver->owner); - } -} -EXPORT_SYMBOL_GPL(usb_role_switch_put); - -static umode_t -usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n) -{ - struct device *dev = container_of(kobj, typeof(*dev), kobj); - struct usb_role_switch *sw = to_role_switch(dev); - - if (sw->allow_userspace_control) - return attr->mode; - - return 0; -} - -static const char * const usb_roles[] = { - [USB_ROLE_NONE] = "none", - [USB_ROLE_HOST] = "host", - [USB_ROLE_DEVICE] = "device", -}; - -static ssize_t -role_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct usb_role_switch *sw = to_role_switch(dev); - enum usb_role role = usb_role_switch_get_role(sw); - - return sprintf(buf, "%s\n", usb_roles[role]); -} - -static ssize_t role_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - struct usb_role_switch *sw = to_role_switch(dev); - int ret; - - ret = sysfs_match_string(usb_roles, buf); - if (ret < 0) { - bool res; - - /* Extra check if the user wants to disable the switch */ - ret = kstrtobool(buf, &res); - if (ret || res) - return -EINVAL; - } - - ret = usb_role_switch_set_role(sw, ret); - if (ret) - return ret; - - return size; -} -static DEVICE_ATTR_RW(role); - -static struct attribute *usb_role_switch_attrs[] = { - &dev_attr_role.attr, - NULL, -}; - -static const struct attribute_group usb_role_switch_group = { - .is_visible = usb_role_switch_is_visible, - .attrs = usb_role_switch_attrs, -}; - -static const struct attribute_group *usb_role_switch_groups[] = { - &usb_role_switch_group, - NULL, -}; - -static int -usb_role_switch_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - int ret; - - ret = add_uevent_var(env, "USB_ROLE_SWITCH=%s", dev_name(dev)); - if (ret) - dev_err(dev, "failed to add uevent USB_ROLE_SWITCH\n"); - - return ret; -} - -static void usb_role_switch_release(struct device *dev) -{ - struct usb_role_switch *sw = to_role_switch(dev); - - kfree(sw); -} - -static const struct device_type usb_role_dev_type = { - .name = "usb_role_switch", - .groups = usb_role_switch_groups, - .uevent = usb_role_switch_uevent, - .release = usb_role_switch_release, -}; - -/** - * usb_role_switch_register - Register USB Role Switch - * @parent: Parent device for the switch - * @desc: Description of the switch - * - * USB Role Switch is a device capable or choosing the role for USB connector. - * On platforms where the USB controller is dual-role capable, the controller - * driver will need to register the switch. On platforms where the USB host and - * USB device controllers behind the connector are separate, there will be a - * mux, and the driver for that mux will need to register the switch. - * - * Returns handle to a new role switch or ERR_PTR. The content of @desc is - * copied. - */ -struct usb_role_switch * -usb_role_switch_register(struct device *parent, - const struct usb_role_switch_desc *desc) -{ - struct usb_role_switch *sw; - int ret; - - if (!desc || !desc->set) - return ERR_PTR(-EINVAL); - - sw = kzalloc(sizeof(*sw), GFP_KERNEL); - if (!sw) - return ERR_PTR(-ENOMEM); - - mutex_init(&sw->lock); - - sw->allow_userspace_control = desc->allow_userspace_control; - sw->usb2_port = desc->usb2_port; - sw->usb3_port = desc->usb3_port; - sw->udc = desc->udc; - sw->set = desc->set; - sw->get = desc->get; - - sw->dev.parent = parent; - sw->dev.class = role_class; - sw->dev.type = &usb_role_dev_type; - dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent)); - - ret = device_register(&sw->dev); - if (ret) { - put_device(&sw->dev); - return ERR_PTR(ret); - } - - /* TODO: Symlinks for the host port and the device controller. */ - - return sw; -} -EXPORT_SYMBOL_GPL(usb_role_switch_register); - -/** - * usb_role_switch_unregister - Unregsiter USB Role Switch - * @sw: USB Role Switch - * - * Unregister switch that was registered with usb_role_switch_register(). - */ -void usb_role_switch_unregister(struct usb_role_switch *sw) -{ - if (!IS_ERR_OR_NULL(sw)) - device_unregister(&sw->dev); -} -EXPORT_SYMBOL_GPL(usb_role_switch_unregister); - -static int __init usb_roles_init(void) -{ - role_class = class_create(THIS_MODULE, "usb_role"); - return PTR_ERR_OR_ZERO(role_class); -} -subsys_initcall(usb_roles_init); - -static void __exit usb_roles_exit(void) -{ - class_destroy(role_class); -} -module_exit(usb_roles_exit); - -MODULE_AUTHOR("Heikki Krogerus "); -MODULE_AUTHOR("Hans de Goede "); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("USB Role Class"); diff --git a/drivers/usb/roles/Kconfig b/drivers/usb/roles/Kconfig index f5a5e6f79f1b..e4194ac94510 100644 --- a/drivers/usb/roles/Kconfig +++ b/drivers/usb/roles/Kconfig @@ -1,3 +1,16 @@ +config USB_ROLE_SWITCH + tristate "USB Role Switch Support" + help + USB Role Switch is a device that can select the USB role - host or + device - for a USB port (connector). In most cases dual-role capable + USB controller will also represent the switch, but on some platforms + multiplexer/demultiplexer switch is used to route the data lines on + the USB connector between separate USB host and device controllers. + + Say Y here if your USB connectors support both device and host roles. + To compile the driver as module, choose M here: the module will be + called roles.ko. + if USB_ROLE_SWITCH config USB_ROLES_INTEL_XHCI diff --git a/drivers/usb/roles/Makefile b/drivers/usb/roles/Makefile index e44b179ba275..c02873206fc1 100644 --- a/drivers/usb/roles/Makefile +++ b/drivers/usb/roles/Makefile @@ -1 +1,3 @@ -obj-$(CONFIG_USB_ROLES_INTEL_XHCI) += intel-xhci-usb-role-switch.o +obj-$(CONFIG_USB_ROLE_SWITCH) += roles.o +roles-y := class.o +obj-$(CONFIG_USB_ROLES_INTEL_XHCI) += intel-xhci-usb-role-switch.o diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c new file mode 100644 index 000000000000..99116af07f1d --- /dev/null +++ b/drivers/usb/roles/class.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Role Switch Support + * + * Copyright (C) 2018 Intel Corporation + * Author: Heikki Krogerus + * Hans de Goede + */ + +#include +#include +#include +#include +#include + +static struct class *role_class; + +struct usb_role_switch { + struct device dev; + struct mutex lock; /* device lock*/ + enum usb_role role; + + /* From descriptor */ + struct device *usb2_port; + struct device *usb3_port; + struct device *udc; + usb_role_switch_set_t set; + usb_role_switch_get_t get; + bool allow_userspace_control; +}; + +#define to_role_switch(d) container_of(d, struct usb_role_switch, dev) + +/** + * usb_role_switch_set_role - Set USB role for a switch + * @sw: USB role switch + * @role: USB role to be switched to + * + * Set USB role @role for @sw. + */ +int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role) +{ + int ret; + + if (IS_ERR_OR_NULL(sw)) + return 0; + + mutex_lock(&sw->lock); + + ret = sw->set(sw->dev.parent, role); + if (!ret) + sw->role = role; + + mutex_unlock(&sw->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_role_switch_set_role); + +/** + * usb_role_switch_get_role - Get the USB role for a switch + * @sw: USB role switch + * + * Depending on the role-switch-driver this function returns either a cached + * value of the last set role, or reads back the actual value from the hardware. + */ +enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) +{ + enum usb_role role; + + if (IS_ERR_OR_NULL(sw)) + return USB_ROLE_NONE; + + mutex_lock(&sw->lock); + + if (sw->get) + role = sw->get(sw->dev.parent); + else + role = sw->role; + + mutex_unlock(&sw->lock); + + return role; +} +EXPORT_SYMBOL_GPL(usb_role_switch_get_role); + +static int __switch_match(struct device *dev, const void *name) +{ + return !strcmp((const char *)name, dev_name(dev)); +} + +static void *usb_role_switch_match(struct device_connection *con, int ep, + void *data) +{ + struct device *dev; + + dev = class_find_device(role_class, NULL, con->endpoint[ep], + __switch_match); + + return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); +} + +/** + * usb_role_switch_get - Find USB role switch linked with the caller + * @dev: The caller device + * + * Finds and returns role switch linked with @dev. The reference count for the + * found switch is incremented. + */ +struct usb_role_switch *usb_role_switch_get(struct device *dev) +{ + struct usb_role_switch *sw; + + sw = device_connection_find_match(dev, "usb-role-switch", NULL, + usb_role_switch_match); + + if (!IS_ERR_OR_NULL(sw)) + WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); + + return sw; +} +EXPORT_SYMBOL_GPL(usb_role_switch_get); + +/** + * usb_role_switch_put - Release handle to a switch + * @sw: USB Role Switch + * + * Decrement reference count for @sw. + */ +void usb_role_switch_put(struct usb_role_switch *sw) +{ + if (!IS_ERR_OR_NULL(sw)) { + put_device(&sw->dev); + module_put(sw->dev.parent->driver->owner); + } +} +EXPORT_SYMBOL_GPL(usb_role_switch_put); + +static umode_t +usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, typeof(*dev), kobj); + struct usb_role_switch *sw = to_role_switch(dev); + + if (sw->allow_userspace_control) + return attr->mode; + + return 0; +} + +static const char * const usb_roles[] = { + [USB_ROLE_NONE] = "none", + [USB_ROLE_HOST] = "host", + [USB_ROLE_DEVICE] = "device", +}; + +static ssize_t +role_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_role_switch *sw = to_role_switch(dev); + enum usb_role role = usb_role_switch_get_role(sw); + + return sprintf(buf, "%s\n", usb_roles[role]); +} + +static ssize_t role_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct usb_role_switch *sw = to_role_switch(dev); + int ret; + + ret = sysfs_match_string(usb_roles, buf); + if (ret < 0) { + bool res; + + /* Extra check if the user wants to disable the switch */ + ret = kstrtobool(buf, &res); + if (ret || res) + return -EINVAL; + } + + ret = usb_role_switch_set_role(sw, ret); + if (ret) + return ret; + + return size; +} +static DEVICE_ATTR_RW(role); + +static struct attribute *usb_role_switch_attrs[] = { + &dev_attr_role.attr, + NULL, +}; + +static const struct attribute_group usb_role_switch_group = { + .is_visible = usb_role_switch_is_visible, + .attrs = usb_role_switch_attrs, +}; + +static const struct attribute_group *usb_role_switch_groups[] = { + &usb_role_switch_group, + NULL, +}; + +static int +usb_role_switch_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + int ret; + + ret = add_uevent_var(env, "USB_ROLE_SWITCH=%s", dev_name(dev)); + if (ret) + dev_err(dev, "failed to add uevent USB_ROLE_SWITCH\n"); + + return ret; +} + +static void usb_role_switch_release(struct device *dev) +{ + struct usb_role_switch *sw = to_role_switch(dev); + + kfree(sw); +} + +static const struct device_type usb_role_dev_type = { + .name = "usb_role_switch", + .groups = usb_role_switch_groups, + .uevent = usb_role_switch_uevent, + .release = usb_role_switch_release, +}; + +/** + * usb_role_switch_register - Register USB Role Switch + * @parent: Parent device for the switch + * @desc: Description of the switch + * + * USB Role Switch is a device capable or choosing the role for USB connector. + * On platforms where the USB controller is dual-role capable, the controller + * driver will need to register the switch. On platforms where the USB host and + * USB device controllers behind the connector are separate, there will be a + * mux, and the driver for that mux will need to register the switch. + * + * Returns handle to a new role switch or ERR_PTR. The content of @desc is + * copied. + */ +struct usb_role_switch * +usb_role_switch_register(struct device *parent, + const struct usb_role_switch_desc *desc) +{ + struct usb_role_switch *sw; + int ret; + + if (!desc || !desc->set) + return ERR_PTR(-EINVAL); + + sw = kzalloc(sizeof(*sw), GFP_KERNEL); + if (!sw) + return ERR_PTR(-ENOMEM); + + mutex_init(&sw->lock); + + sw->allow_userspace_control = desc->allow_userspace_control; + sw->usb2_port = desc->usb2_port; + sw->usb3_port = desc->usb3_port; + sw->udc = desc->udc; + sw->set = desc->set; + sw->get = desc->get; + + sw->dev.parent = parent; + sw->dev.class = role_class; + sw->dev.type = &usb_role_dev_type; + dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent)); + + ret = device_register(&sw->dev); + if (ret) { + put_device(&sw->dev); + return ERR_PTR(ret); + } + + /* TODO: Symlinks for the host port and the device controller. */ + + return sw; +} +EXPORT_SYMBOL_GPL(usb_role_switch_register); + +/** + * usb_role_switch_unregister - Unregsiter USB Role Switch + * @sw: USB Role Switch + * + * Unregister switch that was registered with usb_role_switch_register(). + */ +void usb_role_switch_unregister(struct usb_role_switch *sw) +{ + if (!IS_ERR_OR_NULL(sw)) + device_unregister(&sw->dev); +} +EXPORT_SYMBOL_GPL(usb_role_switch_unregister); + +static int __init usb_roles_init(void) +{ + role_class = class_create(THIS_MODULE, "usb_role"); + return PTR_ERR_OR_ZERO(role_class); +} +subsys_initcall(usb_roles_init); + +static void __exit usb_roles_exit(void) +{ + class_destroy(role_class); +} +module_exit(usb_roles_exit); + +MODULE_AUTHOR("Heikki Krogerus "); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("USB Role Class"); -- cgit v1.2.3-59-g8ed1b From 8dc7623bf608495b6e6743e805807c7840673573 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 14 Dec 2018 11:36:15 +0200 Subject: usb: ehci-omap: Fix deferred probe for phy handling PHY model is being used on omap5 platforms even if port mode is not OMAP_EHCI_PORT_MODE_PHY. So don't guess if PHY is required or not based on PHY mode. If PHY is provided in device tree, it must be required. So, if devm_usb_get_phy_by_phandle() gives us an error code other than -ENODEV (no PHY) then error out. This fixes USB Ethernet on omap5-uevm if PHY happens to probe after EHCI thus causing a -EPROBE_DEFER. Cc: Johan Hovold Cc: Ladislav Michl Reported-by: Peter Ujfalusi Signed-off-by: Roger Quadros Tested-by: Peter Ujfalusi Acked-by: Tony Lindgren Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-omap.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 7e4c13346a1e..7d20296cbe9f 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -159,11 +159,12 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) /* get the PHY device */ phy = devm_usb_get_phy_by_phandle(dev, "phys", i); if (IS_ERR(phy)) { - /* Don't bail out if PHY is not absolutely necessary */ - if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) + ret = PTR_ERR(phy); + if (ret == -ENODEV) { /* no PHY */ + phy = NULL; continue; + } - ret = PTR_ERR(phy); if (ret != -EPROBE_DEFER) dev_err(dev, "Can't get PHY for port %d: %d\n", i, ret); -- cgit v1.2.3-59-g8ed1b From 1d6e81a288e28d8d0e38e0501a324216f79bba35 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Fri, 14 Dec 2018 08:27:03 +0000 Subject: usb: renesas_usbhs: add support for RZ/G2E HS-USB found in RZ/G2E (a.k.a. r8a774c0) is very similar to the one found in R-Car E3 (a.k.a. r8a77990), as it needs to release the PLL reset by the UGCTRL register like R-Car E3, therefore add r8a774c0 support in a similar fashion to what was done for the r8a77990. Signed-off-by: Fabrizio Castro Reviewed-by: Simon Horman Acked-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 2ff7991f4517..249fbee97f3f 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -539,6 +539,10 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) * platform functions */ static const struct of_device_id usbhs_of_match[] = { + { + .compatible = "renesas,usbhs-r8a774c0", + .data = (void *)USBHS_TYPE_RCAR_GEN3_WITH_PLL, + }, { .compatible = "renesas,usbhs-r8a7790", .data = (void *)USBHS_TYPE_RCAR_GEN2, -- cgit v1.2.3-59-g8ed1b From 8d503f206c336677954160ac62f0c7d9c219cd89 Mon Sep 17 00:00:00 2001 From: Scott Chen Date: Thu, 13 Dec 2018 06:01:47 -0500 Subject: USB: serial: pl2303: add ids for Hewlett-Packard HP POS pole displays Add device ids to pl2303 for the HP POS pole displays: LM920: 03f0:026b TD620: 03f0:0956 LD960TA: 03f0:4439 LD220TA: 03f0:4349 LM940: 03f0:5039 Signed-off-by: Scott Chen Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/pl2303.c | 5 +++++ drivers/usb/serial/pl2303.h | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index a4e0d13fc121..98e7a5df0f6d 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -91,9 +91,14 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) }, { USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) }, { USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) }, + { USB_DEVICE(HP_VENDOR_ID, HP_LD220TA_PRODUCT_ID) }, { USB_DEVICE(HP_VENDOR_ID, HP_LD960_PRODUCT_ID) }, + { USB_DEVICE(HP_VENDOR_ID, HP_LD960TA_PRODUCT_ID) }, { USB_DEVICE(HP_VENDOR_ID, HP_LCM220_PRODUCT_ID) }, { USB_DEVICE(HP_VENDOR_ID, HP_LCM960_PRODUCT_ID) }, + { USB_DEVICE(HP_VENDOR_ID, HP_LM920_PRODUCT_ID) }, + { USB_DEVICE(HP_VENDOR_ID, HP_LM940_PRODUCT_ID) }, + { USB_DEVICE(HP_VENDOR_ID, HP_TD620_PRODUCT_ID) }, { USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) }, { USB_DEVICE(ZEAGLE_VENDOR_ID, ZEAGLE_N2ITION3_PRODUCT_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 26965cc23c17..4e2554d55362 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -119,10 +119,15 @@ /* Hewlett-Packard POS Pole Displays */ #define HP_VENDOR_ID 0x03f0 +#define HP_LM920_PRODUCT_ID 0x026b +#define HP_TD620_PRODUCT_ID 0x0956 #define HP_LD960_PRODUCT_ID 0x0b39 #define HP_LCM220_PRODUCT_ID 0x3139 #define HP_LCM960_PRODUCT_ID 0x3239 #define HP_LD220_PRODUCT_ID 0x3524 +#define HP_LD220TA_PRODUCT_ID 0x4349 +#define HP_LD960TA_PRODUCT_ID 0x4439 +#define HP_LM940_PRODUCT_ID 0x5039 /* Cressi Edy (diving computer) PC interface */ #define CRESSI_VENDOR_ID 0x04b8 -- cgit v1.2.3-59-g8ed1b From 6010abf2c2c0e382d7e8ee44bd11f343aae90cce Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Tue, 18 Dec 2018 07:58:04 -0600 Subject: usb: musb: dsps: fix otg state machine Due to lack of ID pin interrupt event on AM335x devices, the musb dsps driver uses polling to detect usb device attach for dual-role port. But in the case if a micro-A cable adapter is attached without a USB device attached to the cable, the musb state machine gets stuck in a_wait_vrise state waiting for the MUSB_CONNECT interrupt which won't happen due to the usb device is not attached. The state is stuck in a_wait_vrise even after the micro-A cable is detached, which could cause VBUS retention if then the dual-role port is attached to a host port. To fix the problem, make a_wait_vrise as a transient state, then move the state to either a_wait_bcon for host port or a_idle state for dual-role port, if no usb device is attached to the port. Signed-off-by: Bin Liu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_dsps.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 23a0df79ef21..1e6d78b1334e 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -227,8 +227,13 @@ static int dsps_check_status(struct musb *musb, void *unused) switch (musb->xceiv->otg->state) { case OTG_STATE_A_WAIT_VRISE: - dsps_mod_timer_optional(glue); - break; + if (musb->port_mode == MUSB_HOST) { + musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON; + dsps_mod_timer_optional(glue); + break; + } + /* fall through */ + case OTG_STATE_A_WAIT_BCON: /* keep VBUS on for host-only mode */ if (musb->port_mode == MUSB_HOST) { -- cgit v1.2.3-59-g8ed1b From 54578ee883e34d2d1c518d48f1c1e2dd3f387188 Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Tue, 18 Dec 2018 07:58:05 -0600 Subject: usb: musb: dsps: fix runtime pm for peripheral mode Since the runtime PM support was added in musb, dsps relies on the timer calling otg_timer() to activate the usb subsystem. However the driver doesn't enable the timer for peripheral port, then the peripheral port is unable to be enumerated by a host if the other usb port is disabled or in peripheral mode too. So let's start the timer for peripheral port too. Fixes: ea2f35c01d5e ("usb: musb: Fix sleeping function called from invalid context for hdrc glue") Acked-by: Tony Lindgren Signed-off-by: Bin Liu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_dsps.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 1e6d78b1334e..403eb97915f8 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -181,9 +181,11 @@ static void dsps_musb_enable(struct musb *musb) musb_writel(reg_base, wrp->epintr_set, epmask); musb_writel(reg_base, wrp->coreintr_set, coremask); - /* start polling for ID change in dual-role idle mode */ - if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && - musb->port_mode == MUSB_OTG) + /* + * start polling for runtime PM active and idle, + * and for ID change in dual-role idle mode. + */ + if (musb->xceiv->otg->state == OTG_STATE_B_IDLE) dsps_mod_timer(glue, -1); } @@ -254,6 +256,10 @@ static int dsps_check_status(struct musb *musb, void *unused) musb->xceiv->otg->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); } + + if (musb->port_mode == MUSB_PERIPHERAL) + skip_session = 1; + if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) musb_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); -- cgit v1.2.3-59-g8ed1b From c710d0bb76ff0795d8b6c1cda1e01e6e1e661a4a Mon Sep 17 00:00:00 2001 From: Kyle Tso Date: Mon, 17 Dec 2018 21:22:13 +0800 Subject: usb: typec: tcpm: Extend the matching rules on PPS APDO selection Current matching rules ensure that the voltage range of selected Source Capability is entirely within the range defined in one of the Sink Capabilities. This is reasonable but not practical because Sink may not support wide range of voltage when sinking power while Source could advertise its capabilities in relatively wider range. For example, a Source PDO advertising 3.3V-11V@3A (9V Prog of Fixed Nominal Voltage) will not be selected if the Sink requires 5V-12V@3A PPS power. However, the Sink could work well if the requested voltage range in RDOs is 5V-11V@3A. Currently accepted: |--------- source -----| |----------- sink ---------------| Currently not accepted: |--------- source -----| |----------- sink ---------------| |--------- source -----| |----------- sink ---------------| |--------- source -----------------| |------ sink -------| To improve the usability, change the matching rules to what listed below: a. The Source PDO is selectable if any portion of the voltage range overlaps one of the Sink PDO's voltage range. b. The maximum operational voltage will be the lower one between the selected Source PDO and the matching Sink PDO. c. The maximum power will be the maximum operational voltage times the maximum current defined in the selected Source PDO d. Select the Source PDO with the highest maximum power Signed-off-by: Kyle Tso Acked-by: Adam Thomson Reviewed-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 3620efee2688..4bc29b586698 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2213,7 +2213,8 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) unsigned int i, j, max_mw = 0, max_mv = 0; unsigned int min_src_mv, max_src_mv, src_ma, src_mw; unsigned int min_snk_mv, max_snk_mv; - u32 pdo; + unsigned int max_op_mv; + u32 pdo, src, snk; unsigned int src_pdo = 0, snk_pdo = 0; /* @@ -2263,16 +2264,18 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) continue; } - if (max_src_mv <= max_snk_mv && - min_src_mv >= min_snk_mv) { + if (min_src_mv <= max_snk_mv && + max_src_mv >= min_snk_mv) { + max_op_mv = min(max_src_mv, max_snk_mv); + src_mw = (max_op_mv * src_ma) / 1000; /* Prefer higher voltages if available */ if ((src_mw == max_mw && - min_src_mv > max_mv) || + max_op_mv > max_mv) || src_mw > max_mw) { src_pdo = i; snk_pdo = j; max_mw = src_mw; - max_mv = max_src_mv; + max_mv = max_op_mv; } } } @@ -2285,16 +2288,18 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) } if (src_pdo) { - pdo = port->source_caps[src_pdo]; - - port->pps_data.min_volt = pdo_pps_apdo_min_voltage(pdo); - port->pps_data.max_volt = pdo_pps_apdo_max_voltage(pdo); - port->pps_data.max_curr = - min_pps_apdo_current(pdo, port->snk_pdo[snk_pdo]); - port->pps_data.out_volt = - min(pdo_pps_apdo_max_voltage(pdo), port->pps_data.out_volt); - port->pps_data.op_curr = - min(port->pps_data.max_curr, port->pps_data.op_curr); + src = port->source_caps[src_pdo]; + snk = port->snk_pdo[snk_pdo]; + + port->pps_data.min_volt = max(pdo_pps_apdo_min_voltage(src), + pdo_pps_apdo_min_voltage(snk)); + port->pps_data.max_volt = min(pdo_pps_apdo_max_voltage(src), + pdo_pps_apdo_max_voltage(snk)); + port->pps_data.max_curr = min_pps_apdo_current(src, snk); + port->pps_data.out_volt = min(port->pps_data.max_volt, + port->pps_data.out_volt); + port->pps_data.op_curr = min(port->pps_data.max_curr, + port->pps_data.op_curr); } return src_pdo; -- cgit v1.2.3-59-g8ed1b From c85400f886e3d41e69966470879f635a2b50084c Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Tue, 18 Dec 2018 20:04:25 +0800 Subject: usb: r8a66597: Fix a possible concurrency use-after-free bug in r8a66597_endpoint_disable() The function r8a66597_endpoint_disable() and r8a66597_urb_enqueue() may be concurrently executed. The two functions both access a possible shared variable "hep->hcpriv". This shared variable is freed by r8a66597_endpoint_disable() via the call path: r8a66597_endpoint_disable kfree(hep->hcpriv) (line 1995 in Linux-4.19) This variable is read by r8a66597_urb_enqueue() via the call path: r8a66597_urb_enqueue spin_lock_irqsave(&r8a66597->lock) init_pipe_info enable_r8a66597_pipe pipe = hep->hcpriv (line 802 in Linux-4.19) The read operation is protected by a spinlock, but the free operation is not protected by this spinlock, thus a concurrency use-after-free bug may occur. To fix this bug, the spin-lock and spin-unlock function calls in r8a66597_endpoint_disable() are moved to protect the free operation. Signed-off-by: Jia-Ju Bai Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 984892dd72f5..42668aeca57c 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1979,6 +1979,8 @@ static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, static void r8a66597_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) +__acquires(r8a66597->lock) +__releases(r8a66597->lock) { struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); struct r8a66597_pipe *pipe = (struct r8a66597_pipe *)hep->hcpriv; @@ -1991,13 +1993,14 @@ static void r8a66597_endpoint_disable(struct usb_hcd *hcd, return; pipenum = pipe->info.pipenum; + spin_lock_irqsave(&r8a66597->lock, flags); if (pipenum == 0) { kfree(hep->hcpriv); hep->hcpriv = NULL; + spin_unlock_irqrestore(&r8a66597->lock, flags); return; } - spin_lock_irqsave(&r8a66597->lock, flags); pipe_stop(r8a66597, pipe); pipe_irq_disable(r8a66597, pipenum); disable_irq_empty(r8a66597, pipenum); -- cgit v1.2.3-59-g8ed1b From eafb27fa5283599ce6c5492ea18cf636a28222bb Mon Sep 17 00:00:00 2001 From: Macpaul Lin Date: Wed, 19 Dec 2018 12:11:03 +0800 Subject: cdc-acm: fix abnormal DATA RX issue for Mediatek Preloader. Mediatek Preloader is a proprietary embedded boot loader for loading Little Kernel and Linux into device DRAM. This boot loader also handle firmware update. Mediatek Preloader will be enumerated as a virtual COM port when the device is connected to Windows or Linux OS via CDC-ACM class driver. When the USB enumeration has been done, Mediatek Preloader will send out handshake command "READY" to PC actively instead of waiting command from the download tool. Since Linux 4.12, the commit "tty: reset termios state on device registration" (93857edd9829e144acb6c7e72d593f6e01aead66) causes Mediatek Preloader receiving some abnoraml command like "READYXX" as it sent. This will be recognized as an incorrect response. The behavior change also causes the download handshake fail. This change only affects subsequent connects if the reconnected device happens to get the same minor number. By disabling the ECHO termios flag could avoid this problem. However, it cannot be done by user space configuration when download tool open /dev/ttyACM0. This is because the device running Mediatek Preloader will send handshake command "READY" immediately once the CDC-ACM driver is ready. This patch wants to fix above problem by introducing "DISABLE_ECHO" property in driver_info. When Mediatek Preloader is connected, the CDC-ACM driver could disable ECHO flag in termios to avoid the problem. Signed-off-by: Macpaul Lin Cc: stable@vger.kernel.org Reviewed-by: Johan Hovold Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 10 ++++++++++ drivers/usb/class/cdc-acm.h | 1 + 2 files changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 1b68fed464cb..ed8c62b2d9d1 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -581,6 +581,13 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty) if (retval) goto error_init_termios; + /* + * Suppress initial echoing for some devices which might send data + * immediately after acm driver has been installed. + */ + if (acm->quirks & DISABLE_ECHO) + tty->termios.c_lflag &= ~ECHO; + tty->driver_data = acm; return 0; @@ -1657,6 +1664,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0e8d, 0x2000), /* MediaTek Inc Preloader */ + .driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */ + }, { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index ca06b20d7af9..515aad0847ee 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -140,3 +140,4 @@ struct acm { #define QUIRK_CONTROL_LINE_STATE BIT(6) #define CLEAR_HALT_CONDITIONS BIT(7) #define SEND_ZERO_PACKET BIT(8) +#define DISABLE_ECHO BIT(9) -- cgit v1.2.3-59-g8ed1b From 4b2c01ad902ec02fa962b233decd2f14be3714ba Mon Sep 17 00:00:00 2001 From: Jörgen Storvist Date: Fri, 21 Dec 2018 14:40:44 +0100 Subject: USB: serial: option: add Fibocom NL678 series MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added USB serial option driver support for Fibocom NL678 series cellular module: VID 2cb7 and PIDs 0x0104 and 0x0105. Reserved network and ADB interfaces. T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=2cb7 ProdID=0104 Rev=03.10 S: Manufacturer=Fibocom S: Product=Fibocom NL678-E Modem S: SerialNumber=12345678 C: #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option I: If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan I: If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 3 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=2cb7 ProdID=0105 Rev=03.10 S: Manufacturer=Fibocom S: Product=Fibocom NL678-E Modem S: SerialNumber=12345678 C: #Ifs= 7 Cfg#= 1 Atr=a0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option I: If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 4 Alt= 0 #EPs= 1 Cls=02(commc) Sub=06 Prot=00 Driver=cdc_ether I: If#= 5 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_ether I: If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) Signed-off-by: Jörgen Storvist Cc: stable Acked-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 1ce27f3ff7a7..aef15497ff31 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1955,6 +1955,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x1b) }, { USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 */ .driver_info = RSVD(4) | RSVD(5) | RSVD(6) }, + { USB_DEVICE(0x2cb7, 0x0104), /* Fibocom NL678 series */ + .driver_info = RSVD(4) | RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0105, 0xff), /* Fibocom NL678 series */ + .driver_info = RSVD(6) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); -- cgit v1.2.3-59-g8ed1b