mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 12:21:22 -05:00
Merge tag 'usb-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt updates from Greg KH:
"Here is the big set of USB and Thunderbolt driver updates for
6.15-rc1. Included in here are:
- Thunderbolt driver and core api updates for new hardware and
features
- usb-storage const array cleanups
- typec driver updates
- dwc3 driver updates
- xhci driver updates and bugfixes
- small USB documentation updates
- usb cdns3 driver updates
- usb gadget driver updates
- other small driver updates and fixes
All of these have been in linux-next for a while with no reported
issues"
* tag 'usb-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (92 commits)
thunderbolt: Do not add non-active NVM if NVM upgrade is disabled for retimer
thunderbolt: Scan retimers after device router has been enumerated
usb: host: cdns3: forward lost power information to xhci
usb: host: xhci-plat: allow upper layers to signal power loss
usb: xhci: change xhci_resume() parameters to explicit the desired info
usb: cdns3-ti: run HW init at resume() if HW was reset
usb: cdns3-ti: move reg writes to separate function
usb: cdns3: call cdns_power_is_lost() only once in cdns_resume()
usb: cdns3: rename hibernated argument of role->resume() to lost_power
usb: xhci: tegra: rename `runtime` boolean to `is_auto_runtime`
usb: host: xhci-plat: mvebu: use ->quirks instead of ->init_quirk() func
usb: dwc3: Don't use %pK through printk
usb: core: Don't use %pK through printk
usb: gadget: aspeed: Add NULL pointer check in ast_vhub_init_dev()
dt-bindings: usb: qcom,dwc3: Synchronize minItems for interrupts and -names
usb: common: usb-conn-gpio: switch psy_cfg from of_node to fwnode
usb: xhci: Avoid Stop Endpoint retry loop if the endpoint seems Running
usb: xhci: Don't change the status of stalled TDs on failed Stop EP
xhci: Avoid queuing redundant Stop Endpoint command for stalled endpoint
xhci: Handle spurious events on Etron host isoc enpoints
...
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# # Copyright (c) 2021 Aspeed Tehchnology Inc.
|
||||
# # Copyright (c) 2021 Aspeed Technology Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mfd/aspeed-lpc.yaml#
|
||||
|
||||
@@ -51,6 +51,8 @@ properties:
|
||||
- const: core
|
||||
- const: reg
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
|
||||
@@ -9,16 +9,19 @@ title: Microchip USB2514 Hub Controller
|
||||
maintainers:
|
||||
- Fabio Estevam <festevam@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: usb-device.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- usb424,2412
|
||||
- usb424,2417
|
||||
- usb424,2514
|
||||
- usb424,2517
|
||||
oneOf:
|
||||
- enum:
|
||||
- usb424,2412
|
||||
- usb424,2417
|
||||
- usb424,2514
|
||||
- usb424,2517
|
||||
- items:
|
||||
- enum:
|
||||
- usb424,2512
|
||||
- usb424,2513
|
||||
- const: usb424,2514
|
||||
|
||||
reg: true
|
||||
|
||||
@@ -28,6 +31,9 @@ properties:
|
||||
vdd-supply:
|
||||
description: 3.3V power supply.
|
||||
|
||||
vdda-supply:
|
||||
description: 3.3V analog power supply.
|
||||
|
||||
clocks:
|
||||
description: External 24MHz clock connected to the CLKIN pin.
|
||||
maxItems: 1
|
||||
@@ -43,6 +49,18 @@ patternProperties:
|
||||
$ref: /schemas/usb/usb-device.yaml
|
||||
additionalProperties: true
|
||||
|
||||
allOf:
|
||||
- $ref: usb-device.yaml#
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: usb424,2514
|
||||
then:
|
||||
properties:
|
||||
vdda-supply: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
@@ -60,6 +78,7 @@ examples:
|
||||
clocks = <&clks IMX6QDL_CLK_CKO>;
|
||||
reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <®_3v3_hub>;
|
||||
vdda-supply = <®_3v3a_hub>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
|
||||
140
Documentation/devicetree/bindings/usb/parade,ps8830.yaml
Normal file
140
Documentation/devicetree/bindings/usb/parade,ps8830.yaml
Normal file
@@ -0,0 +1,140 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/parade,ps8830.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Parade PS883x USB and DisplayPort Retimer
|
||||
|
||||
maintainers:
|
||||
- Abel Vesa <abel.vesa@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- parade,ps8830
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: XO Clock
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: power supply (1.07V)
|
||||
|
||||
vdd33-supply:
|
||||
description: power supply (3.3V)
|
||||
|
||||
vdd33-cap-supply:
|
||||
description: power supply (3.3V)
|
||||
|
||||
vddar-supply:
|
||||
description: power supply (1.07V)
|
||||
|
||||
vddat-supply:
|
||||
description: power supply (1.07V)
|
||||
|
||||
vddio-supply:
|
||||
description: power supply (1.2V or 1.8V)
|
||||
|
||||
orientation-switch: true
|
||||
retimer-switch: true
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Super Speed (SS) Output endpoint to the Type-C connector
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
description: Super Speed (SS) Input endpoint from the Super-Speed PHY
|
||||
unevaluatedProperties: false
|
||||
|
||||
port@2:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
Sideband Use (SBU) AUX lines endpoint to the Type-C connector for the purpose of
|
||||
handling altmode muxing and orientation switching.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- reset-gpios
|
||||
- vdd-supply
|
||||
- vdd33-supply
|
||||
- vdd33-cap-supply
|
||||
- vddat-supply
|
||||
- vddio-supply
|
||||
- orientation-switch
|
||||
- retimer-switch
|
||||
|
||||
allOf:
|
||||
- $ref: usb-switch.yaml#
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
typec-mux@8 {
|
||||
compatible = "parade,ps8830";
|
||||
reg = <0x8>;
|
||||
|
||||
clocks = <&clk_rtmr_xo>;
|
||||
|
||||
vdd-supply = <&vreg_rtmr_1p15>;
|
||||
vdd33-supply = <&vreg_rtmr_3p3>;
|
||||
vdd33-cap-supply = <&vreg_rtmr_3p3>;
|
||||
vddar-supply = <&vreg_rtmr_1p15>;
|
||||
vddat-supply = <&vreg_rtmr_1p15>;
|
||||
vddio-supply = <&vreg_rtmr_1p8>;
|
||||
|
||||
reset-gpios = <&tlmm 10 GPIO_ACTIVE_LOW>;
|
||||
|
||||
retimer-switch;
|
||||
orientation-switch;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
endpoint {
|
||||
remote-endpoint = <&typec_con_ss>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
endpoint {
|
||||
remote-endpoint = <&usb_phy_ss>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
|
||||
endpoint {
|
||||
remote-endpoint = <&typec_dp_aux>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -404,6 +404,7 @@ allOf:
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
interrupt-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: pwr_event
|
||||
- const: qusb2_phy
|
||||
@@ -425,6 +426,7 @@ allOf:
|
||||
minItems: 3
|
||||
maxItems: 4
|
||||
interrupt-names:
|
||||
minItems: 3
|
||||
items:
|
||||
- const: pwr_event
|
||||
- const: qusb2_phy
|
||||
|
||||
@@ -30,6 +30,9 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vbus-supply:
|
||||
description: VBUS power supply
|
||||
|
||||
wakeup-source:
|
||||
type: boolean
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ select:
|
||||
contains:
|
||||
enum:
|
||||
- rockchip,rk3328-dwc3
|
||||
- rockchip,rk3562-dwc3
|
||||
- rockchip,rk3568-dwc3
|
||||
- rockchip,rk3576-dwc3
|
||||
- rockchip,rk3588-dwc3
|
||||
@@ -37,6 +38,7 @@ properties:
|
||||
items:
|
||||
- enum:
|
||||
- rockchip,rk3328-dwc3
|
||||
- rockchip,rk3562-dwc3
|
||||
- rockchip,rk3568-dwc3
|
||||
- rockchip,rk3576-dwc3
|
||||
- rockchip,rk3588-dwc3
|
||||
@@ -72,6 +74,7 @@ properties:
|
||||
- enum:
|
||||
- grf_clk
|
||||
- utmi
|
||||
- pipe
|
||||
- const: pipe
|
||||
|
||||
power-domains:
|
||||
@@ -111,6 +114,22 @@ allOf:
|
||||
- const: suspend_clk
|
||||
- const: bus_clk
|
||||
- const: grf_clk
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: rockchip,rk3562-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
clock-names:
|
||||
items:
|
||||
- const: ref_clk
|
||||
- const: suspend_clk
|
||||
- const: bus_clk
|
||||
- const: pipe
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
||||
@@ -11,12 +11,17 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- google,gs101-dwusb3
|
||||
- samsung,exynos5250-dwusb3
|
||||
- samsung,exynos5433-dwusb3
|
||||
- samsung,exynos7-dwusb3
|
||||
- samsung,exynos850-dwusb3
|
||||
oneOf:
|
||||
- enum:
|
||||
- google,gs101-dwusb3
|
||||
- samsung,exynos5250-dwusb3
|
||||
- samsung,exynos5433-dwusb3
|
||||
- samsung,exynos7-dwusb3
|
||||
- samsung,exynos7870-dwusb3
|
||||
- samsung,exynos850-dwusb3
|
||||
- items:
|
||||
- const: samsung,exynos990-dwusb3
|
||||
- const: samsung,exynos850-dwusb3
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
@@ -52,7 +57,6 @@ required:
|
||||
- clock-names
|
||||
- ranges
|
||||
- '#size-cells'
|
||||
- vdd10-supply
|
||||
- vdd33-supply
|
||||
|
||||
allOf:
|
||||
@@ -72,6 +76,8 @@ allOf:
|
||||
- const: susp_clk
|
||||
- const: link_aclk
|
||||
- const: link_pclk
|
||||
required:
|
||||
- vdd10-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
@@ -86,6 +92,8 @@ allOf:
|
||||
clock-names:
|
||||
items:
|
||||
- const: usbdrd30
|
||||
required:
|
||||
- vdd10-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
@@ -103,6 +111,8 @@ allOf:
|
||||
- const: susp_clk
|
||||
- const: phyclk
|
||||
- const: pipe_pclk
|
||||
required:
|
||||
- vdd10-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
@@ -119,6 +129,24 @@ allOf:
|
||||
- const: usbdrd30
|
||||
- const: usbdrd30_susp_clk
|
||||
- const: usbdrd30_axius_clk
|
||||
required:
|
||||
- vdd10-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynos7870-dwusb3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 3
|
||||
maxItems: 3
|
||||
clock-names:
|
||||
items:
|
||||
- const: bus_early
|
||||
- const: ref
|
||||
- const: ctrl
|
||||
|
||||
- if:
|
||||
properties:
|
||||
@@ -134,6 +162,8 @@ allOf:
|
||||
items:
|
||||
- const: bus_early
|
||||
- const: ref
|
||||
required:
|
||||
- vdd10-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
||||
@@ -65,6 +65,17 @@ properties:
|
||||
mode.
|
||||
type: boolean
|
||||
|
||||
snps,reserved-endpoints:
|
||||
description:
|
||||
Reserve endpoints for other needs, e.g, for tracing control and output.
|
||||
When set, the driver will avoid using them for the regular USB transfers.
|
||||
$ref: /schemas/types.yaml#/definitions/uint8-array
|
||||
minItems: 1
|
||||
maxItems: 30
|
||||
items:
|
||||
minimum: 2
|
||||
maximum: 31
|
||||
|
||||
snps,dis-start-transfer-quirk:
|
||||
description:
|
||||
When set, disable isoc START TRANSFER command failure SW work-around
|
||||
|
||||
@@ -39,8 +39,10 @@ properties:
|
||||
|
||||
reg:
|
||||
description: the number of the USB hub port or the USB host-controller
|
||||
port to which this device is attached. The range is 1-255.
|
||||
maxItems: 1
|
||||
port to which this device is attached.
|
||||
items:
|
||||
- minimum: 1
|
||||
maximum: 255
|
||||
|
||||
"#address-cells":
|
||||
description: should be 1 for hub nodes with device nodes,
|
||||
|
||||
@@ -613,7 +613,7 @@ endpoints configuration from the hardware, so we use line 12 instruction
|
||||
to bypass reading the configuration from silicon, and rely on a
|
||||
hard-coded table that describes the endpoints configuration instead::
|
||||
|
||||
static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
|
||||
static const struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
|
||||
|
||||
@@ -161,7 +161,7 @@ THANKS file in Inaky's driver):
|
||||
- The people at the linux-usb mailing list, for reading so
|
||||
many messages :) Ok, no more kidding; for all your advises!
|
||||
|
||||
- All the people at the USB Implementors Forum for their
|
||||
- All the people at the USB Implementers Forum for their
|
||||
help and assistance.
|
||||
|
||||
- Nathan Myers <ncm@cantrip.org>, for his advice! (hope you
|
||||
|
||||
@@ -24023,7 +24023,7 @@ F: drivers/thunderbolt/dma_test.c
|
||||
THUNDERBOLT DRIVER
|
||||
M: Andreas Noever <andreas.noever@gmail.com>
|
||||
M: Michael Jamet <michael.jamet@intel.com>
|
||||
M: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
M: Mika Westerberg <westeri@kernel.org>
|
||||
M: Yehezkel Bernat <YehezkelShB@gmail.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
@@ -24034,7 +24034,7 @@ F: include/linux/thunderbolt.h
|
||||
|
||||
THUNDERBOLT NETWORK DRIVER
|
||||
M: Michael Jamet <michael.jamet@intel.com>
|
||||
M: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
M: Mika Westerberg <westeri@kernel.org>
|
||||
M: Yehezkel Bernat <YehezkelShB@gmail.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
@@ -93,9 +93,11 @@ static int tb_retimer_nvm_add(struct tb_retimer *rt)
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
|
||||
ret = tb_nvm_add_non_active(nvm, nvm_write);
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
if (!rt->no_nvm_upgrade) {
|
||||
ret = tb_nvm_add_non_active(nvm, nvm_write);
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
}
|
||||
|
||||
rt->nvm = nvm;
|
||||
dev_dbg(&rt->dev, "NVM version %x.%x\n", nvm->major, nvm->minor);
|
||||
|
||||
@@ -1305,11 +1305,15 @@ static void tb_scan_port(struct tb_port *port)
|
||||
goto out_rpm_put;
|
||||
}
|
||||
|
||||
tb_retimer_scan(port, true);
|
||||
|
||||
sw = tb_switch_alloc(port->sw->tb, &port->sw->dev,
|
||||
tb_downstream_route(port));
|
||||
if (IS_ERR(sw)) {
|
||||
/*
|
||||
* Make the downstream retimers available even if there
|
||||
* is no router connected.
|
||||
*/
|
||||
tb_retimer_scan(port, true);
|
||||
|
||||
/*
|
||||
* If there is an error accessing the connected switch
|
||||
* it may be connected to another domain. Also we allow
|
||||
@@ -1359,6 +1363,14 @@ static void tb_scan_port(struct tb_port *port)
|
||||
upstream_port = tb_upstream_port(sw);
|
||||
tb_configure_link(port, upstream_port, sw);
|
||||
|
||||
/*
|
||||
* Scan for downstream retimers. We only scan them after the
|
||||
* router has been enumerated to avoid issues with certain
|
||||
* Pluggable devices that expect the host to enumerate them
|
||||
* within certain timeout.
|
||||
*/
|
||||
tb_retimer_scan(port, true);
|
||||
|
||||
/*
|
||||
* CL0s and CL1 are enabled and supported together.
|
||||
* Silently ignore CLx enabling in case CLx is not supported.
|
||||
|
||||
@@ -2229,19 +2229,15 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
|
||||
|
||||
path = tb_path_alloc(tb, down, TB_USB3_HOPID, up, TB_USB3_HOPID, 0,
|
||||
"USB3 Down");
|
||||
if (!path) {
|
||||
tb_tunnel_put(tunnel);
|
||||
return NULL;
|
||||
}
|
||||
if (!path)
|
||||
goto err_free;
|
||||
tb_usb3_init_path(path);
|
||||
tunnel->paths[TB_USB3_PATH_DOWN] = path;
|
||||
|
||||
path = tb_path_alloc(tb, up, TB_USB3_HOPID, down, TB_USB3_HOPID, 0,
|
||||
"USB3 Up");
|
||||
if (!path) {
|
||||
tb_tunnel_put(tunnel);
|
||||
return NULL;
|
||||
}
|
||||
if (!path)
|
||||
goto err_free;
|
||||
tb_usb3_init_path(path);
|
||||
tunnel->paths[TB_USB3_PATH_UP] = path;
|
||||
|
||||
@@ -2258,6 +2254,10 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
|
||||
}
|
||||
|
||||
return tunnel;
|
||||
|
||||
err_free:
|
||||
tb_tunnel_put(tunnel);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3468,7 +3468,7 @@ __must_hold(&cdns->lock)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated)
|
||||
static int cdns3_gadget_resume(struct cdns *cdns, bool lost_power)
|
||||
{
|
||||
struct cdns3_device *priv_dev = cdns->gadget_dev;
|
||||
|
||||
@@ -3476,7 +3476,7 @@ static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated)
|
||||
return 0;
|
||||
|
||||
cdns3_gadget_config(priv_dev);
|
||||
if (hibernated)
|
||||
if (lost_power)
|
||||
writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -58,6 +58,7 @@ struct cdns_ti {
|
||||
unsigned vbus_divider:1;
|
||||
struct clk *usb2_refclk;
|
||||
struct clk *lpm_clk;
|
||||
int usb2_refclk_rate_code;
|
||||
};
|
||||
|
||||
static const int cdns_ti_rate_table[] = { /* in KHZ */
|
||||
@@ -98,15 +99,50 @@ static const struct of_dev_auxdata cdns_ti_auxdata[] = {
|
||||
{},
|
||||
};
|
||||
|
||||
static void cdns_ti_reset_and_init_hw(struct cdns_ti *data)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* assert RESET */
|
||||
reg = cdns_ti_readl(data, USBSS_W1);
|
||||
reg &= ~USBSS_W1_PWRUP_RST;
|
||||
cdns_ti_writel(data, USBSS_W1, reg);
|
||||
|
||||
/* set static config */
|
||||
reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
|
||||
reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
|
||||
reg |= data->usb2_refclk_rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
|
||||
|
||||
reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
|
||||
if (data->vbus_divider)
|
||||
reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
|
||||
|
||||
cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
|
||||
reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
|
||||
|
||||
/* set USB2_ONLY mode if requested */
|
||||
reg = cdns_ti_readl(data, USBSS_W1);
|
||||
if (data->usb2_only)
|
||||
reg |= USBSS_W1_USB2_ONLY;
|
||||
|
||||
/* set default modestrap */
|
||||
reg |= USBSS_W1_MODESTRAP_SEL;
|
||||
reg &= ~USBSS_W1_MODESTRAP_MASK;
|
||||
reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT;
|
||||
cdns_ti_writel(data, USBSS_W1, reg);
|
||||
|
||||
/* de-assert RESET */
|
||||
reg |= USBSS_W1_PWRUP_RST;
|
||||
cdns_ti_writel(data, USBSS_W1, reg);
|
||||
}
|
||||
|
||||
static int cdns_ti_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct cdns_ti *data;
|
||||
int error;
|
||||
u32 reg;
|
||||
int rate_code, i;
|
||||
unsigned long rate;
|
||||
int error, i;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
@@ -146,7 +182,17 @@ static int cdns_ti_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rate_code = i;
|
||||
data->usb2_refclk_rate_code = i;
|
||||
data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
|
||||
data->usb2_only = device_property_read_bool(dev, "ti,usb2-only");
|
||||
|
||||
/*
|
||||
* The call below to pm_runtime_get_sync() MIGHT reset hardware, if it
|
||||
* detects it as uninitialised. We want to enforce a reset at probe,
|
||||
* and so do it manually here. This means the first runtime_resume()
|
||||
* will be a no-op.
|
||||
*/
|
||||
cdns_ti_reset_and_init_hw(data);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
error = pm_runtime_get_sync(dev);
|
||||
@@ -155,40 +201,6 @@ static int cdns_ti_probe(struct platform_device *pdev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* assert RESET */
|
||||
reg = cdns_ti_readl(data, USBSS_W1);
|
||||
reg &= ~USBSS_W1_PWRUP_RST;
|
||||
cdns_ti_writel(data, USBSS_W1, reg);
|
||||
|
||||
/* set static config */
|
||||
reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
|
||||
reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
|
||||
reg |= rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
|
||||
|
||||
reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
|
||||
data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
|
||||
if (data->vbus_divider)
|
||||
reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
|
||||
|
||||
cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
|
||||
reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
|
||||
|
||||
/* set USB2_ONLY mode if requested */
|
||||
reg = cdns_ti_readl(data, USBSS_W1);
|
||||
data->usb2_only = device_property_read_bool(dev, "ti,usb2-only");
|
||||
if (data->usb2_only)
|
||||
reg |= USBSS_W1_USB2_ONLY;
|
||||
|
||||
/* set default modestrap */
|
||||
reg |= USBSS_W1_MODESTRAP_SEL;
|
||||
reg &= ~USBSS_W1_MODESTRAP_MASK;
|
||||
reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT;
|
||||
cdns_ti_writel(data, USBSS_W1, reg);
|
||||
|
||||
/* de-assert RESET */
|
||||
reg |= USBSS_W1_PWRUP_RST;
|
||||
cdns_ti_writel(data, USBSS_W1, reg);
|
||||
|
||||
error = of_platform_populate(node, NULL, cdns_ti_auxdata, dev);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to create children: %d\n", error);
|
||||
@@ -224,6 +236,24 @@ static void cdns_ti_remove(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
}
|
||||
|
||||
static int cdns_ti_runtime_resume(struct device *dev)
|
||||
{
|
||||
const u32 mask = USBSS_W1_PWRUP_RST | USBSS_W1_MODESTRAP_SEL;
|
||||
struct cdns_ti *data = dev_get_drvdata(dev);
|
||||
u32 w1;
|
||||
|
||||
w1 = cdns_ti_readl(data, USBSS_W1);
|
||||
if ((w1 & mask) != mask)
|
||||
cdns_ti_reset_and_init_hw(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cdns_ti_pm_ops = {
|
||||
RUNTIME_PM_OPS(NULL, cdns_ti_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id cdns_ti_of_match[] = {
|
||||
{ .compatible = "ti,j721e-usb", },
|
||||
{ .compatible = "ti,am64-usb", },
|
||||
@@ -237,6 +267,7 @@ static struct platform_driver cdns_ti_driver = {
|
||||
.driver = {
|
||||
.name = "cdns3-ti",
|
||||
.of_match_table = cdns_ti_of_match,
|
||||
.pm = pm_ptr(&cdns_ti_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1974,7 +1974,7 @@ static int cdnsp_gadget_suspend(struct cdns *cdns, bool do_wakeup)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdnsp_gadget_resume(struct cdns *cdns, bool hibernated)
|
||||
static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power)
|
||||
{
|
||||
struct cdnsp_device *pdev = cdns->gadget_dev;
|
||||
enum usb_device_speed max_speed;
|
||||
|
||||
@@ -524,11 +524,12 @@ EXPORT_SYMBOL_GPL(cdns_suspend);
|
||||
|
||||
int cdns_resume(struct cdns *cdns)
|
||||
{
|
||||
bool power_lost = cdns_power_is_lost(cdns);
|
||||
enum usb_role real_role;
|
||||
bool role_changed = false;
|
||||
int ret = 0;
|
||||
|
||||
if (cdns_power_is_lost(cdns)) {
|
||||
if (power_lost) {
|
||||
if (!cdns->role_sw) {
|
||||
real_role = cdns_hw_role_state_machine(cdns);
|
||||
if (real_role != cdns->role) {
|
||||
@@ -551,7 +552,7 @@ int cdns_resume(struct cdns *cdns)
|
||||
}
|
||||
|
||||
if (cdns->roles[cdns->role]->resume)
|
||||
cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns));
|
||||
cdns->roles[cdns->role]->resume(cdns, power_lost);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ struct cdns_role_driver {
|
||||
int (*start)(struct cdns *cdns);
|
||||
void (*stop)(struct cdns *cdns);
|
||||
int (*suspend)(struct cdns *cdns, bool do_wakeup);
|
||||
int (*resume)(struct cdns *cdns, bool hibernated);
|
||||
int (*resume)(struct cdns *cdns, bool lost_power);
|
||||
const char *name;
|
||||
#define CDNS_ROLE_STATE_INACTIVE 0
|
||||
#define CDNS_ROLE_STATE_ACTIVE 1
|
||||
|
||||
@@ -138,6 +138,16 @@ static void cdns_host_exit(struct cdns *cdns)
|
||||
cdns_drd_host_off(cdns);
|
||||
}
|
||||
|
||||
static int cdns_host_resume(struct cdns *cdns, bool power_lost)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(cdns->host_dev);
|
||||
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
|
||||
|
||||
priv->power_lost = power_lost;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cdns_host_init(struct cdns *cdns)
|
||||
{
|
||||
struct cdns_role_driver *rdrv;
|
||||
@@ -148,6 +158,7 @@ int cdns_host_init(struct cdns *cdns)
|
||||
|
||||
rdrv->start = __cdns_host_init;
|
||||
rdrv->stop = cdns_host_exit;
|
||||
rdrv->resume = cdns_host_resume;
|
||||
rdrv->state = CDNS_ROLE_STATE_INACTIVE;
|
||||
rdrv->name = "host";
|
||||
|
||||
|
||||
@@ -440,7 +440,7 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
|
||||
else if (data->oc_pol_configured)
|
||||
reg &= ~MX6_BM_OVER_CUR_POLARITY;
|
||||
}
|
||||
/* If the polarity is not set keep it as setup by the bootlader */
|
||||
/* If the polarity is not set keep it as setup by the bootloader */
|
||||
if (data->pwr_pol == 1)
|
||||
reg |= MX6_BM_PWR_POLARITY;
|
||||
writel(reg, usbmisc->base + data->index * 4);
|
||||
@@ -645,7 +645,7 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
|
||||
else if (data->oc_pol_configured)
|
||||
reg &= ~MX6_BM_OVER_CUR_POLARITY;
|
||||
}
|
||||
/* If the polarity is not set keep it as setup by the bootlader */
|
||||
/* If the polarity is not set keep it as setup by the bootloader */
|
||||
if (data->pwr_pol == 1)
|
||||
reg |= MX6_BM_PWR_POLARITY;
|
||||
writel(reg, usbmisc->base);
|
||||
@@ -939,7 +939,7 @@ static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data)
|
||||
else if (data->oc_pol_configured)
|
||||
reg &= ~MX6_BM_OVER_CUR_POLARITY;
|
||||
}
|
||||
/* If the polarity is not set keep it as setup by the bootlader */
|
||||
/* If the polarity is not set keep it as setup by the bootloader */
|
||||
if (data->pwr_pol == 1)
|
||||
reg |= MX6_BM_PWR_POLARITY;
|
||||
|
||||
@@ -1185,7 +1185,7 @@ int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup)
|
||||
if (usbmisc->ops->hsic_set_clk && data->hsic)
|
||||
ret = usbmisc->ops->hsic_set_clk(data, false);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
|
||||
dev_err(data->dev, "hsic_set_clk failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1224,7 +1224,7 @@ int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup)
|
||||
if (usbmisc->ops->hsic_set_clk && data->hsic)
|
||||
ret = usbmisc->ops->hsic_set_clk(data, true);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
|
||||
dev_err(data->dev, "hsic_set_clk failed, ret=%d\n", ret);
|
||||
goto hsic_set_clk_fail;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ static int usb_conn_psy_register(struct usb_conn_info *info)
|
||||
struct device *dev = info->dev;
|
||||
struct power_supply_desc *desc = &info->desc;
|
||||
struct power_supply_config cfg = {
|
||||
.of_node = dev->of_node,
|
||||
.fwnode = dev_fwnode(dev),
|
||||
};
|
||||
|
||||
desc->name = "usb-charger";
|
||||
|
||||
@@ -64,6 +64,37 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
|
||||
memcpy(&ep->ssp_isoc_ep_comp, desc, USB_DT_SSP_ISOC_EP_COMP_SIZE);
|
||||
}
|
||||
|
||||
static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev,
|
||||
int cfgno, int inum, int asnum, struct usb_host_endpoint *ep,
|
||||
unsigned char *buffer, int size)
|
||||
{
|
||||
struct usb_eusb2_isoc_ep_comp_descriptor *desc;
|
||||
struct usb_descriptor_header *h;
|
||||
|
||||
/*
|
||||
* eUSB2 isochronous endpoint companion descriptor for this endpoint
|
||||
* shall be declared before the next endpoint or interface descriptor
|
||||
*/
|
||||
while (size >= USB_DT_EUSB2_ISOC_EP_COMP_SIZE) {
|
||||
h = (struct usb_descriptor_header *)buffer;
|
||||
|
||||
if (h->bDescriptorType == USB_DT_EUSB2_ISOC_ENDPOINT_COMP) {
|
||||
desc = (struct usb_eusb2_isoc_ep_comp_descriptor *)buffer;
|
||||
ep->eusb2_isoc_ep_comp = *desc;
|
||||
return;
|
||||
}
|
||||
if (h->bDescriptorType == USB_DT_ENDPOINT ||
|
||||
h->bDescriptorType == USB_DT_INTERFACE)
|
||||
break;
|
||||
|
||||
buffer += h->bLength;
|
||||
size -= h->bLength;
|
||||
}
|
||||
|
||||
dev_notice(ddev, "No eUSB2 isoc ep %d companion for config %d interface %d altsetting %d\n",
|
||||
ep->desc.bEndpointAddress, cfgno, inum, asnum);
|
||||
}
|
||||
|
||||
static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
||||
int inum, int asnum, struct usb_host_endpoint *ep,
|
||||
unsigned char *buffer, int size)
|
||||
@@ -258,8 +289,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
|
||||
int n, i, j, retval;
|
||||
unsigned int maxp;
|
||||
const unsigned short *maxpacket_maxes;
|
||||
u16 bcdUSB;
|
||||
|
||||
d = (struct usb_endpoint_descriptor *) buffer;
|
||||
bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB);
|
||||
buffer += d->bLength;
|
||||
size -= d->bLength;
|
||||
|
||||
@@ -409,15 +442,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
|
||||
|
||||
/*
|
||||
* Validate the wMaxPacketSize field.
|
||||
* Some devices have isochronous endpoints in altsetting 0;
|
||||
* the USB-2 spec requires such endpoints to have wMaxPacketSize = 0
|
||||
* (see the end of section 5.6.3), so don't warn about them.
|
||||
* eUSB2 devices (see USB 2.0 Double Isochronous IN ECN 9.6.6 Endpoint)
|
||||
* and devices with isochronous endpoints in altsetting 0 (see USB 2.0
|
||||
* end of section 5.6.3) have wMaxPacketSize = 0.
|
||||
* So don't warn about those.
|
||||
*/
|
||||
maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize);
|
||||
if (maxp == 0 && !(usb_endpoint_xfer_isoc(d) && asnum == 0)) {
|
||||
|
||||
if (maxp == 0 && bcdUSB != 0x0220 &&
|
||||
!(usb_endpoint_xfer_isoc(d) && asnum == 0))
|
||||
dev_notice(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n",
|
||||
cfgno, inum, asnum, d->bEndpointAddress);
|
||||
}
|
||||
|
||||
/* Find the highest legal maxpacket size for this endpoint */
|
||||
i = 0; /* additional transactions per microframe */
|
||||
@@ -465,6 +500,12 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
|
||||
maxp);
|
||||
}
|
||||
|
||||
/* Parse a possible eUSB2 periodic endpoint companion descriptor */
|
||||
if (bcdUSB == 0x0220 && d->wMaxPacketSize == 0 &&
|
||||
(usb_endpoint_xfer_isoc(d) || usb_endpoint_xfer_int(d)))
|
||||
usb_parse_eusb2_isoc_endpoint_companion(ddev, cfgno, inum, asnum,
|
||||
endpoint, buffer, size);
|
||||
|
||||
/* Parse a possible SuperSpeed endpoint companion descriptor */
|
||||
if (udev->speed >= USB_SPEED_SUPER)
|
||||
usb_parse_ss_endpoint_companion(ddev, cfgno,
|
||||
|
||||
@@ -1609,7 +1609,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
|
||||
if (retval == 0)
|
||||
retval = -EINPROGRESS;
|
||||
else if (retval != -EIDRM && retval != -EBUSY)
|
||||
dev_dbg(&udev->dev, "hcd_unlink_urb %pK fail %d\n",
|
||||
dev_dbg(&udev->dev, "hcd_unlink_urb %p fail %d\n",
|
||||
urb, retval);
|
||||
usb_put_dev(udev);
|
||||
}
|
||||
@@ -1786,7 +1786,7 @@ void usb_hcd_flush_endpoint(struct usb_device *udev,
|
||||
/* kick hcd */
|
||||
unlink1(hcd, urb, -ESHUTDOWN);
|
||||
dev_dbg (hcd->self.controller,
|
||||
"shutdown urb %pK ep%d%s-%s\n",
|
||||
"shutdown urb %p ep%d%s-%s\n",
|
||||
urb, usb_endpoint_num(&ep->desc),
|
||||
is_in ? "in" : "out",
|
||||
usb_ep_type_string(usb_endpoint_type(&ep->desc)));
|
||||
|
||||
@@ -4708,8 +4708,6 @@ void usb_ep0_reinit(struct usb_device *udev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_ep0_reinit);
|
||||
|
||||
#define usb_sndaddr0pipe() (PIPE_CONTROL << 30)
|
||||
|
||||
static int hub_set_address(struct usb_device *udev, int devnum)
|
||||
{
|
||||
int retval;
|
||||
@@ -4733,7 +4731,7 @@ static int hub_set_address(struct usb_device *udev, int devnum)
|
||||
if (hcd->driver->address_device)
|
||||
retval = hcd->driver->address_device(hcd, udev, timeout_ms);
|
||||
else
|
||||
retval = usb_control_msg(udev, usb_sndaddr0pipe(),
|
||||
retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_ADDRESS, 0, devnum, 0,
|
||||
NULL, 0, timeout_ms);
|
||||
if (retval == 0) {
|
||||
|
||||
@@ -376,7 +376,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
if (!urb || !urb->complete)
|
||||
return -EINVAL;
|
||||
if (urb->hcpriv) {
|
||||
WARN_ONCE(1, "URB %pK submitted while active\n", urb);
|
||||
WARN_ONCE(1, "URB %p submitted while active\n", urb);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
|
||||
/* Backup global regs */
|
||||
gr = &hsotg->gr_backup;
|
||||
|
||||
gr->gintsts = dwc2_readl(hsotg, GINTSTS);
|
||||
gr->gotgctl = dwc2_readl(hsotg, GOTGCTL);
|
||||
gr->gintmsk = dwc2_readl(hsotg, GINTMSK);
|
||||
gr->gahbcfg = dwc2_readl(hsotg, GAHBCFG);
|
||||
|
||||
@@ -667,6 +667,7 @@ struct dwc2_hw_params {
|
||||
/**
|
||||
* struct dwc2_gregs_backup - Holds global registers state before
|
||||
* entering partial power down
|
||||
* @gintsts: Backup of GINTSTS register
|
||||
* @gotgctl: Backup of GOTGCTL register
|
||||
* @gintmsk: Backup of GINTMSK register
|
||||
* @gahbcfg: Backup of GAHBCFG register
|
||||
@@ -683,6 +684,7 @@ struct dwc2_hw_params {
|
||||
* @valid: True if registers values backuped.
|
||||
*/
|
||||
struct dwc2_gregs_backup {
|
||||
u32 gintsts;
|
||||
u32 gotgctl;
|
||||
u32 gintmsk;
|
||||
u32 gahbcfg;
|
||||
@@ -1127,6 +1129,9 @@ struct dwc2_hsotg {
|
||||
#define DWC2_FS_IOT_ID 0x55310000
|
||||
#define DWC2_HS_IOT_ID 0x55320000
|
||||
|
||||
#define DWC2_RESTORE_DCTL BIT(0)
|
||||
#define DWC2_RESTORE_DCFG BIT(1)
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
union dwc2_hcd_internal_flags {
|
||||
u32 d32;
|
||||
@@ -1420,7 +1425,7 @@ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
|
||||
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
|
||||
#define dwc2_is_device_enabled(hsotg) (hsotg->enabled)
|
||||
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup);
|
||||
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, unsigned int flags);
|
||||
int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||
int rem_wakeup, int reset);
|
||||
@@ -1435,6 +1440,9 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
|
||||
unsigned int flags);
|
||||
static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg)
|
||||
{ hsotg->fifo_map = 0; }
|
||||
#else
|
||||
@@ -1459,7 +1467,7 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
|
||||
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg,
|
||||
int remote_wakeup)
|
||||
unsigned int flags)
|
||||
{ return 0; }
|
||||
static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
@@ -1482,6 +1490,11 @@ static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {}
|
||||
static inline int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
|
||||
unsigned int flags)
|
||||
{ return 0; }
|
||||
static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) {}
|
||||
#endif
|
||||
|
||||
@@ -1505,6 +1518,8 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
|
||||
void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup);
|
||||
bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2);
|
||||
int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg);
|
||||
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg)
|
||||
{ schedule_work(&hsotg->phy_reset_work); }
|
||||
#else
|
||||
@@ -1544,6 +1559,10 @@ static inline void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg,
|
||||
int rem_wakeup) {}
|
||||
static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2)
|
||||
{ return false; }
|
||||
static inline int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5204,11 +5204,11 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
|
||||
* if controller power were disabled.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
* @remote_wakeup: Indicates whether resume is initiated by Device or Host.
|
||||
* @flags: Defines which registers should be restored.
|
||||
*
|
||||
* Return: 0 if successful, negative error code otherwise
|
||||
*/
|
||||
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
|
||||
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, unsigned int flags)
|
||||
{
|
||||
struct dwc2_dregs_backup *dr;
|
||||
int i;
|
||||
@@ -5224,7 +5224,10 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
|
||||
}
|
||||
dr->valid = false;
|
||||
|
||||
if (!remote_wakeup)
|
||||
if (flags & DWC2_RESTORE_DCFG)
|
||||
dwc2_writel(hsotg, dr->dcfg, DCFG);
|
||||
|
||||
if (flags & DWC2_RESTORE_DCTL)
|
||||
dwc2_writel(hsotg, dr->dctl, DCTL);
|
||||
|
||||
dwc2_writel(hsotg, dr->daintmsk, DAINTMSK);
|
||||
@@ -5310,6 +5313,49 @@ void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
|
||||
dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK));
|
||||
}
|
||||
|
||||
int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Backup all registers */
|
||||
ret = dwc2_backup_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc2_backup_device_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup device registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
|
||||
unsigned int flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dwc2_restore_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
ret = dwc2_restore_device_registers(hsotg, flags);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_enter_hibernation() - Put controller in Hibernation.
|
||||
*
|
||||
@@ -5327,18 +5373,9 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||
/* Change to L2(suspend) state */
|
||||
hsotg->lx_state = DWC2_L2;
|
||||
dev_dbg(hsotg->dev, "Start of hibernation completed\n");
|
||||
ret = dwc2_backup_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
|
||||
__func__);
|
||||
ret = dwc2_gadget_backup_critical_registers(hsotg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = dwc2_backup_device_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup device registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpwrdn = GPWRDN_PWRDNRSTN;
|
||||
udelay(10);
|
||||
@@ -5415,6 +5452,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||
u32 gpwrdn;
|
||||
u32 dctl;
|
||||
int ret = 0;
|
||||
unsigned int flags = 0;
|
||||
struct dwc2_gregs_backup *gr;
|
||||
struct dwc2_dregs_backup *dr;
|
||||
|
||||
@@ -5477,6 +5515,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||
dctl = dwc2_readl(hsotg, DCTL);
|
||||
dctl |= DCTL_PWRONPRGDONE;
|
||||
dwc2_writel(hsotg, dctl, DCTL);
|
||||
flags |= DWC2_RESTORE_DCTL;
|
||||
}
|
||||
/* Wait for interrupts which must be cleared */
|
||||
mdelay(2);
|
||||
@@ -5484,20 +5523,9 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
|
||||
|
||||
/* Restore global registers */
|
||||
ret = dwc2_restore_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore registers\n",
|
||||
__func__);
|
||||
ret = dwc2_gadget_restore_critical_registers(hsotg, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Restore device registers */
|
||||
ret = dwc2_restore_device_registers(hsotg, rem_wakeup);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rem_wakeup) {
|
||||
mdelay(10);
|
||||
@@ -5531,19 +5559,9 @@ int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg)
|
||||
dev_dbg(hsotg->dev, "Entering device partial power down started.\n");
|
||||
|
||||
/* Backup all registers */
|
||||
ret = dwc2_backup_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
|
||||
__func__);
|
||||
ret = dwc2_gadget_backup_critical_registers(hsotg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc2_backup_device_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup device registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear any pending interrupts since dwc2 will not be able to
|
||||
@@ -5590,11 +5608,8 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
|
||||
{
|
||||
u32 pcgcctl;
|
||||
u32 dctl;
|
||||
struct dwc2_dregs_backup *dr;
|
||||
int ret = 0;
|
||||
|
||||
dr = &hsotg->dr_backup;
|
||||
|
||||
dev_dbg(hsotg->dev, "Exiting device partial Power Down started.\n");
|
||||
|
||||
pcgcctl = dwc2_readl(hsotg, PCGCTL);
|
||||
@@ -5611,21 +5626,10 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
|
||||
|
||||
udelay(100);
|
||||
if (restore) {
|
||||
ret = dwc2_restore_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore registers\n",
|
||||
__func__);
|
||||
ret = dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL |
|
||||
DWC2_RESTORE_DCFG);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
/* Restore DCFG */
|
||||
dwc2_writel(hsotg, dr->dcfg, DCFG);
|
||||
|
||||
ret = dwc2_restore_device_registers(hsotg, 0);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the Power-On Programming done bit */
|
||||
|
||||
@@ -5474,6 +5474,49 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Backup all registers */
|
||||
ret = dwc2_backup_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc2_backup_host_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup host registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dwc2_restore_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc2_restore_host_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore host registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_host_enter_hibernation() - Put controller in Hibernation.
|
||||
*
|
||||
@@ -5489,18 +5532,9 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||
u32 gpwrdn;
|
||||
|
||||
dev_dbg(hsotg->dev, "Preparing host for hibernation\n");
|
||||
ret = dwc2_backup_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
|
||||
__func__);
|
||||
ret = dwc2_host_backup_critical_registers(hsotg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = dwc2_backup_host_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup host registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enter USB Suspend Mode */
|
||||
hprt0 = dwc2_readl(hsotg, HPRT0);
|
||||
@@ -5694,20 +5728,9 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
|
||||
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
|
||||
|
||||
/* Restore global registers */
|
||||
ret = dwc2_restore_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore registers\n",
|
||||
__func__);
|
||||
ret = dwc2_host_restore_critical_registers(hsotg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Restore host registers */
|
||||
ret = dwc2_restore_host_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore host registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rem_wakeup) {
|
||||
dwc2_hcd_rem_wakeup(hsotg);
|
||||
@@ -5774,19 +5797,9 @@ int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg)
|
||||
dev_warn(hsotg->dev, "Suspend wasn't generated\n");
|
||||
|
||||
/* Backup all registers */
|
||||
ret = dwc2_backup_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
|
||||
__func__);
|
||||
ret = dwc2_host_backup_critical_registers(hsotg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc2_backup_host_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup host registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear any pending interrupts since dwc2 will not be able to
|
||||
@@ -5855,19 +5868,9 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
|
||||
|
||||
udelay(100);
|
||||
if (restore) {
|
||||
ret = dwc2_restore_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore registers\n",
|
||||
__func__);
|
||||
ret = dwc2_host_restore_critical_registers(hsotg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc2_restore_host_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore host registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Drive resume signaling and exit suspend mode on the port. */
|
||||
|
||||
@@ -685,6 +685,14 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
|
||||
regulator_disable(dwc2->usb33d);
|
||||
}
|
||||
|
||||
if (is_device_mode)
|
||||
ret = dwc2_gadget_backup_critical_registers(dwc2);
|
||||
else
|
||||
ret = dwc2_host_backup_critical_registers(dwc2);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dwc2->ll_hw_enabled &&
|
||||
(is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
|
||||
ret = __dwc2_lowlevel_hw_disable(dwc2);
|
||||
@@ -694,6 +702,24 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc2_restore_critical_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_gregs_backup *gr;
|
||||
|
||||
gr = &hsotg->gr_backup;
|
||||
|
||||
if (!gr->valid) {
|
||||
dev_err(hsotg->dev, "No valid register backup, failed to restore\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gr->gintsts & GINTSTS_CURMODE_HOST)
|
||||
return dwc2_host_restore_critical_registers(hsotg);
|
||||
|
||||
return dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL |
|
||||
DWC2_RESTORE_DCFG);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc2_resume(struct device *dev)
|
||||
{
|
||||
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
|
||||
@@ -706,6 +732,18 @@ static int __maybe_unused dwc2_resume(struct device *dev)
|
||||
}
|
||||
dwc2->phy_off_for_suspend = false;
|
||||
|
||||
/*
|
||||
* During suspend it's possible that the power domain for the
|
||||
* DWC2 controller is disabled and all register values get lost.
|
||||
* In case the GUSBCFG register is not initialized, it's clear the
|
||||
* registers must be restored.
|
||||
*/
|
||||
if (!(dwc2_readl(dwc2, GUSBCFG) & GUSBCFG_TOUTCAL_MASK)) {
|
||||
ret = dwc2_restore_critical_registers(dwc2);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dwc2->params.activate_stm_id_vb_detection) {
|
||||
unsigned long flags;
|
||||
u32 ggpio, gotgctl;
|
||||
|
||||
@@ -153,11 +153,11 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
|
||||
{
|
||||
struct device *dev = am62->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct of_phandle_args args;
|
||||
struct regmap *syscon;
|
||||
int ret;
|
||||
|
||||
syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-phy-pll-refclk");
|
||||
syscon = syscon_regmap_lookup_by_phandle_args(node, "ti,syscon-phy-pll-refclk",
|
||||
1, &am62->offset);
|
||||
if (IS_ERR(syscon)) {
|
||||
dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n");
|
||||
return PTR_ERR(syscon);
|
||||
@@ -165,14 +165,6 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
|
||||
|
||||
am62->syscon = syscon;
|
||||
|
||||
ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1,
|
||||
0, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
of_node_put(args.np);
|
||||
am62->offset = args.args[0];
|
||||
|
||||
/* Core voltage. PHY_CORE_VOLTAGE bit Recommended to be 0 always */
|
||||
ret = regmap_update_bits(am62->syscon, am62->offset, PHY_CORE_VOLTAGE_MASK, 0);
|
||||
if (ret) {
|
||||
|
||||
@@ -163,6 +163,12 @@ static const struct dwc3_exynos_driverdata exynos7_drvdata = {
|
||||
.suspend_clk_idx = 1,
|
||||
};
|
||||
|
||||
static const struct dwc3_exynos_driverdata exynos7870_drvdata = {
|
||||
.clk_names = { "bus_early", "ref", "ctrl" },
|
||||
.num_clks = 3,
|
||||
.suspend_clk_idx = -1,
|
||||
};
|
||||
|
||||
static const struct dwc3_exynos_driverdata exynos850_drvdata = {
|
||||
.clk_names = { "bus_early", "ref" },
|
||||
.num_clks = 2,
|
||||
@@ -185,6 +191,9 @@ static const struct of_device_id exynos_dwc3_match[] = {
|
||||
}, {
|
||||
.compatible = "samsung,exynos7-dwusb3",
|
||||
.data = &exynos7_drvdata,
|
||||
}, {
|
||||
.compatible = "samsung,exynos7870-dwusb3",
|
||||
.data = &exynos7870_drvdata,
|
||||
}, {
|
||||
.compatible = "samsung,exynos850-dwusb3",
|
||||
.data = &exynos850_drvdata,
|
||||
|
||||
@@ -148,11 +148,21 @@ static const struct property_entry dwc3_pci_intel_byt_properties[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* Intel Merrifield SoC uses these endpoints for tracing and they cannot
|
||||
* be re-allocated if being used because the side band flow control signals
|
||||
* are hard wired to certain endpoints:
|
||||
* - 1 High BW Bulk IN (IN#1) (RTIT)
|
||||
* - 1 1KB BW Bulk IN (IN#8) + 1 1KB BW Bulk OUT (Run Control) (OUT#8)
|
||||
*/
|
||||
static const u8 dwc3_pci_mrfld_reserved_endpoints[] = { 3, 16, 17 };
|
||||
|
||||
static const struct property_entry dwc3_pci_mrfld_properties[] = {
|
||||
PROPERTY_ENTRY_STRING("dr_mode", "otg"),
|
||||
PROPERTY_ENTRY_STRING("linux,extcon-name", "mrfld_bcove_pwrsrc"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
|
||||
PROPERTY_ENTRY_U8_ARRAY("snps,reserved-endpoints", dwc3_pci_mrfld_reserved_endpoints),
|
||||
PROPERTY_ENTRY_BOOL("snps,usb2-gadget-lpm-disable"),
|
||||
PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
|
||||
{}
|
||||
|
||||
@@ -225,7 +225,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
dwc3_data->syscfg_reg_off = res->start;
|
||||
|
||||
dev_vdbg(dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n",
|
||||
dev_vdbg(dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n",
|
||||
dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
|
||||
|
||||
struct device_node *child __free(device_node) = of_get_compatible_child(node,
|
||||
|
||||
@@ -547,6 +547,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
|
||||
int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
struct dwc3_ep *dep;
|
||||
u32 cmd;
|
||||
int i;
|
||||
int ret;
|
||||
@@ -563,8 +564,13 @@ int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
|
||||
return ret;
|
||||
|
||||
/* Reset resource allocation flags */
|
||||
for (i = resource_index; i < dwc->num_eps && dwc->eps[i]; i++)
|
||||
dwc->eps[i]->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
|
||||
for (i = resource_index; i < dwc->num_eps; i++) {
|
||||
dep = dwc->eps[i];
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
dep->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -751,9 +757,11 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
|
||||
|
||||
dwc->last_fifo_depth = fifo_depth;
|
||||
/* Clear existing TXFIFO for all IN eps except ep0 */
|
||||
for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM);
|
||||
num += 2) {
|
||||
for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM); num += 2) {
|
||||
dep = dwc->eps[num];
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
/* Don't change TXFRAMNUM on usb31 version */
|
||||
size = DWC3_IP_IS(DWC3) ? 0 :
|
||||
dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
|
||||
@@ -1971,12 +1979,12 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
if (WARN(req->dep != dep, "request %pK belongs to '%s'\n",
|
||||
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
|
||||
&req->request, req->dep->name))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED,
|
||||
"%s: request %pK already in flight\n",
|
||||
"%s: request %p already in flight\n",
|
||||
dep->name, &req->request))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -2165,7 +2173,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
||||
}
|
||||
}
|
||||
|
||||
dev_err(dwc->dev, "request %pK was not queued to %s\n",
|
||||
dev_err(dwc->dev, "request %p was not queued to %s\n",
|
||||
request, ep->name);
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
@@ -3429,14 +3437,53 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_get_reserved_endpoints(struct dwc3 *dwc, const char *propname,
|
||||
u8 *eps, u8 num)
|
||||
{
|
||||
u8 count;
|
||||
int ret;
|
||||
|
||||
if (!device_property_present(dwc->dev, propname))
|
||||
return 0;
|
||||
|
||||
ret = device_property_count_u8(dwc->dev, propname);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
count = ret;
|
||||
|
||||
ret = device_property_read_u8_array(dwc->dev, propname, eps, min(num, count));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
|
||||
{
|
||||
const char *propname = "snps,reserved-endpoints";
|
||||
u8 epnum;
|
||||
u8 reserved_eps[DWC3_ENDPOINTS_NUM];
|
||||
u8 count;
|
||||
u8 num;
|
||||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&dwc->gadget->ep_list);
|
||||
|
||||
ret = dwc3_gadget_get_reserved_endpoints(dwc, propname,
|
||||
reserved_eps, ARRAY_SIZE(reserved_eps));
|
||||
if (ret < 0) {
|
||||
dev_err(dwc->dev, "failed to read %s\n", propname);
|
||||
return ret;
|
||||
}
|
||||
count = ret;
|
||||
|
||||
for (epnum = 0; epnum < total; epnum++) {
|
||||
int ret;
|
||||
for (num = 0; num < count; num++) {
|
||||
if (epnum == reserved_eps[num])
|
||||
break;
|
||||
}
|
||||
if (num < count)
|
||||
continue;
|
||||
|
||||
ret = dwc3_gadget_init_endpoint(dwc, epnum);
|
||||
if (ret)
|
||||
@@ -3703,6 +3750,8 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
|
||||
|
||||
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
|
||||
dep = dwc->eps[i];
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||
continue;
|
||||
@@ -3852,6 +3901,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
u8 epnum = event->endpoint_number;
|
||||
|
||||
dep = dwc->eps[epnum];
|
||||
if (!dep) {
|
||||
dev_warn(dwc->dev, "spurious event, endpoint %u is not allocated\n", epnum);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED)) {
|
||||
if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED))
|
||||
|
||||
@@ -122,8 +122,6 @@ static const struct vb2_ops uvc_queue_qops = {
|
||||
.queue_setup = uvc_queue_setup,
|
||||
.buf_prepare = uvc_buffer_prepare,
|
||||
.buf_queue = uvc_buffer_queue,
|
||||
.wait_prepare = vb2_ops_wait_prepare,
|
||||
.wait_finish = vb2_ops_wait_finish,
|
||||
};
|
||||
|
||||
int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2_buf_type type,
|
||||
|
||||
@@ -548,6 +548,9 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
|
||||
d->vhub = vhub;
|
||||
d->index = idx;
|
||||
d->name = devm_kasprintf(parent, GFP_KERNEL, "port%d", idx+1);
|
||||
if (!d->name)
|
||||
return -ENOMEM;
|
||||
|
||||
d->regs = vhub->regs + 0x100 + 0x10 * idx;
|
||||
|
||||
ast_vhub_init_ep0(vhub, &d->ep0, d);
|
||||
|
||||
@@ -1946,6 +1946,12 @@ max3421_remove(struct spi_device *spi)
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
static const struct spi_device_id max3421_spi_ids[] = {
|
||||
{ "max3421" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, max3421_spi_ids);
|
||||
|
||||
static const struct of_device_id max3421_of_match_table[] = {
|
||||
{ .compatible = "maxim,max3421", },
|
||||
{},
|
||||
@@ -1955,6 +1961,7 @@ MODULE_DEVICE_TABLE(of, max3421_of_match_table);
|
||||
static struct spi_driver max3421_driver = {
|
||||
.probe = max3421_probe,
|
||||
.remove = max3421_remove,
|
||||
.id_table = max3421_spi_ids,
|
||||
.driver = {
|
||||
.name = "max3421-hcd",
|
||||
.of_match_table = max3421_of_match_table,
|
||||
|
||||
@@ -355,7 +355,7 @@ static int __maybe_unused xhci_histb_resume(struct device *dev)
|
||||
if (!device_may_wakeup(dev))
|
||||
xhci_histb_host_enable(histb);
|
||||
|
||||
return xhci_resume(xhci, PMSG_RESUME);
|
||||
return xhci_resume(xhci, false, false);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops xhci_histb_pm_ops = {
|
||||
|
||||
@@ -1953,7 +1953,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
||||
xhci->interrupters = NULL;
|
||||
|
||||
xhci->page_size = 0;
|
||||
xhci->page_shift = 0;
|
||||
xhci->usb2_rhub.bus_state.bus_suspended = 0;
|
||||
xhci->usb3_rhub.bus_state.bus_suspended = 0;
|
||||
}
|
||||
@@ -2372,6 +2371,22 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
|
||||
|
||||
static void xhci_hcd_page_size(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 page_size;
|
||||
|
||||
page_size = readl(&xhci->op_regs->page_size) & XHCI_PAGE_SIZE_MASK;
|
||||
if (!is_power_of_2(page_size)) {
|
||||
xhci_warn(xhci, "Invalid page size register = 0x%x\n", page_size);
|
||||
/* Fallback to 4K page size, since that's common */
|
||||
page_size = 1;
|
||||
}
|
||||
|
||||
xhci->page_size = page_size << 12;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "HCD page size set to %iK",
|
||||
xhci->page_size >> 10);
|
||||
}
|
||||
|
||||
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
{
|
||||
struct xhci_interrupter *ir;
|
||||
@@ -2379,7 +2394,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
dma_addr_t dma;
|
||||
unsigned int val, val2;
|
||||
u64 val_64;
|
||||
u32 page_size, temp;
|
||||
u32 temp;
|
||||
int i;
|
||||
|
||||
INIT_LIST_HEAD(&xhci->cmd_list);
|
||||
@@ -2388,20 +2403,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
|
||||
init_completion(&xhci->cmd_ring_stop_completion);
|
||||
|
||||
page_size = readl(&xhci->op_regs->page_size);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Supported page size register = 0x%x", page_size);
|
||||
i = ffs(page_size);
|
||||
if (i < 16)
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Supported page size of %iK", (1 << (i+12)) / 1024);
|
||||
else
|
||||
xhci_warn(xhci, "WARN: no supported page size\n");
|
||||
/* Use 4K pages, since that's common and the minimum the HC supports */
|
||||
xhci->page_shift = 12;
|
||||
xhci->page_size = 1 << xhci->page_shift;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"HCD page size set to %iK", xhci->page_size / 1024);
|
||||
xhci_hcd_page_size(xhci);
|
||||
|
||||
/*
|
||||
* Program the Number of Device Slots Enabled field in the CONFIG
|
||||
|
||||
@@ -73,13 +73,3 @@ int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
||||
/* Without reset on resume, the HC won't work at all */
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,16 +12,10 @@ struct usb_hcd;
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
|
||||
int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd);
|
||||
int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd);
|
||||
#else
|
||||
static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* __LINUX_XHCI_MVEBU_H */
|
||||
|
||||
@@ -807,8 +807,10 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
|
||||
|
||||
static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
bool power_lost = msg.event == PM_EVENT_RESTORE;
|
||||
bool is_auto_resume = msg.event == PM_EVENT_AUTO_RESUME;
|
||||
|
||||
reset_control_reset(xhci->reset);
|
||||
|
||||
@@ -839,7 +841,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
|
||||
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
|
||||
xhci_pme_quirk(hcd);
|
||||
|
||||
return xhci_resume(xhci, msg);
|
||||
return xhci_resume(xhci, power_lost, is_auto_resume);
|
||||
}
|
||||
|
||||
static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
|
||||
|
||||
@@ -106,7 +106,7 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada = {
|
||||
};
|
||||
|
||||
static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
|
||||
.init_quirk = xhci_mvebu_a3700_init_quirk,
|
||||
.quirks = XHCI_RESET_ON_RESUME,
|
||||
};
|
||||
|
||||
static const struct xhci_plat_priv xhci_plat_brcm = {
|
||||
@@ -479,9 +479,10 @@ static int xhci_plat_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg)
|
||||
static int xhci_plat_resume_common(struct device *dev, bool power_lost)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
int ret;
|
||||
|
||||
@@ -501,7 +502,7 @@ static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg)
|
||||
if (ret)
|
||||
goto disable_clks;
|
||||
|
||||
ret = xhci_resume(xhci, pmsg);
|
||||
ret = xhci_resume(xhci, power_lost || priv->power_lost, false);
|
||||
if (ret)
|
||||
goto disable_clks;
|
||||
|
||||
@@ -522,12 +523,12 @@ static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg)
|
||||
|
||||
static int xhci_plat_resume(struct device *dev)
|
||||
{
|
||||
return xhci_plat_resume_common(dev, PMSG_RESUME);
|
||||
return xhci_plat_resume_common(dev, false);
|
||||
}
|
||||
|
||||
static int xhci_plat_restore(struct device *dev)
|
||||
{
|
||||
return xhci_plat_resume_common(dev, PMSG_RESTORE);
|
||||
return xhci_plat_resume_common(dev, true);
|
||||
}
|
||||
|
||||
static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev)
|
||||
@@ -548,7 +549,7 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev)
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
||||
return xhci_resume(xhci, PMSG_AUTO_RESUME);
|
||||
return xhci_resume(xhci, false, true);
|
||||
}
|
||||
|
||||
const struct dev_pm_ops xhci_plat_pm_ops = {
|
||||
|
||||
@@ -15,6 +15,7 @@ struct usb_hcd;
|
||||
struct xhci_plat_priv {
|
||||
const char *firmware_name;
|
||||
unsigned long long quirks;
|
||||
bool power_lost;
|
||||
void (*plat_start)(struct usb_hcd *);
|
||||
int (*init_quirk)(struct usb_hcd *);
|
||||
int (*suspend_quirk)(struct usb_hcd *);
|
||||
|
||||
@@ -203,6 +203,50 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If enqueue points at a link TRB, follow links until an ordinary TRB is reached.
|
||||
* Toggle the cycle bit of passed link TRBs and optionally chain them.
|
||||
*/
|
||||
static void inc_enq_past_link(struct xhci_hcd *xhci, struct xhci_ring *ring, u32 chain)
|
||||
{
|
||||
unsigned int link_trb_count = 0;
|
||||
|
||||
while (trb_is_link(ring->enqueue)) {
|
||||
|
||||
/*
|
||||
* Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
|
||||
* set, but other sections talk about dealing with the chain bit set. This was
|
||||
* fixed in the 0.96 specification errata, but we have to assume that all 0.95
|
||||
* xHCI hardware can't handle the chain bit being cleared on a link TRB.
|
||||
*
|
||||
* On 0.95 and some 0.96 HCs the chain bit is set once at segment initalization
|
||||
* and never changed here. On all others, modify it as requested by the caller.
|
||||
*/
|
||||
if (!xhci_link_chain_quirk(xhci, ring->type)) {
|
||||
ring->enqueue->link.control &= cpu_to_le32(~TRB_CHAIN);
|
||||
ring->enqueue->link.control |= cpu_to_le32(chain);
|
||||
}
|
||||
|
||||
/* Give this link TRB to the hardware */
|
||||
wmb();
|
||||
ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (link_trb_toggles_cycle(ring->enqueue))
|
||||
ring->cycle_state ^= 1;
|
||||
|
||||
ring->enq_seg = ring->enq_seg->next;
|
||||
ring->enqueue = ring->enq_seg->trbs;
|
||||
|
||||
trace_xhci_inc_enq(ring);
|
||||
|
||||
if (link_trb_count++ > ring->num_segs) {
|
||||
xhci_warn(xhci, "Link TRB loop at enqueue\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* See Cycle bit rules. SW is the consumer for the event ring only.
|
||||
*
|
||||
@@ -211,11 +255,6 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
* If we've enqueued the last TRB in a TD, make sure the following link TRBs
|
||||
* have their chain bit cleared (so that each Link TRB is a separate TD).
|
||||
*
|
||||
* Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
|
||||
* set, but other sections talk about dealing with the chain bit set. This was
|
||||
* fixed in the 0.96 specification errata, but we have to assume that all 0.95
|
||||
* xHCI hardware can't handle the chain bit being cleared on a link TRB.
|
||||
*
|
||||
* @more_trbs_coming: Will you enqueue more TRBs before calling
|
||||
* prepare_transfer()?
|
||||
*/
|
||||
@@ -223,8 +262,6 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
||||
bool more_trbs_coming)
|
||||
{
|
||||
u32 chain;
|
||||
union xhci_trb *next;
|
||||
unsigned int link_trb_count = 0;
|
||||
|
||||
chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
|
||||
|
||||
@@ -233,48 +270,67 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
||||
return;
|
||||
}
|
||||
|
||||
next = ++(ring->enqueue);
|
||||
ring->enqueue++;
|
||||
|
||||
/* Update the dequeue pointer further if that was a link TRB */
|
||||
while (trb_is_link(next)) {
|
||||
/*
|
||||
* If we are in the middle of a TD or the caller plans to enqueue more
|
||||
* TDs as one transfer (eg. control), traverse any link TRBs right now.
|
||||
* Otherwise, enqueue can stay on a link until the next prepare_ring().
|
||||
* This avoids enqueue entering deq_seg and simplifies ring expansion.
|
||||
*/
|
||||
if (trb_is_link(ring->enqueue) && (chain || more_trbs_coming))
|
||||
inc_enq_past_link(xhci, ring, chain);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the caller doesn't plan on enqueueing more TDs before
|
||||
* ringing the doorbell, then we don't want to give the link TRB
|
||||
* to the hardware just yet. We'll give the link TRB back in
|
||||
* prepare_ring() just before we enqueue the TD at the top of
|
||||
* the ring.
|
||||
*/
|
||||
if (!chain && !more_trbs_coming)
|
||||
break;
|
||||
/*
|
||||
* If the suspect DMA address is a TRB in this TD, this function returns that
|
||||
* TRB's segment. Otherwise it returns 0.
|
||||
*/
|
||||
static struct xhci_segment *trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma)
|
||||
{
|
||||
dma_addr_t start_dma;
|
||||
dma_addr_t end_seg_dma;
|
||||
dma_addr_t end_trb_dma;
|
||||
struct xhci_segment *cur_seg;
|
||||
|
||||
/* If we're not dealing with 0.95 hardware or isoc rings on
|
||||
* AMD 0.96 host, carry over the chain bit of the previous TRB
|
||||
* (which may mean the chain bit is cleared).
|
||||
*/
|
||||
if (!xhci_link_chain_quirk(xhci, ring->type)) {
|
||||
next->link.control &= cpu_to_le32(~TRB_CHAIN);
|
||||
next->link.control |= cpu_to_le32(chain);
|
||||
start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb);
|
||||
cur_seg = td->start_seg;
|
||||
|
||||
do {
|
||||
if (start_dma == 0)
|
||||
return NULL;
|
||||
/* We may get an event for a Link TRB in the middle of a TD */
|
||||
end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
|
||||
&cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
|
||||
/* If the end TRB isn't in this segment, this is set to 0 */
|
||||
end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb);
|
||||
|
||||
if (end_trb_dma > 0) {
|
||||
/* The end TRB is in this segment, so suspect should be here */
|
||||
if (start_dma <= end_trb_dma) {
|
||||
if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma)
|
||||
return cur_seg;
|
||||
} else {
|
||||
/* Case for one segment with
|
||||
* a TD wrapped around to the top
|
||||
*/
|
||||
if ((suspect_dma >= start_dma &&
|
||||
suspect_dma <= end_seg_dma) ||
|
||||
(suspect_dma >= cur_seg->dma &&
|
||||
suspect_dma <= end_trb_dma))
|
||||
return cur_seg;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/* Give this link TRB to the hardware */
|
||||
wmb();
|
||||
next->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
/* Might still be somewhere in this segment */
|
||||
if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma)
|
||||
return cur_seg;
|
||||
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (link_trb_toggles_cycle(next))
|
||||
ring->cycle_state ^= 1;
|
||||
cur_seg = cur_seg->next;
|
||||
start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
|
||||
} while (cur_seg != td->start_seg);
|
||||
|
||||
ring->enq_seg = ring->enq_seg->next;
|
||||
ring->enqueue = ring->enq_seg->trbs;
|
||||
next = ring->enqueue;
|
||||
|
||||
trace_xhci_inc_enq(ring);
|
||||
|
||||
if (link_trb_count++ > ring->num_segs) {
|
||||
xhci_warn(xhci, "%s: Ring link TRB loop\n", __func__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -505,8 +561,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
|
||||
* pointer command pending because the device can choose to start any
|
||||
* stream once the endpoint is on the HW schedule.
|
||||
*/
|
||||
if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) ||
|
||||
(ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT))
|
||||
if (ep_state & (EP_STOP_CMD_PENDING | SET_DEQ_PENDING | EP_HALTED |
|
||||
EP_CLEARING_TT | EP_STALLED))
|
||||
return;
|
||||
|
||||
trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id));
|
||||
@@ -1014,7 +1070,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
||||
td->urb->stream_id);
|
||||
hw_deq &= ~0xf;
|
||||
|
||||
if (td->cancel_status == TD_HALTED || trb_in_td(xhci, td, hw_deq, false)) {
|
||||
if (td->cancel_status == TD_HALTED || trb_in_td(td, hw_deq)) {
|
||||
switch (td->cancel_status) {
|
||||
case TD_CLEARED: /* TD is already no-op */
|
||||
case TD_CLEARING_CACHE: /* set TR deq command already queued */
|
||||
@@ -1104,7 +1160,7 @@ static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep)
|
||||
hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0);
|
||||
hw_deq &= ~0xf;
|
||||
td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list);
|
||||
if (trb_in_td(ep->xhci, td, hw_deq, false))
|
||||
if (trb_in_td(td, hw_deq))
|
||||
return td;
|
||||
}
|
||||
return NULL;
|
||||
@@ -1164,7 +1220,14 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
||||
*/
|
||||
switch (GET_EP_CTX_STATE(ep_ctx)) {
|
||||
case EP_STATE_HALTED:
|
||||
xhci_dbg(xhci, "Stop ep completion raced with stall, reset ep\n");
|
||||
xhci_dbg(xhci, "Stop ep completion raced with stall\n");
|
||||
/*
|
||||
* If the halt happened before Stop Endpoint failed, its transfer event
|
||||
* should have already been handled and Reset Endpoint should be pending.
|
||||
*/
|
||||
if (ep->ep_state & EP_HALTED)
|
||||
goto reset_done;
|
||||
|
||||
if (ep->ep_state & EP_HAS_STREAMS) {
|
||||
reset_type = EP_SOFT_RESET;
|
||||
} else {
|
||||
@@ -1175,8 +1238,11 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
||||
}
|
||||
/* reset ep, reset handler cleans up cancelled tds */
|
||||
err = xhci_handle_halted_endpoint(xhci, ep, td, reset_type);
|
||||
xhci_dbg(xhci, "Stop ep completion resetting ep, status %d\n", err);
|
||||
if (err)
|
||||
break;
|
||||
reset_done:
|
||||
/* Reset EP handler will clean up cancelled TDs */
|
||||
ep->ep_state &= ~EP_STOP_CMD_PENDING;
|
||||
return;
|
||||
case EP_STATE_STOPPED:
|
||||
@@ -1198,16 +1264,19 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
||||
* Stopped state, but it will soon change to Running.
|
||||
*
|
||||
* Assume this bug on unexpected Stop Endpoint failures.
|
||||
* Keep retrying until the EP starts and stops again, on
|
||||
* chips where this is known to help. Wait for 100ms.
|
||||
* Keep retrying until the EP starts and stops again.
|
||||
*/
|
||||
if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100)))
|
||||
break;
|
||||
fallthrough;
|
||||
case EP_STATE_RUNNING:
|
||||
/* Race, HW handled stop ep cmd before ep was running */
|
||||
xhci_dbg(xhci, "Stop ep completion ctx error, ctx_state %d\n",
|
||||
GET_EP_CTX_STATE(ep_ctx));
|
||||
/*
|
||||
* Don't retry forever if we guessed wrong or a defective HC never starts
|
||||
* the EP or says 'Running' but fails the command. We must give back TDs.
|
||||
*/
|
||||
if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100)))
|
||||
break;
|
||||
|
||||
command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||||
if (!command) {
|
||||
@@ -1332,43 +1401,6 @@ void xhci_hc_died(struct xhci_hcd *xhci)
|
||||
usb_hc_died(xhci_to_hcd(xhci));
|
||||
}
|
||||
|
||||
static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_device *dev,
|
||||
struct xhci_ring *ep_ring,
|
||||
unsigned int ep_index)
|
||||
{
|
||||
union xhci_trb *dequeue_temp;
|
||||
|
||||
dequeue_temp = ep_ring->dequeue;
|
||||
|
||||
/* If we get two back-to-back stalls, and the first stalled transfer
|
||||
* ends just before a link TRB, the dequeue pointer will be left on
|
||||
* the link TRB by the code in the while loop. So we have to update
|
||||
* the dequeue pointer one segment further, or we'll jump off
|
||||
* the segment into la-la-land.
|
||||
*/
|
||||
if (trb_is_link(ep_ring->dequeue)) {
|
||||
ep_ring->deq_seg = ep_ring->deq_seg->next;
|
||||
ep_ring->dequeue = ep_ring->deq_seg->trbs;
|
||||
}
|
||||
|
||||
while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
|
||||
/* We have more usable TRBs */
|
||||
ep_ring->dequeue++;
|
||||
if (trb_is_link(ep_ring->dequeue)) {
|
||||
if (ep_ring->dequeue ==
|
||||
dev->eps[ep_index].queued_deq_ptr)
|
||||
break;
|
||||
ep_ring->deq_seg = ep_ring->deq_seg->next;
|
||||
ep_ring->dequeue = ep_ring->deq_seg->trbs;
|
||||
}
|
||||
if (ep_ring->dequeue == dequeue_temp) {
|
||||
xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get a completion for a Set Transfer Ring Dequeue Pointer command,
|
||||
* we need to clear the set deq pending flag in the endpoint ring state, so that
|
||||
@@ -1473,8 +1505,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
||||
/* Update the ring's dequeue segment and dequeue pointer
|
||||
* to reflect the new position.
|
||||
*/
|
||||
update_ring_for_set_deq_completion(xhci, ep->vdev,
|
||||
ep_ring, ep_index);
|
||||
ep_ring->deq_seg = ep->queued_deq_seg;
|
||||
ep_ring->dequeue = ep->queued_deq_ptr;
|
||||
} else {
|
||||
xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n");
|
||||
xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n",
|
||||
@@ -2116,67 +2148,6 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
|
||||
spin_lock(&xhci->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the suspect DMA address is a TRB in this TD, this function returns that
|
||||
* TRB's segment. Otherwise it returns 0.
|
||||
*/
|
||||
struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_addr_t suspect_dma,
|
||||
bool debug)
|
||||
{
|
||||
dma_addr_t start_dma;
|
||||
dma_addr_t end_seg_dma;
|
||||
dma_addr_t end_trb_dma;
|
||||
struct xhci_segment *cur_seg;
|
||||
|
||||
start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb);
|
||||
cur_seg = td->start_seg;
|
||||
|
||||
do {
|
||||
if (start_dma == 0)
|
||||
return NULL;
|
||||
/* We may get an event for a Link TRB in the middle of a TD */
|
||||
end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
|
||||
&cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
|
||||
/* If the end TRB isn't in this segment, this is set to 0 */
|
||||
end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb);
|
||||
|
||||
if (debug)
|
||||
xhci_warn(xhci,
|
||||
"Looking for event-dma %016llx trb-start %016llx trb-end %016llx seg-start %016llx seg-end %016llx\n",
|
||||
(unsigned long long)suspect_dma,
|
||||
(unsigned long long)start_dma,
|
||||
(unsigned long long)end_trb_dma,
|
||||
(unsigned long long)cur_seg->dma,
|
||||
(unsigned long long)end_seg_dma);
|
||||
|
||||
if (end_trb_dma > 0) {
|
||||
/* The end TRB is in this segment, so suspect should be here */
|
||||
if (start_dma <= end_trb_dma) {
|
||||
if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma)
|
||||
return cur_seg;
|
||||
} else {
|
||||
/* Case for one segment with
|
||||
* a TD wrapped around to the top
|
||||
*/
|
||||
if ((suspect_dma >= start_dma &&
|
||||
suspect_dma <= end_seg_dma) ||
|
||||
(suspect_dma >= cur_seg->dma &&
|
||||
suspect_dma <= end_trb_dma))
|
||||
return cur_seg;
|
||||
}
|
||||
return NULL;
|
||||
} else {
|
||||
/* Might still be somewhere in this segment */
|
||||
if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma)
|
||||
return cur_seg;
|
||||
}
|
||||
cur_seg = cur_seg->next;
|
||||
start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
|
||||
} while (cur_seg != td->start_seg);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||
struct xhci_virt_ep *ep)
|
||||
{
|
||||
@@ -2476,6 +2447,12 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
if (ep_trb != td->end_trb)
|
||||
td->error_mid_td = true;
|
||||
break;
|
||||
case COMP_MISSED_SERVICE_ERROR:
|
||||
frame->status = -EXDEV;
|
||||
sum_trbs_for_length = true;
|
||||
if (ep_trb != td->end_trb)
|
||||
td->error_mid_td = true;
|
||||
break;
|
||||
case COMP_INCOMPATIBLE_DEVICE_ERROR:
|
||||
case COMP_STALL_ERROR:
|
||||
frame->status = -EPROTO;
|
||||
@@ -2596,6 +2573,9 @@ static void process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
|
||||
xhci_handle_halted_endpoint(xhci, ep, td, EP_SOFT_RESET);
|
||||
return;
|
||||
case COMP_STALL_ERROR:
|
||||
ep->ep_state |= EP_STALLED;
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
@@ -2644,6 +2624,22 @@ static int handle_transferless_tx_event(struct xhci_hcd *xhci, struct xhci_virt_
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool xhci_spurious_success_tx_event(struct xhci_hcd *xhci,
|
||||
struct xhci_ring *ring)
|
||||
{
|
||||
switch (ring->old_trb_comp_code) {
|
||||
case COMP_SHORT_PACKET:
|
||||
return xhci->quirks & XHCI_SPURIOUS_SUCCESS;
|
||||
case COMP_USB_TRANSACTION_ERROR:
|
||||
case COMP_BABBLE_DETECTED_ERROR:
|
||||
case COMP_ISOCH_BUFFER_OVERRUN:
|
||||
return xhci->quirks & XHCI_ETRON_HOST &&
|
||||
ring->type == TYPE_ISOC;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If this function returns an error condition, it means it got a Transfer
|
||||
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
|
||||
@@ -2664,6 +2660,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
int status = -EINPROGRESS;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
u32 trb_comp_code;
|
||||
bool ring_xrun_event = false;
|
||||
|
||||
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
|
||||
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
|
||||
@@ -2697,8 +2694,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
case COMP_SUCCESS:
|
||||
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
|
||||
trb_comp_code = COMP_SHORT_PACKET;
|
||||
xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u with last td short %d\n",
|
||||
slot_id, ep_index, ep_ring->last_td_was_short);
|
||||
xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u with last td comp code %d\n",
|
||||
slot_id, ep_index, ep_ring->old_trb_comp_code);
|
||||
}
|
||||
break;
|
||||
case COMP_SHORT_PACKET:
|
||||
@@ -2770,14 +2767,12 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
* Underrun Event for OUT Isoch endpoint.
|
||||
*/
|
||||
xhci_dbg(xhci, "Underrun event on slot %u ep %u\n", slot_id, ep_index);
|
||||
if (ep->skip)
|
||||
break;
|
||||
return 0;
|
||||
ring_xrun_event = true;
|
||||
break;
|
||||
case COMP_RING_OVERRUN:
|
||||
xhci_dbg(xhci, "Overrun event on slot %u ep %u\n", slot_id, ep_index);
|
||||
if (ep->skip)
|
||||
break;
|
||||
return 0;
|
||||
ring_xrun_event = true;
|
||||
break;
|
||||
case COMP_MISSED_SERVICE_ERROR:
|
||||
/*
|
||||
* When encounter missed service error, one or more isoc tds
|
||||
@@ -2787,9 +2782,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
*/
|
||||
ep->skip = true;
|
||||
xhci_dbg(xhci,
|
||||
"Miss service interval error for slot %u ep %u, set skip flag\n",
|
||||
slot_id, ep_index);
|
||||
return 0;
|
||||
"Miss service interval error for slot %u ep %u, set skip flag%s\n",
|
||||
slot_id, ep_index, ep_trb_dma ? ", skip now" : "");
|
||||
break;
|
||||
case COMP_NO_PING_RESPONSE_ERROR:
|
||||
ep->skip = true;
|
||||
xhci_dbg(xhci,
|
||||
@@ -2832,11 +2827,15 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
*/
|
||||
td = list_first_entry_or_null(&ep_ring->td_list, struct xhci_td, td_list);
|
||||
|
||||
if (td && td->error_mid_td && !trb_in_td(xhci, td, ep_trb_dma, false)) {
|
||||
if (td && td->error_mid_td && !trb_in_td(td, ep_trb_dma)) {
|
||||
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
|
||||
xhci_dequeue_td(xhci, td, ep_ring, td->status);
|
||||
}
|
||||
|
||||
/* If the TRB pointer is NULL, missed TDs will be skipped on the next event */
|
||||
if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !ep_trb_dma)
|
||||
return 0;
|
||||
|
||||
if (list_empty(&ep_ring->td_list)) {
|
||||
/*
|
||||
* Don't print wanings if ring is empty due to a stopped endpoint generating an
|
||||
@@ -2846,7 +2845,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
*/
|
||||
if (trb_comp_code != COMP_STOPPED &&
|
||||
trb_comp_code != COMP_STOPPED_LENGTH_INVALID &&
|
||||
!ep_ring->last_td_was_short) {
|
||||
!ring_xrun_event &&
|
||||
!xhci_spurious_success_tx_event(xhci, ep_ring)) {
|
||||
xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n",
|
||||
slot_id, ep_index);
|
||||
}
|
||||
@@ -2860,14 +2860,31 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
td_list);
|
||||
|
||||
/* Is this a TRB in the currently executing TD? */
|
||||
ep_seg = trb_in_td(xhci, td, ep_trb_dma, false);
|
||||
ep_seg = trb_in_td(td, ep_trb_dma);
|
||||
|
||||
if (!ep_seg) {
|
||||
|
||||
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
|
||||
/* this event is unlikely to match any TD, don't skip them all */
|
||||
if (trb_comp_code == COMP_STOPPED_LENGTH_INVALID)
|
||||
return 0;
|
||||
|
||||
skip_isoc_td(xhci, td, ep, status);
|
||||
if (!list_empty(&ep_ring->td_list))
|
||||
|
||||
if (!list_empty(&ep_ring->td_list)) {
|
||||
if (ring_xrun_event) {
|
||||
/*
|
||||
* If we are here, we are on xHCI 1.0 host with no
|
||||
* idea how many TDs were missed or where the xrun
|
||||
* occurred. New TDs may have been added after the
|
||||
* xrun, so skip only one TD to be safe.
|
||||
*/
|
||||
xhci_dbg(xhci, "Skipped one TD for slot %u ep %u",
|
||||
slot_id, ep_index);
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
xhci_dbg(xhci, "All TDs skipped for slot %u ep %u. Clear skip flag.\n",
|
||||
slot_id, ep_index);
|
||||
@@ -2876,6 +2893,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
goto check_endpoint_halted;
|
||||
}
|
||||
|
||||
/* TD was queued after xrun, maybe xrun was on a link, don't panic yet */
|
||||
if (ring_xrun_event)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Skip the Force Stopped Event. The 'ep_trb' of FSE is not in the current
|
||||
* TD pointed by 'ep_ring->dequeue' because that the hardware dequeue
|
||||
@@ -2890,21 +2911,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
|
||||
/*
|
||||
* Some hosts give a spurious success event after a short
|
||||
* transfer. Ignore it.
|
||||
* transfer or error on last TRB. Ignore it.
|
||||
*/
|
||||
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
|
||||
ep_ring->last_td_was_short) {
|
||||
ep_ring->last_td_was_short = false;
|
||||
if (xhci_spurious_success_tx_event(xhci, ep_ring)) {
|
||||
xhci_dbg(xhci, "Spurious event dma %pad, comp_code %u after %u\n",
|
||||
&ep_trb_dma, trb_comp_code, ep_ring->old_trb_comp_code);
|
||||
ep_ring->old_trb_comp_code = trb_comp_code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* HC is busted, give up! */
|
||||
xhci_err(xhci,
|
||||
"ERROR Transfer event TRB DMA ptr not part of current TD ep_index %d comp_code %u\n",
|
||||
ep_index, trb_comp_code);
|
||||
trb_in_td(xhci, td, ep_trb_dma, true);
|
||||
|
||||
return -ESHUTDOWN;
|
||||
goto debug_finding_td;
|
||||
}
|
||||
|
||||
if (ep->skip) {
|
||||
@@ -2922,10 +2939,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
*/
|
||||
} while (ep->skip);
|
||||
|
||||
if (trb_comp_code == COMP_SHORT_PACKET)
|
||||
ep_ring->last_td_was_short = true;
|
||||
else
|
||||
ep_ring->last_td_was_short = false;
|
||||
ep_ring->old_trb_comp_code = trb_comp_code;
|
||||
|
||||
/* Get out if a TD was queued at enqueue after the xrun occurred */
|
||||
if (ring_xrun_event)
|
||||
return 0;
|
||||
|
||||
ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)];
|
||||
trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma);
|
||||
@@ -2957,6 +2975,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
|
||||
return 0;
|
||||
|
||||
debug_finding_td:
|
||||
xhci_err(xhci, "Event dma %pad for ep %d status %d not part of TD at %016llx - %016llx\n",
|
||||
&ep_trb_dma, ep_index, trb_comp_code,
|
||||
(unsigned long long)xhci_trb_virt_to_dma(td->start_seg, td->start_trb),
|
||||
(unsigned long long)xhci_trb_virt_to_dma(td->end_seg, td->end_trb));
|
||||
|
||||
xhci_for_each_ring_seg(ep_ring->first_seg, ep_seg)
|
||||
xhci_warn(xhci, "Ring seg %u dma %pad\n", ep_seg->num, &ep_seg->dma);
|
||||
|
||||
return -ESHUTDOWN;
|
||||
|
||||
err_out:
|
||||
xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n",
|
||||
(unsigned long long) xhci_trb_virt_to_dma(
|
||||
@@ -3216,7 +3245,6 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
||||
static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
|
||||
u32 ep_state, unsigned int num_trbs, gfp_t mem_flags)
|
||||
{
|
||||
unsigned int link_trb_count = 0;
|
||||
unsigned int new_segs = 0;
|
||||
|
||||
/* Make sure the endpoint has been added to xHC schedule */
|
||||
@@ -3264,33 +3292,9 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
|
||||
}
|
||||
}
|
||||
|
||||
while (trb_is_link(ep_ring->enqueue)) {
|
||||
/* If we're not dealing with 0.95 hardware or isoc rings
|
||||
* on AMD 0.96 host, clear the chain bit.
|
||||
*/
|
||||
if (!xhci_link_chain_quirk(xhci, ep_ring->type))
|
||||
ep_ring->enqueue->link.control &=
|
||||
cpu_to_le32(~TRB_CHAIN);
|
||||
else
|
||||
ep_ring->enqueue->link.control |=
|
||||
cpu_to_le32(TRB_CHAIN);
|
||||
|
||||
wmb();
|
||||
ep_ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (link_trb_toggles_cycle(ep_ring->enqueue))
|
||||
ep_ring->cycle_state ^= 1;
|
||||
|
||||
ep_ring->enq_seg = ep_ring->enq_seg->next;
|
||||
ep_ring->enqueue = ep_ring->enq_seg->trbs;
|
||||
|
||||
/* prevent infinite loop if all first trbs are link trbs */
|
||||
if (link_trb_count++ > ep_ring->num_segs) {
|
||||
xhci_warn(xhci, "Ring is an endless link TRB loop\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
/* Ensure that new TRBs won't overwrite a link */
|
||||
if (trb_is_link(ep_ring->enqueue))
|
||||
inc_enq_past_link(xhci, ep_ring, 0);
|
||||
|
||||
if (last_trb_on_seg(ep_ring->enq_seg, ep_ring->enqueue)) {
|
||||
xhci_warn(xhci, "Missing link TRB at end of ring segment\n");
|
||||
|
||||
@@ -2162,11 +2162,11 @@ static void tegra_xhci_program_utmi_power_lp0_exit(struct tegra_xusb *tegra)
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
|
||||
static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool is_auto_resume)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
|
||||
struct device *dev = tegra->dev;
|
||||
bool wakeup = runtime ? true : device_may_wakeup(dev);
|
||||
bool wakeup = is_auto_resume ? true : device_may_wakeup(dev);
|
||||
unsigned int i;
|
||||
int err;
|
||||
u32 usbcmd;
|
||||
@@ -2232,11 +2232,11 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
|
||||
static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool is_auto_resume)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
|
||||
struct device *dev = tegra->dev;
|
||||
bool wakeup = runtime ? true : device_may_wakeup(dev);
|
||||
bool wakeup = is_auto_resume ? true : device_may_wakeup(dev);
|
||||
unsigned int i;
|
||||
u32 usbcmd;
|
||||
int err;
|
||||
@@ -2287,7 +2287,7 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
|
||||
if (wakeup)
|
||||
tegra_xhci_disable_phy_sleepwalk(tegra);
|
||||
|
||||
err = xhci_resume(xhci, runtime ? PMSG_AUTO_RESUME : PMSG_RESUME);
|
||||
err = xhci_resume(xhci, false, is_auto_resume);
|
||||
if (err < 0) {
|
||||
dev_err(tegra->dev, "failed to resume XHCI: %d\n", err);
|
||||
goto disable_phy;
|
||||
|
||||
@@ -994,16 +994,14 @@ EXPORT_SYMBOL_GPL(xhci_suspend);
|
||||
* This is called when the machine transition from S3/S4 mode.
|
||||
*
|
||||
*/
|
||||
int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
|
||||
int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
|
||||
{
|
||||
bool hibernated = (msg.event == PM_EVENT_RESTORE);
|
||||
u32 command, temp = 0;
|
||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||
int retval = 0;
|
||||
bool comp_timer_running = false;
|
||||
bool pending_portevent = false;
|
||||
bool suspended_usb3_devs = false;
|
||||
bool reinit_xhc = false;
|
||||
|
||||
if (!hcd->state)
|
||||
return 0;
|
||||
@@ -1022,10 +1020,10 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
|
||||
|
||||
spin_lock_irq(&xhci->lock);
|
||||
|
||||
if (hibernated || xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend)
|
||||
reinit_xhc = true;
|
||||
if (xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend)
|
||||
power_lost = true;
|
||||
|
||||
if (!reinit_xhc) {
|
||||
if (!power_lost) {
|
||||
/*
|
||||
* Some controllers might lose power during suspend, so wait
|
||||
* for controller not ready bit to clear, just as in xHC init.
|
||||
@@ -1065,12 +1063,12 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
|
||||
/* re-initialize the HC on Restore Error, or Host Controller Error */
|
||||
if ((temp & (STS_SRE | STS_HCE)) &&
|
||||
!(xhci->xhc_state & XHCI_STATE_REMOVING)) {
|
||||
reinit_xhc = true;
|
||||
if (!xhci->broken_suspend)
|
||||
if (!power_lost)
|
||||
xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp);
|
||||
power_lost = true;
|
||||
}
|
||||
|
||||
if (reinit_xhc) {
|
||||
if (power_lost) {
|
||||
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
|
||||
!(xhci_all_ports_seen_u0(xhci))) {
|
||||
del_timer_sync(&xhci->comp_mode_recovery_timer);
|
||||
@@ -1168,8 +1166,7 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
|
||||
|
||||
pending_portevent = xhci_pending_portevent(xhci);
|
||||
|
||||
if (suspended_usb3_devs && !pending_portevent &&
|
||||
msg.event == PM_EVENT_AUTO_RESUME) {
|
||||
if (suspended_usb3_devs && !pending_portevent && is_auto_resume) {
|
||||
msleep(120);
|
||||
pending_portevent = xhci_pending_portevent(xhci);
|
||||
}
|
||||
@@ -1608,6 +1605,11 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
|
||||
goto free_priv;
|
||||
}
|
||||
|
||||
/* Class driver might not be aware ep halted due to async URB giveback */
|
||||
if (*ep_state & EP_STALLED)
|
||||
dev_dbg(&urb->dev->dev, "URB %p queued before clearing halt\n",
|
||||
urb);
|
||||
|
||||
switch (usb_endpoint_type(&urb->ep->desc)) {
|
||||
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
@@ -1768,8 +1770,8 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* In this case no commands are pending but the endpoint is stopped */
|
||||
if (ep->ep_state & EP_CLEARING_TT) {
|
||||
/* In these cases no commands are pending but the endpoint is stopped */
|
||||
if (ep->ep_state & (EP_CLEARING_TT | EP_STALLED)) {
|
||||
/* and cancelled TDs can be given back right away */
|
||||
xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n",
|
||||
urb->dev->slot_id, ep_index, ep->ep_state);
|
||||
@@ -3207,8 +3209,11 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
|
||||
|
||||
ep = &vdev->eps[ep_index];
|
||||
|
||||
/* Bail out if toggle is already being cleared by a endpoint reset */
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
ep->ep_state &= ~EP_STALLED;
|
||||
|
||||
/* Bail out if toggle is already being cleared by a endpoint reset */
|
||||
if (ep->ep_state & EP_HARD_CLEAR_TOGGLE) {
|
||||
ep->ep_state &= ~EP_HARD_CLEAR_TOGGLE;
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
@@ -4759,8 +4764,8 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
|
||||
*/
|
||||
if (timeout_ns <= USB3_LPM_U1_MAX_TIMEOUT)
|
||||
return timeout_ns;
|
||||
dev_dbg(&udev->dev, "Hub-initiated U1 disabled "
|
||||
"due to long timeout %llu ms\n", timeout_ns);
|
||||
dev_dbg(&udev->dev, "Hub-initiated U1 disabled due to long timeout %lluus\n",
|
||||
timeout_ns);
|
||||
return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1);
|
||||
}
|
||||
|
||||
@@ -4817,8 +4822,8 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
|
||||
*/
|
||||
if (timeout_ns <= USB3_LPM_U2_MAX_TIMEOUT)
|
||||
return timeout_ns;
|
||||
dev_dbg(&udev->dev, "Hub-initiated U2 disabled "
|
||||
"due to long timeout %llu ms\n", timeout_ns);
|
||||
dev_dbg(&udev->dev, "Hub-initiated U2 disabled due to long timeout %lluus\n",
|
||||
timeout_ns * 256);
|
||||
return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U2);
|
||||
}
|
||||
|
||||
|
||||
@@ -211,6 +211,9 @@ struct xhci_op_regs {
|
||||
#define CONFIG_CIE (1 << 9)
|
||||
/* bits 10:31 - reserved and should be preserved */
|
||||
|
||||
/* bits 15:0 - HCD page shift bit */
|
||||
#define XHCI_PAGE_SIZE_MASK 0xffff
|
||||
|
||||
/**
|
||||
* struct xhci_intr_reg - Interrupt Register Set
|
||||
* @irq_pending: IMAN - Interrupt Management Register. Used to enable
|
||||
@@ -661,7 +664,7 @@ struct xhci_virt_ep {
|
||||
unsigned int err_count;
|
||||
unsigned int ep_state;
|
||||
#define SET_DEQ_PENDING (1 << 0)
|
||||
#define EP_HALTED (1 << 1) /* For stall handling */
|
||||
#define EP_HALTED (1 << 1) /* Halted host ep handling */
|
||||
#define EP_STOP_CMD_PENDING (1 << 2) /* For URB cancellation */
|
||||
/* Transitioning the endpoint to using streams, don't enqueue URBs */
|
||||
#define EP_GETTING_STREAMS (1 << 3)
|
||||
@@ -672,6 +675,7 @@ struct xhci_virt_ep {
|
||||
#define EP_SOFT_CLEAR_TOGGLE (1 << 7)
|
||||
/* usb_hub_clear_tt_buffer is in progress */
|
||||
#define EP_CLEARING_TT (1 << 8)
|
||||
#define EP_STALLED (1 << 9) /* For stall handling */
|
||||
/* ---- Related to URB cancellation ---- */
|
||||
struct list_head cancelled_td_list;
|
||||
struct xhci_hcd *xhci;
|
||||
@@ -1371,7 +1375,7 @@ struct xhci_ring {
|
||||
unsigned int num_trbs_free; /* used only by xhci DbC */
|
||||
unsigned int bounce_buf_len;
|
||||
enum xhci_ring_type type;
|
||||
bool last_td_was_short;
|
||||
u32 old_trb_comp_code;
|
||||
struct radix_tree_root *trb_address_map;
|
||||
};
|
||||
|
||||
@@ -1514,10 +1518,7 @@ struct xhci_hcd {
|
||||
u16 max_interrupters;
|
||||
/* imod_interval in ns (I * 250ns) */
|
||||
u32 imod_interval;
|
||||
/* 4KB min, 128MB max */
|
||||
int page_size;
|
||||
/* Valid values are 12 to 20, inclusive */
|
||||
int page_shift;
|
||||
u32 page_size;
|
||||
/* MSI-X/MSI vectors */
|
||||
int nvecs;
|
||||
/* optional clocks */
|
||||
@@ -1759,11 +1760,20 @@ static inline void xhci_write_64(struct xhci_hcd *xhci,
|
||||
}
|
||||
|
||||
|
||||
/* Link TRB chain should always be set on 0.95 hosts, and AMD 0.96 ISOC rings */
|
||||
/*
|
||||
* Reportedly, some chapters of v0.95 spec said that Link TRB always has its chain bit set.
|
||||
* Other chapters and later specs say that it should only be set if the link is inside a TD
|
||||
* which continues from the end of one segment to the next segment.
|
||||
*
|
||||
* Some 0.95 hardware was found to misbehave if any link TRB doesn't have the chain bit set.
|
||||
*
|
||||
* 0.96 hardware from AMD and NEC was found to ignore unchained isochronous link TRBs when
|
||||
* "resynchronizing the pipe" after a Missed Service Error.
|
||||
*/
|
||||
static inline bool xhci_link_chain_quirk(struct xhci_hcd *xhci, enum xhci_ring_type type)
|
||||
{
|
||||
return (xhci->quirks & XHCI_LINK_TRB_QUIRK) ||
|
||||
(type == TYPE_ISOC && (xhci->quirks & XHCI_AMD_0x96_HOST));
|
||||
(type == TYPE_ISOC && (xhci->quirks & (XHCI_AMD_0x96_HOST | XHCI_NEC_HOST)));
|
||||
}
|
||||
|
||||
/* xHCI debugging */
|
||||
@@ -1870,7 +1880,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
|
||||
int xhci_ext_cap_init(struct xhci_hcd *xhci);
|
||||
|
||||
int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
|
||||
int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg);
|
||||
int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume);
|
||||
|
||||
irqreturn_t xhci_irq(struct usb_hcd *hcd);
|
||||
irqreturn_t xhci_msi_irq(int irq, void *hcd);
|
||||
@@ -1884,8 +1894,6 @@ int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
|
||||
|
||||
/* xHCI ring, segment, TRB, and TD functions */
|
||||
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
|
||||
struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||
dma_addr_t suspect_dma, bool debug);
|
||||
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
|
||||
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
|
||||
int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
|
||||
@@ -23,6 +23,13 @@ static const struct onboard_dev_pdata microchip_usb424_data = {
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_dev_pdata microchip_usb2514_data = {
|
||||
.reset_us = 1,
|
||||
.num_supplies = 2,
|
||||
.supply_names = { "vdd", "vdda" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_dev_pdata microchip_usb5744_data = {
|
||||
.reset_us = 0,
|
||||
.power_on_delay_us = 10000,
|
||||
@@ -96,7 +103,7 @@ static const struct onboard_dev_pdata xmos_xvf3500_data = {
|
||||
|
||||
static const struct of_device_id onboard_dev_match[] = {
|
||||
{ .compatible = "usb424,2412", .data = µchip_usb424_data, },
|
||||
{ .compatible = "usb424,2514", .data = µchip_usb424_data, },
|
||||
{ .compatible = "usb424,2514", .data = µchip_usb2514_data, },
|
||||
{ .compatible = "usb424,2517", .data = µchip_usb424_data, },
|
||||
{ .compatible = "usb424,2744", .data = µchip_usb5744_data, },
|
||||
{ .compatible = "usb424,5744", .data = µchip_usb5744_data, },
|
||||
|
||||
@@ -636,10 +636,8 @@ static int usb251xb_probe(struct usb251xb *hub)
|
||||
|
||||
if (np && usb_data) {
|
||||
err = usb251xb_get_ofdata(hub, usb_data);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to get ofdata: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "failed to get ofdata\n");
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -59,7 +59,7 @@ static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
|
||||
static const struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
|
||||
@@ -205,7 +205,7 @@ static const struct musb_hdrc_platform_data jz4740_musb_pdata = {
|
||||
.platform_ops = &jz4740_musb_ops,
|
||||
};
|
||||
|
||||
static struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = {
|
||||
static const struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
|
||||
|
||||
@@ -365,7 +365,7 @@ static const struct musb_platform_ops mtk_musb_ops = {
|
||||
#define MTK_MUSB_MAX_EP_NUM 8
|
||||
#define MTK_MUSB_RAM_BITS 11
|
||||
|
||||
static struct musb_fifo_cfg mtk_musb_mode_cfg[] = {
|
||||
static const struct musb_fifo_cfg mtk_musb_mode_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
|
||||
|
||||
@@ -29,7 +29,7 @@ struct mpfs_glue {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static struct musb_fifo_cfg mpfs_musb_mode_cfg[] = {
|
||||
static const struct musb_fifo_cfg mpfs_musb_mode_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
|
||||
|
||||
@@ -1271,7 +1271,7 @@ MODULE_PARM_DESC(fifo_mode, "initial endpoint configuration");
|
||||
*/
|
||||
|
||||
/* mode 0 - fits in 2KB */
|
||||
static struct musb_fifo_cfg mode_0_cfg[] = {
|
||||
static const struct musb_fifo_cfg mode_0_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, },
|
||||
@@ -1280,7 +1280,7 @@ static struct musb_fifo_cfg mode_0_cfg[] = {
|
||||
};
|
||||
|
||||
/* mode 1 - fits in 4KB */
|
||||
static struct musb_fifo_cfg mode_1_cfg[] = {
|
||||
static const struct musb_fifo_cfg mode_1_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, .mode = BUF_DOUBLE, },
|
||||
@@ -1289,7 +1289,7 @@ static struct musb_fifo_cfg mode_1_cfg[] = {
|
||||
};
|
||||
|
||||
/* mode 2 - fits in 4KB */
|
||||
static struct musb_fifo_cfg mode_2_cfg[] = {
|
||||
static const struct musb_fifo_cfg mode_2_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
|
||||
@@ -1299,7 +1299,7 @@ static struct musb_fifo_cfg mode_2_cfg[] = {
|
||||
};
|
||||
|
||||
/* mode 3 - fits in 4KB */
|
||||
static struct musb_fifo_cfg mode_3_cfg[] = {
|
||||
static const struct musb_fifo_cfg mode_3_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
|
||||
@@ -1309,7 +1309,7 @@ static struct musb_fifo_cfg mode_3_cfg[] = {
|
||||
};
|
||||
|
||||
/* mode 4 - fits in 16KB */
|
||||
static struct musb_fifo_cfg mode_4_cfg[] = {
|
||||
static const struct musb_fifo_cfg mode_4_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
|
||||
@@ -1340,7 +1340,7 @@ static struct musb_fifo_cfg mode_4_cfg[] = {
|
||||
};
|
||||
|
||||
/* mode 5 - fits in 8KB */
|
||||
static struct musb_fifo_cfg mode_5_cfg[] = {
|
||||
static const struct musb_fifo_cfg mode_5_cfg[] = {
|
||||
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
|
||||
@@ -1447,7 +1447,7 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep,
|
||||
return offset + (maxpacket << ((c_size & MUSB_FIFOSZ_DPB) ? 1 : 0));
|
||||
}
|
||||
|
||||
static struct musb_fifo_cfg ep0_cfg = {
|
||||
static const struct musb_fifo_cfg ep0_cfg = {
|
||||
.style = FIFO_RXTX, .maxpacket = 64,
|
||||
};
|
||||
|
||||
|
||||
@@ -629,7 +629,7 @@ static const struct musb_platform_ops sunxi_musb_ops = {
|
||||
#define SUNXI_MUSB_RAM_BITS 11
|
||||
|
||||
/* Allwinner OTG supports up to 5 endpoints */
|
||||
static struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = {
|
||||
static const struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = {
|
||||
MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512),
|
||||
MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512),
|
||||
MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512),
|
||||
@@ -643,7 +643,7 @@ static struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = {
|
||||
};
|
||||
|
||||
/* H3/V3s OTG supports only 4 endpoints */
|
||||
static struct musb_fifo_cfg sunxi_musb_mode_cfg_4eps[] = {
|
||||
static const struct musb_fifo_cfg sunxi_musb_mode_cfg_4eps[] = {
|
||||
MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512),
|
||||
MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512),
|
||||
MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512),
|
||||
|
||||
@@ -769,11 +769,9 @@ static int mxs_phy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(base);
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"can't get the clock, err=%ld", PTR_ERR(clk));
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(clk),
|
||||
"can't get the clock\n");
|
||||
|
||||
mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL);
|
||||
if (!mxs_phy)
|
||||
|
||||
@@ -255,29 +255,6 @@ static void otg_ulpi_init(struct usb_phy *phy, struct usb_otg *otg,
|
||||
otg->set_vbus = ulpi_set_vbus;
|
||||
}
|
||||
|
||||
struct usb_phy *
|
||||
otg_ulpi_create(struct usb_phy_io_ops *ops,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct usb_phy *phy;
|
||||
struct usb_otg *otg;
|
||||
|
||||
phy = kzalloc(sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return NULL;
|
||||
|
||||
otg = kzalloc(sizeof(*otg), GFP_KERNEL);
|
||||
if (!otg) {
|
||||
kfree(phy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
otg_ulpi_init(phy, otg, ops, flags);
|
||||
|
||||
return phy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(otg_ulpi_create);
|
||||
|
||||
struct usb_phy *
|
||||
devm_otg_ulpi_create(struct device *dev,
|
||||
struct usb_phy_io_ops *ops,
|
||||
|
||||
@@ -66,29 +66,16 @@
|
||||
|
||||
#define MOS_WDR_TIMEOUT 5000 /* default urb timeout */
|
||||
|
||||
#define MOS_PORT1 0x0200
|
||||
#define MOS_PORT2 0x0300
|
||||
#define MOS_VENREG 0x0000
|
||||
#define MOS_MAX_PORT 0x02
|
||||
#define MOS_WRITE 0x0E
|
||||
#define MOS_READ 0x0D
|
||||
|
||||
/* Requests */
|
||||
#define MCS_RD_RTYPE 0xC0
|
||||
#define MCS_WR_RTYPE 0x40
|
||||
#define MCS_RDREQ 0x0D
|
||||
#define MCS_WRREQ 0x0E
|
||||
#define MCS_CTRL_TIMEOUT 500
|
||||
#define VENDOR_READ_LENGTH (0x01)
|
||||
|
||||
#define MAX_NAME_LEN 64
|
||||
|
||||
#define ZLP_REG1 0x3A /* Zero_Flag_Reg1 58 */
|
||||
#define ZLP_REG5 0x3E /* Zero_Flag_Reg5 62 */
|
||||
|
||||
/* For higher baud Rates use TIOCEXBAUD */
|
||||
#define TIOCEXBAUD 0x5462
|
||||
|
||||
/*
|
||||
* Vendor id and device id defines
|
||||
*
|
||||
|
||||
@@ -174,7 +174,7 @@ struct alauda_card_info {
|
||||
unsigned char zoneshift; /* 1<<zs blocks per zone */
|
||||
};
|
||||
|
||||
static struct alauda_card_info alauda_card_ids[] = {
|
||||
static const struct alauda_card_info alauda_card_ids[] = {
|
||||
/* NAND flash */
|
||||
{ 0x6e, 20, 8, 4, 8}, /* 1 MB */
|
||||
{ 0xe8, 20, 8, 4, 8}, /* 1 MB */
|
||||
@@ -200,7 +200,7 @@ static struct alauda_card_info alauda_card_ids[] = {
|
||||
{ 0,}
|
||||
};
|
||||
|
||||
static struct alauda_card_info *alauda_card_find_id(unsigned char id)
|
||||
static const struct alauda_card_info *alauda_card_find_id(unsigned char id)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -383,7 +383,7 @@ static int alauda_init_media(struct us_data *us)
|
||||
{
|
||||
unsigned char *data = us->iobuf;
|
||||
int ready = 0;
|
||||
struct alauda_card_info *media_info;
|
||||
const struct alauda_card_info *media_info;
|
||||
unsigned int num_zones;
|
||||
|
||||
while (ready == 0) {
|
||||
@@ -1132,7 +1132,7 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
int rc;
|
||||
struct alauda_info *info = (struct alauda_info *) us->extra;
|
||||
unsigned char *ptr = us->iobuf;
|
||||
static unsigned char inquiry_response[36] = {
|
||||
static const unsigned char inquiry_response[36] = {
|
||||
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@ static int datafab_determine_lun(struct us_data *us,
|
||||
//
|
||||
// There might be a better way of doing this?
|
||||
|
||||
static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
|
||||
static const unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *buf;
|
||||
int count = 0, rc;
|
||||
@@ -384,7 +384,7 @@ static int datafab_id_device(struct us_data *us,
|
||||
// to the ATA spec, 'Sector Count' isn't used but the Windows driver
|
||||
// sets this bit so we do too...
|
||||
//
|
||||
static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
|
||||
static const unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *reply;
|
||||
int rc;
|
||||
@@ -437,16 +437,16 @@ static int datafab_handle_mode_sense(struct us_data *us,
|
||||
struct scsi_cmnd * srb,
|
||||
int sense_6)
|
||||
{
|
||||
static unsigned char rw_err_page[12] = {
|
||||
static const unsigned char rw_err_page[12] = {
|
||||
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
|
||||
};
|
||||
static unsigned char cache_page[12] = {
|
||||
static const unsigned char cache_page[12] = {
|
||||
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static unsigned char rbac_page[12] = {
|
||||
static const unsigned char rbac_page[12] = {
|
||||
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static unsigned char timer_page[8] = {
|
||||
static const unsigned char timer_page[8] = {
|
||||
0x1C, 0x6, 0, 0, 0, 0
|
||||
};
|
||||
unsigned char pc, page_code;
|
||||
@@ -550,7 +550,7 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
int rc;
|
||||
unsigned long block, blocks;
|
||||
unsigned char *ptr = us->iobuf;
|
||||
static unsigned char inquiry_reply[8] = {
|
||||
static const unsigned char inquiry_reply[8] = {
|
||||
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ int usb_stor_ucr61s2b_init(struct us_data *us)
|
||||
struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap*) us->iobuf;
|
||||
int res;
|
||||
unsigned int partial;
|
||||
static char init_string[] = "\xec\x0a\x06\x00$PCCHIPS";
|
||||
static const char init_string[] = "\xec\x0a\x06\x00$PCCHIPS";
|
||||
|
||||
usb_stor_dbg(us, "Sending UCR-61S2B initialization packet...\n");
|
||||
|
||||
|
||||
@@ -367,16 +367,16 @@ static int jumpshot_handle_mode_sense(struct us_data *us,
|
||||
struct scsi_cmnd * srb,
|
||||
int sense_6)
|
||||
{
|
||||
static unsigned char rw_err_page[12] = {
|
||||
static const unsigned char rw_err_page[12] = {
|
||||
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
|
||||
};
|
||||
static unsigned char cache_page[12] = {
|
||||
static const unsigned char cache_page[12] = {
|
||||
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static unsigned char rbac_page[12] = {
|
||||
static const unsigned char rbac_page[12] = {
|
||||
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static unsigned char timer_page[8] = {
|
||||
static const unsigned char timer_page[8] = {
|
||||
0x1C, 0x6, 0, 0, 0, 0
|
||||
};
|
||||
unsigned char pc, page_code;
|
||||
@@ -477,7 +477,7 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
int rc;
|
||||
unsigned long block, blocks;
|
||||
unsigned char *ptr = us->iobuf;
|
||||
static unsigned char inquiry_response[8] = {
|
||||
static const unsigned char inquiry_response[8] = {
|
||||
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ MODULE_DEVICE_TABLE(usb, realtek_cr_ids);
|
||||
.initFunction = init_function, \
|
||||
}
|
||||
|
||||
static struct us_unusual_dev realtek_cr_unusual_dev_list[] = {
|
||||
static const struct us_unusual_dev realtek_cr_unusual_dev_list[] = {
|
||||
# include "unusual_realtek.h"
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
@@ -797,10 +797,10 @@ static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
{
|
||||
struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
|
||||
static int card_first_show = 1;
|
||||
static u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0,
|
||||
static const u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0x3A, 0, 0, 0, 0, 0
|
||||
};
|
||||
static u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0,
|
||||
static const u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0x24, 0, 0, 0, 0, 0
|
||||
};
|
||||
int ret;
|
||||
|
||||
@@ -144,7 +144,7 @@ static inline char *nand_flash_manufacturer(int manuf_id) {
|
||||
* 256 MB NAND flash has a 5-byte ID with 2nd byte 0xaa, 0xba, 0xca or 0xda.
|
||||
*/
|
||||
|
||||
static struct nand_flash_dev nand_flash_ids[] = {
|
||||
static const struct nand_flash_dev nand_flash_ids[] = {
|
||||
/* NAND flash */
|
||||
{ 0x6e, 20, 8, 4, 8, 2}, /* 1 MB */
|
||||
{ 0xe8, 20, 8, 4, 8, 2}, /* 1 MB */
|
||||
@@ -169,7 +169,7 @@ static struct nand_flash_dev nand_flash_ids[] = {
|
||||
{ 0,}
|
||||
};
|
||||
|
||||
static struct nand_flash_dev *
|
||||
static const struct nand_flash_dev *
|
||||
nand_find_id(unsigned char id) {
|
||||
int i;
|
||||
|
||||
@@ -1133,9 +1133,9 @@ sddr09_reset(struct us_data *us) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct nand_flash_dev *
|
||||
static const struct nand_flash_dev *
|
||||
sddr09_get_cardinfo(struct us_data *us, unsigned char flags) {
|
||||
struct nand_flash_dev *cardinfo;
|
||||
const struct nand_flash_dev *cardinfo;
|
||||
unsigned char deviceID[4];
|
||||
char blurbtxt[256];
|
||||
int result;
|
||||
@@ -1545,12 +1545,12 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
|
||||
struct sddr09_card_info *info;
|
||||
|
||||
static unsigned char inquiry_response[8] = {
|
||||
static const unsigned char inquiry_response[8] = {
|
||||
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* note: no block descriptor support */
|
||||
static unsigned char mode_page_01[19] = {
|
||||
static const unsigned char mode_page_01[19] = {
|
||||
0x00, 0x0F, 0x00, 0x0, 0x0, 0x0, 0x00,
|
||||
0x01, 0x0A,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
@@ -1584,7 +1584,7 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == READ_CAPACITY) {
|
||||
struct nand_flash_dev *cardinfo;
|
||||
const struct nand_flash_dev *cardinfo;
|
||||
|
||||
sddr09_get_wp(us, info); /* read WP bit */
|
||||
|
||||
|
||||
@@ -775,11 +775,11 @@ static void sddr55_card_info_destructor(void *extra) {
|
||||
static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
{
|
||||
int result;
|
||||
static unsigned char inquiry_response[8] = {
|
||||
static const unsigned char inquiry_response[8] = {
|
||||
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
|
||||
};
|
||||
// write-protected for now, no block descriptor support
|
||||
static unsigned char mode_page_01[20] = {
|
||||
static const unsigned char mode_page_01[20] = {
|
||||
0x0, 0x12, 0x00, 0x80, 0x0, 0x0, 0x0, 0x0,
|
||||
0x01, 0x0A,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
|
||||
@@ -1683,7 +1683,7 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us)
|
||||
struct usbat_info *info = (struct usbat_info *) (us->extra);
|
||||
unsigned long block, blocks;
|
||||
unsigned char *ptr = us->iobuf;
|
||||
static unsigned char inquiry_response[36] = {
|
||||
static const unsigned char inquiry_response[36] = {
|
||||
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
|
||||
@@ -528,7 +528,7 @@ static void last_sector_hacks(struct us_data *us, struct scsi_cmnd *srb)
|
||||
u32 sector;
|
||||
|
||||
/* To Report "Medium Error: Record Not Found */
|
||||
static unsigned char record_not_found[18] = {
|
||||
static const unsigned char record_not_found[18] = {
|
||||
[0] = 0x70, /* current error */
|
||||
[2] = MEDIUM_ERROR, /* = 0x03 */
|
||||
[7] = 0x0a, /* additional length */
|
||||
|
||||
@@ -112,7 +112,7 @@ static void tbt_altmode_work(struct work_struct *work)
|
||||
return;
|
||||
|
||||
disable_plugs:
|
||||
for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
|
||||
for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) {
|
||||
if (tbt->plug[i])
|
||||
typec_altmode_put_plug(tbt->plug[i]);
|
||||
|
||||
@@ -143,7 +143,7 @@ static int tbt_enter_modes_ordered(struct typec_altmode *alt)
|
||||
if (tbt->plug[TYPEC_PLUG_SOP_P]) {
|
||||
ret = typec_cable_altmode_enter(alt, TYPEC_PLUG_SOP_P, NULL);
|
||||
if (ret < 0) {
|
||||
for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
|
||||
for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) {
|
||||
if (tbt->plug[i])
|
||||
typec_altmode_put_plug(tbt->plug[i]);
|
||||
|
||||
@@ -324,7 +324,7 @@ static void tbt_altmode_remove(struct typec_altmode *alt)
|
||||
{
|
||||
struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
|
||||
|
||||
for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
|
||||
for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) {
|
||||
if (tbt->plug[i])
|
||||
typec_altmode_put_plug(tbt->plug[i]);
|
||||
}
|
||||
@@ -351,10 +351,10 @@ static bool tbt_ready(struct typec_altmode *alt)
|
||||
*/
|
||||
for (int i = 0; i < TYPEC_PLUG_SOP_PP + 1; i++) {
|
||||
plug = typec_altmode_get_plug(tbt->alt, i);
|
||||
if (IS_ERR(plug))
|
||||
if (!plug)
|
||||
continue;
|
||||
|
||||
if (!plug || plug->svid != USB_TYPEC_TBT_SID)
|
||||
if (plug->svid != USB_TYPEC_TBT_SID)
|
||||
break;
|
||||
|
||||
plug->desc = "Thunderbolt3";
|
||||
|
||||
@@ -56,6 +56,16 @@ config TYPEC_MUX_NB7VPQ904M
|
||||
Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C
|
||||
redriver chip found on some devices with a Type-C port.
|
||||
|
||||
config TYPEC_MUX_PS883X
|
||||
tristate "Parade PS883x Type-C retimer driver"
|
||||
depends on I2C
|
||||
depends on DRM || DRM=n
|
||||
select DRM_AUX_BRIDGE if DRM_BRIDGE && OF
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y or M if your system has a Parade PS883x Type-C retimer chip
|
||||
found on some devices with a Type-C port.
|
||||
|
||||
config TYPEC_MUX_PTN36502
|
||||
tristate "NXP PTN36502 Type-C redriver driver"
|
||||
depends on I2C
|
||||
|
||||
@@ -6,6 +6,7 @@ obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o
|
||||
obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o
|
||||
obj-$(CONFIG_TYPEC_MUX_IT5205) += it5205.o
|
||||
obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M) += nb7vpq904m.o
|
||||
obj-$(CONFIG_TYPEC_MUX_PS883X) += ps883x.o
|
||||
obj-$(CONFIG_TYPEC_MUX_PTN36502) += ptn36502.o
|
||||
obj-$(CONFIG_TYPEC_MUX_TUSB1046) += tusb1046.o
|
||||
obj-$(CONFIG_TYPEC_MUX_WCD939X_USBSS) += wcd939x-usbss.o
|
||||
|
||||
466
drivers/usb/typec/mux/ps883x.c
Normal file
466
drivers/usb/typec/mux/ps883x.c
Normal file
@@ -0,0 +1,466 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Parade ps883x usb retimer driver
|
||||
*
|
||||
* Copyright (C) 2024 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <drm/bridge/aux-bridge.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/usb/typec_altmode.h>
|
||||
#include <linux/usb/typec_dp.h>
|
||||
#include <linux/usb/typec_mux.h>
|
||||
#include <linux/usb/typec_retimer.h>
|
||||
|
||||
#define REG_USB_PORT_CONN_STATUS_0 0x00
|
||||
|
||||
#define CONN_STATUS_0_CONNECTION_PRESENT BIT(0)
|
||||
#define CONN_STATUS_0_ORIENTATION_REVERSED BIT(1)
|
||||
#define CONN_STATUS_0_USB_3_1_CONNECTED BIT(5)
|
||||
|
||||
#define REG_USB_PORT_CONN_STATUS_1 0x01
|
||||
|
||||
#define CONN_STATUS_1_DP_CONNECTED BIT(0)
|
||||
#define CONN_STATUS_1_DP_SINK_REQUESTED BIT(1)
|
||||
#define CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D BIT(2)
|
||||
#define CONN_STATUS_1_DP_HPD_LEVEL BIT(7)
|
||||
|
||||
#define REG_USB_PORT_CONN_STATUS_2 0x02
|
||||
|
||||
struct ps883x_retimer {
|
||||
struct i2c_client *client;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct regmap *regmap;
|
||||
struct typec_switch_dev *sw;
|
||||
struct typec_retimer *retimer;
|
||||
struct clk *xo_clk;
|
||||
struct regulator *vdd_supply;
|
||||
struct regulator *vdd33_supply;
|
||||
struct regulator *vdd33_cap_supply;
|
||||
struct regulator *vddat_supply;
|
||||
struct regulator *vddar_supply;
|
||||
struct regulator *vddio_supply;
|
||||
|
||||
struct typec_switch *typec_switch;
|
||||
struct typec_mux *typec_mux;
|
||||
|
||||
struct mutex lock; /* protect non-concurrent retimer & switch */
|
||||
|
||||
enum typec_orientation orientation;
|
||||
unsigned long mode;
|
||||
unsigned int svid;
|
||||
};
|
||||
|
||||
static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0,
|
||||
int cfg1, int cfg2)
|
||||
{
|
||||
struct device *dev = &retimer->client->dev;
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to write conn_status_0: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_1, cfg1);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to write conn_status_1: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_2, cfg2);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to write conn_status_2: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ps883x_set(struct ps883x_retimer *retimer)
|
||||
{
|
||||
int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT;
|
||||
int cfg1 = 0x00;
|
||||
int cfg2 = 0x00;
|
||||
|
||||
if (retimer->orientation == TYPEC_ORIENTATION_NONE ||
|
||||
retimer->mode == TYPEC_STATE_SAFE) {
|
||||
return ps883x_configure(retimer, cfg0, cfg1, cfg2);
|
||||
}
|
||||
|
||||
if (retimer->mode != TYPEC_STATE_USB && retimer->svid != USB_TYPEC_DP_SID)
|
||||
return -EINVAL;
|
||||
|
||||
if (retimer->orientation == TYPEC_ORIENTATION_REVERSE)
|
||||
cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED;
|
||||
|
||||
switch (retimer->mode) {
|
||||
case TYPEC_STATE_USB:
|
||||
cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED;
|
||||
break;
|
||||
|
||||
case TYPEC_DP_STATE_C:
|
||||
cfg1 = CONN_STATUS_1_DP_CONNECTED |
|
||||
CONN_STATUS_1_DP_SINK_REQUESTED |
|
||||
CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D |
|
||||
CONN_STATUS_1_DP_HPD_LEVEL;
|
||||
break;
|
||||
|
||||
case TYPEC_DP_STATE_D:
|
||||
cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED;
|
||||
cfg1 = CONN_STATUS_1_DP_CONNECTED |
|
||||
CONN_STATUS_1_DP_SINK_REQUESTED |
|
||||
CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D |
|
||||
CONN_STATUS_1_DP_HPD_LEVEL;
|
||||
break;
|
||||
|
||||
case TYPEC_DP_STATE_E:
|
||||
cfg1 = CONN_STATUS_1_DP_CONNECTED |
|
||||
CONN_STATUS_1_DP_HPD_LEVEL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return ps883x_configure(retimer, cfg0, cfg1, cfg2);
|
||||
}
|
||||
|
||||
static int ps883x_sw_set(struct typec_switch_dev *sw,
|
||||
enum typec_orientation orientation)
|
||||
{
|
||||
struct ps883x_retimer *retimer = typec_switch_get_drvdata(sw);
|
||||
int ret = 0;
|
||||
|
||||
ret = typec_switch_set(retimer->typec_switch, orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&retimer->lock);
|
||||
|
||||
if (retimer->orientation != orientation) {
|
||||
retimer->orientation = orientation;
|
||||
|
||||
ret = ps883x_set(retimer);
|
||||
}
|
||||
|
||||
mutex_unlock(&retimer->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ps883x_retimer_set(struct typec_retimer *rtmr,
|
||||
struct typec_retimer_state *state)
|
||||
{
|
||||
struct ps883x_retimer *retimer = typec_retimer_get_drvdata(rtmr);
|
||||
struct typec_mux_state mux_state;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&retimer->lock);
|
||||
|
||||
if (state->mode != retimer->mode) {
|
||||
retimer->mode = state->mode;
|
||||
|
||||
if (state->alt)
|
||||
retimer->svid = state->alt->svid;
|
||||
else
|
||||
retimer->svid = 0;
|
||||
|
||||
ret = ps883x_set(retimer);
|
||||
}
|
||||
|
||||
mutex_unlock(&retimer->lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mux_state.alt = state->alt;
|
||||
mux_state.data = state->data;
|
||||
mux_state.mode = state->mode;
|
||||
|
||||
return typec_mux_set(retimer->typec_mux, &mux_state);
|
||||
}
|
||||
|
||||
static int ps883x_enable_vregs(struct ps883x_retimer *retimer)
|
||||
{
|
||||
struct device *dev = &retimer->client->dev;
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(retimer->vdd33_supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot enable VDD 3.3V regulator: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(retimer->vdd33_cap_supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot enable VDD 3.3V CAP regulator: %d\n", ret);
|
||||
goto err_vdd33_disable;
|
||||
}
|
||||
|
||||
usleep_range(4000, 10000);
|
||||
|
||||
ret = regulator_enable(retimer->vdd_supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot enable VDD regulator: %d\n", ret);
|
||||
goto err_vdd33_cap_disable;
|
||||
}
|
||||
|
||||
ret = regulator_enable(retimer->vddar_supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot enable VDD AR regulator: %d\n", ret);
|
||||
goto err_vdd_disable;
|
||||
}
|
||||
|
||||
ret = regulator_enable(retimer->vddat_supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot enable VDD AT regulator: %d\n", ret);
|
||||
goto err_vddar_disable;
|
||||
}
|
||||
|
||||
ret = regulator_enable(retimer->vddio_supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot enable VDD IO regulator: %d\n", ret);
|
||||
goto err_vddat_disable;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_vddat_disable:
|
||||
regulator_disable(retimer->vddat_supply);
|
||||
err_vddar_disable:
|
||||
regulator_disable(retimer->vddar_supply);
|
||||
err_vdd_disable:
|
||||
regulator_disable(retimer->vdd_supply);
|
||||
err_vdd33_cap_disable:
|
||||
regulator_disable(retimer->vdd33_cap_supply);
|
||||
err_vdd33_disable:
|
||||
regulator_disable(retimer->vdd33_supply);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ps883x_disable_vregs(struct ps883x_retimer *retimer)
|
||||
{
|
||||
regulator_disable(retimer->vddio_supply);
|
||||
regulator_disable(retimer->vddat_supply);
|
||||
regulator_disable(retimer->vddar_supply);
|
||||
regulator_disable(retimer->vdd_supply);
|
||||
regulator_disable(retimer->vdd33_cap_supply);
|
||||
regulator_disable(retimer->vdd33_supply);
|
||||
}
|
||||
|
||||
static int ps883x_get_vregs(struct ps883x_retimer *retimer)
|
||||
{
|
||||
struct device *dev = &retimer->client->dev;
|
||||
|
||||
retimer->vdd_supply = devm_regulator_get(dev, "vdd");
|
||||
if (IS_ERR(retimer->vdd_supply))
|
||||
return dev_err_probe(dev, PTR_ERR(retimer->vdd_supply),
|
||||
"failed to get VDD\n");
|
||||
|
||||
retimer->vdd33_supply = devm_regulator_get(dev, "vdd33");
|
||||
if (IS_ERR(retimer->vdd33_supply))
|
||||
return dev_err_probe(dev, PTR_ERR(retimer->vdd33_supply),
|
||||
"failed to get VDD 3.3V\n");
|
||||
|
||||
retimer->vdd33_cap_supply = devm_regulator_get(dev, "vdd33-cap");
|
||||
if (IS_ERR(retimer->vdd33_cap_supply))
|
||||
return dev_err_probe(dev, PTR_ERR(retimer->vdd33_cap_supply),
|
||||
"failed to get VDD CAP 3.3V\n");
|
||||
|
||||
retimer->vddat_supply = devm_regulator_get(dev, "vddat");
|
||||
if (IS_ERR(retimer->vddat_supply))
|
||||
return dev_err_probe(dev, PTR_ERR(retimer->vddat_supply),
|
||||
"failed to get VDD AT\n");
|
||||
|
||||
retimer->vddar_supply = devm_regulator_get(dev, "vddar");
|
||||
if (IS_ERR(retimer->vddar_supply))
|
||||
return dev_err_probe(dev, PTR_ERR(retimer->vddar_supply),
|
||||
"failed to get VDD AR\n");
|
||||
|
||||
retimer->vddio_supply = devm_regulator_get(dev, "vddio");
|
||||
if (IS_ERR(retimer->vddio_supply))
|
||||
return dev_err_probe(dev, PTR_ERR(retimer->vddio_supply),
|
||||
"failed to get VDD IO\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config ps883x_retimer_regmap = {
|
||||
.max_register = 0x1f,
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int ps883x_retimer_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct typec_switch_desc sw_desc = { };
|
||||
struct typec_retimer_desc rtmr_desc = { };
|
||||
struct ps883x_retimer *retimer;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
retimer = devm_kzalloc(dev, sizeof(*retimer), GFP_KERNEL);
|
||||
if (!retimer)
|
||||
return -ENOMEM;
|
||||
|
||||
retimer->client = client;
|
||||
|
||||
mutex_init(&retimer->lock);
|
||||
|
||||
retimer->regmap = devm_regmap_init_i2c(client, &ps883x_retimer_regmap);
|
||||
if (IS_ERR(retimer->regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(retimer->regmap),
|
||||
"failed to allocate register map\n");
|
||||
|
||||
ret = ps883x_get_vregs(retimer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
retimer->xo_clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(retimer->xo_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(retimer->xo_clk),
|
||||
"failed to get xo clock\n");
|
||||
|
||||
retimer->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
|
||||
if (IS_ERR(retimer->reset_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(retimer->reset_gpio),
|
||||
"failed to get reset gpio\n");
|
||||
|
||||
retimer->typec_switch = typec_switch_get(dev);
|
||||
if (IS_ERR(retimer->typec_switch))
|
||||
return dev_err_probe(dev, PTR_ERR(retimer->typec_switch),
|
||||
"failed to acquire orientation-switch\n");
|
||||
|
||||
retimer->typec_mux = typec_mux_get(dev);
|
||||
if (IS_ERR(retimer->typec_mux)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(retimer->typec_mux),
|
||||
"failed to acquire mode-mux\n");
|
||||
goto err_switch_put;
|
||||
}
|
||||
|
||||
ret = drm_aux_bridge_register(dev);
|
||||
if (ret)
|
||||
goto err_mux_put;
|
||||
|
||||
ret = ps883x_enable_vregs(retimer);
|
||||
if (ret)
|
||||
goto err_mux_put;
|
||||
|
||||
ret = clk_prepare_enable(retimer->xo_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable XO: %d\n", ret);
|
||||
goto err_vregs_disable;
|
||||
}
|
||||
|
||||
/* skip resetting if already configured */
|
||||
if (regmap_test_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0,
|
||||
CONN_STATUS_0_CONNECTION_PRESENT) == 1) {
|
||||
gpiod_direction_output(retimer->reset_gpio, 0);
|
||||
} else {
|
||||
gpiod_direction_output(retimer->reset_gpio, 1);
|
||||
|
||||
/* VDD IO supply enable to reset release delay */
|
||||
usleep_range(4000, 14000);
|
||||
|
||||
gpiod_set_value(retimer->reset_gpio, 0);
|
||||
|
||||
/* firmware initialization delay */
|
||||
msleep(60);
|
||||
|
||||
/* make sure device is accessible */
|
||||
ret = regmap_read(retimer->regmap, REG_USB_PORT_CONN_STATUS_0,
|
||||
&val);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to read conn_status_0: %d\n", ret);
|
||||
if (ret == -ENXIO)
|
||||
ret = -EIO;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
}
|
||||
|
||||
sw_desc.drvdata = retimer;
|
||||
sw_desc.fwnode = dev_fwnode(dev);
|
||||
sw_desc.set = ps883x_sw_set;
|
||||
|
||||
retimer->sw = typec_switch_register(dev, &sw_desc);
|
||||
if (IS_ERR(retimer->sw)) {
|
||||
ret = PTR_ERR(retimer->sw);
|
||||
dev_err(dev, "failed to register typec switch: %d\n", ret);
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
rtmr_desc.drvdata = retimer;
|
||||
rtmr_desc.fwnode = dev_fwnode(dev);
|
||||
rtmr_desc.set = ps883x_retimer_set;
|
||||
|
||||
retimer->retimer = typec_retimer_register(dev, &rtmr_desc);
|
||||
if (IS_ERR(retimer->retimer)) {
|
||||
ret = PTR_ERR(retimer->retimer);
|
||||
dev_err(dev, "failed to register typec retimer: %d\n", ret);
|
||||
goto err_switch_unregister;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_switch_unregister:
|
||||
typec_switch_unregister(retimer->sw);
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(retimer->xo_clk);
|
||||
err_vregs_disable:
|
||||
gpiod_set_value(retimer->reset_gpio, 1);
|
||||
ps883x_disable_vregs(retimer);
|
||||
err_mux_put:
|
||||
typec_mux_put(retimer->typec_mux);
|
||||
err_switch_put:
|
||||
typec_switch_put(retimer->typec_switch);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ps883x_retimer_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ps883x_retimer *retimer = i2c_get_clientdata(client);
|
||||
|
||||
typec_retimer_unregister(retimer->retimer);
|
||||
typec_switch_unregister(retimer->sw);
|
||||
|
||||
gpiod_set_value(retimer->reset_gpio, 1);
|
||||
|
||||
clk_disable_unprepare(retimer->xo_clk);
|
||||
|
||||
ps883x_disable_vregs(retimer);
|
||||
|
||||
typec_mux_put(retimer->typec_mux);
|
||||
typec_switch_put(retimer->typec_switch);
|
||||
}
|
||||
|
||||
static const struct of_device_id ps883x_retimer_of_table[] = {
|
||||
{ .compatible = "parade,ps8830" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ps883x_retimer_of_table);
|
||||
|
||||
static struct i2c_driver ps883x_retimer_driver = {
|
||||
.driver = {
|
||||
.name = "ps883x_retimer",
|
||||
.of_match_table = ps883x_retimer_of_table,
|
||||
},
|
||||
.probe = ps883x_retimer_probe,
|
||||
.remove = ps883x_retimer_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(ps883x_retimer_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Parade ps883x Type-C Retimer driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -105,12 +105,13 @@ static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd)
|
||||
static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci,
|
||||
void *data, size_t size)
|
||||
{
|
||||
struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi);
|
||||
int ret;
|
||||
|
||||
ret = ucsi_sync_control_common(ucsi, cmd);
|
||||
ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size);
|
||||
switch (ret) {
|
||||
case -EBUSY:
|
||||
/* EC may return -EBUSY if CCI.busy is set.
|
||||
@@ -205,12 +206,19 @@ static int cros_ucsi_event(struct notifier_block *nb,
|
||||
{
|
||||
struct cros_ucsi_data *udata = container_of(nb, struct cros_ucsi_data, nb);
|
||||
|
||||
if (!(host_event & PD_EVENT_PPM))
|
||||
return NOTIFY_OK;
|
||||
if (host_event & PD_EVENT_INIT) {
|
||||
/* Late init event received from ChromeOS EC. Treat this as a
|
||||
* system resume to re-enable communication with the PPM.
|
||||
*/
|
||||
dev_dbg(udata->dev, "Late PD init received\n");
|
||||
ucsi_resume(udata->ucsi);
|
||||
}
|
||||
|
||||
dev_dbg(udata->dev, "UCSI notification received\n");
|
||||
flush_work(&udata->work);
|
||||
schedule_work(&udata->work);
|
||||
if (host_event & PD_EVENT_PPM) {
|
||||
dev_dbg(udata->dev, "UCSI notification received\n");
|
||||
flush_work(&udata->work);
|
||||
schedule_work(&udata->work);
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
@@ -28,11 +28,12 @@ static int ucsi_cmd(void *data, u64 val)
|
||||
ucsi->debugfs->status = 0;
|
||||
|
||||
switch (UCSI_COMMAND(val)) {
|
||||
case UCSI_SET_UOM:
|
||||
case UCSI_SET_CCOM:
|
||||
case UCSI_SET_UOR:
|
||||
case UCSI_SET_PDR:
|
||||
case UCSI_CONNECTOR_RESET:
|
||||
case UCSI_SET_SINK_PATH:
|
||||
case UCSI_SET_NEW_CAM:
|
||||
ret = ucsi_send_command(ucsi, val, NULL, 0);
|
||||
break;
|
||||
case UCSI_GET_CAPABILITY:
|
||||
@@ -42,6 +43,9 @@ static int ucsi_cmd(void *data, u64 val)
|
||||
case UCSI_GET_PDOS:
|
||||
case UCSI_GET_CABLE_PROPERTY:
|
||||
case UCSI_GET_CONNECTOR_STATUS:
|
||||
case UCSI_GET_ERROR_STATUS:
|
||||
case UCSI_GET_CAM_CS:
|
||||
case UCSI_GET_LPM_PPM_INFO:
|
||||
ret = ucsi_send_command(ucsi, val,
|
||||
&ucsi->debugfs->response,
|
||||
sizeof(ucsi->debugfs->response));
|
||||
|
||||
@@ -12,7 +12,7 @@ static const char * const ucsi_cmd_strs[] = {
|
||||
[UCSI_SET_NOTIFICATION_ENABLE] = "SET_NOTIFICATION_ENABLE",
|
||||
[UCSI_GET_CAPABILITY] = "GET_CAPABILITY",
|
||||
[UCSI_GET_CONNECTOR_CAPABILITY] = "GET_CONNECTOR_CAPABILITY",
|
||||
[UCSI_SET_UOM] = "SET_UOM",
|
||||
[UCSI_SET_CCOM] = "SET_CCOM",
|
||||
[UCSI_SET_UOR] = "SET_UOR",
|
||||
[UCSI_SET_PDM] = "SET_PDM",
|
||||
[UCSI_SET_PDR] = "SET_PDR",
|
||||
|
||||
@@ -55,7 +55,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_notify_common);
|
||||
|
||||
int ucsi_sync_control_common(struct ucsi *ucsi, u64 command)
|
||||
int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
|
||||
void *data, size_t size)
|
||||
{
|
||||
bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI;
|
||||
int ret;
|
||||
@@ -80,6 +81,13 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command)
|
||||
else
|
||||
clear_bit(COMMAND_PENDING, &ucsi->flags);
|
||||
|
||||
if (!ret && cci)
|
||||
ret = ucsi->ops->read_cci(ucsi, cci);
|
||||
|
||||
if (!ret && data &&
|
||||
(*cci & UCSI_CCI_COMMAND_COMPLETE))
|
||||
ret = ucsi->ops->read_message_in(ucsi, data, size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_sync_control_common);
|
||||
@@ -95,7 +103,7 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack)
|
||||
ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
|
||||
}
|
||||
|
||||
return ucsi->ops->sync_control(ucsi, ctrl);
|
||||
return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
|
||||
@@ -108,9 +116,7 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
|
||||
if (size > UCSI_MAX_DATA_LENGTH(ucsi))
|
||||
return -EINVAL;
|
||||
|
||||
ret = ucsi->ops->sync_control(ucsi, command);
|
||||
if (ucsi->ops->read_cci(ucsi, cci))
|
||||
return -EIO;
|
||||
ret = ucsi->ops->sync_control(ucsi, command, cci, data, size);
|
||||
|
||||
if (*cci & UCSI_CCI_BUSY)
|
||||
return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
|
||||
@@ -127,9 +133,6 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
|
||||
else
|
||||
err = 0;
|
||||
|
||||
if (!err && data && UCSI_CCI_LENGTH(*cci))
|
||||
err = ucsi->ops->read_message_in(ucsi, data, size);
|
||||
|
||||
/*
|
||||
* Don't ACK connection change if there was an error.
|
||||
*/
|
||||
|
||||
@@ -79,7 +79,8 @@ struct ucsi_operations {
|
||||
int (*read_cci)(struct ucsi *ucsi, u32 *cci);
|
||||
int (*poll_cci)(struct ucsi *ucsi, u32 *cci);
|
||||
int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len);
|
||||
int (*sync_control)(struct ucsi *ucsi, u64 command);
|
||||
int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci,
|
||||
void *data, size_t size);
|
||||
int (*async_control)(struct ucsi *ucsi, u64 command);
|
||||
bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig,
|
||||
struct ucsi_altmode *updated);
|
||||
@@ -108,7 +109,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
|
||||
#define UCSI_GET_CAPABILITY_SIZE 128
|
||||
#define UCSI_GET_CONNECTOR_CAPABILITY 0x07
|
||||
#define UCSI_GET_CONNECTOR_CAPABILITY_SIZE 32
|
||||
#define UCSI_SET_UOM 0x08
|
||||
#define UCSI_SET_CCOM 0x08
|
||||
#define UCSI_SET_UOR 0x09
|
||||
#define UCSI_SET_PDM 0x0a
|
||||
#define UCSI_SET_PDR 0x0b
|
||||
@@ -123,7 +124,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
|
||||
#define UCSI_GET_CONNECTOR_STATUS_SIZE 152
|
||||
#define UCSI_GET_ERROR_STATUS 0x13
|
||||
#define UCSI_GET_PD_MESSAGE 0x15
|
||||
#define UCSI_GET_CAM_CS 0x18
|
||||
#define UCSI_SET_SINK_PATH 0x1c
|
||||
#define UCSI_GET_LPM_PPM_INFO 0x22
|
||||
|
||||
#define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16)
|
||||
#define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff)
|
||||
@@ -531,7 +534,8 @@ void ucsi_altmode_update_active(struct ucsi_connector *con);
|
||||
int ucsi_resume(struct ucsi *ucsi);
|
||||
|
||||
void ucsi_notify_common(struct ucsi *ucsi, u32 cci);
|
||||
int ucsi_sync_control_common(struct ucsi *ucsi, u64 command);
|
||||
int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
|
||||
void *data, size_t size);
|
||||
|
||||
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
|
||||
int ucsi_register_port_psy(struct ucsi_connector *con);
|
||||
|
||||
@@ -105,17 +105,23 @@ static const struct ucsi_operations ucsi_acpi_ops = {
|
||||
.async_control = ucsi_acpi_async_control
|
||||
};
|
||||
|
||||
static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
|
||||
static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
|
||||
void *val, size_t len)
|
||||
{
|
||||
u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE |
|
||||
UCSI_CONSTAT_PDOS_CHANGE;
|
||||
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||
int ret;
|
||||
|
||||
ret = ucsi_acpi_read_message_in(ucsi, val, val_len);
|
||||
ret = ucsi_sync_control_common(ucsi, command, cci, val, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
|
||||
ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
|
||||
ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
|
||||
ua->check_bogus_event = true;
|
||||
|
||||
if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS &&
|
||||
ua->check_bogus_event) {
|
||||
/* Clear the bogus change */
|
||||
@@ -128,28 +134,11 @@ static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_le
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
|
||||
{
|
||||
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||
int ret;
|
||||
|
||||
ret = ucsi_sync_control_common(ucsi, command);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
|
||||
ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
|
||||
ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
|
||||
ua->check_bogus_event = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct ucsi_operations ucsi_gram_ops = {
|
||||
.read_version = ucsi_acpi_read_version,
|
||||
.read_cci = ucsi_acpi_read_cci,
|
||||
.poll_cci = ucsi_acpi_poll_cci,
|
||||
.read_message_in = ucsi_gram_read_message_in,
|
||||
.read_message_in = ucsi_acpi_read_message_in,
|
||||
.sync_control = ucsi_gram_sync_control,
|
||||
.async_control = ucsi_acpi_async_control
|
||||
};
|
||||
|
||||
@@ -222,7 +222,6 @@ struct ucsi_ccg {
|
||||
u16 fw_build;
|
||||
struct work_struct pm_work;
|
||||
|
||||
u64 last_cmd_sent;
|
||||
bool has_multiple_dp;
|
||||
struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
|
||||
struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];
|
||||
@@ -538,9 +537,10 @@ static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc,
|
||||
* first and then vdo=0x3
|
||||
*/
|
||||
static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc,
|
||||
struct ucsi_altmode *alt)
|
||||
struct ucsi_altmode *alt,
|
||||
u64 command)
|
||||
{
|
||||
switch (UCSI_ALTMODE_OFFSET(uc->last_cmd_sent)) {
|
||||
switch (UCSI_ALTMODE_OFFSET(command)) {
|
||||
case NVIDIA_FTB_DP_OFFSET:
|
||||
if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DBG_VDO)
|
||||
alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DP_VDO |
|
||||
@@ -578,37 +578,11 @@ static int ucsi_ccg_read_cci(struct ucsi *ucsi, u32 *cci)
|
||||
static int ucsi_ccg_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
|
||||
{
|
||||
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
|
||||
struct ucsi_capability *cap;
|
||||
struct ucsi_altmode *alt;
|
||||
|
||||
spin_lock(&uc->op_lock);
|
||||
memcpy(val, uc->op_data.message_in, val_len);
|
||||
spin_unlock(&uc->op_lock);
|
||||
|
||||
switch (UCSI_COMMAND(uc->last_cmd_sent)) {
|
||||
case UCSI_GET_CURRENT_CAM:
|
||||
if (uc->has_multiple_dp)
|
||||
ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)val);
|
||||
break;
|
||||
case UCSI_GET_ALTERNATE_MODES:
|
||||
if (UCSI_ALTMODE_RECIPIENT(uc->last_cmd_sent) ==
|
||||
UCSI_RECIPIENT_SOP) {
|
||||
alt = val;
|
||||
if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID)
|
||||
ucsi_ccg_nvidia_altmode(uc, alt);
|
||||
}
|
||||
break;
|
||||
case UCSI_GET_CAPABILITY:
|
||||
if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) {
|
||||
cap = val;
|
||||
cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
uc->last_cmd_sent = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -628,7 +602,8 @@ static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command)
|
||||
return ccg_write(uc, reg, (u8 *)&command, sizeof(command));
|
||||
}
|
||||
|
||||
static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
|
||||
static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
|
||||
void *data, size_t size)
|
||||
{
|
||||
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
|
||||
struct ucsi_connector *con;
|
||||
@@ -638,11 +613,9 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
|
||||
mutex_lock(&uc->lock);
|
||||
pm_runtime_get_sync(uc->dev);
|
||||
|
||||
uc->last_cmd_sent = command;
|
||||
|
||||
if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM &&
|
||||
if (UCSI_COMMAND(command) == UCSI_SET_NEW_CAM &&
|
||||
uc->has_multiple_dp) {
|
||||
con_index = (uc->last_cmd_sent >> 16) &
|
||||
con_index = (command >> 16) &
|
||||
UCSI_CMD_CONNECTOR_MASK;
|
||||
if (con_index == 0) {
|
||||
ret = -EINVAL;
|
||||
@@ -652,7 +625,31 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
|
||||
ucsi_ccg_update_set_new_cam_cmd(uc, con, &command);
|
||||
}
|
||||
|
||||
ret = ucsi_sync_control_common(ucsi, command);
|
||||
ret = ucsi_sync_control_common(ucsi, command, cci, data, size);
|
||||
|
||||
switch (UCSI_COMMAND(command)) {
|
||||
case UCSI_GET_CURRENT_CAM:
|
||||
if (uc->has_multiple_dp)
|
||||
ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)data);
|
||||
break;
|
||||
case UCSI_GET_ALTERNATE_MODES:
|
||||
if (UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_SOP) {
|
||||
struct ucsi_altmode *alt = data;
|
||||
|
||||
if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID)
|
||||
ucsi_ccg_nvidia_altmode(uc, alt, command);
|
||||
}
|
||||
break;
|
||||
case UCSI_GET_CAPABILITY:
|
||||
if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) {
|
||||
struct ucsi_capability *cap = data;
|
||||
|
||||
cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
err_put:
|
||||
pm_runtime_put_sync(uc->dev);
|
||||
@@ -1391,22 +1388,35 @@ static ssize_t do_flash_store(struct device *dev,
|
||||
if (!flash)
|
||||
return n;
|
||||
|
||||
if (uc->fw_build == 0x0) {
|
||||
dev_err(dev, "fail to flash FW due to missing FW build info\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
schedule_work(&uc->work);
|
||||
return n;
|
||||
}
|
||||
|
||||
static umode_t ucsi_ccg_attrs_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev));
|
||||
|
||||
if (!uc->fw_build)
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_WO(do_flash);
|
||||
|
||||
static struct attribute *ucsi_ccg_attrs[] = {
|
||||
&dev_attr_do_flash.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ucsi_ccg);
|
||||
static struct attribute_group ucsi_ccg_attr_group = {
|
||||
.attrs = ucsi_ccg_attrs,
|
||||
.is_visible = ucsi_ccg_attrs_is_visible,
|
||||
};
|
||||
static const struct attribute_group *ucsi_ccg_groups[] = {
|
||||
&ucsi_ccg_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int ucsi_ccg_probe(struct i2c_client *client)
|
||||
{
|
||||
@@ -1433,11 +1443,10 @@ static int ucsi_ccg_probe(struct i2c_client *client)
|
||||
uc->fw_build = CCG_FW_BUILD_NVIDIA_TEGRA;
|
||||
else if (!strcmp(fw_name, "nvidia,gpu"))
|
||||
uc->fw_build = CCG_FW_BUILD_NVIDIA;
|
||||
if (!uc->fw_build)
|
||||
dev_err(uc->dev, "failed to get FW build information\n");
|
||||
}
|
||||
|
||||
if (!uc->fw_build)
|
||||
dev_err(uc->dev, "failed to get FW build information\n");
|
||||
|
||||
/* reset ccg device and initialize ucsi */
|
||||
status = ucsi_ccg_init(uc);
|
||||
if (status < 0) {
|
||||
|
||||
@@ -5046,6 +5046,7 @@ struct ec_response_pd_status {
|
||||
#define PD_EVENT_DATA_SWAP BIT(3)
|
||||
#define PD_EVENT_TYPEC BIT(4)
|
||||
#define PD_EVENT_PPM BIT(5)
|
||||
#define PD_EVENT_INIT BIT(6)
|
||||
|
||||
struct ec_response_host_event_status {
|
||||
uint32_t status; /* PD MCU host event status */
|
||||
|
||||
@@ -51,6 +51,7 @@ struct ep_device;
|
||||
* @desc: descriptor for this endpoint, wMaxPacketSize in native byteorder
|
||||
* @ss_ep_comp: SuperSpeed companion descriptor for this endpoint
|
||||
* @ssp_isoc_ep_comp: SuperSpeedPlus isoc companion descriptor for this endpoint
|
||||
* @eusb2_isoc_ep_comp: eUSB2 isoc companion descriptor for this endpoint
|
||||
* @urb_list: urbs queued to this endpoint; maintained by usbcore
|
||||
* @hcpriv: for use by HCD; typically holds hardware dma queue head (QH)
|
||||
* with one or more transfer descriptors (TDs) per urb
|
||||
@@ -64,9 +65,10 @@ struct ep_device;
|
||||
* descriptor within an active interface in a given USB configuration.
|
||||
*/
|
||||
struct usb_host_endpoint {
|
||||
struct usb_endpoint_descriptor desc;
|
||||
struct usb_ss_ep_comp_descriptor ss_ep_comp;
|
||||
struct usb_ssp_isoc_ep_comp_descriptor ssp_isoc_ep_comp;
|
||||
struct usb_endpoint_descriptor desc;
|
||||
struct usb_ss_ep_comp_descriptor ss_ep_comp;
|
||||
struct usb_ssp_isoc_ep_comp_descriptor ssp_isoc_ep_comp;
|
||||
struct usb_eusb2_isoc_ep_comp_descriptor eusb2_isoc_ep_comp;
|
||||
struct list_head urb_list;
|
||||
void *hcpriv;
|
||||
struct ep_device *ep_dev; /* For sysfs info */
|
||||
|
||||
@@ -61,7 +61,7 @@ struct musb_hdrc_eps_bits {
|
||||
};
|
||||
|
||||
struct musb_hdrc_config {
|
||||
struct musb_fifo_cfg *fifo_cfg; /* board fifo configuration */
|
||||
const struct musb_fifo_cfg *fifo_cfg; /* board fifo configuration */
|
||||
unsigned fifo_cfg_size; /* size of the fifo configuration */
|
||||
|
||||
/* MUSB configuration-specific details */
|
||||
|
||||
@@ -49,19 +49,10 @@
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_ULPI)
|
||||
struct usb_phy *otg_ulpi_create(struct usb_phy_io_ops *ops,
|
||||
unsigned int flags);
|
||||
|
||||
struct usb_phy *devm_otg_ulpi_create(struct device *dev,
|
||||
struct usb_phy_io_ops *ops,
|
||||
unsigned int flags);
|
||||
#else
|
||||
static inline struct usb_phy *otg_ulpi_create(struct usb_phy_io_ops *ops,
|
||||
unsigned int flags)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct usb_phy *devm_otg_ulpi_create(struct device *dev,
|
||||
struct usb_phy_io_ops *ops,
|
||||
unsigned int flags)
|
||||
|
||||
@@ -253,6 +253,9 @@ struct usb_ctrlrequest {
|
||||
#define USB_DT_BOS 0x0f
|
||||
#define USB_DT_DEVICE_CAPABILITY 0x10
|
||||
#define USB_DT_WIRELESS_ENDPOINT_COMP 0x11
|
||||
/* From the eUSB2 spec */
|
||||
#define USB_DT_EUSB2_ISOC_ENDPOINT_COMP 0x12
|
||||
/* From Wireless USB spec */
|
||||
#define USB_DT_WIRE_ADAPTER 0x21
|
||||
/* From USB Device Firmware Upgrade Specification, Revision 1.1 */
|
||||
#define USB_DT_DFU_FUNCTIONAL 0x21
|
||||
@@ -676,6 +679,18 @@ static inline int usb_endpoint_interrupt_type(
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* USB_DT_EUSB2_ISOC_ENDPOINT_COMP: eUSB2 Isoch Endpoint Companion descriptor */
|
||||
struct usb_eusb2_isoc_ep_comp_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__le16 wMaxPacketSize;
|
||||
__le32 dwBytesPerInterval;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define USB_DT_EUSB2_ISOC_EP_COMP_SIZE 8
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* USB_DT_SSP_ISOC_ENDPOINT_COMP: SuperSpeedPlus Isochronous Endpoint Companion
|
||||
* descriptor
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user