forked from luck/tmp_suning_uos_patched
USB: changes for v5.8 merge window
Rather busy cycle. We have a total 99 non-merge commits going into v5.8 merge window. The majority of the changes are in dwc3 this around (31.7% of all changes). It's composed mostly Thinh's recent updates to get dwc3 to behave correctly with stream transfers. We have also have Roger's for Keystone platforms and Neil's updates for the meson glue layer. Apart from those, we have the usual set of non-critical fixes, new device IDs, spelling fixes all over the place. Signed-off-by: Felipe Balbi <balbi@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEElLzh7wn96CXwjh2IzL64meEamQYFAl7LktsRHGJhbGJpQGtl cm5lbC5vcmcACgkQzL64meEamQZjHRAA1lTH1Uv8wMoLpsefXYvc5w98kUu1CQGA a23m14xNCv66pzgWTDKIk0zQpPiIpoWgvuwohVnDV0ygyCmEyg4Qp5Y6HQcFbDsY JEQDK41TT9GIEza6b/rhBSzR90MXlXxAMnFRKnxlLgMw1na8Y6Jmn3c0MtQNCfZo 3roOhWnSittEbczNRzMVdZXV/CI8/CL6ykmnsjUipYrtQJHtdzx+M/BWxj/inoUP Hl9hr6jH6zxQS+8UyoHSzNKtfWYvPMFUrwKNscsdAqjfpQyWV4uGoSFqGc+lGhXx wrWdR+8WGchWNgJPlykAeaHVf4yyV/bOeQjpngC3HU2FQxc5Ohn3UVtfG7SOeMxD ZNKZukZzRhZzXX8ha28nYu3r6++heKWS+rspOHwKR56HVhpQlSwvNwoqzTkPxxB5 p9ODyIfirsn2+Maj4weCpNARNxlc31rAybaQ8+uxAg8q6XcSD4lB5U929ajxpHQK UErgDkIbjGpYY13Lrm7GjBuagiYyyvMKp3+6lR50tKlLYQSFB5EjTaYW15az2Yc+ xwTqSusxhP9MNMp3brU9ZJwzIA4s1gyjelbLsTYs/D3pgYA2YiYnajbwqmHJDINu Nh+C6xyKiTC9OJspQv6+mHjMzc0VjBhr1KMPdRAdwLw1dHENyEhf6DrfHt/FjGxC 3fS6/uQ5jao= =OiS0 -----END PGP SIGNATURE----- Merge tag 'usb-for-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: USB: changes for v5.8 merge window Rather busy cycle. We have a total 99 non-merge commits going into v5.8 merge window. The majority of the changes are in dwc3 this around (31.7% of all changes). It's composed mostly Thinh's recent updates to get dwc3 to behave correctly with stream transfers. We have also have Roger's for Keystone platforms and Neil's updates for the meson glue layer. Apart from those, we have the usual set of non-critical fixes, new device IDs, spelling fixes all over the place. Signed-off-by: Felipe Balbi <balbi@kernel.org> * tag 'usb-for-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (99 commits) usb: dwc3: keystone: Turn on USB3 PHY before controller dt-bindings: usb: ti,keystone-dwc3.yaml: Add USB3.0 PHY property dt-bindings: usb: convert keystone-usb.txt to YAML usb: dwc3: gadget: Check for prepared TRBs usb: gadget: Fix issue with config_ep_by_speed function usb: cdns3: ep0: delete the redundant status stage usb: dwc2: Update Core Reset programming flow. usb: gadget: fsl: Fix a wrong judgment in fsl_udc_probe() usb: gadget: fix potential double-free in m66592_probe. usb: cdns3: Fix runtime PM imbalance on error usb: gadget: lpc32xx_udc: don't dereference ep pointer before null check usb: dwc3: Increase timeout for CmdAct cleared by device controller USB: dummy-hcd: use configurable endpoint naming scheme usb: cdns3: gadget: assign interrupt number to USB gadget structure usb: gadget: core: sync interrupt before unbind the udc arm64: dts: qcom: sc7180: Add interconnect properties for USB arm64: dts: qcom: sdm845: Add interconnect properties for USB dt-bindings: usb: qcom,dwc3: Introduce interconnect properties for Qualcomm DWC3 driver ARM: dts: at91: Remove the USB EP child node dt-bindings: usb: atmel: Mark EP child node as deprecated ...
This commit is contained in:
commit
ca681aa492
|
@ -1,31 +0,0 @@
|
|||
* Amlogic Meson GXL and GXM USB3 PHY and OTG detection binding
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "amlogic,meson-gxl-usb3-phy"
|
||||
- #phys-cells: must be 0 (see phy-bindings.txt in this directory)
|
||||
- reg: The base address and length of the registers
|
||||
- interrupts: the interrupt specifier for the OTG detection
|
||||
- clocks: phandles to the clocks for
|
||||
- the USB3 PHY
|
||||
- and peripheral mode/OTG detection
|
||||
- clock-names: must contain "phy" and "peripheral"
|
||||
- resets: phandle to the reset lines for:
|
||||
- the USB3 PHY and
|
||||
- peripheral mode/OTG detection
|
||||
- reset-names: must contain "phy" and "peripheral"
|
||||
|
||||
Optional properties:
|
||||
- phy-supply: see phy-bindings.txt in this directory
|
||||
|
||||
|
||||
Example:
|
||||
usb3_phy0: phy@78080 {
|
||||
compatible = "amlogic,meson-gxl-usb3-phy";
|
||||
#phy-cells = <0>;
|
||||
reg = <0x0 0x78080 0x0 0x20>;
|
||||
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clkc CLKID_USB_OTG>, <&clkc_AO CLKID_AO_CEC_32K>;
|
||||
clock-names = "phy", "peripheral";
|
||||
resets = <&reset RESET_USB_OTG>, <&reset RESET_USB_OTG>;
|
||||
reset-names = "phy", "peripheral";
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
Amlogic Meson GX DWC3 USB SoC controller
|
||||
|
||||
Required properties:
|
||||
- compatible: depending on the SoC this should contain one of:
|
||||
* amlogic,meson-axg-dwc3
|
||||
* amlogic,meson-gxl-dwc3
|
||||
- clocks: a handle for the "USB general" clock
|
||||
- clock-names: must be "usb_general"
|
||||
- resets: a handle for the shared "USB OTG" reset line
|
||||
- reset-names: must be "usb_otg"
|
||||
|
||||
Required child node:
|
||||
A child node must exist to represent the core DWC3 IP block. The name of
|
||||
the node is not important. The content of the node is defined in dwc3.txt.
|
||||
|
||||
PHY documentation is provided in the following places:
|
||||
- Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
|
||||
- Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
|
||||
|
||||
Example device nodes:
|
||||
usb0: usb@ff500000 {
|
||||
compatible = "amlogic,meson-axg-dwc3";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
clocks = <&clkc CLKID_USB>;
|
||||
clock-names = "usb_general";
|
||||
resets = <&reset RESET_USB_OTG>;
|
||||
reset-names = "usb_otg";
|
||||
|
||||
dwc3: dwc3@ff500000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x0 0xff500000 0x0 0x100000>;
|
||||
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dr_mode = "host";
|
||||
maximum-speed = "high-speed";
|
||||
snps,dis_u2_susphy_quirk;
|
||||
phys = <&usb3_phy>, <&usb2_phy0>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
};
|
||||
};
|
|
@ -25,9 +25,13 @@ description: |
|
|||
The Amlogic A1 embeds a DWC3 USB IP Core configured for USB2 in
|
||||
host-only mode.
|
||||
|
||||
The Amlogic GXL & GXM SoCs doesn't embed an USB3 PHY.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-gxl-usb-ctrl
|
||||
- amlogic,meson-gxm-usb-ctrl
|
||||
- amlogic,meson-g12a-usb-ctrl
|
||||
- amlogic,meson-a1-usb-ctrl
|
||||
|
||||
|
@ -41,6 +45,11 @@ properties:
|
|||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
|
@ -52,10 +61,8 @@ properties:
|
|||
maxItems: 1
|
||||
|
||||
phy-names:
|
||||
items:
|
||||
- const: usb2-phy0 # USB2 PHY0 if USBHOST_A port is used
|
||||
- const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
|
||||
- const: usb3-phy0 # USB3 PHY if USB3_0 is used
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
phys:
|
||||
minItems: 1
|
||||
|
@ -89,6 +96,61 @@ required:
|
|||
- dr_mode
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-g12a-usb-ctrl
|
||||
|
||||
then:
|
||||
properties:
|
||||
phy-names:
|
||||
items:
|
||||
- const: usb2-phy0 # USB2 PHY0 if USBHOST_A port is used
|
||||
- const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
|
||||
- const: usb3-phy0 # USB3 PHY if USB3_0 is used
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-gxl-usb-ctrl
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: usb_ctrl
|
||||
- const: ddr
|
||||
phy-names:
|
||||
items:
|
||||
- const: usb2-phy0 # USB2 PHY0 if USBHOST_A port is used
|
||||
- const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
|
||||
required:
|
||||
- clock-names
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-gxm-usb-ctrl
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: usb_ctrl
|
||||
- const: ddr
|
||||
phy-names:
|
||||
items:
|
||||
- const: usb2-phy0 # USB2 PHY0 if USBHOST_A port is used
|
||||
- const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
|
||||
- const: usb2-phy2 # USB2 PHY2 if USBOTG_C port is used
|
||||
|
||||
required:
|
||||
- clock-names
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -97,6 +159,9 @@ allOf:
|
|||
|
||||
then:
|
||||
properties:
|
||||
phy-names:
|
||||
items:
|
||||
- const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
|
||||
clocks:
|
||||
minItems: 3
|
||||
clock-names:
|
||||
|
|
|
@ -52,6 +52,59 @@ properties:
|
|||
minimum: 1
|
||||
maximum: 21
|
||||
|
||||
vhub-vendor-id:
|
||||
description: vhub Vendor ID
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- maximum: 65535
|
||||
|
||||
vhub-product-id:
|
||||
description: vhub Product ID
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- maximum: 65535
|
||||
|
||||
vhub-device-revision:
|
||||
description: vhub Device Revision in binary-coded decimal
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- maximum: 65535
|
||||
|
||||
vhub-strings:
|
||||
type: object
|
||||
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^string@[0-9a-f]+$':
|
||||
type: object
|
||||
description: string descriptors of the specific language
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: 16-bit Language Identifier defined by USB-IF
|
||||
|
||||
manufacturer:
|
||||
description: vhub manufacturer
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
product:
|
||||
description: vhub product name
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
serial-number:
|
||||
description: vhub device serial number
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -74,4 +127,19 @@ examples:
|
|||
aspeed,vhub-generic-endpoints = <15>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usb2ad_default>;
|
||||
|
||||
vhub-vendor-id = <0x1d6b>;
|
||||
vhub-product-id = <0x0107>;
|
||||
vhub-device-revision = <0x0100>;
|
||||
vhub-strings {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
string@0409 {
|
||||
reg = <0x0409>;
|
||||
manufacturer = "ASPEED";
|
||||
product = "USB Virtual Hub";
|
||||
serial-number = "0000";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -88,13 +88,15 @@ Required properties:
|
|||
- clock-names: Should contain two strings
|
||||
"pclk" for the peripheral clock
|
||||
"hclk" for the host clock
|
||||
|
||||
Deprecated property:
|
||||
- ep childnode: To specify the number of endpoints and their properties.
|
||||
|
||||
Optional properties:
|
||||
- atmel,vbus-gpio: If present, specifies a gpio that allows to detect whether
|
||||
vbus is present (USB is connected).
|
||||
|
||||
Required child node properties:
|
||||
Deprecated child node properties:
|
||||
- name: Name of the endpoint.
|
||||
- reg: Num of the endpoint.
|
||||
- atmel,fifo-size: Size of the fifo.
|
||||
|
@ -112,56 +114,4 @@ usb2: gadget@fff78000 {
|
|||
clocks = <&utmi>, <&udphs_clk>;
|
||||
clock-names = "hclk", "pclk";
|
||||
atmel,vbus-gpio = <&pioB 19 0>;
|
||||
|
||||
ep@0 {
|
||||
reg = <0>;
|
||||
atmel,fifo-size = <64>;
|
||||
atmel,nb-banks = <1>;
|
||||
};
|
||||
|
||||
ep@1 {
|
||||
reg = <1>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@2 {
|
||||
reg = <2>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@3 {
|
||||
reg = <3>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@4 {
|
||||
reg = <4>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@5 {
|
||||
reg = <5>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@6 {
|
||||
reg = <6>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -15,8 +15,6 @@ Required properties:
|
|||
Exception for clocks:
|
||||
clocks are optional if the parent node (i.e. glue-layer) is compatible to
|
||||
one of the following:
|
||||
"amlogic,meson-axg-dwc3"
|
||||
"amlogic,meson-gxl-dwc3"
|
||||
"cavium,octeon-7130-usb-uctl"
|
||||
"qcom,dwc3"
|
||||
"samsung,exynos5250-dwusb3"
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
TI Keystone Soc USB Controller
|
||||
|
||||
DWC3 GLUE
|
||||
|
||||
Required properties:
|
||||
- compatible: should be
|
||||
"ti,keystone-dwc3" for Keystone 2 SoCs
|
||||
"ti,am654-dwc3" for AM654 SoC
|
||||
- #address-cells, #size-cells : should be '1' if the device has sub-nodes
|
||||
with 'reg' property.
|
||||
- reg : Address and length of the register set for the USB subsystem on
|
||||
the SOC.
|
||||
- interrupts : The irq number of this device that is used to interrupt the
|
||||
MPU.
|
||||
- ranges: allows valid 1:1 translation between child's address space and
|
||||
parent's address space.
|
||||
|
||||
SoC-specific Required Properties:
|
||||
The following are mandatory properties for Keystone 2 66AK2HK, 66AK2L and 66AK2E
|
||||
SoCs only:
|
||||
|
||||
- clocks: Clock ID for USB functional clock.
|
||||
- clock-names: Must be "usb".
|
||||
|
||||
|
||||
The following are mandatory properties for 66AK2G and AM654:
|
||||
|
||||
- power-domains: Should contain a phandle to a PM domain provider node
|
||||
and an args specifier containing the USB device id
|
||||
value. This property is as per the binding,
|
||||
Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt
|
||||
|
||||
Sub-nodes:
|
||||
The dwc3 core should be added as subnode to Keystone DWC3 glue.
|
||||
- dwc3 :
|
||||
The binding details of dwc3 can be found in:
|
||||
Documentation/devicetree/bindings/usb/dwc3.txt
|
||||
|
||||
Example:
|
||||
usb: usb@2680000 {
|
||||
compatible = "ti,keystone-dwc3";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0x2680000 0x10000>;
|
||||
clocks = <&clkusb>;
|
||||
clock-names = "usb";
|
||||
interrupts = <GIC_SPI 393 IRQ_TYPE_EDGE_RISING>;
|
||||
ranges;
|
||||
|
||||
dwc3@2690000 {
|
||||
compatible = "synopsys,dwc3";
|
||||
reg = <0x2690000 0x70000>;
|
||||
interrupts = <GIC_SPI 393 IRQ_TYPE_EDGE_RISING>;
|
||||
usb-phy = <&usb_phy>, <&usb_phy>;
|
||||
};
|
||||
};
|
|
@ -21,6 +21,7 @@ properties:
|
|||
- enum:
|
||||
- nvidia,tegra210-xudc # For Tegra210
|
||||
- nvidia,tegra186-xudc # For Tegra186
|
||||
- nvidia,tegra194-xudc # For Tegra194
|
||||
|
||||
reg:
|
||||
minItems: 2
|
||||
|
@ -144,6 +145,7 @@ allOf:
|
|||
contains:
|
||||
enum:
|
||||
- nvidia,tegra186-xudc
|
||||
- nvidia,tegra194-xudc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
Qualcomm SuperSpeed DWC3 USB SoC controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Compatible list, contains
|
||||
"qcom,dwc3"
|
||||
"qcom,msm8996-dwc3" for msm8996 SOC.
|
||||
"qcom,msm8998-dwc3" for msm8998 SOC.
|
||||
"qcom,sdm845-dwc3" for sdm845 SOC.
|
||||
- reg: Offset and length of register set for QSCRATCH wrapper
|
||||
- power-domains: specifies a phandle to PM domain provider node
|
||||
- clocks: A list of phandle + clock-specifier pairs for the
|
||||
clocks listed in clock-names
|
||||
- clock-names: Should contain the following:
|
||||
"core" Master/Core clock, have to be >= 125 MHz for SS
|
||||
operation and >= 60MHz for HS operation
|
||||
"mock_utmi" Mock utmi clock needed for ITP/SOF generation in
|
||||
host mode. Its frequency should be 19.2MHz.
|
||||
"sleep" Sleep clock, used for wakeup when USB3 core goes
|
||||
into low power mode (U3).
|
||||
|
||||
Optional clocks:
|
||||
"iface" System bus AXI clock.
|
||||
Not present on "qcom,msm8996-dwc3" compatible.
|
||||
"cfg_noc" System Config NOC clock.
|
||||
Not present on "qcom,msm8996-dwc3" compatible.
|
||||
- assigned-clocks: Should be:
|
||||
MOCK_UTMI_CLK
|
||||
MASTER_CLK
|
||||
- assigned-clock-rates: Should be:
|
||||
19.2Mhz (192000000) for MOCK_UTMI_CLK
|
||||
>=125Mhz (125000000) for MASTER_CLK in SS mode
|
||||
>=60Mhz (60000000) for MASTER_CLK in HS mode
|
||||
|
||||
Optional properties:
|
||||
- resets: Phandle to reset control that resets core and wrapper.
|
||||
- interrupts: specifies interrupts from controller wrapper used
|
||||
to wakeup from low power/susepnd state. Must contain
|
||||
one or more entry for interrupt-names property
|
||||
- interrupt-names: Must include the following entries:
|
||||
- "hs_phy_irq": The interrupt that is asserted when a
|
||||
wakeup event is received on USB2 bus
|
||||
- "ss_phy_irq": The interrupt that is asserted when a
|
||||
wakeup event is received on USB3 bus
|
||||
- "dm_hs_phy_irq" and "dp_hs_phy_irq": Separate
|
||||
interrupts for any wakeup event on DM and DP lines
|
||||
- qcom,select-utmi-as-pipe-clk: if present, disable USB3 pipe_clk requirement.
|
||||
Used when dwc3 operates without SSPHY and only
|
||||
HS/FS/LS modes are supported.
|
||||
|
||||
Required child node:
|
||||
A child node must exist to represent the core DWC3 IP block. The name of
|
||||
the node is not important. The content of the node is defined in dwc3.txt.
|
||||
|
||||
Phy documentation is provided in the following places:
|
||||
Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt - USB3 QMP PHY
|
||||
Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml - USB2 QUSB2 PHY
|
||||
|
||||
Example device nodes:
|
||||
|
||||
hs_phy: phy@100f8800 {
|
||||
compatible = "qcom,qusb2-v2-phy";
|
||||
...
|
||||
};
|
||||
|
||||
ss_phy: phy@100f8830 {
|
||||
compatible = "qcom,qmp-v3-usb3-phy";
|
||||
...
|
||||
};
|
||||
|
||||
usb3_0: usb30@a6f8800 {
|
||||
compatible = "qcom,dwc3";
|
||||
reg = <0xa6f8800 0x400>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
interrupts = <0 131 0>, <0 486 0>, <0 488 0>, <0 489 0>;
|
||||
interrupt-names = "hs_phy_irq", "ss_phy_irq",
|
||||
"dm_hs_phy_irq", "dp_hs_phy_irq";
|
||||
|
||||
clocks = <&gcc GCC_USB30_PRIM_MASTER_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_SLEEP_CLK>;
|
||||
clock-names = "core", "mock_utmi", "sleep";
|
||||
|
||||
assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MASTER_CLK>;
|
||||
assigned-clock-rates = <19200000>, <133000000>;
|
||||
|
||||
resets = <&gcc GCC_USB30_PRIM_BCR>;
|
||||
reset-names = "core_reset";
|
||||
power-domains = <&gcc USB30_PRIM_GDSC>;
|
||||
qcom,select-utmi-as-pipe-clk;
|
||||
|
||||
dwc3@10000000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x10000000 0xcd00>;
|
||||
interrupts = <0 205 0x4>;
|
||||
phys = <&hs_phy>, <&ss_phy>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
dr_mode = "host";
|
||||
};
|
||||
};
|
||||
|
167
Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
Normal file
167
Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
Normal file
|
@ -0,0 +1,167 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/qcom,dwc3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm SuperSpeed DWC3 USB SoC controller
|
||||
|
||||
maintainers:
|
||||
- Manu Gautam <mgautam@codeaurora.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,msm8996-dwc3
|
||||
- qcom,msm8998-dwc3
|
||||
- qcom,sc7180-dwc3
|
||||
- qcom,sdm845-dwc3
|
||||
- const: qcom,dwc3
|
||||
|
||||
reg:
|
||||
description: Offset and length of register set for QSCRATCH wrapper
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
enum: [ 1, 2 ]
|
||||
|
||||
"#size-cells":
|
||||
enum: [ 1, 2 ]
|
||||
|
||||
power-domains:
|
||||
description: specifies a phandle to PM domain provider node
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
description:
|
||||
A list of phandle and clock-specifier pairs for the clocks
|
||||
listed in clock-names.
|
||||
items:
|
||||
- description: System Config NOC clock.
|
||||
- description: Master/Core clock, has to be >= 125 MHz
|
||||
for SS operation and >= 60MHz for HS operation.
|
||||
- description: System bus AXI clock.
|
||||
- description: Mock utmi clock needed for ITP/SOF generation
|
||||
in host mode. Its frequency should be 19.2MHz.
|
||||
- description: Sleep clock, used for wakeup when
|
||||
USB3 core goes into low power mode (U3).
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: cfg_noc
|
||||
- const: core
|
||||
- const: iface
|
||||
- const: mock_utmi
|
||||
- const: sleep
|
||||
|
||||
assigned-clocks:
|
||||
items:
|
||||
- description: Phandle and clock specifier of MOCK_UTMI_CLK.
|
||||
- description: Phandle and clock specifoer of MASTER_CLK.
|
||||
|
||||
assigned-clock-rates:
|
||||
maxItems: 2
|
||||
items:
|
||||
- description: Must be 19.2MHz (19200000).
|
||||
- description: Must be >= 60 MHz in HS mode, >= 125 MHz in SS mode.
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
interconnects:
|
||||
maxItems: 2
|
||||
|
||||
interconnect-names:
|
||||
items:
|
||||
- const: usb-ddr
|
||||
- const: apps-usb
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: The interrupt that is asserted
|
||||
when a wakeup event is received on USB2 bus.
|
||||
- description: The interrupt that is asserted
|
||||
when a wakeup event is received on USB3 bus.
|
||||
- description: Wakeup event on DM line.
|
||||
- description: Wakeup event on DP line.
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: hs_phy_irq
|
||||
- const: ss_phy_irq
|
||||
- const: dm_hs_phy_irq
|
||||
- const: dp_hs_phy_irq
|
||||
|
||||
qcom,select-utmi-as-pipe-clk:
|
||||
description:
|
||||
If present, disable USB3 pipe_clk requirement.
|
||||
Used when dwc3 operates without SSPHY and only
|
||||
HS/FS/LS modes are supported.
|
||||
type: boolean
|
||||
|
||||
# Required child node:
|
||||
|
||||
patternProperties:
|
||||
"^dwc3@[0-9a-f]+$":
|
||||
type: object
|
||||
description:
|
||||
A child node must exist to represent the core DWC3 IP block
|
||||
The content of the node is defined in dwc3.txt.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- power-domains
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-sdm845.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
usb@a6f8800 {
|
||||
compatible = "qcom,sdm845-dwc3", "qcom,dwc3";
|
||||
reg = <0 0x0a6f8800 0 0x400>;
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MASTER_CLK>,
|
||||
<&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_SLEEP_CLK>;
|
||||
clock-names = "cfg_noc", "core", "iface", "mock_utmi",
|
||||
"sleep";
|
||||
|
||||
assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MASTER_CLK>;
|
||||
assigned-clock-rates = <19200000>, <150000000>;
|
||||
|
||||
interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 486 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 488 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 489 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "hs_phy_irq", "ss_phy_irq",
|
||||
"dm_hs_phy_irq", "dp_hs_phy_irq";
|
||||
|
||||
power-domains = <&gcc USB30_PRIM_GDSC>;
|
||||
|
||||
resets = <&gcc GCC_USB30_PRIM_BCR>;
|
||||
|
||||
dwc3@a600000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0 0x0a600000 0 0xcd00>;
|
||||
interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
|
||||
iommus = <&apps_smmu 0x740 0>;
|
||||
snps,dis_u2_susphy_quirk;
|
||||
snps,dis_enblslpm_quirk;
|
||||
phys = <&usb_1_hsphy>, <&usb_1_ssphy>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
};
|
||||
};
|
77
Documentation/devicetree/bindings/usb/ti,keystone-dwc3.yaml
Normal file
77
Documentation/devicetree/bindings/usb/ti,keystone-dwc3.yaml
Normal file
|
@ -0,0 +1,77 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/ti,keystone-dwc3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI Keystone Soc USB Controller
|
||||
|
||||
maintainers:
|
||||
- Roger Quadros <rogerq@ti.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: "ti,keystone-dwc3"
|
||||
- const: "ti,am654-dwc3"
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: Address and length of the register set for the USB subsystem on
|
||||
the SOC.
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description: The irq number of this device that is used to interrupt the MPU.
|
||||
|
||||
|
||||
clocks:
|
||||
description: Clock ID for USB functional clock.
|
||||
|
||||
power-domains:
|
||||
description: Should contain a phandle to a PM domain provider node
|
||||
and an args specifier containing the USB device id
|
||||
value. This property is as per the binding,
|
||||
Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt
|
||||
|
||||
phys:
|
||||
description:
|
||||
PHY specifier for the USB3.0 PHY. Some SoCs need the USB3.0 PHY
|
||||
to be turned on before the controller.
|
||||
Documentation/devicetree/bindings/phy/phy-bindings.txt
|
||||
|
||||
phy-names:
|
||||
items:
|
||||
- const: "usb3-phy"
|
||||
|
||||
dwc3:
|
||||
description: This is the node representing the DWC3 controller instance
|
||||
Documentation/devicetree/bindings/usb/dwc3.txt
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
usb: usb@2680000 {
|
||||
compatible = "ti,keystone-dwc3";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0x2680000 0x10000>;
|
||||
clocks = <&clkusb>;
|
||||
clock-names = "usb";
|
||||
interrupts = <GIC_SPI 393 IRQ_TYPE_EDGE_RISING>;
|
||||
ranges;
|
||||
|
||||
dwc3@2690000 {
|
||||
compatible = "synopsys,dwc3";
|
||||
reg = <0x2690000 0x70000>;
|
||||
interrupts = <GIC_SPI 393 IRQ_TYPE_EDGE_RISING>;
|
||||
usb-phy = <&usb_phy>, <&usb_phy>;
|
||||
};
|
||||
};
|
|
@ -1195,8 +1195,6 @@ spi1: spi@fffa8000 {
|
|||
};
|
||||
|
||||
usb2: gadget@fff78000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91sam9g45-udc";
|
||||
reg = <0x00600000 0x80000
|
||||
0xfff78000 0x400>;
|
||||
|
@ -1204,58 +1202,6 @@ usb2: gadget@fff78000 {
|
|||
clocks = <&udphs_clk>, <&utmi>;
|
||||
clock-names = "pclk", "hclk";
|
||||
status = "disabled";
|
||||
|
||||
ep@0 {
|
||||
reg = <0>;
|
||||
atmel,fifo-size = <64>;
|
||||
atmel,nb-banks = <1>;
|
||||
};
|
||||
|
||||
ep@1 {
|
||||
reg = <1>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@2 {
|
||||
reg = <2>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@3 {
|
||||
reg = <3>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@4 {
|
||||
reg = <4>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@5 {
|
||||
reg = <5>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@6 {
|
||||
reg = <6>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
};
|
||||
|
||||
clk32k: sckc@fffffd50 {
|
||||
|
|
|
@ -299,8 +299,6 @@ trigger3 {
|
|||
};
|
||||
|
||||
usb0: gadget@fffd4000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91sam9rl-udc";
|
||||
reg = <0x00600000 0x100000>,
|
||||
<0xfffd4000 0x4000>;
|
||||
|
@ -308,58 +306,6 @@ usb0: gadget@fffd4000 {
|
|||
clocks = <&pmc PMC_TYPE_PERIPHERAL 22>, <&pmc PMC_TYPE_CORE PMC_UTMI>;
|
||||
clock-names = "pclk", "hclk";
|
||||
status = "disabled";
|
||||
|
||||
ep@0 {
|
||||
reg = <0>;
|
||||
atmel,fifo-size = <64>;
|
||||
atmel,nb-banks = <1>;
|
||||
};
|
||||
|
||||
ep@1 {
|
||||
reg = <1>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@2 {
|
||||
reg = <2>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@3 {
|
||||
reg = <3>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@4 {
|
||||
reg = <4>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@5 {
|
||||
reg = <5>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@6 {
|
||||
reg = <6>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
};
|
||||
|
||||
dma0: dma-controller@ffffe600 {
|
||||
|
|
|
@ -867,8 +867,6 @@ spi1: spi@f0004000 {
|
|||
};
|
||||
|
||||
usb2: gadget@f803c000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91sam9g45-udc";
|
||||
reg = <0x00500000 0x80000
|
||||
0xf803c000 0x400>;
|
||||
|
@ -876,58 +874,6 @@ usb2: gadget@f803c000 {
|
|||
clocks = <&pmc PMC_TYPE_CORE PMC_UTMI>, <&pmc PMC_TYPE_PERIPHERAL 23>;
|
||||
clock-names = "hclk", "pclk";
|
||||
status = "disabled";
|
||||
|
||||
ep@0 {
|
||||
reg = <0>;
|
||||
atmel,fifo-size = <64>;
|
||||
atmel,nb-banks = <1>;
|
||||
};
|
||||
|
||||
ep@1 {
|
||||
reg = <1>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@2 {
|
||||
reg = <2>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@3 {
|
||||
reg = <3>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@4 {
|
||||
reg = <4>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@5 {
|
||||
reg = <5>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@6 {
|
||||
reg = <6>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
};
|
||||
|
||||
watchdog: watchdog@fffffe40 {
|
||||
|
|
|
@ -113,8 +113,6 @@ nfc_sram: sram@100000 {
|
|||
};
|
||||
|
||||
usb0: gadget@300000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,sama5d3-udc";
|
||||
reg = <0x00300000 0x100000
|
||||
0xfc02c000 0x400>;
|
||||
|
@ -122,124 +120,6 @@ usb0: gadget@300000 {
|
|||
clocks = <&pmc PMC_TYPE_PERIPHERAL 42>, <&pmc PMC_TYPE_CORE PMC_UTMI>;
|
||||
clock-names = "pclk", "hclk";
|
||||
status = "disabled";
|
||||
|
||||
ep@0 {
|
||||
reg = <0>;
|
||||
atmel,fifo-size = <64>;
|
||||
atmel,nb-banks = <1>;
|
||||
};
|
||||
|
||||
ep@1 {
|
||||
reg = <1>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@2 {
|
||||
reg = <2>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@3 {
|
||||
reg = <3>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@4 {
|
||||
reg = <4>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@5 {
|
||||
reg = <5>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@6 {
|
||||
reg = <6>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@7 {
|
||||
reg = <7>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@8 {
|
||||
reg = <8>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@9 {
|
||||
reg = <9>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@10 {
|
||||
reg = <10>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@11 {
|
||||
reg = <11>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@12 {
|
||||
reg = <12>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@13 {
|
||||
reg = <13>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@14 {
|
||||
reg = <14>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@15 {
|
||||
reg = <15>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
};
|
||||
|
||||
usb1: ohci@400000 {
|
||||
|
|
|
@ -1420,8 +1420,6 @@ nfc_sram: sram@200000 {
|
|||
};
|
||||
|
||||
usb0: gadget@500000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,sama5d3-udc";
|
||||
reg = <0x00500000 0x100000
|
||||
0xf8030000 0x4000>;
|
||||
|
@ -1429,111 +1427,6 @@ usb0: gadget@500000 {
|
|||
clocks = <&udphs_clk>, <&utmi>;
|
||||
clock-names = "pclk", "hclk";
|
||||
status = "disabled";
|
||||
|
||||
ep@0 {
|
||||
reg = <0>;
|
||||
atmel,fifo-size = <64>;
|
||||
atmel,nb-banks = <1>;
|
||||
};
|
||||
|
||||
ep@1 {
|
||||
reg = <1>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@2 {
|
||||
reg = <2>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@3 {
|
||||
reg = <3>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@4 {
|
||||
reg = <4>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@5 {
|
||||
reg = <5>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@6 {
|
||||
reg = <6>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@7 {
|
||||
reg = <7>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
};
|
||||
|
||||
ep@8 {
|
||||
reg = <8>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
};
|
||||
|
||||
ep@9 {
|
||||
reg = <9>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
};
|
||||
|
||||
ep@10 {
|
||||
reg = <10>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
};
|
||||
|
||||
ep@11 {
|
||||
reg = <11>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
};
|
||||
|
||||
ep@12 {
|
||||
reg = <12>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
};
|
||||
|
||||
ep@13 {
|
||||
reg = <13>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
};
|
||||
|
||||
ep@14 {
|
||||
reg = <14>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
};
|
||||
|
||||
ep@15 {
|
||||
reg = <15>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
usb1: ohci@600000 {
|
||||
|
|
|
@ -96,8 +96,6 @@ nfc_sram: sram@100000 {
|
|||
};
|
||||
|
||||
usb0: gadget@400000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,sama5d3-udc";
|
||||
reg = <0x00400000 0x100000
|
||||
0xfc02c000 0x4000>;
|
||||
|
@ -105,124 +103,6 @@ usb0: gadget@400000 {
|
|||
clocks = <&pmc PMC_TYPE_PERIPHERAL 47>, <&pmc PMC_TYPE_CORE PMC_UTMI>;
|
||||
clock-names = "pclk", "hclk";
|
||||
status = "disabled";
|
||||
|
||||
ep@0 {
|
||||
reg = <0>;
|
||||
atmel,fifo-size = <64>;
|
||||
atmel,nb-banks = <1>;
|
||||
};
|
||||
|
||||
ep@1 {
|
||||
reg = <1>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@2 {
|
||||
reg = <2>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <3>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@3 {
|
||||
reg = <3>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@4 {
|
||||
reg = <4>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@5 {
|
||||
reg = <5>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@6 {
|
||||
reg = <6>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@7 {
|
||||
reg = <7>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-dma;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@8 {
|
||||
reg = <8>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@9 {
|
||||
reg = <9>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@10 {
|
||||
reg = <10>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@11 {
|
||||
reg = <11>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@12 {
|
||||
reg = <12>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@13 {
|
||||
reg = <13>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@14 {
|
||||
reg = <14>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
|
||||
ep@15 {
|
||||
reg = <15>;
|
||||
atmel,fifo-size = <1024>;
|
||||
atmel,nb-banks = <2>;
|
||||
atmel,can-isoc;
|
||||
};
|
||||
};
|
||||
|
||||
usb1: ohci@500000 {
|
||||
|
|
|
@ -360,8 +360,9 @@ &uart_AO {
|
|||
status = "okay";
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
&usb {
|
||||
status = "okay";
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
||||
&usb2_phy0 {
|
||||
|
|
|
@ -223,6 +223,7 @@ &uart_AO {
|
|||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
&usb {
|
||||
status = "okay";
|
||||
dr_mode = "otg";
|
||||
};
|
||||
|
|
|
@ -243,6 +243,7 @@ &uart_AO {
|
|||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
&usb {
|
||||
status = "okay";
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
|
|
@ -216,6 +216,7 @@ &uart_AO {
|
|||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
&usb {
|
||||
status = "okay";
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
|
|
@ -29,3 +29,7 @@ status {
|
|||
&cvbs_vdac_port {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&usb {
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
|
|
@ -20,3 +20,7 @@ memory@0 {
|
|||
reg = <0x0 0x0 0x0 0x40000000>;
|
||||
};
|
||||
};
|
||||
|
||||
&usb {
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
|
|
@ -24,3 +24,7 @@ memory@0 {
|
|||
&ir {
|
||||
linux,rc-map-name = "rc-tanix-tx3mini";
|
||||
};
|
||||
|
||||
&usb {
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
|
|
@ -207,3 +207,7 @@ &uart_AO_B {
|
|||
pinctrl-0 = <&uart_ao_b_pins>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&usb {
|
||||
dr_mode = "peripheral";
|
||||
};
|
||||
|
|
|
@ -272,8 +272,9 @@ &uart_AO {
|
|||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
&usb {
|
||||
status = "okay";
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
||||
&usb2_phy0 {
|
||||
|
|
|
@ -218,6 +218,7 @@ &uart_AO {
|
|||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
&usb {
|
||||
status = "okay";
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
|
|
@ -195,8 +195,9 @@ &uart_AO {
|
|||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
&usb {
|
||||
status = "okay";
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
||||
&usb2_phy0 {
|
||||
|
|
|
@ -14,26 +14,43 @@ / {
|
|||
compatible = "amlogic,meson-gxl";
|
||||
|
||||
soc {
|
||||
usb0: usb@c9000000 {
|
||||
status = "disabled";
|
||||
compatible = "amlogic,meson-gxl-dwc3";
|
||||
usb: usb@d0078080 {
|
||||
compatible = "amlogic,meson-gxl-usb-ctrl";
|
||||
reg = <0x0 0xd0078080 0x0 0x20>;
|
||||
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
clocks = <&clkc CLKID_USB>;
|
||||
clock-names = "usb_general";
|
||||
clocks = <&clkc CLKID_USB>, <&clkc CLKID_USB1_DDR_BRIDGE>;
|
||||
clock-names = "usb_ctrl", "ddr";
|
||||
resets = <&reset RESET_USB_OTG>;
|
||||
reset-names = "usb_otg";
|
||||
|
||||
dwc3: dwc3@c9000000 {
|
||||
dr_mode = "otg";
|
||||
|
||||
phys = <&usb2_phy0>, <&usb2_phy1>;
|
||||
phy-names = "usb2-phy0", "usb2-phy1";
|
||||
|
||||
dwc2: usb@c9100000 {
|
||||
compatible = "amlogic,meson-g12a-usb", "snps,dwc2";
|
||||
reg = <0x0 0xc9100000 0x0 0x40000>;
|
||||
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clkc CLKID_USB1>;
|
||||
clock-names = "otg";
|
||||
phys = <&usb2_phy1>;
|
||||
dr_mode = "peripheral";
|
||||
g-rx-fifo-size = <192>;
|
||||
g-np-tx-fifo-size = <128>;
|
||||
g-tx-fifo-size = <128 128 16 16 16>;
|
||||
};
|
||||
|
||||
dwc3: usb@c9000000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x0 0xc9000000 0x0 0x100000>;
|
||||
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dr_mode = "host";
|
||||
maximum-speed = "high-speed";
|
||||
snps,dis_u2_susphy_quirk;
|
||||
phys = <&usb3_phy>, <&usb2_phy0>, <&usb2_phy1>;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -71,18 +88,6 @@ usb2_phy1: phy@78020 {
|
|||
reset-names = "phy";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb3_phy: phy@78080 {
|
||||
compatible = "amlogic,meson-gxl-usb3-phy";
|
||||
#phy-cells = <0>;
|
||||
reg = <0x0 0x78080 0x0 0x20>;
|
||||
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clkc CLKID_USB>, <&clkc_AO CLKID_AO_CEC_32K>;
|
||||
clock-names = "phy", "peripheral";
|
||||
resets = <&reset RESET_USB_OTG>, <&reset RESET_USB_OTG>;
|
||||
reset-names = "phy", "peripheral";
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
&efuse {
|
||||
|
|
|
@ -380,6 +380,7 @@ &saradc {
|
|||
vref-supply = <&vddio_ao18>;
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
&usb {
|
||||
status = "okay";
|
||||
dr_mode = "peripheral";
|
||||
};
|
||||
|
|
|
@ -179,6 +179,7 @@ &uart_AO {
|
|||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
&usb {
|
||||
status = "okay";
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
|
|
@ -39,3 +39,7 @@ external_phy: ethernet-phy@0 {
|
|||
&ir {
|
||||
linux,rc-map-name = "rc-vega-s9x";
|
||||
};
|
||||
|
||||
&usb {
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
|
|
@ -169,8 +169,11 @@ &hdmi_tx {
|
|||
compatible = "amlogic,meson-gxm-dw-hdmi", "amlogic,meson-gx-dw-hdmi";
|
||||
};
|
||||
|
||||
&dwc3 {
|
||||
phys = <&usb3_phy>, <&usb2_phy0>, <&usb2_phy1>, <&usb2_phy2>;
|
||||
&usb {
|
||||
compatible = "amlogic,meson-gxm-usb-ctrl";
|
||||
|
||||
phy-names = "usb2-phy0", "usb2-phy1", "usb2-phy2";
|
||||
phys = <&usb2_phy0>, <&usb2_phy1>, <&usb2_phy2>;
|
||||
};
|
||||
|
||||
&vdec {
|
||||
|
|
|
@ -1447,6 +1447,10 @@ usb_1: usb@a6f8800 {
|
|||
|
||||
resets = <&gcc GCC_USB30_PRIM_BCR>;
|
||||
|
||||
interconnects = <&aggre2_noc MASTER_USB3 &mc_virt SLAVE_EBI1>,
|
||||
<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_USB3>;
|
||||
interconnect-names = "usb-ddr", "apps-usb";
|
||||
|
||||
usb_1_dwc3: dwc3@a600000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0 0x0a600000 0 0xe000>;
|
||||
|
|
|
@ -3097,6 +3097,10 @@ usb_1: usb@a6f8800 {
|
|||
|
||||
resets = <&gcc GCC_USB30_PRIM_BCR>;
|
||||
|
||||
interconnects = <&aggre2_noc MASTER_USB3_0 &mem_noc SLAVE_EBI1>,
|
||||
<&gladiator_noc MASTER_APPSS_PROC &config_noc SLAVE_USB3_0>;
|
||||
interconnect-names = "usb-ddr", "apps-usb";
|
||||
|
||||
usb_1_dwc3: dwc3@a600000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0 0x0a600000 0 0xcd00>;
|
||||
|
@ -3141,6 +3145,10 @@ usb_2: usb@a8f8800 {
|
|||
|
||||
resets = <&gcc GCC_USB30_SEC_BCR>;
|
||||
|
||||
interconnects = <&aggre2_noc MASTER_USB3_1 &mem_noc SLAVE_EBI1>,
|
||||
<&gladiator_noc MASTER_APPSS_PROC &config_noc SLAVE_USB3_1>;
|
||||
interconnect-names = "usb-ddr", "apps-usb";
|
||||
|
||||
usb_2_dwc3: dwc3@a800000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0 0x0a800000 0 0xcd00>;
|
||||
|
|
|
@ -27,18 +27,6 @@ config PHY_MESON_GXL_USB2
|
|||
GXL and GXM SoCs.
|
||||
If unsure, say N.
|
||||
|
||||
config PHY_MESON_GXL_USB3
|
||||
tristate "Meson GXL and GXM USB3 PHY drivers"
|
||||
default ARCH_MESON
|
||||
depends on OF && (ARCH_MESON || COMPILE_TEST)
|
||||
depends on USB_SUPPORT
|
||||
select GENERIC_PHY
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Enable this to support the Meson USB3 PHY and OTG detection
|
||||
IP block found in Meson GXL and GXM SoCs.
|
||||
If unsure, say N.
|
||||
|
||||
config PHY_MESON_G12A_USB2
|
||||
tristate "Meson G12A USB2 PHY driver"
|
||||
default ARCH_MESON
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o
|
||||
obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o
|
||||
obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o
|
||||
obj-$(CONFIG_PHY_MESON_GXL_USB3) += phy-meson-gxl-usb3.o
|
||||
obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o
|
||||
obj-$(CONFIG_PHY_MESON_AXG_PCIE) += phy-meson-axg-pcie.o
|
||||
obj-$(CONFIG_PHY_MESON_AXG_MIPI_PCIE_ANALOG) += phy-meson-axg-mipi-pcie-analog.o
|
||||
|
|
|
@ -1,283 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Meson GXL USB3 PHY and OTG mode detection driver
|
||||
*
|
||||
* Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define USB_R0 0x00
|
||||
#define USB_R0_P30_FSEL_MASK GENMASK(5, 0)
|
||||
#define USB_R0_P30_PHY_RESET BIT(6)
|
||||
#define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7)
|
||||
#define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8)
|
||||
#define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9)
|
||||
#define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14)
|
||||
#define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
|
||||
#define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
|
||||
#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
|
||||
#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
|
||||
#define USB_R0_U2D_ACT BIT(31)
|
||||
|
||||
#define USB_R1 0x04
|
||||
#define USB_R1_U3H_BIGENDIAN_GS BIT(0)
|
||||
#define USB_R1_U3H_PME_ENABLE BIT(1)
|
||||
#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2)
|
||||
#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7)
|
||||
#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12)
|
||||
#define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16)
|
||||
#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17)
|
||||
#define USB_R1_U3H_HOST_MSI_ENABLE BIT(18)
|
||||
#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
|
||||
#define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
|
||||
|
||||
#define USB_R2 0x08
|
||||
#define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0)
|
||||
#define USB_R2_P30_CR_READ BIT(16)
|
||||
#define USB_R2_P30_CR_WRITE BIT(17)
|
||||
#define USB_R2_P30_CR_CAP_ADDR BIT(18)
|
||||
#define USB_R2_P30_CR_CAP_DATA BIT(19)
|
||||
#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
|
||||
#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
|
||||
|
||||
#define USB_R3 0x0c
|
||||
#define USB_R3_P30_SSC_ENABLE BIT(0)
|
||||
#define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
|
||||
#define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
|
||||
#define USB_R3_P30_REF_SSP_EN BIT(13)
|
||||
#define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16)
|
||||
#define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19)
|
||||
#define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24)
|
||||
|
||||
#define USB_R4 0x10
|
||||
#define USB_R4_P21_PORT_RESET_0 BIT(0)
|
||||
#define USB_R4_P21_SLEEP_M0 BIT(1)
|
||||
#define USB_R4_MEM_PD_MASK GENMASK(3, 2)
|
||||
#define USB_R4_P21_ONLY BIT(4)
|
||||
|
||||
#define USB_R5 0x14
|
||||
#define USB_R5_ID_DIG_SYNC BIT(0)
|
||||
#define USB_R5_ID_DIG_REG BIT(1)
|
||||
#define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
|
||||
#define USB_R5_ID_DIG_EN_0 BIT(4)
|
||||
#define USB_R5_ID_DIG_EN_1 BIT(5)
|
||||
#define USB_R5_ID_DIG_CURR BIT(6)
|
||||
#define USB_R5_ID_DIG_IRQ BIT(7)
|
||||
#define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
|
||||
#define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
|
||||
|
||||
/* read-only register */
|
||||
#define USB_R6 0x18
|
||||
#define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0)
|
||||
#define USB_R6_P30_CR_ACK BIT(16)
|
||||
|
||||
struct phy_meson_gxl_usb3_priv {
|
||||
struct regmap *regmap;
|
||||
enum phy_mode mode;
|
||||
struct clk *clk_phy;
|
||||
struct clk *clk_peripheral;
|
||||
struct reset_control *reset;
|
||||
};
|
||||
|
||||
static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = USB_R6,
|
||||
};
|
||||
|
||||
static int phy_meson_gxl_usb3_power_on(struct phy *phy)
|
||||
{
|
||||
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0,
|
||||
USB_R5_ID_DIG_EN_0);
|
||||
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1,
|
||||
USB_R5_ID_DIG_EN_1);
|
||||
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK,
|
||||
FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_gxl_usb3_power_off(struct phy *phy)
|
||||
{
|
||||
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, 0);
|
||||
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_gxl_usb3_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
switch (mode) {
|
||||
case PHY_MODE_USB_HOST:
|
||||
regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0);
|
||||
regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
|
||||
0);
|
||||
break;
|
||||
|
||||
case PHY_MODE_USB_DEVICE:
|
||||
regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT,
|
||||
USB_R0_U2D_ACT);
|
||||
regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
|
||||
USB_R4_P21_SLEEP_M0);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&phy->dev, "unsupported PHY mode %d\n", mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_gxl_usb3_init(struct phy *phy)
|
||||
{
|
||||
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
ret = reset_control_reset(priv->reset);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = clk_prepare_enable(priv->clk_phy);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = clk_prepare_enable(priv->clk_peripheral);
|
||||
if (ret)
|
||||
goto err_disable_clk_phy;
|
||||
|
||||
ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode, 0);
|
||||
if (ret)
|
||||
goto err_disable_clk_peripheral;
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
|
||||
FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_clk_peripheral:
|
||||
clk_disable_unprepare(priv->clk_peripheral);
|
||||
err_disable_clk_phy:
|
||||
clk_disable_unprepare(priv->clk_phy);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int phy_meson_gxl_usb3_exit(struct phy *phy)
|
||||
{
|
||||
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
clk_disable_unprepare(priv->clk_peripheral);
|
||||
clk_disable_unprepare(priv->clk_phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops phy_meson_gxl_usb3_ops = {
|
||||
.power_on = phy_meson_gxl_usb3_power_on,
|
||||
.power_off = phy_meson_gxl_usb3_power_off,
|
||||
.set_mode = phy_meson_gxl_usb3_set_mode,
|
||||
.init = phy_meson_gxl_usb3_init,
|
||||
.exit = phy_meson_gxl_usb3_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct phy_meson_gxl_usb3_priv *priv;
|
||||
struct resource *res;
|
||||
struct phy *phy;
|
||||
struct phy_provider *phy_provider;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(dev, base,
|
||||
&phy_meson_gxl_usb3_regmap_conf);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->clk_phy = devm_clk_get(dev, "phy");
|
||||
if (IS_ERR(priv->clk_phy))
|
||||
return PTR_ERR(priv->clk_phy);
|
||||
|
||||
priv->clk_peripheral = devm_clk_get(dev, "peripheral");
|
||||
if (IS_ERR(priv->clk_peripheral))
|
||||
return PTR_ERR(priv->clk_peripheral);
|
||||
|
||||
priv->reset = devm_reset_control_array_get_shared(dev);
|
||||
if (IS_ERR(priv->reset))
|
||||
return PTR_ERR(priv->reset);
|
||||
|
||||
/*
|
||||
* default to host mode as hardware defaults and/or boot-loader
|
||||
* behavior can result in this PHY starting up in device mode. this
|
||||
* default and the initialization in phy_meson_gxl_usb3_init ensure
|
||||
* that we reproducibly start in a known mode on all devices.
|
||||
*/
|
||||
priv->mode = PHY_MODE_USB_HOST;
|
||||
|
||||
phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
ret = PTR_ERR(phy);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy_set_drvdata(phy, priv);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id phy_meson_gxl_usb3_of_match[] = {
|
||||
{ .compatible = "amlogic,meson-gxl-usb3-phy", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
|
||||
|
||||
static struct platform_driver phy_meson_gxl_usb3_driver = {
|
||||
.probe = phy_meson_gxl_usb3_probe,
|
||||
.driver = {
|
||||
.name = "phy-meson-gxl-usb3",
|
||||
.of_match_table = phy_meson_gxl_usb3_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(phy_meson_gxl_usb3_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
|
||||
MODULE_DESCRIPTION("Meson GXL USB3 PHY and OTG detection driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -138,7 +138,7 @@ static int cdns_ti_probe(struct platform_device *pdev)
|
|||
error = pm_runtime_get_sync(dev);
|
||||
if (error < 0) {
|
||||
dev_err(dev, "pm_runtime_get_sync failed: %d\n", error);
|
||||
goto err_get;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* assert RESET */
|
||||
|
@ -185,7 +185,6 @@ static int cdns_ti_probe(struct platform_device *pdev)
|
|||
|
||||
err:
|
||||
pm_runtime_put_sync(data->dev);
|
||||
err_get:
|
||||
pm_runtime_disable(data->dev);
|
||||
|
||||
return error;
|
||||
|
|
|
@ -332,13 +332,6 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
|
|||
case TEST_K:
|
||||
case TEST_SE0_NAK:
|
||||
case TEST_PACKET:
|
||||
cdns3_ep0_complete_setup(priv_dev, 0, 1);
|
||||
/**
|
||||
* Little delay to give the controller some time
|
||||
* for sending status stage.
|
||||
* This time should be less then 3ms.
|
||||
*/
|
||||
mdelay(1);
|
||||
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
|
||||
USB_CMD_STMODE |
|
||||
USB_STS_TMODE_SEL(tmode - 1));
|
||||
|
|
|
@ -512,8 +512,8 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
|
|||
}
|
||||
|
||||
static struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
|
||||
struct cdns3_endpoint *priv_ep,
|
||||
struct cdns3_request *priv_req)
|
||||
struct cdns3_endpoint *priv_ep,
|
||||
struct cdns3_request *priv_req)
|
||||
{
|
||||
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
|
||||
priv_req->flags & REQUEST_INTERNAL) {
|
||||
|
@ -552,8 +552,8 @@ static struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_d
|
|||
}
|
||||
|
||||
static int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
|
||||
struct cdns3_endpoint *priv_ep,
|
||||
struct cdns3_request *priv_req)
|
||||
struct cdns3_endpoint *priv_ep,
|
||||
struct cdns3_request *priv_req)
|
||||
{
|
||||
int deferred = 0;
|
||||
|
||||
|
@ -1905,7 +1905,7 @@ static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
|
|||
}
|
||||
|
||||
static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
|
||||
struct cdns3_endpoint *priv_ep)
|
||||
struct cdns3_endpoint *priv_ep)
|
||||
{
|
||||
if (!priv_ep->use_streams || priv_dev->gadget.speed < USB_SPEED_SUPER)
|
||||
return;
|
||||
|
@ -1926,7 +1926,7 @@ static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
|
|||
}
|
||||
|
||||
static void cdns3_configure_dmult(struct cdns3_device *priv_dev,
|
||||
struct cdns3_endpoint *priv_ep)
|
||||
struct cdns3_endpoint *priv_ep)
|
||||
{
|
||||
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
|
||||
|
||||
|
@ -3069,6 +3069,7 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
|
|||
priv_dev->gadget.name = "usb-ss-gadget";
|
||||
priv_dev->gadget.sg_supported = 1;
|
||||
priv_dev->gadget.quirk_avoids_skb_reserve = 1;
|
||||
priv_dev->gadget.irq = cdns->dev_irq;
|
||||
|
||||
spin_lock_init(&priv_dev->lock);
|
||||
INIT_WORK(&priv_dev->pending_status_wq,
|
||||
|
|
|
@ -524,10 +524,25 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
|
|||
greset |= GRSTCTL_CSFTRST;
|
||||
dwc2_writel(hsotg, greset, GRSTCTL);
|
||||
|
||||
if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 10000)) {
|
||||
dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
|
||||
__func__);
|
||||
return -EBUSY;
|
||||
if ((hsotg->hw_params.snpsid & DWC2_CORE_REV_MASK) <
|
||||
(DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
|
||||
if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL,
|
||||
GRSTCTL_CSFTRST, 10000)) {
|
||||
dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST\n",
|
||||
__func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
} else {
|
||||
if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL,
|
||||
GRSTCTL_CSFTRST_DONE, 10000)) {
|
||||
dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST_DONE\n",
|
||||
__func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
greset = dwc2_readl(hsotg, GRSTCTL);
|
||||
greset &= ~GRSTCTL_CSFTRST;
|
||||
greset |= GRSTCTL_CSFTRST_DONE;
|
||||
dwc2_writel(hsotg, greset, GRSTCTL);
|
||||
}
|
||||
|
||||
/* Wait for AHB master IDLE state */
|
||||
|
|
|
@ -1103,8 +1103,10 @@ struct dwc2_hsotg {
|
|||
#define DWC2_CORE_REV_3_00a 0x4f54300a
|
||||
#define DWC2_CORE_REV_3_10a 0x4f54310a
|
||||
#define DWC2_CORE_REV_4_00a 0x4f54400a
|
||||
#define DWC2_CORE_REV_4_20a 0x4f54420a
|
||||
#define DWC2_FS_IOT_REV_1_00a 0x5531100a
|
||||
#define DWC2_HS_IOT_REV_1_00a 0x5532100a
|
||||
#define DWC2_CORE_REV_MASK 0x0000ffff
|
||||
|
||||
/* DWC OTG HW Core ID */
|
||||
#define DWC2_OTG_ID 0x4f540000
|
||||
|
@ -1309,6 +1311,8 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
|
|||
|
||||
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
|
||||
|
||||
int dwc2_check_core_version(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
* Common core Functions.
|
||||
* The following functions support managing the DWC_otg controller in either
|
||||
|
|
|
@ -416,10 +416,13 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
|||
if (ret && (ret != -ENOTSUPP))
|
||||
dev_err(hsotg->dev, "exit power_down failed\n");
|
||||
|
||||
/* Change to L0 state */
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
call_gadget(hsotg, resume);
|
||||
} else {
|
||||
/* Change to L0 state */
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
}
|
||||
/* Change to L0 state */
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
} else {
|
||||
if (hsotg->params.power_down)
|
||||
return;
|
||||
|
|
|
@ -126,6 +126,7 @@
|
|||
#define GRSTCTL HSOTG_REG(0x010)
|
||||
#define GRSTCTL_AHBIDLE BIT(31)
|
||||
#define GRSTCTL_DMAREQ BIT(30)
|
||||
#define GRSTCTL_CSFTRST_DONE BIT(29)
|
||||
#define GRSTCTL_TXFNUM_MASK (0x1f << 6)
|
||||
#define GRSTCTL_TXFNUM_SHIFT 6
|
||||
#define GRSTCTL_TXFNUM_LIMIT 0x1f
|
||||
|
|
|
@ -782,25 +782,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
|||
u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
|
||||
u32 grxfsiz;
|
||||
|
||||
/*
|
||||
* Attempt to ensure this device is really a DWC_otg Controller.
|
||||
* Read and verify the GSNPSID register contents. The value should be
|
||||
* 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
|
||||
*/
|
||||
|
||||
hw->snpsid = dwc2_readl(hsotg, GSNPSID);
|
||||
if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
|
||||
(hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
|
||||
(hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
|
||||
dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
|
||||
hw->snpsid);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
|
||||
hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
|
||||
hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
|
||||
|
||||
hwcfg1 = dwc2_readl(hsotg, GHWCFG1);
|
||||
hwcfg2 = dwc2_readl(hsotg, GHWCFG2);
|
||||
hwcfg3 = dwc2_readl(hsotg, GHWCFG3);
|
||||
|
|
|
@ -362,6 +362,37 @@ static bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg)
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check core version
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*
|
||||
*/
|
||||
int dwc2_check_core_version(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_hw_params *hw = &hsotg->hw_params;
|
||||
|
||||
/*
|
||||
* Attempt to ensure this device is really a DWC_otg Controller.
|
||||
* Read and verify the GSNPSID register contents. The value should be
|
||||
* 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
|
||||
*/
|
||||
|
||||
hw->snpsid = dwc2_readl(hsotg, GSNPSID);
|
||||
if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
|
||||
(hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
|
||||
(hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
|
||||
dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
|
||||
hw->snpsid);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
|
||||
hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
|
||||
hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
|
||||
* driver
|
||||
|
@ -444,6 +475,14 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
of_property_read_bool(dev->dev.of_node,
|
||||
"snps,need-phy-for-wake");
|
||||
|
||||
/*
|
||||
* Before performing any core related operations
|
||||
* check core version.
|
||||
*/
|
||||
retval = dwc2_check_core_version(hsotg);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* Reset before dwc2_get_hwparams() then it could get power-on real
|
||||
* reset value form registers.
|
||||
|
|
|
@ -85,7 +85,9 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
|
|||
* specified or set to OTG, then set the mode to peripheral.
|
||||
*/
|
||||
if (mode == USB_DR_MODE_OTG &&
|
||||
dwc->revision >= DWC3_REVISION_330A)
|
||||
(!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
|
||||
!device_property_read_bool(dwc->dev, "usb-role-switch")) &&
|
||||
!DWC3_VER_IS_PRIOR(DWC3, 330A))
|
||||
mode = USB_DR_MODE_PERIPHERAL;
|
||||
}
|
||||
|
||||
|
@ -121,17 +123,19 @@ static void __dwc3_set_mode(struct work_struct *work)
|
|||
if (dwc->dr_mode != USB_DR_MODE_OTG)
|
||||
return;
|
||||
|
||||
pm_runtime_get_sync(dwc->dev);
|
||||
|
||||
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
|
||||
dwc3_otg_update(dwc, 0);
|
||||
|
||||
if (!dwc->desired_dr_role)
|
||||
return;
|
||||
goto out;
|
||||
|
||||
if (dwc->desired_dr_role == dwc->current_dr_role)
|
||||
return;
|
||||
goto out;
|
||||
|
||||
if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
|
||||
return;
|
||||
goto out;
|
||||
|
||||
switch (dwc->current_dr_role) {
|
||||
case DWC3_GCTL_PRTCAP_HOST:
|
||||
|
@ -190,6 +194,9 @@ static void __dwc3_set_mode(struct work_struct *work)
|
|||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
pm_runtime_mark_last_busy(dwc->dev);
|
||||
pm_runtime_put_autosuspend(dwc->dev);
|
||||
}
|
||||
|
||||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
|
||||
|
@ -257,7 +264,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
|
|||
* take a little more than 50ms. Set the polling rate at 20ms
|
||||
* for 10 times instead.
|
||||
*/
|
||||
if (dwc3_is_usb31(dwc) && dwc->revision >= DWC3_USB31_REVISION_190A)
|
||||
if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
|
||||
retries = 10;
|
||||
|
||||
do {
|
||||
|
@ -265,8 +272,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
|
|||
if (!(reg & DWC3_DCTL_CSFTRST))
|
||||
goto done;
|
||||
|
||||
if (dwc3_is_usb31(dwc) &&
|
||||
dwc->revision >= DWC3_USB31_REVISION_190A)
|
||||
if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
|
||||
msleep(20);
|
||||
else
|
||||
udelay(1);
|
||||
|
@ -283,7 +289,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
|
|||
* is cleared, we must wait at least 50ms before accessing the PHY
|
||||
* domain (synchronization delay).
|
||||
*/
|
||||
if (dwc3_is_usb31(dwc) && dwc->revision <= DWC3_USB31_REVISION_180A)
|
||||
if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A))
|
||||
msleep(50);
|
||||
|
||||
return 0;
|
||||
|
@ -298,7 +304,7 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
|
|||
u32 reg;
|
||||
u32 dft;
|
||||
|
||||
if (dwc->revision < DWC3_REVISION_250A)
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
|
||||
return;
|
||||
|
||||
if (dwc->fladj == 0)
|
||||
|
@ -579,7 +585,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
|||
* will be '0' when the core is reset. Application needs to set it
|
||||
* to '1' after the core initialization is completed.
|
||||
*/
|
||||
if (dwc->revision > DWC3_REVISION_194A)
|
||||
if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
|
||||
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
|
||||
|
||||
/*
|
||||
|
@ -670,7 +676,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
|||
* be '0' when the core is reset. Application needs to set it to
|
||||
* '1' after the core initialization is completed.
|
||||
*/
|
||||
if (dwc->revision > DWC3_REVISION_194A)
|
||||
if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
|
||||
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
|
||||
/*
|
||||
|
@ -719,15 +725,13 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
|
|||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
|
||||
dwc->ip = DWC3_GSNPS_ID(reg);
|
||||
|
||||
/* This should read as U3 followed by revision number */
|
||||
if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
|
||||
/* Detected DWC_usb3 IP */
|
||||
if (DWC3_IP_IS(DWC3)) {
|
||||
dwc->revision = reg;
|
||||
} else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) {
|
||||
/* Detected DWC_usb31 IP */
|
||||
} else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
|
||||
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
|
||||
dwc->revision |= DWC3_REVISION_IS_DWC31;
|
||||
dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
|
||||
} else {
|
||||
return false;
|
||||
|
@ -760,8 +764,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
|
|||
*/
|
||||
if ((dwc->dr_mode == USB_DR_MODE_HOST ||
|
||||
dwc->dr_mode == USB_DR_MODE_OTG) &&
|
||||
(dwc->revision >= DWC3_REVISION_210A &&
|
||||
dwc->revision <= DWC3_REVISION_250A))
|
||||
DWC3_VER_IS_WITHIN(DWC3, 210A, 250A))
|
||||
reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
|
||||
else
|
||||
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
|
||||
|
@ -804,7 +807,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
|
|||
* and falls back to high-speed mode which causes
|
||||
* the device to enter a Connect/Disconnect loop
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_190A)
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 190A))
|
||||
reg |= DWC3_GCTL_U2RSTECN;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
@ -957,7 +960,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
goto err0a;
|
||||
|
||||
if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
|
||||
dwc->revision > DWC3_REVISION_194A) {
|
||||
!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) {
|
||||
if (!dwc->dis_u3_susphy_quirk) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
||||
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
|
||||
|
@ -1004,20 +1007,20 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
* the DWC_usb3 controller. It is NOT available in the
|
||||
* DWC_usb31 controller.
|
||||
*/
|
||||
if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) {
|
||||
if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
|
||||
reg |= DWC3_GUCTL2_RST_ACTBITLATER;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
|
||||
}
|
||||
|
||||
if (dwc->revision >= DWC3_REVISION_250A) {
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
|
||||
|
||||
/*
|
||||
* Enable hardware control of sending remote wakeup
|
||||
* in HS when the device is in the L1 state.
|
||||
*/
|
||||
if (dwc->revision >= DWC3_REVISION_290A)
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 290A))
|
||||
reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
|
||||
|
||||
if (dwc->dis_tx_ipgap_linecheck_quirk)
|
||||
|
@ -1049,7 +1052,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
* Must config both number of packets and max burst settings to enable
|
||||
* RX and/or TX threshold.
|
||||
*/
|
||||
if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) {
|
||||
if (!DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) {
|
||||
u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
|
||||
u8 rx_maxburst = dwc->rx_max_burst_prd;
|
||||
u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
|
||||
|
@ -1371,10 +1374,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
/* check whether the core supports IMOD */
|
||||
bool dwc3_has_imod(struct dwc3 *dwc)
|
||||
{
|
||||
return ((dwc3_is_usb3(dwc) &&
|
||||
dwc->revision >= DWC3_REVISION_300A) ||
|
||||
(dwc3_is_usb31(dwc) &&
|
||||
dwc->revision >= DWC3_USB31_REVISION_120A));
|
||||
return DWC3_VER_IS_WITHIN(DWC3, 300A, ANY) ||
|
||||
DWC3_VER_IS_WITHIN(DWC31, 120A, ANY) ||
|
||||
DWC3_IP_IS(DWC32);
|
||||
}
|
||||
|
||||
static void dwc3_check_params(struct dwc3 *dwc)
|
||||
|
@ -1395,7 +1397,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
|
|||
* affected version.
|
||||
*/
|
||||
if (!dwc->imod_interval &&
|
||||
(dwc->revision == DWC3_REVISION_300A))
|
||||
DWC3_VER_IS(DWC3, 300A))
|
||||
dwc->imod_interval = 1;
|
||||
|
||||
/* Check the maximum_speed parameter */
|
||||
|
@ -1417,7 +1419,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
|
|||
/*
|
||||
* default to superspeed plus if we are capable.
|
||||
*/
|
||||
if (dwc3_is_usb31(dwc) &&
|
||||
if ((DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) &&
|
||||
(DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
|
||||
DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
|
||||
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* core.h - DesignWare USB3 DRD Core Header
|
||||
*
|
||||
|
@ -69,6 +69,7 @@
|
|||
#define DWC3_GEVNTCOUNT_EHB BIT(31)
|
||||
#define DWC3_GSNPSID_MASK 0xffff0000
|
||||
#define DWC3_GSNPSREV_MASK 0xffff
|
||||
#define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16)
|
||||
|
||||
/* DWC3 registers memory space boundries */
|
||||
#define DWC3_XHCI_REGS_START 0x0
|
||||
|
@ -365,6 +366,9 @@
|
|||
#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10)
|
||||
#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
|
||||
|
||||
/* DWC_usb32 only */
|
||||
#define DWC3_GHWPARAMS6_MDWIDTH(n) ((n) & (0x3 << 8))
|
||||
|
||||
/* Global HWPARAMS7 Register */
|
||||
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
|
||||
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
|
||||
|
@ -491,6 +495,7 @@
|
|||
#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
|
||||
#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
|
||||
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
|
||||
#define DWC3_DGCMD_SET_ENDPOINT_PRIME 0x0d
|
||||
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
|
||||
|
||||
#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
|
||||
|
@ -697,6 +702,10 @@ struct dwc3_ep {
|
|||
#define DWC3_EP_END_TRANSFER_PENDING BIT(4)
|
||||
#define DWC3_EP_PENDING_REQUEST BIT(5)
|
||||
#define DWC3_EP_DELAY_START BIT(6)
|
||||
#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
|
||||
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
|
||||
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
|
||||
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
|
||||
|
||||
/* This last one is specific to EP0 */
|
||||
#define DWC3_EP0_DIR_IN BIT(31)
|
||||
|
@ -949,7 +958,8 @@ struct dwc3_scratchpad_array {
|
|||
* @nr_scratch: number of scratch buffers
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
* @revision: revision register contents
|
||||
* @ip: controller's ID
|
||||
* @revision: controller's version of an IP
|
||||
* @version_type: VERSIONTYPE register contents, a sub release of a revision
|
||||
* @dr_mode: requested mode of operation
|
||||
* @current_dr_role: current role of operation when in dual-role mode
|
||||
|
@ -1110,15 +1120,15 @@ struct dwc3 {
|
|||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
|
||||
/*
|
||||
* All 3.1 IP version constants are greater than the 3.0 IP
|
||||
* version constants. This works for most version checks in
|
||||
* dwc3. However, in the future, this may not apply as
|
||||
* features may be developed on newer versions of the 3.0 IP
|
||||
* that are not in the 3.1 IP.
|
||||
*/
|
||||
u32 ip;
|
||||
|
||||
#define DWC3_IP 0x5533
|
||||
#define DWC31_IP 0x3331
|
||||
#define DWC32_IP 0x3332
|
||||
|
||||
u32 revision;
|
||||
|
||||
#define DWC3_REVISION_ANY 0x0
|
||||
#define DWC3_REVISION_173A 0x5533173a
|
||||
#define DWC3_REVISION_175A 0x5533175a
|
||||
#define DWC3_REVISION_180A 0x5533180a
|
||||
|
@ -1143,20 +1153,20 @@ struct dwc3 {
|
|||
#define DWC3_REVISION_310A 0x5533310a
|
||||
#define DWC3_REVISION_330A 0x5533330a
|
||||
|
||||
/*
|
||||
* NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
|
||||
* just so dwc31 revisions are always larger than dwc3.
|
||||
*/
|
||||
#define DWC3_REVISION_IS_DWC31 0x80000000
|
||||
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_180A (0x3138302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_190A (0x3139302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC31_REVISION_ANY 0x0
|
||||
#define DWC31_REVISION_110A 0x3131302a
|
||||
#define DWC31_REVISION_120A 0x3132302a
|
||||
#define DWC31_REVISION_160A 0x3136302a
|
||||
#define DWC31_REVISION_170A 0x3137302a
|
||||
#define DWC31_REVISION_180A 0x3138302a
|
||||
#define DWC31_REVISION_190A 0x3139302a
|
||||
|
||||
#define DWC32_REVISION_ANY 0x0
|
||||
#define DWC32_REVISION_100A 0x3130302a
|
||||
|
||||
u32 version_type;
|
||||
|
||||
#define DWC31_VERSIONTYPE_ANY 0x0
|
||||
#define DWC31_VERSIONTYPE_EA01 0x65613031
|
||||
#define DWC31_VERSIONTYPE_EA02 0x65613032
|
||||
#define DWC31_VERSIONTYPE_EA03 0x65613033
|
||||
|
@ -1298,6 +1308,10 @@ struct dwc3_event_depevt {
|
|||
#define DEPEVT_STREAMEVT_FOUND 1
|
||||
#define DEPEVT_STREAMEVT_NOTFOUND 2
|
||||
|
||||
/* Stream event parameter */
|
||||
#define DEPEVT_STREAM_PRIME 0xfffe
|
||||
#define DEPEVT_STREAM_NOSTREAM 0x0
|
||||
|
||||
/* Control-only Status */
|
||||
#define DEPEVT_STATUS_CONTROL_DATA 1
|
||||
#define DEPEVT_STATUS_CONTROL_STATUS 2
|
||||
|
@ -1400,17 +1414,26 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
|
|||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
|
||||
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
|
||||
|
||||
/* check whether we are on the DWC_usb3 core */
|
||||
static inline bool dwc3_is_usb3(struct dwc3 *dwc)
|
||||
{
|
||||
return !(dwc->revision & DWC3_REVISION_IS_DWC31);
|
||||
}
|
||||
#define DWC3_IP_IS(_ip) \
|
||||
(dwc->ip == _ip##_IP)
|
||||
|
||||
/* check whether we are on the DWC_usb31 core */
|
||||
static inline bool dwc3_is_usb31(struct dwc3 *dwc)
|
||||
{
|
||||
return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
|
||||
}
|
||||
#define DWC3_VER_IS(_ip, _ver) \
|
||||
(DWC3_IP_IS(_ip) && dwc->revision == _ip##_REVISION_##_ver)
|
||||
|
||||
#define DWC3_VER_IS_PRIOR(_ip, _ver) \
|
||||
(DWC3_IP_IS(_ip) && dwc->revision < _ip##_REVISION_##_ver)
|
||||
|
||||
#define DWC3_VER_IS_WITHIN(_ip, _from, _to) \
|
||||
(DWC3_IP_IS(_ip) && \
|
||||
dwc->revision >= _ip##_REVISION_##_from && \
|
||||
(!(_ip##_REVISION_##_to) || \
|
||||
dwc->revision <= _ip##_REVISION_##_to))
|
||||
|
||||
#define DWC3_VER_TYPE_IS_WITHIN(_ip, _ver, _from, _to) \
|
||||
(DWC3_VER_IS(_ip, _ver) && \
|
||||
dwc->version_type >= _ip##_VERSIONTYPE_##_from && \
|
||||
(!(_ip##_VERSIONTYPE_##_to) || \
|
||||
dwc->version_type <= _ip##_VERSIONTYPE_##_to))
|
||||
|
||||
bool dwc3_has_imod(struct dwc3 *dwc);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/**
|
||||
* debug.h - DesignWare USB3 DRD Controller Debug Header
|
||||
*
|
||||
|
@ -68,6 +68,8 @@ dwc3_gadget_generic_cmd_string(u8 cmd)
|
|||
return "All FIFO Flush";
|
||||
case DWC3_DGCMD_SET_ENDPOINT_NRDY:
|
||||
return "Set Endpoint NRDY";
|
||||
case DWC3_DGCMD_SET_ENDPOINT_PRIME:
|
||||
return "Set Endpoint Prime";
|
||||
case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
|
||||
return "Run SoC Bus Loopback Test";
|
||||
default:
|
||||
|
|
|
@ -635,13 +635,18 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
|
|||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
int mdwidth;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
|
||||
|
||||
/* Convert to bytes */
|
||||
val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
if (DWC3_IP_IS(DWC32))
|
||||
mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
|
||||
|
||||
val *= mdwidth;
|
||||
val >>= 3;
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
@ -654,13 +659,18 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
|
|||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
int mdwidth;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
|
||||
|
||||
/* Convert to bytes */
|
||||
val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
if (DWC3_IP_IS(DWC32))
|
||||
mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
|
||||
|
||||
val *= mdwidth;
|
||||
val >>= 3;
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
|
|
@ -56,7 +56,7 @@ static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
|
|||
spin_lock(&dwc->lock);
|
||||
if (dwc->otg_restart_host) {
|
||||
dwc3_otg_host_init(dwc);
|
||||
dwc->otg_restart_host = 0;
|
||||
dwc->otg_restart_host = false;
|
||||
}
|
||||
|
||||
spin_unlock(&dwc->lock);
|
||||
|
@ -82,7 +82,7 @@ static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
|
|||
|
||||
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
|
||||
!(reg & DWC3_OEVT_DEVICEMODE))
|
||||
dwc->otg_restart_host = 1;
|
||||
dwc->otg_restart_host = true;
|
||||
dwc3_writel(dwc->regs, DWC3_OEVT, reg);
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
@ -653,6 +653,6 @@ void dwc3_drd_exit(struct dwc3 *dwc)
|
|||
break;
|
||||
}
|
||||
|
||||
if (!dwc->edev)
|
||||
if (dwc->otg_irq)
|
||||
free_irq(dwc->otg_irq, dwc);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
/* USBSS register offsets */
|
||||
|
@ -34,6 +35,7 @@
|
|||
struct dwc3_keystone {
|
||||
struct device *dev;
|
||||
void __iomem *usbss;
|
||||
struct phy *usb3_phy;
|
||||
};
|
||||
|
||||
static inline u32 kdwc3_readl(void __iomem *base, u32 offset)
|
||||
|
@ -95,8 +97,38 @@ static int kdwc3_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(kdwc->usbss))
|
||||
return PTR_ERR(kdwc->usbss);
|
||||
|
||||
pm_runtime_enable(kdwc->dev);
|
||||
/* PSC dependency on AM65 needs SERDES0 to be powered before USB0 */
|
||||
kdwc->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
|
||||
if (IS_ERR(kdwc->usb3_phy)) {
|
||||
error = PTR_ERR(kdwc->usb3_phy);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev, "couldn't get usb3 phy: %d\n", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
phy_pm_runtime_get_sync(kdwc->usb3_phy);
|
||||
|
||||
error = phy_reset(kdwc->usb3_phy);
|
||||
if (error < 0) {
|
||||
dev_err(dev, "usb3 phy reset failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = phy_init(kdwc->usb3_phy);
|
||||
if (error < 0) {
|
||||
dev_err(dev, "usb3 phy init failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = phy_power_on(kdwc->usb3_phy);
|
||||
if (error < 0) {
|
||||
dev_err(dev, "usb3 phy power on failed: %d\n", error);
|
||||
phy_exit(kdwc->usb3_phy);
|
||||
return error;
|
||||
}
|
||||
|
||||
pm_runtime_enable(kdwc->dev);
|
||||
error = pm_runtime_get_sync(kdwc->dev);
|
||||
if (error < 0) {
|
||||
dev_err(kdwc->dev, "pm_runtime_get_sync failed, error %d\n",
|
||||
|
@ -138,6 +170,9 @@ static int kdwc3_probe(struct platform_device *pdev)
|
|||
err_irq:
|
||||
pm_runtime_put_sync(kdwc->dev);
|
||||
pm_runtime_disable(kdwc->dev);
|
||||
phy_power_off(kdwc->usb3_phy);
|
||||
phy_exit(kdwc->usb3_phy);
|
||||
phy_pm_runtime_put_sync(kdwc->usb3_phy);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -163,6 +198,10 @@ static int kdwc3_remove(struct platform_device *pdev)
|
|||
pm_runtime_put_sync(kdwc->dev);
|
||||
pm_runtime_disable(kdwc->dev);
|
||||
|
||||
phy_power_off(kdwc->usb3_phy);
|
||||
phy_exit(kdwc->usb3_phy);
|
||||
phy_pm_runtime_put_sync(kdwc->usb3_phy);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include <linux/usb/role.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* USB2 Ports Control Registers */
|
||||
/* USB2 Ports Control Registers, offsets are per-port */
|
||||
|
||||
#define U2P_REG_SIZE 0x20
|
||||
|
||||
|
@ -50,14 +50,16 @@
|
|||
|
||||
/* USB Glue Control Registers */
|
||||
|
||||
#define USB_R0 0x80
|
||||
#define G12A_GLUE_OFFSET 0x80
|
||||
|
||||
#define USB_R0 0x00
|
||||
#define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
|
||||
#define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
|
||||
#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
|
||||
#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
|
||||
#define USB_R0_U2D_ACT BIT(31)
|
||||
|
||||
#define USB_R1 0x84
|
||||
#define USB_R1 0x04
|
||||
#define USB_R1_U3H_BIGENDIAN_GS BIT(0)
|
||||
#define USB_R1_U3H_PME_ENABLE BIT(1)
|
||||
#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2)
|
||||
|
@ -69,23 +71,23 @@
|
|||
#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
|
||||
#define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
|
||||
|
||||
#define USB_R2 0x88
|
||||
#define USB_R2 0x08
|
||||
#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
|
||||
#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
|
||||
|
||||
#define USB_R3 0x8c
|
||||
#define USB_R3 0x0c
|
||||
#define USB_R3_P30_SSC_ENABLE BIT(0)
|
||||
#define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
|
||||
#define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
|
||||
#define USB_R3_P30_REF_SSP_EN BIT(13)
|
||||
|
||||
#define USB_R4 0x90
|
||||
#define USB_R4 0x10
|
||||
#define USB_R4_P21_PORT_RESET_0 BIT(0)
|
||||
#define USB_R4_P21_SLEEP_M0 BIT(1)
|
||||
#define USB_R4_MEM_PD_MASK GENMASK(3, 2)
|
||||
#define USB_R4_P21_ONLY BIT(4)
|
||||
|
||||
#define USB_R5 0x94
|
||||
#define USB_R5 0x14
|
||||
#define USB_R5_ID_DIG_SYNC BIT(0)
|
||||
#define USB_R5_ID_DIG_REG BIT(1)
|
||||
#define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
|
||||
|
@ -96,15 +98,12 @@
|
|||
#define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
|
||||
#define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
|
||||
|
||||
enum {
|
||||
USB2_HOST_PHY = 0,
|
||||
USB2_OTG_PHY,
|
||||
USB3_HOST_PHY,
|
||||
PHY_COUNT,
|
||||
};
|
||||
#define PHY_COUNT 3
|
||||
#define USB2_OTG_PHY 1
|
||||
|
||||
static const char *phy_names[PHY_COUNT] = {
|
||||
"usb2-phy0", "usb2-phy1", "usb3-phy0",
|
||||
static struct clk_bulk_data meson_gxl_clocks[] = {
|
||||
{ .id = "usb_ctrl" },
|
||||
{ .id = "ddr" },
|
||||
};
|
||||
|
||||
static struct clk_bulk_data meson_g12a_clocks[] = {
|
||||
|
@ -117,27 +116,133 @@ static struct clk_bulk_data meson_a1_clocks[] = {
|
|||
{ .id = "xtal_usb_ctrl" },
|
||||
};
|
||||
|
||||
static const char *meson_gxm_phy_names[] = {
|
||||
"usb2-phy0", "usb2-phy1", "usb2-phy2",
|
||||
};
|
||||
|
||||
static const char *meson_g12a_phy_names[] = {
|
||||
"usb2-phy0", "usb2-phy1", "usb3-phy0",
|
||||
};
|
||||
|
||||
/*
|
||||
* Amlogic A1 has a single physical PHY, in slot 1, but still has the
|
||||
* two U2 PHY controls register blocks like G12A.
|
||||
* Handling the first PHY on slot 1 would need a large amount of code
|
||||
* changes, and the current management is generic enough to handle it
|
||||
* correctly when only the "usb2-phy1" phy is specified on-par with the
|
||||
* DT bindings.
|
||||
*/
|
||||
static const char *meson_a1_phy_names[] = {
|
||||
"usb2-phy0", "usb2-phy1"
|
||||
};
|
||||
|
||||
struct dwc3_meson_g12a;
|
||||
|
||||
struct dwc3_meson_g12a_drvdata {
|
||||
bool otg_switch_supported;
|
||||
bool otg_phy_host_port_disable;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
const char **phy_names;
|
||||
int num_phys;
|
||||
int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base);
|
||||
int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i,
|
||||
enum phy_mode mode);
|
||||
int (*set_phy_mode)(struct dwc3_meson_g12a *priv, int i,
|
||||
enum phy_mode mode);
|
||||
int (*usb_init)(struct dwc3_meson_g12a *priv);
|
||||
int (*usb_post_init)(struct dwc3_meson_g12a *priv);
|
||||
};
|
||||
|
||||
static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
|
||||
void __iomem *base);
|
||||
static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
|
||||
void __iomem *base);
|
||||
|
||||
static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
|
||||
enum phy_mode mode);
|
||||
static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
|
||||
enum phy_mode mode);
|
||||
|
||||
static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
|
||||
int i, enum phy_mode mode);
|
||||
static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
|
||||
int i, enum phy_mode mode);
|
||||
|
||||
static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv);
|
||||
static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv);
|
||||
|
||||
static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
|
||||
|
||||
/*
|
||||
* For GXL and GXM SoCs:
|
||||
* USB Phy muxing between the DWC2 Device controller and the DWC3 Host
|
||||
* controller is buggy when switching from Device to Host when USB port
|
||||
* is unpopulated, it causes the DWC3 to hard crash.
|
||||
* When populated (including OTG switching with ID pin), the switch works
|
||||
* like a charm like on the G12A platforms.
|
||||
* In order to still switch from Host to Device on an USB Type-A port,
|
||||
* an U2_PORT_DISABLE bit has been added to disconnect the DWC3 Host
|
||||
* controller from the port, but when used the DWC3 controller must be
|
||||
* reset to recover usage of the port.
|
||||
*/
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
|
||||
.otg_switch_supported = true,
|
||||
.otg_phy_host_port_disable = true,
|
||||
.clks = meson_gxl_clocks,
|
||||
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
|
||||
.phy_names = meson_a1_phy_names,
|
||||
.num_phys = ARRAY_SIZE(meson_a1_phy_names),
|
||||
.setup_regmaps = dwc3_meson_gxl_setup_regmaps,
|
||||
.usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
|
||||
.set_phy_mode = dwc3_meson_gxl_set_phy_mode,
|
||||
.usb_init = dwc3_meson_gxl_usb_init,
|
||||
.usb_post_init = dwc3_meson_gxl_usb_post_init,
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
|
||||
.otg_switch_supported = true,
|
||||
.otg_phy_host_port_disable = true,
|
||||
.clks = meson_gxl_clocks,
|
||||
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
|
||||
.phy_names = meson_gxm_phy_names,
|
||||
.num_phys = ARRAY_SIZE(meson_gxm_phy_names),
|
||||
.setup_regmaps = dwc3_meson_gxl_setup_regmaps,
|
||||
.usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
|
||||
.set_phy_mode = dwc3_meson_gxl_set_phy_mode,
|
||||
.usb_init = dwc3_meson_gxl_usb_init,
|
||||
.usb_post_init = dwc3_meson_gxl_usb_post_init,
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
|
||||
.otg_switch_supported = true,
|
||||
.clks = meson_g12a_clocks,
|
||||
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
|
||||
.phy_names = meson_g12a_phy_names,
|
||||
.num_phys = ARRAY_SIZE(meson_g12a_phy_names),
|
||||
.setup_regmaps = dwc3_meson_g12a_setup_regmaps,
|
||||
.usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
|
||||
.set_phy_mode = dwc3_meson_g12a_set_phy_mode,
|
||||
.usb_init = dwc3_meson_g12a_usb_init,
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata a1_drvdata = {
|
||||
.otg_switch_supported = false,
|
||||
.clks = meson_a1_clocks,
|
||||
.num_clks = ARRAY_SIZE(meson_a1_clocks),
|
||||
.phy_names = meson_a1_phy_names,
|
||||
.num_phys = ARRAY_SIZE(meson_a1_phy_names),
|
||||
.setup_regmaps = dwc3_meson_g12a_setup_regmaps,
|
||||
.usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
|
||||
.set_phy_mode = dwc3_meson_g12a_set_phy_mode,
|
||||
.usb_init = dwc3_meson_g12a_usb_init,
|
||||
};
|
||||
|
||||
struct dwc3_meson_g12a {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct regmap *u2p_regmap[PHY_COUNT];
|
||||
struct regmap *usb_glue_regmap;
|
||||
struct reset_control *reset;
|
||||
struct phy *phys[PHY_COUNT];
|
||||
enum usb_dr_mode otg_mode;
|
||||
|
@ -150,49 +255,78 @@ struct dwc3_meson_g12a {
|
|||
const struct dwc3_meson_g12a_drvdata *drvdata;
|
||||
};
|
||||
|
||||
static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
|
||||
int i, enum phy_mode mode)
|
||||
static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
|
||||
int i, enum phy_mode mode)
|
||||
{
|
||||
return phy_set_mode(priv->phys[i], mode);
|
||||
}
|
||||
|
||||
static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
|
||||
enum phy_mode mode)
|
||||
{
|
||||
/* On GXL PHY must be started in device mode for DWC2 init */
|
||||
return priv->drvdata->set_phy_mode(priv, i,
|
||||
(i == USB2_OTG_PHY) ? PHY_MODE_USB_DEVICE
|
||||
: PHY_MODE_USB_HOST);
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
|
||||
int i, enum phy_mode mode)
|
||||
{
|
||||
if (mode == PHY_MODE_USB_HOST)
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
|
||||
U2P_R0_HOST_DEVICE,
|
||||
U2P_R0_HOST_DEVICE);
|
||||
else
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
|
||||
U2P_R0_HOST_DEVICE, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
|
||||
static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
|
||||
enum phy_mode mode)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
|
||||
priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
|
||||
else
|
||||
priv->otg_phy_mode = PHY_MODE_USB_HOST;
|
||||
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
|
||||
U2P_R0_POWER_ON_RESET,
|
||||
U2P_R0_POWER_ON_RESET);
|
||||
|
||||
for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
|
||||
if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
|
||||
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
|
||||
|
||||
ret = priv->drvdata->set_phy_mode(priv, i, mode);
|
||||
} else
|
||||
ret = priv->drvdata->set_phy_mode(priv, i,
|
||||
PHY_MODE_USB_HOST);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
|
||||
U2P_R0_POWER_ON_RESET, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv,
|
||||
enum phy_mode mode)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < priv->drvdata->num_phys; ++i) {
|
||||
if (!priv->phys[i])
|
||||
continue;
|
||||
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_POWER_ON_RESET,
|
||||
U2P_R0_POWER_ON_RESET);
|
||||
if (!strstr(priv->drvdata->phy_names[i], "usb2"))
|
||||
continue;
|
||||
|
||||
if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
|
||||
regmap_update_bits(priv->regmap,
|
||||
U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
|
||||
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, i,
|
||||
priv->otg_phy_mode);
|
||||
} else
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, i,
|
||||
PHY_MODE_USB_HOST);
|
||||
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_POWER_ON_RESET, 0);
|
||||
ret = priv->drvdata->usb2_init_phy(priv, i, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -200,7 +334,7 @@ static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
|
|||
|
||||
static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
regmap_update_bits(priv->regmap, USB_R3,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R3,
|
||||
USB_R3_P30_SSC_RANGE_MASK |
|
||||
USB_R3_P30_REF_SSP_EN,
|
||||
USB_R3_P30_SSC_ENABLE |
|
||||
|
@ -208,61 +342,77 @@ static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
|
|||
USB_R3_P30_REF_SSP_EN);
|
||||
udelay(2);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R2,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R2,
|
||||
USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
|
||||
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R2,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R2,
|
||||
USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
|
||||
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
|
||||
|
||||
udelay(2);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R1,
|
||||
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
|
||||
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R1,
|
||||
USB_R1_P30_PCS_TX_SWING_FULL_MASK,
|
||||
FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
|
||||
}
|
||||
|
||||
static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv)
|
||||
static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv,
|
||||
enum phy_mode mode)
|
||||
{
|
||||
if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) {
|
||||
regmap_update_bits(priv->regmap, USB_R0,
|
||||
if (mode == PHY_MODE_USB_DEVICE) {
|
||||
if (priv->otg_mode != USB_DR_MODE_OTG &&
|
||||
priv->drvdata->otg_phy_host_port_disable)
|
||||
/* Isolate the OTG PHY port from the Host Controller */
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R1,
|
||||
USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
|
||||
FIELD_PREP(USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
|
||||
BIT(USB2_OTG_PHY)));
|
||||
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R0,
|
||||
USB_R0_U2D_ACT, USB_R0_U2D_ACT);
|
||||
regmap_update_bits(priv->regmap, USB_R0,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R0,
|
||||
USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
|
||||
regmap_update_bits(priv->regmap, USB_R4,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R4,
|
||||
USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
|
||||
} else {
|
||||
regmap_update_bits(priv->regmap, USB_R0,
|
||||
if (priv->otg_mode != USB_DR_MODE_OTG &&
|
||||
priv->drvdata->otg_phy_host_port_disable) {
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R1,
|
||||
USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0);
|
||||
msleep(500);
|
||||
}
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R0,
|
||||
USB_R0_U2D_ACT, 0);
|
||||
regmap_update_bits(priv->regmap, USB_R4,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R4,
|
||||
USB_R4_P21_SLEEP_M0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
|
||||
static int dwc3_meson_g12a_usb_init_glue(struct dwc3_meson_g12a *priv,
|
||||
enum phy_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dwc3_meson_g12a_usb2_init(priv);
|
||||
ret = dwc3_meson_g12a_usb2_init(priv, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R1,
|
||||
USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
|
||||
FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R5,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
|
||||
USB_R5_ID_DIG_EN_0,
|
||||
USB_R5_ID_DIG_EN_0);
|
||||
regmap_update_bits(priv->regmap, USB_R5,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
|
||||
USB_R5_ID_DIG_EN_1,
|
||||
USB_R5_ID_DIG_EN_1);
|
||||
regmap_update_bits(priv->regmap, USB_R5,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
|
||||
USB_R5_ID_DIG_TH_MASK,
|
||||
FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
|
||||
|
||||
|
@ -270,12 +420,13 @@ static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
|
|||
if (priv->usb3_ports)
|
||||
dwc3_meson_g12a_usb3_init(priv);
|
||||
|
||||
dwc3_meson_g12a_usb_otg_apply_mode(priv);
|
||||
dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
|
||||
static const struct regmap_config phy_meson_g12a_usb_glue_regmap_conf = {
|
||||
.name = "usb-glue",
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
|
@ -284,17 +435,19 @@ static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
|
|||
|
||||
static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
const char *phy_name;
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]);
|
||||
for (i = 0 ; i < priv->drvdata->num_phys ; ++i) {
|
||||
phy_name = priv->drvdata->phy_names[i];
|
||||
priv->phys[i] = devm_phy_optional_get(priv->dev, phy_name);
|
||||
if (!priv->phys[i])
|
||||
continue;
|
||||
|
||||
if (IS_ERR(priv->phys[i]))
|
||||
return PTR_ERR(priv->phys[i]);
|
||||
|
||||
if (i == USB3_HOST_PHY)
|
||||
if (strstr(phy_name, "usb3"))
|
||||
priv->usb3_ports++;
|
||||
else
|
||||
priv->usb2_ports++;
|
||||
|
@ -310,7 +463,7 @@ static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
|
|||
{
|
||||
u32 reg;
|
||||
|
||||
regmap_read(priv->regmap, USB_R5, ®);
|
||||
regmap_read(priv->usb_glue_regmap, USB_R5, ®);
|
||||
|
||||
if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
|
||||
return PHY_MODE_USB_DEVICE;
|
||||
|
@ -342,9 +495,11 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
|
|||
|
||||
priv->otg_phy_mode = mode;
|
||||
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
|
||||
ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dwc3_meson_g12a_usb_otg_apply_mode(priv);
|
||||
dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -364,6 +519,13 @@ static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
|
|||
if (mode == priv->otg_phy_mode)
|
||||
return 0;
|
||||
|
||||
if (priv->drvdata->otg_phy_host_port_disable)
|
||||
dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\
|
||||
"SoC, when manual switching from "\
|
||||
"Host to device, DWC3 controller "\
|
||||
"will need to be resetted in order "\
|
||||
"to recover usage of the Host port");
|
||||
|
||||
return dwc3_meson_g12a_otg_mode_set(priv, mode);
|
||||
}
|
||||
|
||||
|
@ -386,7 +548,8 @@ static irqreturn_t dwc3_meson_g12a_irq_thread(int irq, void *data)
|
|||
dev_warn(priv->dev, "Failed to switch OTG mode\n");
|
||||
}
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
|
||||
USB_R5_ID_DIG_IRQ, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -421,7 +584,7 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
|
|||
|
||||
if (priv->otg_mode == USB_DR_MODE_OTG) {
|
||||
/* Ack irq before registering */
|
||||
regmap_update_bits(priv->regmap, USB_R5,
|
||||
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
|
||||
USB_R5_ID_DIG_IRQ, 0);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
@ -457,6 +620,77 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
|
||||
void __iomem *base)
|
||||
{
|
||||
/* GXL controls the PHY mode in the PHY registers unlike G12A */
|
||||
priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base,
|
||||
&phy_meson_g12a_usb_glue_regmap_conf);
|
||||
if (IS_ERR(priv->usb_glue_regmap))
|
||||
return PTR_ERR(priv->usb_glue_regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
|
||||
void __iomem *base)
|
||||
{
|
||||
int i;
|
||||
|
||||
priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev,
|
||||
base + G12A_GLUE_OFFSET,
|
||||
&phy_meson_g12a_usb_glue_regmap_conf);
|
||||
if (IS_ERR(priv->usb_glue_regmap))
|
||||
return PTR_ERR(priv->usb_glue_regmap);
|
||||
|
||||
/* Create a regmap for each USB2 PHY control register set */
|
||||
for (i = 0; i < priv->usb2_ports; i++) {
|
||||
struct regmap_config u2p_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = U2P_R1,
|
||||
};
|
||||
|
||||
u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL,
|
||||
"u2p-%d", i);
|
||||
if (!u2p_regmap_config.name)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev,
|
||||
base + (i * U2P_REG_SIZE),
|
||||
&u2p_regmap_config);
|
||||
if (IS_ERR(priv->u2p_regmap[i]))
|
||||
return PTR_ERR(priv->u2p_regmap[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode);
|
||||
}
|
||||
|
||||
static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
return dwc3_meson_g12a_usb_init_glue(priv, PHY_MODE_USB_DEVICE);
|
||||
}
|
||||
|
||||
static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY,
|
||||
priv->otg_phy_mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dwc3_meson_g12a_usb_otg_apply_mode(priv, priv->otg_phy_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv;
|
||||
|
@ -473,10 +707,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(dev, base,
|
||||
&phy_meson_g12a_usb3_regmap_conf);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
priv->drvdata = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
priv->dev = dev;
|
||||
ret = priv->drvdata->setup_regmaps(priv, base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->vbus = devm_regulator_get_optional(dev, "vbus");
|
||||
if (IS_ERR(priv->vbus)) {
|
||||
|
@ -485,8 +721,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
|||
priv->vbus = NULL;
|
||||
}
|
||||
|
||||
priv->drvdata = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
ret = devm_clk_bulk_get(dev,
|
||||
priv->drvdata->num_clks,
|
||||
priv->drvdata->clks);
|
||||
|
@ -499,9 +733,8 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
priv->dev = dev;
|
||||
|
||||
priv->reset = devm_reset_control_get(dev, NULL);
|
||||
priv->reset = devm_reset_control_get_shared(dev, NULL);
|
||||
if (IS_ERR(priv->reset)) {
|
||||
ret = PTR_ERR(priv->reset);
|
||||
dev_err(dev, "failed to get device reset, err=%d\n", ret);
|
||||
|
@ -525,7 +758,14 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
|||
/* Get dr_mode */
|
||||
priv->otg_mode = usb_get_dr_mode(dev);
|
||||
|
||||
dwc3_meson_g12a_usb_init(priv);
|
||||
if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
|
||||
priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
|
||||
else
|
||||
priv->otg_phy_mode = PHY_MODE_USB_HOST;
|
||||
|
||||
ret = priv->drvdata->usb_init(priv);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
|
||||
/* Init PHYs */
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
|
@ -541,6 +781,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
|||
goto err_phys_exit;
|
||||
}
|
||||
|
||||
if (priv->drvdata->usb_post_init) {
|
||||
ret = priv->drvdata->usb_post_init(priv);
|
||||
if (ret)
|
||||
goto err_phys_power;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret)
|
||||
goto err_phys_power;
|
||||
|
@ -642,7 +888,9 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
|
|||
|
||||
reset_control_deassert(priv->reset);
|
||||
|
||||
dwc3_meson_g12a_usb_init(priv);
|
||||
ret = priv->drvdata->usb_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Init PHYs */
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
|
@ -674,6 +922,14 @@ static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
|
|||
};
|
||||
|
||||
static const struct of_device_id dwc3_meson_g12a_match[] = {
|
||||
{
|
||||
.compatible = "amlogic,meson-gxl-usb-ctrl",
|
||||
.data = &gxl_drvdata,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-gxm-usb-ctrl",
|
||||
.data = &gxm_drvdata,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-g12a-usb-ctrl",
|
||||
.data = &g12a_drvdata,
|
||||
|
|
|
@ -27,7 +27,6 @@ struct dwc3_of_simple {
|
|||
struct clk_bulk_data *clks;
|
||||
int num_clocks;
|
||||
struct reset_control *resets;
|
||||
bool pulse_resets;
|
||||
bool need_reset;
|
||||
};
|
||||
|
||||
|
@ -38,7 +37,6 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
|
|||
struct device_node *np = dev->of_node;
|
||||
|
||||
int ret;
|
||||
bool shared_resets = false;
|
||||
|
||||
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
|
||||
if (!simple)
|
||||
|
@ -54,13 +52,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
|
|||
if (of_device_is_compatible(np, "rockchip,rk3399-dwc3"))
|
||||
simple->need_reset = true;
|
||||
|
||||
if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") ||
|
||||
of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) {
|
||||
shared_resets = true;
|
||||
simple->pulse_resets = true;
|
||||
}
|
||||
|
||||
simple->resets = of_reset_control_array_get(np, shared_resets, true,
|
||||
simple->resets = of_reset_control_array_get(np, false, true,
|
||||
true);
|
||||
if (IS_ERR(simple->resets)) {
|
||||
ret = PTR_ERR(simple->resets);
|
||||
|
@ -68,15 +60,9 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (simple->pulse_resets) {
|
||||
ret = reset_control_reset(simple->resets);
|
||||
if (ret)
|
||||
goto err_resetc_put;
|
||||
} else {
|
||||
ret = reset_control_deassert(simple->resets);
|
||||
if (ret)
|
||||
goto err_resetc_put;
|
||||
}
|
||||
ret = reset_control_deassert(simple->resets);
|
||||
if (ret)
|
||||
goto err_resetc_put;
|
||||
|
||||
ret = clk_bulk_get_all(simple->dev, &simple->clks);
|
||||
if (ret < 0)
|
||||
|
@ -102,8 +88,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
|
|||
clk_bulk_put_all(simple->num_clocks, simple->clks);
|
||||
|
||||
err_resetc_assert:
|
||||
if (!simple->pulse_resets)
|
||||
reset_control_assert(simple->resets);
|
||||
reset_control_assert(simple->resets);
|
||||
|
||||
err_resetc_put:
|
||||
reset_control_put(simple->resets);
|
||||
|
@ -118,8 +103,7 @@ static void __dwc3_of_simple_teardown(struct dwc3_of_simple *simple)
|
|||
clk_bulk_put_all(simple->num_clocks, simple->clks);
|
||||
simple->num_clocks = 0;
|
||||
|
||||
if (!simple->pulse_resets)
|
||||
reset_control_assert(simple->resets);
|
||||
reset_control_assert(simple->resets);
|
||||
|
||||
reset_control_put(simple->resets);
|
||||
|
||||
|
@ -191,8 +175,6 @@ static const struct of_device_id of_dwc3_simple_match[] = {
|
|||
{ .compatible = "xlnx,zynqmp-dwc3" },
|
||||
{ .compatible = "cavium,octeon-7130-usb-uctl" },
|
||||
{ .compatible = "sprd,sc9860-dwc3" },
|
||||
{ .compatible = "amlogic,meson-axg-dwc3" },
|
||||
{ .compatible = "amlogic,meson-gxl-dwc3" },
|
||||
{ .compatible = "allwinner,sun50i-h6-dwc3" },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
|
|
|
@ -95,7 +95,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
|||
* Wait until device controller is ready. Only applies to 1.94a and
|
||||
* later RTL.
|
||||
*/
|
||||
if (dwc->revision >= DWC3_REVISION_194A) {
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
|
||||
while (--retries) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
if (reg & DWC3_DSTS_DCNRD)
|
||||
|
@ -122,7 +122,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
|||
* The following code is racy when called from dwc3_gadget_wakeup,
|
||||
* and is not needed, at least on newer versions
|
||||
*/
|
||||
if (dwc->revision >= DWC3_REVISION_194A)
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
|
||||
return 0;
|
||||
|
||||
/* wait for a change in DSTS */
|
||||
|
@ -273,7 +273,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
|||
{
|
||||
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 timeout = 1000;
|
||||
u32 timeout = 5000;
|
||||
u32 saved_config = 0;
|
||||
u32 reg;
|
||||
|
||||
|
@ -356,6 +356,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
|||
ret = 0;
|
||||
break;
|
||||
case DEPEVT_TRANSFER_NO_RESOURCE:
|
||||
dev_WARN(dwc->dev, "No resource for %s\n",
|
||||
dep->name);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
case DEPEVT_TRANSFER_BUS_EXPIRY:
|
||||
|
@ -387,9 +389,12 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
|||
|
||||
trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
|
||||
|
||||
if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
|
||||
dep->flags |= DWC3_EP_TRANSFER_STARTED;
|
||||
dwc3_gadget_ep_get_transfer_index(dep);
|
||||
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
|
||||
if (ret == 0)
|
||||
dep->flags |= DWC3_EP_TRANSFER_STARTED;
|
||||
|
||||
if (ret != -ETIMEDOUT)
|
||||
dwc3_gadget_ep_get_transfer_index(dep);
|
||||
}
|
||||
|
||||
if (saved_config) {
|
||||
|
@ -415,7 +420,8 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
|
|||
* IN transfers due to a mishandled error condition. Synopsys
|
||||
* STAR 9000614252.
|
||||
*/
|
||||
if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) &&
|
||||
if (dep->direction &&
|
||||
!DWC3_VER_IS_PRIOR(DWC3, 260A) &&
|
||||
(dwc->gadget.speed >= USB_SPEED_SUPER))
|
||||
cmd |= DWC3_DEPCMD_CLEARPENDIN;
|
||||
|
||||
|
@ -573,6 +579,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
|
|||
|
||||
if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
|
||||
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
|
||||
| DWC3_DEPCFG_XFER_COMPLETE_EN
|
||||
| DWC3_DEPCFG_STREAM_EVENT_EN;
|
||||
dep->stream_capable = true;
|
||||
}
|
||||
|
@ -603,6 +610,9 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
|
|||
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
||||
bool interrupt);
|
||||
|
||||
/**
|
||||
* __dwc3_gadget_ep_enable - initializes a hw endpoint
|
||||
* @dep: endpoint to be initialized
|
||||
|
@ -663,7 +673,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
|
|||
* Issue StartTransfer here with no-op TRB so we can always rely on No
|
||||
* Response Update Transfer command.
|
||||
*/
|
||||
if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) ||
|
||||
if (usb_endpoint_xfer_bulk(desc) ||
|
||||
usb_endpoint_xfer_int(desc)) {
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
struct dwc3_trb *trb;
|
||||
|
@ -682,6 +692,29 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
|
|||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (dep->stream_capable) {
|
||||
/*
|
||||
* For streams, at start, there maybe a race where the
|
||||
* host primes the endpoint before the function driver
|
||||
* queues a request to initiate a stream. In that case,
|
||||
* the controller will not see the prime to generate the
|
||||
* ERDY and start stream. To workaround this, issue a
|
||||
* no-op TRB as normal, but end it immediately. As a
|
||||
* result, when the function driver queues the request,
|
||||
* the next START_TRANSFER command will cause the
|
||||
* controller to generate an ERDY to initiate the
|
||||
* stream.
|
||||
*/
|
||||
dwc3_stop_active_transfer(dep, true, true);
|
||||
|
||||
/*
|
||||
* All stream eps will reinitiate stream on NoStream
|
||||
* rejection until we can determine that the host can
|
||||
* prime after the first transfer.
|
||||
*/
|
||||
dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -690,8 +723,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
||||
bool interrupt);
|
||||
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
|
@ -912,7 +943,8 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
|
|||
|
||||
static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
|
||||
dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
|
||||
unsigned stream_id, unsigned short_not_ok, unsigned no_interrupt)
|
||||
unsigned stream_id, unsigned short_not_ok,
|
||||
unsigned no_interrupt, unsigned is_last)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct usb_gadget *gadget = &dwc->gadget;
|
||||
|
@ -1005,6 +1037,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
|
|||
|
||||
if (chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CHN;
|
||||
else if (dep->stream_capable && is_last)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_LST;
|
||||
|
||||
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
|
||||
|
@ -1032,6 +1066,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
unsigned stream_id = req->request.stream_id;
|
||||
unsigned short_not_ok = req->request.short_not_ok;
|
||||
unsigned no_interrupt = req->request.no_interrupt;
|
||||
unsigned is_last = req->request.is_last;
|
||||
|
||||
if (req->request.num_sgs > 0) {
|
||||
length = sg_dma_len(req->start_sg);
|
||||
|
@ -1052,7 +1087,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
req->num_trbs++;
|
||||
|
||||
__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
|
||||
stream_id, short_not_ok, no_interrupt);
|
||||
stream_id, short_not_ok, no_interrupt, is_last);
|
||||
}
|
||||
|
||||
static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
|
||||
|
@ -1097,7 +1132,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
|
|||
maxp - rem, false, 1,
|
||||
req->request.stream_id,
|
||||
req->request.short_not_ok,
|
||||
req->request.no_interrupt);
|
||||
req->request.no_interrupt,
|
||||
req->request.is_last);
|
||||
} else {
|
||||
dwc3_prepare_one_trb(dep, req, chain, i);
|
||||
}
|
||||
|
@ -1141,7 +1177,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
|
|||
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
|
||||
false, 1, req->request.stream_id,
|
||||
req->request.short_not_ok,
|
||||
req->request.no_interrupt);
|
||||
req->request.no_interrupt,
|
||||
req->request.is_last);
|
||||
} else if (req->request.zero && req->request.length &&
|
||||
(IS_ALIGNED(req->request.length, maxp))) {
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
@ -1158,7 +1195,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
|
|||
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
|
||||
false, 1, req->request.stream_id,
|
||||
req->request.short_not_ok,
|
||||
req->request.no_interrupt);
|
||||
req->request.no_interrupt,
|
||||
req->request.is_last);
|
||||
} else {
|
||||
dwc3_prepare_one_trb(dep, req, false, 0);
|
||||
}
|
||||
|
@ -1194,6 +1232,14 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
|
|||
|
||||
if (!dwc3_calc_trbs_left(dep))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Don't prepare beyond a transfer. In DWC_usb32, its transfer
|
||||
* burst capability may try to read and use TRBs beyond the
|
||||
* active transfer instead of stopping.
|
||||
*/
|
||||
if (dep->stream_capable && req->request.is_last)
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
|
||||
|
@ -1217,9 +1263,19 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
|
|||
|
||||
if (!dwc3_calc_trbs_left(dep))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Don't prepare beyond a transfer. In DWC_usb32, its transfer
|
||||
* burst capability may try to read and use TRBs beyond the
|
||||
* active transfer instead of stopping.
|
||||
*/
|
||||
if (dep->stream_capable && req->request.is_last)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep);
|
||||
|
||||
static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
|
@ -1259,17 +1315,26 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
|
|||
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* FIXME we need to iterate over the list of requests
|
||||
* here and stop, unmap, free and del each of the linked
|
||||
* requests instead of what we do now.
|
||||
*/
|
||||
if (req->trb)
|
||||
memset(req->trb, 0, sizeof(struct dwc3_trb));
|
||||
dwc3_gadget_del_and_unmap_request(dep, req, ret);
|
||||
struct dwc3_request *tmp;
|
||||
|
||||
if (ret == -EAGAIN)
|
||||
return ret;
|
||||
|
||||
dwc3_stop_active_transfer(dep, true, true);
|
||||
|
||||
list_for_each_entry_safe(req, tmp, &dep->started_list, list)
|
||||
dwc3_gadget_move_cancelled_request(req);
|
||||
|
||||
/* If ep isn't started, then there's no end transfer pending */
|
||||
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
|
||||
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dep->stream_capable && req->request.is_last)
|
||||
dep->flags |= DWC3_EP_WAIT_TRANSFER_COMPLETE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1402,17 +1467,15 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
|
|||
int ret;
|
||||
int i;
|
||||
|
||||
if (list_empty(&dep->pending_list)) {
|
||||
if (list_empty(&dep->pending_list) &&
|
||||
list_empty(&dep->started_list)) {
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) &&
|
||||
(dwc->revision <= DWC3_USB31_REVISION_160A ||
|
||||
(dwc->revision == DWC3_USB31_REVISION_170A &&
|
||||
dwc->version_type >= DWC31_VERSIONTYPE_EA01 &&
|
||||
dwc->version_type <= DWC31_VERSIONTYPE_EA06))) {
|
||||
|
||||
if (!dwc->dis_start_transfer_quirk &&
|
||||
(DWC3_VER_IS_PRIOR(DWC31, 170A) ||
|
||||
DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) {
|
||||
if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
|
||||
return dwc3_gadget_start_isoc_quirk(dep);
|
||||
}
|
||||
|
@ -1425,6 +1488,27 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
|
|||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* After a number of unsuccessful start attempts due to bus-expiry
|
||||
* status, issue END_TRANSFER command and retry on the next XferNotReady
|
||||
* event.
|
||||
*/
|
||||
if (ret == -EAGAIN) {
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
u32 cmd;
|
||||
|
||||
cmd = DWC3_DEPCMD_ENDTRANSFER |
|
||||
DWC3_DEPCMD_CMDIOC |
|
||||
DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
|
||||
dep->resource_index = 0;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
if (!ret)
|
||||
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1457,6 +1541,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
list_add_tail(&req->list, &dep->pending_list);
|
||||
req->status = DWC3_REQUEST_STATUS_QUEUED;
|
||||
|
||||
if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)
|
||||
return 0;
|
||||
|
||||
/* Start the transfer only after the END_TRANSFER is completed */
|
||||
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
|
||||
dep->flags |= DWC3_EP_DELAY_START;
|
||||
|
@ -1508,6 +1595,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
|
|||
{
|
||||
int i;
|
||||
|
||||
/* If req->trb is not set, then the request has not started */
|
||||
if (!req->trb)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If request was already started, this means we had to
|
||||
* stop the transfer. With that we also need to ignore
|
||||
|
@ -1556,39 +1647,40 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
list_for_each_entry(r, &dep->pending_list, list) {
|
||||
list_for_each_entry(r, &dep->cancelled_list, list) {
|
||||
if (r == req)
|
||||
break;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (r != req) {
|
||||
list_for_each_entry(r, &dep->started_list, list) {
|
||||
if (r == req)
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(r, &dep->pending_list, list) {
|
||||
if (r == req) {
|
||||
dwc3_gadget_giveback(dep, req, -ECONNRESET);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(r, &dep->started_list, list) {
|
||||
if (r == req) {
|
||||
struct dwc3_request *t;
|
||||
|
||||
/* wait until it is processed */
|
||||
dwc3_stop_active_transfer(dep, true, true);
|
||||
|
||||
if (!r->trb)
|
||||
goto out0;
|
||||
/*
|
||||
* Remove any started request if the transfer is
|
||||
* cancelled.
|
||||
*/
|
||||
list_for_each_entry_safe(r, t, &dep->started_list, list)
|
||||
dwc3_gadget_move_cancelled_request(r);
|
||||
|
||||
dwc3_gadget_move_cancelled_request(req);
|
||||
if (dep->flags & DWC3_EP_TRANSFER_STARTED)
|
||||
goto out0;
|
||||
else
|
||||
goto out1;
|
||||
goto out;
|
||||
}
|
||||
dev_err(dwc->dev, "request %pK was not queued to %s\n",
|
||||
request, ep->name);
|
||||
ret = -EINVAL;
|
||||
goto out0;
|
||||
}
|
||||
|
||||
out1:
|
||||
dwc3_gadget_giveback(dep, req, -ECONNRESET);
|
||||
|
||||
out0:
|
||||
dev_err(dwc->dev, "request %pK was not queued to %s\n",
|
||||
request, ep->name);
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
|
@ -1598,6 +1690,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
|||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_request *req;
|
||||
struct dwc3_request *tmp;
|
||||
int ret;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
|
@ -1634,13 +1728,37 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
|||
else
|
||||
dep->flags |= DWC3_EP_STALL;
|
||||
} else {
|
||||
/*
|
||||
* Don't issue CLEAR_STALL command to control endpoints. The
|
||||
* controller automatically clears the STALL when it receives
|
||||
* the SETUP token.
|
||||
*/
|
||||
if (dep->number <= 1) {
|
||||
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = dwc3_send_clear_stall_ep_cmd(dep);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to clear STALL on %s\n",
|
||||
dep->name);
|
||||
else
|
||||
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
|
||||
|
||||
dwc3_stop_active_transfer(dep, true, true);
|
||||
|
||||
list_for_each_entry_safe(req, tmp, &dep->started_list, list)
|
||||
dwc3_gadget_move_cancelled_request(req);
|
||||
|
||||
list_for_each_entry_safe(req, tmp, &dep->pending_list, list)
|
||||
dwc3_gadget_move_cancelled_request(req);
|
||||
|
||||
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) {
|
||||
dep->flags &= ~DWC3_EP_DELAY_START;
|
||||
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1756,7 +1874,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
|
|||
}
|
||||
|
||||
/* Recent versions do this automatically */
|
||||
if (dwc->revision < DWC3_REVISION_194A) {
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
|
||||
/* write zeroes to Link Change Request */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
||||
|
@ -1818,12 +1936,12 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
|||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (is_on) {
|
||||
if (dwc->revision <= DWC3_REVISION_187A) {
|
||||
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
|
||||
reg &= ~DWC3_DCTL_TRGTULST_MASK;
|
||||
reg |= DWC3_DCTL_TRGTULST_RX_DET;
|
||||
}
|
||||
|
||||
if (dwc->revision >= DWC3_REVISION_194A)
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
|
||||
reg &= ~DWC3_DCTL_KEEP_CONNECT;
|
||||
reg |= DWC3_DCTL_RUN_STOP;
|
||||
|
||||
|
@ -1897,7 +2015,7 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
|
|||
DWC3_DEVTEN_USBRSTEN |
|
||||
DWC3_DEVTEN_DISCONNEVTEN);
|
||||
|
||||
if (dwc->revision < DWC3_REVISION_250A)
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
|
||||
reg |= DWC3_DEVTEN_ULSTCNGEN;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
||||
|
@ -1942,6 +2060,8 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
|
|||
|
||||
ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
|
||||
mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
if (DWC3_IP_IS(DWC32))
|
||||
mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
|
||||
|
||||
nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
|
||||
nump = min_t(u32, nump, 16);
|
||||
|
@ -1978,10 +2098,10 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
|
|||
* bursts of data without going through any sort of endpoint throttling.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
|
||||
if (dwc3_is_usb31(dwc))
|
||||
reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
|
||||
else
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
|
||||
else
|
||||
reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
|
||||
|
||||
|
@ -2154,7 +2274,7 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
|
|||
* STAR#9000525659: Clock Domain Crossing on DCTL in
|
||||
* USB 2.0 Mode
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_220A &&
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
|
||||
!dwc->dis_metastability_quirk) {
|
||||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
} else {
|
||||
|
@ -2172,18 +2292,18 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
|
|||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
break;
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
if (dwc3_is_usb31(dwc))
|
||||
reg |= DWC3_DCFG_SUPERSPEED_PLUS;
|
||||
else
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
else
|
||||
reg |= DWC3_DCFG_SUPERSPEED_PLUS;
|
||||
break;
|
||||
default:
|
||||
dev_err(dwc->dev, "invalid speed (%d)\n", speed);
|
||||
|
||||
if (dwc->revision & DWC3_REVISION_IS_DWC31)
|
||||
reg |= DWC3_DCFG_SUPERSPEED_PLUS;
|
||||
else
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
else
|
||||
reg |= DWC3_DCFG_SUPERSPEED_PLUS;
|
||||
}
|
||||
}
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
@ -2226,14 +2346,17 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
|
|||
int size;
|
||||
|
||||
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
if (DWC3_IP_IS(DWC32))
|
||||
mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
|
||||
|
||||
/* MDWIDTH is represented in bits, we need it in bytes */
|
||||
mdwidth /= 8;
|
||||
|
||||
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
|
||||
if (dwc3_is_usb31(dwc))
|
||||
size = DWC31_GTXFIFOSIZ_TXFDEP(size);
|
||||
else
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
size = DWC3_GTXFIFOSIZ_TXFDEP(size);
|
||||
else
|
||||
size = DWC31_GTXFIFOSIZ_TXFDEP(size);
|
||||
|
||||
/* FIFO Depth is in MDWDITH bytes. Multiply */
|
||||
size *= mdwidth;
|
||||
|
@ -2270,16 +2393,18 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
|
|||
int size;
|
||||
|
||||
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
if (DWC3_IP_IS(DWC32))
|
||||
mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
|
||||
|
||||
/* MDWIDTH is represented in bits, convert to bytes */
|
||||
mdwidth /= 8;
|
||||
|
||||
/* All OUT endpoints share a single RxFIFO space */
|
||||
size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
|
||||
if (dwc3_is_usb31(dwc))
|
||||
size = DWC31_GRXFIFOSIZ_RXFDEP(size);
|
||||
else
|
||||
if (DWC3_IP_IS(DWC3))
|
||||
size = DWC3_GRXFIFOSIZ_RXFDEP(size);
|
||||
else
|
||||
size = DWC31_GRXFIFOSIZ_RXFDEP(size);
|
||||
|
||||
/* FIFO depth is in MDWDITH bytes */
|
||||
size *= mdwidth;
|
||||
|
@ -2531,10 +2656,8 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
|
|||
|
||||
req->request.actual = req->request.length - req->remaining;
|
||||
|
||||
if (!dwc3_gadget_ep_request_completed(req)) {
|
||||
__dwc3_gadget_kick_transfer(dep);
|
||||
if (!dwc3_gadget_ep_request_completed(req))
|
||||
goto out;
|
||||
}
|
||||
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
|
||||
|
@ -2558,41 +2681,53 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
|
|||
}
|
||||
}
|
||||
|
||||
static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
|
||||
if (!list_empty(&dep->pending_list))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* We only need to check the first entry of the started list. We can
|
||||
* assume the completed requests are removed from the started list.
|
||||
*/
|
||||
req = next_request(&dep->started_list);
|
||||
if (!req)
|
||||
return false;
|
||||
|
||||
return !dwc3_gadget_ep_request_completed(req);
|
||||
}
|
||||
|
||||
static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
dep->frame_number = event->parameters;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
|
||||
const struct dwc3_event_depevt *event)
|
||||
static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
|
||||
const struct dwc3_event_depevt *event, int status)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned status = 0;
|
||||
bool stop = false;
|
||||
|
||||
dwc3_gadget_endpoint_frame_from_event(dep, event);
|
||||
|
||||
if (event->status & DEPEVT_STATUS_BUSERR)
|
||||
status = -ECONNRESET;
|
||||
|
||||
if (event->status & DEPEVT_STATUS_MISSED_ISOC) {
|
||||
status = -EXDEV;
|
||||
|
||||
if (list_empty(&dep->started_list))
|
||||
stop = true;
|
||||
}
|
||||
bool no_started_trb = true;
|
||||
|
||||
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
|
||||
|
||||
if (stop)
|
||||
dwc3_stop_active_transfer(dep, true, true);
|
||||
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
|
||||
goto out;
|
||||
|
||||
if (status == -EXDEV && list_empty(&dep->started_list))
|
||||
dwc3_stop_active_transfer(dep, true, true);
|
||||
else if (dwc3_gadget_ep_should_continue(dep))
|
||||
if (__dwc3_gadget_kick_transfer(dep) == 0)
|
||||
no_started_trb = false;
|
||||
|
||||
out:
|
||||
/*
|
||||
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
|
||||
* See dwc3_gadget_linksts_change_interrupt() for 1st half.
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_183A) {
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
|
@ -2603,7 +2738,7 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
|
|||
continue;
|
||||
|
||||
if (!list_empty(&dep->started_list))
|
||||
return;
|
||||
return no_started_trb;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
|
@ -2612,15 +2747,124 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
|
|||
|
||||
dwc->u1u2 = 0;
|
||||
}
|
||||
|
||||
return no_started_trb;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
dwc3_gadget_endpoint_frame_from_event(dep, event);
|
||||
|
||||
if (event->status & DEPEVT_STATUS_BUSERR)
|
||||
status = -ECONNRESET;
|
||||
|
||||
if (event->status & DEPEVT_STATUS_MISSED_ISOC)
|
||||
status = -EXDEV;
|
||||
|
||||
dwc3_gadget_endpoint_trbs_complete(dep, event, status);
|
||||
}
|
||||
|
||||
static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
|
||||
|
||||
if (event->status & DEPEVT_STATUS_BUSERR)
|
||||
status = -ECONNRESET;
|
||||
|
||||
if (dwc3_gadget_endpoint_trbs_complete(dep, event, status))
|
||||
dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
dwc3_gadget_endpoint_frame_from_event(dep, event);
|
||||
|
||||
/*
|
||||
* The XferNotReady event is generated only once before the endpoint
|
||||
* starts. It will be generated again when END_TRANSFER command is
|
||||
* issued. For some controller versions, the XferNotReady event may be
|
||||
* generated while the END_TRANSFER command is still in process. Ignore
|
||||
* it and wait for the next XferNotReady event after the command is
|
||||
* completed.
|
||||
*/
|
||||
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
|
||||
return;
|
||||
|
||||
(void) __dwc3_gadget_start_isoc(dep);
|
||||
}
|
||||
|
||||
static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
if (event->status == DEPEVT_STREAMEVT_FOUND) {
|
||||
dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Note: NoStream rejection event param value is 0 and not 0xFFFF */
|
||||
switch (event->parameters) {
|
||||
case DEPEVT_STREAM_PRIME:
|
||||
/*
|
||||
* If the host can properly transition the endpoint state from
|
||||
* idle to prime after a NoStream rejection, there's no need to
|
||||
* force restarting the endpoint to reinitiate the stream. To
|
||||
* simplify the check, assume the host follows the USB spec if
|
||||
* it primed the endpoint more than once.
|
||||
*/
|
||||
if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) {
|
||||
if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED)
|
||||
dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM;
|
||||
else
|
||||
dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
|
||||
}
|
||||
|
||||
break;
|
||||
case DEPEVT_STREAM_NOSTREAM:
|
||||
if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
|
||||
!(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
|
||||
!(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE))
|
||||
break;
|
||||
|
||||
/*
|
||||
* If the host rejects a stream due to no active stream, by the
|
||||
* USB and xHCI spec, the endpoint will be put back to idle
|
||||
* state. When the host is ready (buffer added/updated), it will
|
||||
* prime the endpoint to inform the usb device controller. This
|
||||
* triggers the device controller to issue ERDY to restart the
|
||||
* stream. However, some hosts don't follow this and keep the
|
||||
* endpoint in the idle state. No prime will come despite host
|
||||
* streams are updated, and the device controller will not be
|
||||
* triggered to generate ERDY to move the next stream data. To
|
||||
* workaround this and maintain compatibility with various
|
||||
* hosts, force to reinitate the stream until the host is ready
|
||||
* instead of waiting for the host to prime the endpoint.
|
||||
*/
|
||||
if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
|
||||
unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
|
||||
|
||||
dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
|
||||
} else {
|
||||
dep->flags |= DWC3_EP_DELAY_START;
|
||||
dwc3_stop_active_transfer(dep, true, true);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
|
||||
}
|
||||
|
||||
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
|
@ -2665,8 +2909,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
dep->flags &= ~DWC3_EP_DELAY_START;
|
||||
}
|
||||
break;
|
||||
case DWC3_DEPEVT_STREAMEVT:
|
||||
case DWC3_DEPEVT_XFERCOMPLETE:
|
||||
dwc3_gadget_endpoint_transfer_complete(dep, event);
|
||||
break;
|
||||
case DWC3_DEPEVT_STREAMEVT:
|
||||
dwc3_gadget_endpoint_stream_event(dep, event);
|
||||
break;
|
||||
case DWC3_DEPEVT_RXTXFIFOEVT:
|
||||
break;
|
||||
}
|
||||
|
@ -2758,6 +3006,14 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
|||
WARN_ON_ONCE(ret);
|
||||
dep->resource_index = 0;
|
||||
|
||||
/*
|
||||
* The END_TRANSFER command will cause the controller to generate a
|
||||
* NoStream Event, and it's not due to the host DP NoStream rejection.
|
||||
* Ignore the next NoStream event.
|
||||
*/
|
||||
if (dep->stream_capable)
|
||||
dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
|
||||
|
||||
if (!interrupt)
|
||||
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
|
||||
else
|
||||
|
@ -2838,7 +3094,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
* STAR#9000466709: RTL: Device : Disconnect event not
|
||||
* generated if setup packet pending in FIFO
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_188A) {
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 188A)) {
|
||||
if (dwc->setup_packet_pending)
|
||||
dwc3_gadget_disconnect_interrupt(dwc);
|
||||
}
|
||||
|
@ -2897,7 +3153,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
* STAR#9000483510: RTL: SS : USB3 reset event may
|
||||
* not be generated always when the link enters poll
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_190A)
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 190A))
|
||||
dwc3_gadget_reset_interrupt(dwc);
|
||||
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
@ -2925,7 +3181,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
|
||||
/* Enable USB2 LPM Capability */
|
||||
|
||||
if ((dwc->revision > DWC3_REVISION_194A) &&
|
||||
if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A) &&
|
||||
(speed != DWC3_DSTS_SUPERSPEED) &&
|
||||
(speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
|
@ -2944,11 +3200,10 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
* BESL value in the LPM token is less than or equal to LPM
|
||||
* NYET threshold.
|
||||
*/
|
||||
WARN_ONCE(dwc->revision < DWC3_REVISION_240A
|
||||
&& dwc->has_lpm_erratum,
|
||||
WARN_ONCE(DWC3_VER_IS_PRIOR(DWC3, 240A) && dwc->has_lpm_erratum,
|
||||
"LPM Erratum not available on dwc3 revisions < 2.40a\n");
|
||||
|
||||
if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
|
||||
if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A))
|
||||
reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold);
|
||||
|
||||
dwc3_gadget_dctl_write_safe(dwc, reg);
|
||||
|
@ -3019,7 +3274,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
|||
* operational mode
|
||||
*/
|
||||
pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
|
||||
if ((dwc->revision < DWC3_REVISION_250A) &&
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 250A) &&
|
||||
(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
|
||||
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
|
||||
(next == DWC3_LINK_STATE_RESUME)) {
|
||||
|
@ -3045,7 +3300,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
|||
* STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
|
||||
* core send LGO_Ux entering U0
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_183A) {
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
|
||||
if (next == DWC3_LINK_STATE_U0) {
|
||||
u32 u1u2;
|
||||
u32 reg;
|
||||
|
@ -3156,7 +3411,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
|||
break;
|
||||
case DWC3_DEVICE_EVENT_EOPF:
|
||||
/* It changed to be suspend event for version 2.30a and above */
|
||||
if (dwc->revision >= DWC3_REVISION_230A) {
|
||||
if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) {
|
||||
/*
|
||||
* Ignore suspend event until the gadget enters into
|
||||
* USB_STATE_CONFIGURED state.
|
||||
|
@ -3401,7 +3656,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
* is less than super speed because we don't have means, yet, to tell
|
||||
* composite.c that we are USB 2.0 + LPM ECN.
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_220A &&
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
|
||||
!dwc->dis_metastability_quirk)
|
||||
dev_info(dwc->dev, "changing max_speed on rev %08x\n",
|
||||
dwc->revision);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* gadget.h - DesignWare USB3 DRD Gadget Header
|
||||
*
|
||||
|
|
|
@ -104,7 +104,7 @@ int dwc3_host_init(struct dwc3 *dwc)
|
|||
*
|
||||
* This following flag tells XHCI to do just that.
|
||||
*/
|
||||
if (dwc->revision <= DWC3_REVISION_300A)
|
||||
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
|
||||
|
||||
if (prop_idx) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/**
|
||||
* io.h - DesignWare USB3 DRD IO Header
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/**
|
||||
* trace.h - DesignWare USB3 DRD Controller Trace Support
|
||||
*
|
||||
|
|
|
@ -96,40 +96,43 @@ function_descriptors(struct usb_function *f,
|
|||
}
|
||||
|
||||
/**
|
||||
* next_ep_desc() - advance to the next EP descriptor
|
||||
* next_desc() - advance to the next desc_type descriptor
|
||||
* @t: currect pointer within descriptor array
|
||||
* @desc_type: descriptor type
|
||||
*
|
||||
* Return: next EP descriptor or NULL
|
||||
* Return: next desc_type descriptor or NULL
|
||||
*
|
||||
* Iterate over @t until either EP descriptor found or
|
||||
* Iterate over @t until either desc_type descriptor found or
|
||||
* NULL (that indicates end of list) encountered
|
||||
*/
|
||||
static struct usb_descriptor_header**
|
||||
next_ep_desc(struct usb_descriptor_header **t)
|
||||
next_desc(struct usb_descriptor_header **t, u8 desc_type)
|
||||
{
|
||||
for (; *t; t++) {
|
||||
if ((*t)->bDescriptorType == USB_DT_ENDPOINT)
|
||||
if ((*t)->bDescriptorType == desc_type)
|
||||
return t;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* for_each_ep_desc()- iterate over endpoint descriptors in the
|
||||
* descriptors list
|
||||
* @start: pointer within descriptor array.
|
||||
* @ep_desc: endpoint descriptor to use as the loop cursor
|
||||
* for_each_desc() - iterate over desc_type descriptors in the
|
||||
* descriptors list
|
||||
* @start: pointer within descriptor array.
|
||||
* @iter_desc: desc_type descriptor to use as the loop cursor
|
||||
* @desc_type: wanted descriptr type
|
||||
*/
|
||||
#define for_each_ep_desc(start, ep_desc) \
|
||||
for (ep_desc = next_ep_desc(start); \
|
||||
ep_desc; ep_desc = next_ep_desc(ep_desc+1))
|
||||
#define for_each_desc(start, iter_desc, desc_type) \
|
||||
for (iter_desc = next_desc(start, desc_type); \
|
||||
iter_desc; iter_desc = next_desc(iter_desc + 1, desc_type))
|
||||
|
||||
/**
|
||||
* config_ep_by_speed() - configures the given endpoint
|
||||
* config_ep_by_speed_and_alt() - configures the given endpoint
|
||||
* according to gadget speed.
|
||||
* @g: pointer to the gadget
|
||||
* @f: usb function
|
||||
* @_ep: the endpoint to configure
|
||||
* @alt: alternate setting number
|
||||
*
|
||||
* Return: error code, 0 on success
|
||||
*
|
||||
|
@ -142,11 +145,13 @@ next_ep_desc(struct usb_descriptor_header **t)
|
|||
* Note: the supplied function should hold all the descriptors
|
||||
* for supported speeds
|
||||
*/
|
||||
int config_ep_by_speed(struct usb_gadget *g,
|
||||
struct usb_function *f,
|
||||
struct usb_ep *_ep)
|
||||
int config_ep_by_speed_and_alt(struct usb_gadget *g,
|
||||
struct usb_function *f,
|
||||
struct usb_ep *_ep,
|
||||
u8 alt)
|
||||
{
|
||||
struct usb_endpoint_descriptor *chosen_desc = NULL;
|
||||
struct usb_interface_descriptor *int_desc = NULL;
|
||||
struct usb_descriptor_header **speed_desc = NULL;
|
||||
|
||||
struct usb_ss_ep_comp_descriptor *comp_desc = NULL;
|
||||
|
@ -182,8 +187,21 @@ int config_ep_by_speed(struct usb_gadget *g,
|
|||
default:
|
||||
speed_desc = f->fs_descriptors;
|
||||
}
|
||||
|
||||
/* find correct alternate setting descriptor */
|
||||
for_each_desc(speed_desc, d_spd, USB_DT_INTERFACE) {
|
||||
int_desc = (struct usb_interface_descriptor *)*d_spd;
|
||||
|
||||
if (int_desc->bAlternateSetting == alt) {
|
||||
speed_desc = d_spd;
|
||||
goto intf_found;
|
||||
}
|
||||
}
|
||||
return -EIO;
|
||||
|
||||
intf_found:
|
||||
/* find descriptors */
|
||||
for_each_ep_desc(speed_desc, d_spd) {
|
||||
for_each_desc(speed_desc, d_spd, USB_DT_ENDPOINT) {
|
||||
chosen_desc = (struct usb_endpoint_descriptor *)*d_spd;
|
||||
if (chosen_desc->bEndpointAddress == _ep->address)
|
||||
goto ep_found;
|
||||
|
@ -237,6 +255,32 @@ int config_ep_by_speed(struct usb_gadget *g,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(config_ep_by_speed_and_alt);
|
||||
|
||||
/**
|
||||
* config_ep_by_speed() - configures the given endpoint
|
||||
* according to gadget speed.
|
||||
* @g: pointer to the gadget
|
||||
* @f: usb function
|
||||
* @_ep: the endpoint to configure
|
||||
*
|
||||
* Return: error code, 0 on success
|
||||
*
|
||||
* This function chooses the right descriptors for a given
|
||||
* endpoint according to gadget speed and saves it in the
|
||||
* endpoint desc field. If the endpoint already has a descriptor
|
||||
* assigned to it - overwrites it with currently corresponding
|
||||
* descriptor. The endpoint maxpacket field is updated according
|
||||
* to the chosen descriptor.
|
||||
* Note: the supplied function should hold all the descriptors
|
||||
* for supported speeds
|
||||
*/
|
||||
int config_ep_by_speed(struct usb_gadget *g,
|
||||
struct usb_function *f,
|
||||
struct usb_ep *_ep)
|
||||
{
|
||||
return config_ep_by_speed_and_alt(g, f, _ep, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(config_ep_by_speed);
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
int check_user_usb_string(const char *name,
|
||||
struct usb_gadget_strings *stringtab_dev)
|
||||
{
|
||||
unsigned primary_lang;
|
||||
unsigned sub_lang;
|
||||
u16 num;
|
||||
int ret;
|
||||
|
||||
|
@ -22,17 +20,7 @@ int check_user_usb_string(const char *name,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
primary_lang = num & 0x3ff;
|
||||
sub_lang = num >> 10;
|
||||
|
||||
/* simple sanity check for valid langid */
|
||||
switch (primary_lang) {
|
||||
case 0:
|
||||
case 0x62 ... 0xfe:
|
||||
case 0x100 ... 0x3ff:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!sub_lang)
|
||||
if (!usb_validate_langid(num))
|
||||
return -EINVAL;
|
||||
|
||||
stringtab_dev->language = num;
|
||||
|
|
|
@ -723,6 +723,20 @@ static void acm_free_func(struct usb_function *f)
|
|||
kfree(acm);
|
||||
}
|
||||
|
||||
static void acm_resume(struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
|
||||
gserial_resume(&acm->port);
|
||||
}
|
||||
|
||||
static void acm_suspend(struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
|
||||
gserial_suspend(&acm->port);
|
||||
}
|
||||
|
||||
static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
|
@ -750,6 +764,8 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
|
|||
acm->port_num = opts->port_num;
|
||||
acm->port.func.unbind = acm_unbind;
|
||||
acm->port.func.free_func = acm_free_func;
|
||||
acm->port.func.resume = acm_resume;
|
||||
acm->port.func.suspend = acm_suspend;
|
||||
|
||||
return &acm->port.func;
|
||||
}
|
||||
|
|
|
@ -291,8 +291,6 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
goto fail;
|
||||
eem->port.out_ep = ep;
|
||||
|
||||
status = -ENOMEM;
|
||||
|
||||
/* support all relevant hardware speeds... we expect that when
|
||||
* hardware is dual speed, all bulk-capable endpoints work at
|
||||
* both speeds
|
||||
|
|
|
@ -2508,7 +2508,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
|
|||
os_descs_count = get_unaligned_le32(data);
|
||||
data += 4;
|
||||
len -= 4;
|
||||
};
|
||||
}
|
||||
|
||||
/* Read descriptors */
|
||||
raw_descs = data;
|
||||
|
|
|
@ -348,6 +348,20 @@ static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
static void gser_resume(struct usb_function *f)
|
||||
{
|
||||
struct f_gser *gser = func_to_gser(f);
|
||||
|
||||
gserial_resume(&gser->port);
|
||||
}
|
||||
|
||||
static void gser_suspend(struct usb_function *f)
|
||||
{
|
||||
struct f_gser *gser = func_to_gser(f);
|
||||
|
||||
gserial_suspend(&gser->port);
|
||||
}
|
||||
|
||||
static struct usb_function *gser_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_gser *gser;
|
||||
|
@ -369,6 +383,8 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
|
|||
gser->port.func.set_alt = gser_set_alt;
|
||||
gser->port.func.disable = gser_disable;
|
||||
gser->port.func.free_func = gser_free;
|
||||
gser->port.func.resume = gser_resume;
|
||||
gser->port.func.suspend = gser_suspend;
|
||||
|
||||
return &gser->port.func;
|
||||
}
|
||||
|
|
|
@ -531,6 +531,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd)
|
|||
stream->req_in->sg = se_cmd->t_data_sg;
|
||||
}
|
||||
|
||||
stream->req_in->is_last = 1;
|
||||
stream->req_in->complete = uasp_status_data_cmpl;
|
||||
stream->req_in->length = se_cmd->data_length;
|
||||
stream->req_in->context = cmd;
|
||||
|
@ -554,6 +555,7 @@ static void uasp_prepare_status(struct usbg_cmd *cmd)
|
|||
*/
|
||||
iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
|
||||
iu->status = se_cmd->scsi_status;
|
||||
stream->req_status->is_last = 1;
|
||||
stream->req_status->context = cmd;
|
||||
stream->req_status->length = se_cmd->scsi_sense_length + 16;
|
||||
stream->req_status->buf = iu;
|
||||
|
@ -991,6 +993,7 @@ static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
|
|||
req->sg = se_cmd->t_data_sg;
|
||||
}
|
||||
|
||||
req->is_last = 1;
|
||||
req->complete = usbg_data_write_cmpl;
|
||||
req->length = se_cmd->data_length;
|
||||
req->context = cmd;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* f_uvc.h -- USB Video Class Gadget driver
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* RNDIS Definitions for Remote NDIS
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* u_audio.h -- interface to USB gadget "ALSA sound card" utilities
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_ecm.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_eem.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* u_ether.h -- interface to USB gadget "ethernet link" utilities
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_ether_configfs.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_fs.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_gether.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_hid.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_midi.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_ncm.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* u_phonet.h - interface to Phonet
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_printer.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_rndis.h
|
||||
*
|
||||
|
|
|
@ -120,6 +120,8 @@ struct gs_port {
|
|||
wait_queue_head_t drain_wait; /* wait while writes drain */
|
||||
bool write_busy;
|
||||
wait_queue_head_t close_wait;
|
||||
bool suspended; /* port suspended */
|
||||
bool start_delayed; /* delay start when suspended */
|
||||
|
||||
/* REVISIT this state ... */
|
||||
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
|
||||
|
@ -630,13 +632,19 @@ static int gs_open(struct tty_struct *tty, struct file *file)
|
|||
|
||||
/* if connected, start the I/O stream */
|
||||
if (port->port_usb) {
|
||||
struct gserial *gser = port->port_usb;
|
||||
/* if port is suspended, wait resume to start I/0 stream */
|
||||
if (!port->suspended) {
|
||||
struct gserial *gser = port->port_usb;
|
||||
|
||||
pr_debug("gs_open: start ttyGS%d\n", port->port_num);
|
||||
gs_start_io(port);
|
||||
pr_debug("gs_open: start ttyGS%d\n", port->port_num);
|
||||
gs_start_io(port);
|
||||
|
||||
if (gser->connect)
|
||||
gser->connect(gser);
|
||||
if (gser->connect)
|
||||
gser->connect(gser);
|
||||
} else {
|
||||
pr_debug("delay start of ttyGS%d\n", port->port_num);
|
||||
port->start_delayed = true;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
|
||||
|
@ -680,7 +688,7 @@ static void gs_close(struct tty_struct *tty, struct file *file)
|
|||
pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
|
||||
|
||||
gser = port->port_usb;
|
||||
if (gser && gser->disconnect)
|
||||
if (gser && !port->suspended && gser->disconnect)
|
||||
gser->disconnect(gser);
|
||||
|
||||
/* wait for circular write buffer to drain, disconnect, or at
|
||||
|
@ -708,6 +716,7 @@ static void gs_close(struct tty_struct *tty, struct file *file)
|
|||
else
|
||||
kfifo_reset(&port->port_write_buf);
|
||||
|
||||
port->start_delayed = false;
|
||||
port->port.count = 0;
|
||||
port->port.tty = NULL;
|
||||
|
||||
|
@ -1403,6 +1412,38 @@ void gserial_disconnect(struct gserial *gser)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_disconnect);
|
||||
|
||||
void gserial_suspend(struct gserial *gser)
|
||||
{
|
||||
struct gs_port *port = gser->ioport;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
port->suspended = true;
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_suspend);
|
||||
|
||||
void gserial_resume(struct gserial *gser)
|
||||
{
|
||||
struct gs_port *port = gser->ioport;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
port->suspended = false;
|
||||
if (!port->start_delayed) {
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_debug("delayed start ttyGS%d\n", port->port_num);
|
||||
gs_start_io(port);
|
||||
if (gser->connect)
|
||||
gser->connect(gser);
|
||||
port->start_delayed = false;
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_resume);
|
||||
|
||||
static int userial_init(void)
|
||||
{
|
||||
unsigned i;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* u_serial.h - interface to USB gadget "serial port"/TTY utilities
|
||||
*
|
||||
|
@ -68,6 +68,8 @@ ssize_t gserial_get_console(unsigned char port_num, char *page);
|
|||
/* connect/disconnect is handled by individual functions */
|
||||
int gserial_connect(struct gserial *, u8 port_num);
|
||||
void gserial_disconnect(struct gserial *);
|
||||
void gserial_suspend(struct gserial *p);
|
||||
void gserial_resume(struct gserial *p);
|
||||
|
||||
/* functions are bound to configurations by a config or gadget driver */
|
||||
int gser_bind_config(struct usb_configuration *c, u8 port_num);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_tcm.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_uac1.h - Utility definitions for UAC1 function
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_uac2.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_uvc.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* uvc_gadget.h -- USB Video Class Gadget driver
|
||||
*
|
||||
|
@ -77,6 +77,8 @@ struct uvc_video {
|
|||
struct uvc_device *uvc;
|
||||
struct usb_ep *ep;
|
||||
|
||||
struct work_struct pump;
|
||||
|
||||
/* Frame parameters */
|
||||
u8 bpp;
|
||||
u32 fcc;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* uvc_configfs.h
|
||||
*
|
||||
|
|
|
@ -169,7 +169,9 @@ uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return uvcg_video_pump(video);
|
||||
schedule_work(&video->pump);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* uvc_v4l2.h -- USB Video Class Gadget driver
|
||||
*
|
||||
|
|
|
@ -142,44 +142,12 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* I somehow feel that synchronisation won't be easy to achieve here. We have
|
||||
* three events that control USB requests submission:
|
||||
*
|
||||
* - USB request completion: the completion handler will resubmit the request
|
||||
* if a video buffer is available.
|
||||
*
|
||||
* - USB interface setting selection: in response to a SET_INTERFACE request,
|
||||
* the handler will start streaming if a video buffer is available and if
|
||||
* video is not currently streaming.
|
||||
*
|
||||
* - V4L2 buffer queueing: the driver will start streaming if video is not
|
||||
* currently streaming.
|
||||
*
|
||||
* Race conditions between those 3 events might lead to deadlocks or other
|
||||
* nasty side effects.
|
||||
*
|
||||
* The "video currently streaming" condition can't be detected by the irqqueue
|
||||
* being empty, as a request can still be in flight. A separate "queue paused"
|
||||
* flag is thus needed.
|
||||
*
|
||||
* The paused flag will be set when we try to retrieve the irqqueue head if the
|
||||
* queue is empty, and cleared when we queue a buffer.
|
||||
*
|
||||
* The USB request completion handler will get the buffer at the irqqueue head
|
||||
* under protection of the queue spinlock. If the queue is empty, the streaming
|
||||
* paused flag will be set. Right after releasing the spinlock a userspace
|
||||
* application can queue a buffer. The flag will then cleared, and the ioctl
|
||||
* handler will restart the video stream.
|
||||
*/
|
||||
static void
|
||||
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct uvc_video *video = req->context;
|
||||
struct uvc_video_queue *queue = &video->queue;
|
||||
struct uvc_buffer *buf;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
switch (req->status) {
|
||||
case 0:
|
||||
|
@ -188,39 +156,20 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
case -ESHUTDOWN: /* disconnect from host. */
|
||||
uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
|
||||
uvcg_queue_cancel(queue, 1);
|
||||
goto requeue;
|
||||
break;
|
||||
|
||||
default:
|
||||
uvcg_info(&video->uvc->func,
|
||||
"VS request completed with status %d.\n",
|
||||
req->status);
|
||||
uvcg_queue_cancel(queue, 0);
|
||||
goto requeue;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&video->queue.irqlock, flags);
|
||||
buf = uvcg_queue_head(&video->queue);
|
||||
if (buf == NULL) {
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
goto requeue;
|
||||
}
|
||||
|
||||
video->encode(req, video, buf);
|
||||
|
||||
ret = uvcg_video_ep_queue(video, req);
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
|
||||
if (ret < 0) {
|
||||
uvcg_queue_cancel(queue, 0);
|
||||
goto requeue;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
requeue:
|
||||
spin_lock_irqsave(&video->req_lock, flags);
|
||||
list_add_tail(&req->list, &video->req_free);
|
||||
spin_unlock_irqrestore(&video->req_lock, flags);
|
||||
|
||||
schedule_work(&video->pump);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -294,18 +243,15 @@ uvc_video_alloc_requests(struct uvc_video *video)
|
|||
* This function fills the available USB requests (listed in req_free) with
|
||||
* video data from the queued buffers.
|
||||
*/
|
||||
int uvcg_video_pump(struct uvc_video *video)
|
||||
static void uvcg_video_pump(struct work_struct *work)
|
||||
{
|
||||
struct uvc_video *video = container_of(work, struct uvc_video, pump);
|
||||
struct uvc_video_queue *queue = &video->queue;
|
||||
struct usb_request *req;
|
||||
struct uvc_buffer *buf;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* FIXME TODO Race between uvcg_video_pump and requests completion
|
||||
* handler ???
|
||||
*/
|
||||
|
||||
while (1) {
|
||||
/* Retrieve the first available USB request, protected by the
|
||||
* request lock.
|
||||
|
@ -313,7 +259,7 @@ int uvcg_video_pump(struct uvc_video *video)
|
|||
spin_lock_irqsave(&video->req_lock, flags);
|
||||
if (list_empty(&video->req_free)) {
|
||||
spin_unlock_irqrestore(&video->req_lock, flags);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
req = list_first_entry(&video->req_free, struct usb_request,
|
||||
list);
|
||||
|
@ -345,7 +291,7 @@ int uvcg_video_pump(struct uvc_video *video)
|
|||
spin_lock_irqsave(&video->req_lock, flags);
|
||||
list_add_tail(&req->list, &video->req_free);
|
||||
spin_unlock_irqrestore(&video->req_lock, flags);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -363,6 +309,9 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
|
|||
}
|
||||
|
||||
if (!enable) {
|
||||
cancel_work_sync(&video->pump);
|
||||
uvcg_queue_cancel(&video->queue, 0);
|
||||
|
||||
for (i = 0; i < UVC_NUM_REQUESTS; ++i)
|
||||
if (video->req[i])
|
||||
usb_ep_dequeue(video->ep, video->req[i]);
|
||||
|
@ -384,7 +333,9 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
|
|||
} else
|
||||
video->encode = uvc_video_encode_isoc;
|
||||
|
||||
return uvcg_video_pump(video);
|
||||
schedule_work(&video->pump);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -394,6 +345,7 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
|
|||
{
|
||||
INIT_LIST_HEAD(&video->req_free);
|
||||
spin_lock_init(&video->req_lock);
|
||||
INIT_WORK(&video->pump, uvcg_video_pump);
|
||||
|
||||
video->uvc = uvc;
|
||||
video->fcc = V4L2_PIX_FMT_YUYV;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* uvc_video.h -- USB Video Class Gadget driver
|
||||
*
|
||||
|
@ -14,8 +14,6 @@
|
|||
|
||||
struct uvc_video;
|
||||
|
||||
int uvcg_video_pump(struct uvc_video *video);
|
||||
|
||||
int uvcg_video_enable(struct uvc_video *video, int enable);
|
||||
|
||||
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc);
|
||||
|
|
|
@ -229,18 +229,8 @@ static struct usb_composite_driver msg_driver = {
|
|||
.unbind = msg_unbind,
|
||||
};
|
||||
|
||||
module_usb_composite_driver(msg_driver);
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Michal Nazarewicz");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int __init msg_init(void)
|
||||
{
|
||||
return usb_composite_probe(&msg_driver);
|
||||
}
|
||||
module_init(msg_init);
|
||||
|
||||
static void __exit msg_cleanup(void)
|
||||
{
|
||||
usb_composite_unregister(&msg_driver);
|
||||
}
|
||||
module_exit(msg_cleanup);
|
||||
|
|
|
@ -134,11 +134,15 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
|
|||
}
|
||||
|
||||
/* Handle device interrupts */
|
||||
for (i = 0; i < vhub->max_ports; i++) {
|
||||
u32 dev_mask = VHUB_IRQ_DEVICE1 << i;
|
||||
if (istat & vhub->port_irq_mask) {
|
||||
unsigned long bitmap = istat;
|
||||
int offset = VHUB_IRQ_DEV1_BIT;
|
||||
int size = VHUB_IRQ_DEV1_BIT + vhub->max_ports;
|
||||
|
||||
if (istat & dev_mask)
|
||||
for_each_set_bit_from(offset, &bitmap, size) {
|
||||
i = offset - VHUB_IRQ_DEV1_BIT;
|
||||
ast_vhub_dev_irq(&vhub->ports[i].dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle top-level vHub EP0 interrupts */
|
||||
|
@ -332,6 +336,8 @@ static int ast_vhub_probe(struct platform_device *pdev)
|
|||
|
||||
spin_lock_init(&vhub->lock);
|
||||
vhub->pdev = pdev;
|
||||
vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
|
||||
VHUB_IRQ_DEV1_BIT);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
vhub->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
@ -402,7 +408,9 @@ static int ast_vhub_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
|
||||
/* Init hub emulation */
|
||||
ast_vhub_init_hub(vhub);
|
||||
rc = ast_vhub_init_hub(vhub);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
/* Initialize HW */
|
||||
ast_vhub_init_hw(vhub);
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
|
||||
|
||||
enum {
|
||||
AST_VHUB_STR_INDEX_MAX = 4,
|
||||
AST_VHUB_STR_MANUF = 3,
|
||||
AST_VHUB_STR_PRODUCT = 2,
|
||||
AST_VHUB_STR_SERIAL = 1,
|
||||
|
@ -72,13 +73,6 @@ static const struct usb_device_descriptor ast_vhub_dev_desc = {
|
|||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
/* Patches to the above when forcing USB1 mode */
|
||||
static void ast_vhub_patch_dev_desc_usb1(struct usb_device_descriptor *desc)
|
||||
{
|
||||
desc->bcdUSB = cpu_to_le16(0x0100);
|
||||
desc->bDeviceProtocol = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configuration descriptor: same comments as above
|
||||
* regarding handling USB1 mode.
|
||||
|
@ -302,31 +296,81 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
|
|||
if (len > dsize)
|
||||
len = dsize;
|
||||
|
||||
/* Patch it if forcing USB1 */
|
||||
if (desc_type == USB_DT_DEVICE && ep->vhub->force_usb1)
|
||||
ast_vhub_patch_dev_desc_usb1(ep->buf);
|
||||
|
||||
/* Shoot it from the EP buffer */
|
||||
return ast_vhub_reply(ep, NULL, len);
|
||||
}
|
||||
|
||||
static struct usb_gadget_strings*
|
||||
ast_vhub_str_of_container(struct usb_gadget_string_container *container)
|
||||
{
|
||||
return (struct usb_gadget_strings *)container->stash;
|
||||
}
|
||||
|
||||
static int ast_vhub_collect_languages(struct ast_vhub *vhub, void *buf,
|
||||
size_t size)
|
||||
{
|
||||
int rc, hdr_len, nlangs, max_langs;
|
||||
struct usb_gadget_strings *lang_str;
|
||||
struct usb_gadget_string_container *container;
|
||||
struct usb_string_descriptor *sdesc = buf;
|
||||
|
||||
nlangs = 0;
|
||||
hdr_len = sizeof(struct usb_descriptor_header);
|
||||
max_langs = (size - hdr_len) / sizeof(sdesc->wData[0]);
|
||||
list_for_each_entry(container, &vhub->vhub_str_desc, list) {
|
||||
if (nlangs >= max_langs)
|
||||
break;
|
||||
|
||||
lang_str = ast_vhub_str_of_container(container);
|
||||
sdesc->wData[nlangs++] = cpu_to_le16(lang_str->language);
|
||||
}
|
||||
|
||||
rc = hdr_len + nlangs * sizeof(sdesc->wData[0]);
|
||||
sdesc->bLength = rc;
|
||||
sdesc->bDescriptorType = USB_DT_STRING;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct usb_gadget_strings *ast_vhub_lookup_string(struct ast_vhub *vhub,
|
||||
u16 lang_id)
|
||||
{
|
||||
struct usb_gadget_strings *lang_str;
|
||||
struct usb_gadget_string_container *container;
|
||||
|
||||
list_for_each_entry(container, &vhub->vhub_str_desc, list) {
|
||||
lang_str = ast_vhub_str_of_container(container);
|
||||
if (lang_str->language == lang_id)
|
||||
return lang_str;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
|
||||
u8 string_id, u16 lang_id,
|
||||
u16 len)
|
||||
{
|
||||
int rc = usb_gadget_get_string(&ep->vhub->vhub_str_desc,
|
||||
string_id, ep->buf);
|
||||
int rc;
|
||||
u8 buf[256];
|
||||
struct ast_vhub *vhub = ep->vhub;
|
||||
struct usb_gadget_strings *lang_str;
|
||||
|
||||
/*
|
||||
* This should never happen unless we put too big strings in
|
||||
* the array above
|
||||
*/
|
||||
BUG_ON(rc >= AST_VHUB_EP0_MAX_PACKET);
|
||||
if (string_id == 0) {
|
||||
rc = ast_vhub_collect_languages(vhub, buf, sizeof(buf));
|
||||
} else {
|
||||
lang_str = ast_vhub_lookup_string(vhub, lang_id);
|
||||
if (!lang_str)
|
||||
return std_req_stall;
|
||||
|
||||
if (rc < 0)
|
||||
rc = usb_gadget_get_string(lang_str, string_id, buf);
|
||||
}
|
||||
|
||||
if (rc < 0 || rc >= AST_VHUB_EP0_MAX_PACKET)
|
||||
return std_req_stall;
|
||||
|
||||
/* Shoot it from the EP buffer */
|
||||
memcpy(ep->buf, buf, rc);
|
||||
return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
|
||||
}
|
||||
|
||||
|
@ -832,11 +876,148 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub)
|
|||
writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
|
||||
}
|
||||
|
||||
static void ast_vhub_init_desc(struct ast_vhub *vhub)
|
||||
static void ast_vhub_of_parse_dev_desc(struct ast_vhub *vhub,
|
||||
const struct device_node *vhub_np)
|
||||
{
|
||||
u16 id;
|
||||
u32 data;
|
||||
|
||||
if (!of_property_read_u32(vhub_np, "vhub-vendor-id", &data)) {
|
||||
id = (u16)data;
|
||||
vhub->vhub_dev_desc.idVendor = cpu_to_le16(id);
|
||||
}
|
||||
if (!of_property_read_u32(vhub_np, "vhub-product-id", &data)) {
|
||||
id = (u16)data;
|
||||
vhub->vhub_dev_desc.idProduct = cpu_to_le16(id);
|
||||
}
|
||||
if (!of_property_read_u32(vhub_np, "vhub-device-revision", &data)) {
|
||||
id = (u16)data;
|
||||
vhub->vhub_dev_desc.bcdDevice = cpu_to_le16(id);
|
||||
}
|
||||
}
|
||||
|
||||
static void ast_vhub_fixup_usb1_dev_desc(struct ast_vhub *vhub)
|
||||
{
|
||||
vhub->vhub_dev_desc.bcdUSB = cpu_to_le16(0x0100);
|
||||
vhub->vhub_dev_desc.bDeviceProtocol = 0;
|
||||
}
|
||||
|
||||
static struct usb_gadget_string_container*
|
||||
ast_vhub_str_container_alloc(struct ast_vhub *vhub)
|
||||
{
|
||||
unsigned int size;
|
||||
struct usb_string *str_array;
|
||||
struct usb_gadget_strings *lang_str;
|
||||
struct usb_gadget_string_container *container;
|
||||
|
||||
size = sizeof(*container);
|
||||
size += sizeof(struct usb_gadget_strings);
|
||||
size += sizeof(struct usb_string) * AST_VHUB_STR_INDEX_MAX;
|
||||
container = devm_kzalloc(&vhub->pdev->dev, size, GFP_KERNEL);
|
||||
if (!container)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
lang_str = ast_vhub_str_of_container(container);
|
||||
str_array = (struct usb_string *)(lang_str + 1);
|
||||
lang_str->strings = str_array;
|
||||
return container;
|
||||
}
|
||||
|
||||
static void ast_vhub_str_deep_copy(struct usb_gadget_strings *dest,
|
||||
const struct usb_gadget_strings *src)
|
||||
{
|
||||
struct usb_string *src_array = src->strings;
|
||||
struct usb_string *dest_array = dest->strings;
|
||||
|
||||
dest->language = src->language;
|
||||
if (src_array && dest_array) {
|
||||
do {
|
||||
*dest_array = *src_array;
|
||||
dest_array++;
|
||||
src_array++;
|
||||
} while (src_array->s);
|
||||
}
|
||||
}
|
||||
|
||||
static int ast_vhub_str_alloc_add(struct ast_vhub *vhub,
|
||||
const struct usb_gadget_strings *src_str)
|
||||
{
|
||||
struct usb_gadget_strings *dest_str;
|
||||
struct usb_gadget_string_container *container;
|
||||
|
||||
container = ast_vhub_str_container_alloc(vhub);
|
||||
if (IS_ERR(container))
|
||||
return PTR_ERR(container);
|
||||
|
||||
dest_str = ast_vhub_str_of_container(container);
|
||||
ast_vhub_str_deep_copy(dest_str, src_str);
|
||||
list_add_tail(&container->list, &vhub->vhub_str_desc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
u8 id;
|
||||
} str_id_map[] = {
|
||||
{"manufacturer", AST_VHUB_STR_MANUF},
|
||||
{"product", AST_VHUB_STR_PRODUCT},
|
||||
{"serial-number", AST_VHUB_STR_SERIAL},
|
||||
{},
|
||||
};
|
||||
|
||||
static int ast_vhub_of_parse_str_desc(struct ast_vhub *vhub,
|
||||
const struct device_node *desc_np)
|
||||
{
|
||||
u32 langid;
|
||||
int ret = 0;
|
||||
int i, offset;
|
||||
const char *str;
|
||||
struct device_node *child;
|
||||
struct usb_string str_array[AST_VHUB_STR_INDEX_MAX];
|
||||
struct usb_gadget_strings lang_str = {
|
||||
.strings = (struct usb_string *)str_array,
|
||||
};
|
||||
|
||||
for_each_child_of_node(desc_np, child) {
|
||||
if (of_property_read_u32(child, "reg", &langid))
|
||||
continue; /* no language identifier specified */
|
||||
|
||||
if (!usb_validate_langid(langid))
|
||||
continue; /* invalid language identifier */
|
||||
|
||||
lang_str.language = langid;
|
||||
for (i = offset = 0; str_id_map[i].name; i++) {
|
||||
str = of_get_property(child, str_id_map[i].name, NULL);
|
||||
if (str) {
|
||||
str_array[offset].s = str;
|
||||
str_array[offset].id = str_id_map[i].id;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
str_array[offset].id = 0;
|
||||
str_array[offset].s = NULL;
|
||||
|
||||
ret = ast_vhub_str_alloc_add(vhub, &lang_str);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ast_vhub_init_desc(struct ast_vhub *vhub)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *desc_np;
|
||||
const struct device_node *vhub_np = vhub->pdev->dev.of_node;
|
||||
|
||||
/* Initialize vhub Device Descriptor. */
|
||||
memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
|
||||
sizeof(vhub->vhub_dev_desc));
|
||||
ast_vhub_of_parse_dev_desc(vhub, vhub_np);
|
||||
if (vhub->force_usb1)
|
||||
ast_vhub_fixup_usb1_dev_desc(vhub);
|
||||
|
||||
/* Initialize vhub Configuration Descriptor. */
|
||||
memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc,
|
||||
|
@ -848,15 +1029,20 @@ static void ast_vhub_init_desc(struct ast_vhub *vhub)
|
|||
vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports;
|
||||
|
||||
/* Initialize vhub String Descriptors. */
|
||||
memcpy(&vhub->vhub_str_desc, &ast_vhub_strings,
|
||||
sizeof(vhub->vhub_str_desc));
|
||||
INIT_LIST_HEAD(&vhub->vhub_str_desc);
|
||||
desc_np = of_get_child_by_name(vhub_np, "vhub-strings");
|
||||
if (desc_np)
|
||||
ret = ast_vhub_of_parse_str_desc(vhub, desc_np);
|
||||
else
|
||||
ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ast_vhub_init_hub(struct ast_vhub *vhub)
|
||||
int ast_vhub_init_hub(struct ast_vhub *vhub)
|
||||
{
|
||||
vhub->speed = USB_SPEED_UNKNOWN;
|
||||
INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
|
||||
|
||||
ast_vhub_init_desc(vhub);
|
||||
return ast_vhub_init_desc(vhub);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,14 +51,11 @@
|
|||
#define VHUB_CTRL_UPSTREAM_CONNECT (1 << 0)
|
||||
|
||||
/* IER & ISR */
|
||||
#define VHUB_IRQ_DEV1_BIT 9
|
||||
#define VHUB_IRQ_USB_CMD_DEADLOCK (1 << 18)
|
||||
#define VHUB_IRQ_EP_POOL_NAK (1 << 17)
|
||||
#define VHUB_IRQ_EP_POOL_ACK_STALL (1 << 16)
|
||||
#define VHUB_IRQ_DEVICE5 (1 << 13)
|
||||
#define VHUB_IRQ_DEVICE4 (1 << 12)
|
||||
#define VHUB_IRQ_DEVICE3 (1 << 11)
|
||||
#define VHUB_IRQ_DEVICE2 (1 << 10)
|
||||
#define VHUB_IRQ_DEVICE1 (1 << 9)
|
||||
#define VHUB_IRQ_DEVICE1 (1 << (VHUB_IRQ_DEV1_BIT))
|
||||
#define VHUB_IRQ_BUS_RESUME (1 << 8)
|
||||
#define VHUB_IRQ_BUS_SUSPEND (1 << 7)
|
||||
#define VHUB_IRQ_BUS_RESET (1 << 6)
|
||||
|
@ -402,6 +399,7 @@ struct ast_vhub {
|
|||
/* Per-port info */
|
||||
struct ast_vhub_port *ports;
|
||||
u32 max_ports;
|
||||
u32 port_irq_mask;
|
||||
|
||||
/* Generic EP data structures */
|
||||
struct ast_vhub_ep *epns;
|
||||
|
@ -423,7 +421,7 @@ struct ast_vhub {
|
|||
struct usb_device_descriptor vhub_dev_desc;
|
||||
struct ast_vhub_full_cdesc vhub_conf_desc;
|
||||
struct usb_hub_descriptor vhub_hub_desc;
|
||||
struct usb_gadget_strings vhub_str_desc;
|
||||
struct list_head vhub_str_desc;
|
||||
};
|
||||
|
||||
/* Standard request handlers result codes */
|
||||
|
@ -533,7 +531,7 @@ int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...);
|
|||
__VA_ARGS__)
|
||||
|
||||
/* hub.c */
|
||||
void ast_vhub_init_hub(struct ast_vhub *vhub);
|
||||
int ast_vhub_init_hub(struct ast_vhub *vhub);
|
||||
enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
|
||||
struct usb_ctrlrequest *crq);
|
||||
enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
|
||||
|
|
|
@ -2043,10 +2043,56 @@ static const struct usba_udc_errata at91sam9g45_errata = {
|
|||
.pulse_bias = at91sam9g45_pulse_bias,
|
||||
};
|
||||
|
||||
static const struct usba_ep_config ep_config_sam9[] __initconst = {
|
||||
{ .nr_banks = 1 }, /* ep 0 */
|
||||
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */
|
||||
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */
|
||||
{ .nr_banks = 3, .can_dma = 1 }, /* ep 3 */
|
||||
{ .nr_banks = 3, .can_dma = 1 }, /* ep 4 */
|
||||
{ .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */
|
||||
{ .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */
|
||||
};
|
||||
|
||||
static const struct usba_ep_config ep_config_sama5[] __initconst = {
|
||||
{ .nr_banks = 1 }, /* ep 0 */
|
||||
{ .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */
|
||||
{ .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */
|
||||
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 3 */
|
||||
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 4 */
|
||||
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */
|
||||
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */
|
||||
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 7 */
|
||||
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 8 */
|
||||
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 9 */
|
||||
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 10 */
|
||||
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 11 */
|
||||
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 12 */
|
||||
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 13 */
|
||||
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 14 */
|
||||
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 15 */
|
||||
};
|
||||
|
||||
static const struct usba_udc_config udc_at91sam9rl_cfg = {
|
||||
.errata = &at91sam9rl_errata,
|
||||
.config = ep_config_sam9,
|
||||
.num_ep = ARRAY_SIZE(ep_config_sam9),
|
||||
};
|
||||
|
||||
static const struct usba_udc_config udc_at91sam9g45_cfg = {
|
||||
.errata = &at91sam9g45_errata,
|
||||
.config = ep_config_sam9,
|
||||
.num_ep = ARRAY_SIZE(ep_config_sam9),
|
||||
};
|
||||
|
||||
static const struct usba_udc_config udc_sama5d3_cfg = {
|
||||
.config = ep_config_sama5,
|
||||
.num_ep = ARRAY_SIZE(ep_config_sama5),
|
||||
};
|
||||
|
||||
static const struct of_device_id atmel_udc_dt_ids[] = {
|
||||
{ .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata },
|
||||
{ .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata },
|
||||
{ .compatible = "atmel,sama5d3-udc" },
|
||||
{ .compatible = "atmel,at91sam9rl-udc", .data = &udc_at91sam9rl_cfg },
|
||||
{ .compatible = "atmel,at91sam9g45-udc", .data = &udc_at91sam9g45_cfg },
|
||||
{ .compatible = "atmel,sama5d3-udc", .data = &udc_sama5d3_cfg },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
@ -2055,18 +2101,19 @@ MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
|
|||
static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
||||
struct usba_udc *udc)
|
||||
{
|
||||
u32 val;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *pp;
|
||||
int i, ret;
|
||||
struct usba_ep *eps, *ep;
|
||||
const struct usba_udc_config *udc_config;
|
||||
|
||||
match = of_match_node(atmel_udc_dt_ids, np);
|
||||
if (!match)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
udc->errata = match->data;
|
||||
udc_config = match->data;
|
||||
udc->errata = udc_config->errata;
|
||||
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc");
|
||||
if (IS_ERR(udc->pmc))
|
||||
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc");
|
||||
|
@ -2082,8 +2129,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
|||
|
||||
if (fifo_mode == 0) {
|
||||
pp = NULL;
|
||||
while ((pp = of_get_next_child(np, pp)))
|
||||
udc->num_ep++;
|
||||
udc->num_ep = udc_config->num_ep;
|
||||
udc->configured_ep = 1;
|
||||
} else {
|
||||
udc->num_ep = usba_config_fifo_table(udc);
|
||||
|
@ -2100,52 +2146,38 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
|||
|
||||
pp = NULL;
|
||||
i = 0;
|
||||
while ((pp = of_get_next_child(np, pp)) && i < udc->num_ep) {
|
||||
while (i < udc->num_ep) {
|
||||
const struct usba_ep_config *ep_cfg = &udc_config->config[i];
|
||||
|
||||
ep = &eps[i];
|
||||
|
||||
ret = of_property_read_u32(pp, "reg", &val);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "of_probe: reg error(%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : val;
|
||||
ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : i;
|
||||
|
||||
/* Only the first EP is 64 bytes */
|
||||
if (ep->index == 0)
|
||||
ep->fifo_size = 64;
|
||||
else
|
||||
ep->fifo_size = 1024;
|
||||
|
||||
ret = of_property_read_u32(pp, "atmel,fifo-size", &val);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
if (fifo_mode) {
|
||||
if (val < udc->fifo_cfg[i].fifo_size) {
|
||||
if (ep->fifo_size < udc->fifo_cfg[i].fifo_size)
|
||||
dev_warn(&pdev->dev,
|
||||
"Using max fifo-size value from DT\n");
|
||||
ep->fifo_size = val;
|
||||
} else {
|
||||
"Using default max fifo-size value\n");
|
||||
else
|
||||
ep->fifo_size = udc->fifo_cfg[i].fifo_size;
|
||||
}
|
||||
} else {
|
||||
ep->fifo_size = val;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pp, "atmel,nb-banks", &val);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
ep->nr_banks = ep_cfg->nr_banks;
|
||||
if (fifo_mode) {
|
||||
if (val < udc->fifo_cfg[i].nr_banks) {
|
||||
if (ep->nr_banks < udc->fifo_cfg[i].nr_banks)
|
||||
dev_warn(&pdev->dev,
|
||||
"Using max nb-banks value from DT\n");
|
||||
ep->nr_banks = val;
|
||||
} else {
|
||||
"Using default max nb-banks value\n");
|
||||
else
|
||||
ep->nr_banks = udc->fifo_cfg[i].nr_banks;
|
||||
}
|
||||
} else {
|
||||
ep->nr_banks = val;
|
||||
}
|
||||
|
||||
ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
|
||||
ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
|
||||
ep->can_dma = ep_cfg->can_dma;
|
||||
ep->can_isoc = ep_cfg->can_isoc;
|
||||
|
||||
sprintf(ep->name, "ep%d", ep->index);
|
||||
ep->ep.name = ep->name;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user