Merge 7.0-rc4 into usb-next We need the USB fixes in this branch as well to build on top of Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index 11e40d2..901986d 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml
@@ -300,6 +300,40 @@ $ref: /schemas/types.yaml#/definitions/uint8-array maxItems: 4 + sink-load-step: + description: Indicates the preferred load step slew rate in mA/usec for + the port (in sink mode). This property is defined in "6.5.13.7" of + "USB Power Delivery Specification Revision 3.1 Version 1.8". + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [150, 500] + default: 150 + + sink-load-characteristics: + description: Indicates the port's (in sink mode) preferred load + characteristics. Users can leverage SINK_LOAD_CHAR() defined in + dt-bindings/usb/pd.h to populate this field. This property is defined in + "6.5.13.8" of "USB Power Delivery Specification Revision 3.1 Version 1.8". + $ref: /schemas/types.yaml#/definitions/uint16 + + sink-compliance: + description: Represents the types of sources the sink device has been tested + and certified with. This property is defined in "6.5.13.9" of + "USB Power Delivery Specification Revision 3.1 Version 1.8" + Bit 0 when set indicates it has been tested on LPS compliant source + Bit 1 when set indicates it has been tested on PS1 compliant source + Bit 2 when set indicates it has been tested on PS2 compliant source + $ref: /schemas/types.yaml#/definitions/uint8 + maximum: 7 + + charging-adapter-pdp-milliwatt: + description: This corresponds to the Power Delivery Profile rating of the + charging adapter shipped or recommended for use with the connector port. + This property is a requirement to infer the USB PD property + "SPR Sink Operational PDP" given in "6.5.13.14" of + "USB Power Delivery Specification Revision 3.1 Version 1.8". + minimum: 0 + maximum: 100000 + dependencies: sink-vdos-v1: [ sink-vdos ] sink-vdos: [ sink-vdos-v1 ]
diff --git a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml index f454ddd..a199e5b 100644 --- a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml +++ b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml
@@ -85,6 +85,7 @@ allOf: - $ref: usb-drd.yaml# + - $ref: usb-xhci.yaml# unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml index 73e7a60..66d368e 100644 --- a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml
@@ -10,6 +10,8 @@ maintainers: - Li Jun <jun.li@nxp.com> +deprecated: true + properties: compatible: oneOf:
diff --git a/Documentation/devicetree/bindings/usb/maxim,max33359.yaml b/Documentation/devicetree/bindings/usb/maxim,max33359.yaml index 3de4dc4..46a3748 100644 --- a/Documentation/devicetree/bindings/usb/maxim,max33359.yaml +++ b/Documentation/devicetree/bindings/usb/maxim,max33359.yaml
@@ -75,6 +75,10 @@ PDO_FIXED(9000, 2000, 0)>; sink-bc12-completion-time-ms = <500>; pd-revision = /bits/ 8 <0x03 0x01 0x01 0x08>; + sink-load-step = <150>; + sink-load-characteristics = /bits/ 16 <SINK_LOAD_CHAR(0, 1, 1, 2)>; + sink-compliance = /bits/ 8 <(COMPLIANCE_LPS | COMPLIANCE_PS1)>; + charging-adapter-pdp-milliwatt = <18000>; }; }; };
diff --git a/Documentation/devicetree/bindings/usb/maxim,max3421.txt b/Documentation/devicetree/bindings/usb/maxim,max3421.txt deleted file mode 100644 index 90495b1..0000000 --- a/Documentation/devicetree/bindings/usb/maxim,max3421.txt +++ /dev/null
@@ -1,23 +0,0 @@ -Maxim Integrated SPI-based USB 2.0 host controller MAX3421E - -Required properties: - - compatible: Should be "maxim,max3421" - - spi-max-frequency: maximum frequency for this device must not exceed 26 MHz. - - reg: chip select number to which this device is connected. - - maxim,vbus-en-pin: <GPOUTx ACTIVE_LEVEL> - GPOUTx is the number (1-8) of the GPOUT pin of MAX3421E to drive Vbus. - ACTIVE_LEVEL is 0 or 1. - - interrupts: the interrupt line description for the interrupt controller. - The driver configures MAX3421E for active low level triggered interrupts, - configure your interrupt line accordingly. - -Example: - - usb@0 { - compatible = "maxim,max3421"; - reg = <0>; - maxim,vbus-en-pin = <3 1>; - spi-max-frequency = <26000000>; - interrupt-parent = <&PIC>; - interrupts = <42>; - };
diff --git a/Documentation/devicetree/bindings/usb/maxim,max3421.yaml b/Documentation/devicetree/bindings/usb/maxim,max3421.yaml new file mode 100644 index 0000000..4639be7 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/maxim,max3421.yaml
@@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/maxim,max3421.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MAXIM MAX3421e USB Peripheral/Host Controller + +maintainers: + - David Mosberger <davidm@egauge.net> + +description: | + The controller provides USB2.0 compliant with Full Speed or Low Speed when in + the host mode. At peripheral, it operates at Full Speed. At both cases, it + uses a SPI interface. + Datasheet at: + https://www.analog.com/media/en/technical-documentation/data-sheets/max3421e.pdf + +properties: + compatible: + const: maxim,max3421 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + spi-max-frequency: + maximum: 26000000 + + maxim,vbus-en-pin: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + One of eight GPOUT pins to control external VBUS power and the polarity + of the active level. It's an array of GPIO number and the active level of it. + minItems: 2 + maxItems: 2 + +required: + - compatible + - reg + - interrupts + - maxim,vbus-en-pin + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + usb@0 { + compatible = "maxim,max3421"; + reg = <0>; + maxim,vbus-en-pin = <3 1>; + spi-max-frequency = <26000000>; + interrupt-parent = <&gpio>; + interrupts = <42>; + }; + };
diff --git a/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml b/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml index a812317..c4e1c2d 100644 --- a/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml +++ b/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml
@@ -37,6 +37,9 @@ clocks: maxItems: 1 + resets: + maxItems: 1 + microchip,ext-vbus-drv: description: Some ULPI USB PHYs do not support an internal VBUS supply and driving
diff --git a/Documentation/devicetree/bindings/usb/nxp,imx-dwc3.yaml b/Documentation/devicetree/bindings/usb/nxp,imx-dwc3.yaml new file mode 100644 index 0000000..1911e71 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/nxp,imx-dwc3.yaml
@@ -0,0 +1,123 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2026 NXP +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/nxp,imx-dwc3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP i.MX Soc USB Controller + +maintainers: + - Xu Yang <xu.yang_2@nxp.com> + +properties: + compatible: + oneOf: + - items: + - enum: + - nxp,imx94-dwc3 + - nxp,imx95-dwc3 + - const: nxp,imx8mp-dwc3 + - const: nxp,imx8mp-dwc3 + + reg: + items: + - description: DWC3 core registers + - description: HSIO Block Control registers + - description: Wrapper registers of dwc3 core + + reg-names: + items: + - const: core + - const: blkctl + - const: glue + + interrupts: + items: + - description: DWC3 controller interrupt + - description: Wakeup interrupt from glue logic + + interrupt-names: + items: + - const: dwc_usb3 + - const: wakeup + + iommus: + maxItems: 1 + + clocks: + items: + - description: System hsio root clock + - description: SoC Bus Clock for AHB/AXI/Native + - description: Reference clock for generating ITP when UTMI/ULPI PHY is suspended + - description: Suspend clock used for usb wakeup logic + + clock-names: + items: + - const: hsio + - const: bus_early + - const: ref + - const: suspend + + fsl,permanently-attached: + type: boolean + description: + Indicates if the device attached to a downstream port is + permanently attached + + fsl,disable-port-power-control: + type: boolean + description: + Indicates whether the host controller implementation includes port + power control. Defines Bit 3 in capability register (HCCPARAMS) + + fsl,over-current-active-low: + type: boolean + description: + Over current signal polarity is active low + + fsl,power-active-low: + type: boolean + description: + Power pad (PWR) polarity is active low + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - power-domains + +allOf: + - $ref: snps,dwc3-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + usb@4c100000 { + compatible = "nxp,imx94-dwc3", "nxp,imx8mp-dwc3"; + reg = <0x4c100000 0x10000>, + <0x4c010010 0x04>, + <0x4c1f0000 0x20>; + reg-names = "core", "blkctl", "glue"; + clocks = <&scmi_clk 74>, //IMX94_CLK_HSIO + <&scmi_clk 74>, //IMX94_CLK_HSIO + <&scmi_clk 2>, //IMX94_CLK_24M + <&scmi_clk 1>; //IMX94_CLK_32K + clock-names = "hsio", "bus_early", "ref", "suspend"; + interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 386 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "dwc_usb3", "wakeup"; + power-domains = <&scmi_devpd 13>; //IMX94_PD_HSIO_TOP + phys = <&usb3_phy>, <&usb3_phy>; + phy-names = "usb2-phy", "usb3-phy"; + snps,gfladj-refclk-lpm-sel-quirk; + snps,parkmode-disable-ss-quirk; + };
diff --git a/Documentation/devicetree/bindings/usb/ohci-st.txt b/Documentation/devicetree/bindings/usb/ohci-st.txt deleted file mode 100644 index 1c73557..0000000 --- a/Documentation/devicetree/bindings/usb/ohci-st.txt +++ /dev/null
@@ -1,36 +0,0 @@ -ST USB OHCI controller - -Required properties: - - - compatible : must be "st,st-ohci-300x" - - reg : physical base addresses of the controller and length of memory mapped - region - - interrupts : one OHCI controller interrupt should be described here - - clocks : phandle list of usb clocks - - clock-names : should be "ic" for interconnect clock and "clk48" -See: Documentation/devicetree/bindings/clock/clock-bindings.txt - - - phys : phandle for the PHY device - - phy-names : should be "usb" - - - resets : phandle to the powerdown and reset controller for the USB IP - - reset-names : should be "power" and "softreset". -See: Documentation/devicetree/bindings/reset/st,stih407-powerdown.yaml -See: Documentation/devicetree/bindings/reset/reset.txt - -Example: - - ohci0: usb@fe1ffc00 { - compatible = "st,st-ohci-300x"; - reg = <0xfe1ffc00 0x100>; - interrupts = <GIC_SPI 149 IRQ_TYPE_NONE>; - clocks = <&clk_s_a1_ls 0>, - <&clockgen_b0 0>; - clock-names = "ic", "clk48"; - phys = <&usb2_phy>; - phy-names = "usb"; - - resets = <&powerdown STIH416_USB0_POWERDOWN>, - <&softreset STIH416_USB0_SOFTRESET>; - reset-names = "power", "softreset"; - };
diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt deleted file mode 100644 index f0dbc5a..0000000 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ /dev/null
@@ -1,80 +0,0 @@ -OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS - -OMAP MUSB GLUE - - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb" - - ti,hwmods : must be "usb_otg_hs" - - multipoint : Should be "1" indicating the musb controller supports - multipoint. This is a MUSB configuration-specific setting. - - num-eps : Specifies the number of endpoints. This is also a - MUSB configuration-specific setting. Should be set to "16" - - ram-bits : Specifies the ram address size. Should be set to "12" - - interface-type : This is a board specific setting to describe the type of - interface between the controller and the phy. It should be "0" or "1" - specifying ULPI and UTMI respectively. - - mode : Should be "3" to represent OTG. "1" signifies HOST and "2" - represents PERIPHERAL. - - power : Should be "50". This signifies the controller can supply up to - 100mA when operating in host mode. - - usb-phy : the phandle for the PHY device - - phys : the phandle for the PHY device (used by generic PHY framework) - - phy-names : the names of the PHY corresponding to the PHYs present in the - *phy* phandle. - -Optional properties: - - ctrl-module : phandle of the control module this glue uses to write to - mailbox - -SOC specific device node entry -usb_otg_hs: usb_otg_hs@4a0ab000 { - compatible = "ti,omap4-musb"; - ti,hwmods = "usb_otg_hs"; - multipoint = <1>; - num-eps = <16>; - ram-bits = <12>; - ctrl-module = <&omap_control_usb>; - phys = <&usb2_phy>; - phy-names = "usb2-phy"; -}; - -Board specific device node entry -&usb_otg_hs { - interface-type = <1>; - mode = <3>; - power = <50>; -}; - -OMAP DWC3 GLUE - - compatible : Should be - * "ti,dwc3" for OMAP5 and DRA7 - * "ti,am437x-dwc3" for AM437x - - ti,hwmods : Should be "usb_otg_ss" - - reg : Address and length of the register set for the device. - - interrupts : The irq number of this device that is used to interrupt the - MPU - - #address-cells, #size-cells : Must be present if the device has sub-nodes - - utmi-mode : controls the source of UTMI/PIPE status for VBUS and OTG ID. - It should be set to "1" for HW mode and "2" for SW mode. - - ranges: the child address space are mapped 1:1 onto the parent address space - -Optional Properties: - - extcon : phandle for the extcon device omap dwc3 uses to detect - connect/disconnect events. - - vbus-supply : phandle to the regulator device tree node if needed. - -Sub-nodes: -The dwc3 core should be added as subnode to omap dwc3 glue. -- dwc3 : - The binding details of dwc3 can be found in: - Documentation/devicetree/bindings/usb/snps,dwc3.yaml - -omap_dwc3 { - compatible = "ti,dwc3"; - ti,hwmods = "usb_otg_ss"; - reg = <0x4a020000 0x1ff>; - interrupts = <0 93 4>; - #address-cells = <1>; - #size-cells = <1>; - utmi-mode = <2>; - ranges; -}; -
diff --git a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml index 7d784a6..f879e2e 100644 --- a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml
@@ -500,7 +500,7 @@ - const: pwr_event - const: dp_hs_phy_irq - const: dm_hs_phy_irq - - const: ss_phy_irq + - enum: [hs_phy_irq, ss_phy_irq] - if: properties:
diff --git a/Documentation/devicetree/bindings/usb/richtek,rt1711h.yaml b/Documentation/devicetree/bindings/usb/richtek,rt1711h.yaml index ae611f7..1eb611f 100644 --- a/Documentation/devicetree/bindings/usb/richtek,rt1711h.yaml +++ b/Documentation/devicetree/bindings/usb/richtek,rt1711h.yaml
@@ -19,10 +19,11 @@ properties: compatible: enum: + - etekmicro,et7304 - richtek,rt1711h - richtek,rt1715 description: - RT1711H support PD20, RT1715 support PD30 except Fast Role Swap. + RT1711H support PD20, ET7304 and RT1715 support PD30 except Fast Role Swap. reg: maxItems: 1
diff --git a/Documentation/devicetree/bindings/usb/st,st-ohci-300x.yaml b/Documentation/devicetree/bindings/usb/st,st-ohci-300x.yaml new file mode 100644 index 0000000..a225bf5 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/st,st-ohci-300x.yaml
@@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/st,st-ohci-300x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics USB OHCI Controller + +maintainers: + - Peter Griffin <peter.griffin@linaro.org> + +description: + The STMicroelectronics USB Open Host Controller Interface (OHCI) + compliant USB host controller found in ST platforms. The controller + provides full- and low-speed USB host functionality and interfaces + with an external USB PHY. It requires dedicated clock, reset, and + interrupt resources for proper operation. + +allOf: + - $ref: /schemas/usb/usb-hcd.yaml# + +properties: + compatible: + const: st,st-ohci-300x + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: ic + - const: clk48 + + phys: + maxItems: 1 + + phy-names: + items: + - const: usb + + resets: + maxItems: 2 + + reset-names: + items: + - const: power + - const: softreset + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - phys + - phy-names + - resets + - reset-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/reset/stih407-resets.h> + usb@fe1ffc00 { + compatible = "st,st-ohci-300x"; + reg = <0xfe1ffc00 0x100>; + interrupts = <GIC_SPI 149 IRQ_TYPE_NONE>; + clocks = <&clk_s_a1_ls 0>, + <&clockgen_b0 0>; + clock-names = "ic", "clk48"; + phys = <&usb2_phy>; + phy-names = "usb"; + resets = <&powerdown STIH407_USB2_PORT0_POWERDOWN>, + <&softreset STIH407_USB2_PORT0_SOFTRESET>; + reset-names = "power", "softreset"; + }; +...
diff --git a/Documentation/devicetree/bindings/usb/ti,dwc3.yaml b/Documentation/devicetree/bindings/usb/ti,dwc3.yaml new file mode 100644 index 0000000..77ac11c --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ti,dwc3.yaml
@@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/ti,dwc3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments OMAP DWC3 USB Glue Layer + +maintainers: + - Felipe Balbi <balbi@ti.com> + +description: + Texas Instruments glue layer for Synopsys DesignWare USB3 (DWC3) + controller on OMAP and AM43xx SoCs. Manages SoC-specific integration + including register mapping, interrupt routing, UTMI/PIPE interface mode + selection (HW/SW), and child DWC3 core instantiation via address space + translation. Supports both legacy single-instance and multi-instance + (numbered) configurations. + +properties: + compatible: + enum: + - ti,dwc3 + - ti,am437x-dwc3 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + utmi-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Controls the source of UTMI/PIPE status for VBUS and OTG ID. + 1 for HW mode, 2 for SW mode. + enum: [1, 2] + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + + extcon: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle for the extcon device used to detect connect/ + disconnect events. + + vbus-supply: + description: Phandle to the regulator device tree node if needed. + +patternProperties: + "^usb@[0-9a-f]+$": + type: object + $ref: snps,dwc3.yaml# + unevaluatedProperties: false + +required: + - reg + - compatible + - interrupts + - "#address-cells" + - "#size-cells" + - utmi-mode + - ranges + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + omap_dwc3_1@0 { + compatible = "ti,dwc3"; + reg = <0x0 0x10000>; + interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <1>; + utmi-mode = <2>; + ranges = <0 0 0x20000>; + + usb@10000 { + compatible = "snps,dwc3"; + reg = <0x10000 0x17000>; + interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "peripheral", "host", "otg"; + phys = <&usb2_phy1>, <&usb3_phy1>; + phy-names = "usb2-phy", "usb3-phy"; + maximum-speed = "super-speed"; + dr_mode = "otg"; + snps,dis_u3_susphy_quirk; + snps,dis_u2_susphy_quirk; + }; + }; +...
diff --git a/Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml b/Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml new file mode 100644 index 0000000..a3d15f2 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml
@@ -0,0 +1,120 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/ti,omap4-musb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments OMAP MUSB USB OTG Controller + +maintainers: + - Felipe Balbi <balbi@ti.com> + +description: + Texas Instruments glue layer for the Mentor Graphics MUSB OTG controller. + Handles SoC-specific integration including PHY interface bridging(ULPI/ + UTMI), interrupt aggregation, DMA engine coordination (internal/ + external), VBUS/session control via control module mailbox, and + clock/reset management. Provides fixed hardware configuration parameters + to the generic MUSB core driver. + +properties: + compatible: + enum: + - ti,omap3-musb + - ti,omap4-musb + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + items: + - const: mc + - const: dma + + multipoint: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Indicates the MUSB controller supports multipoint. This is a MUSB + configuration-specific setting. + const: 1 + + num-eps: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Specifies the number of endpoints. This is a MUSB configuration + specific setting. + const: 16 + + ram-bits: + description: Specifies the RAM address size. + const: 12 + + interface-type: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Describes the type of interface between the controller and the PHY. + 0 for ULPI, 1 for UTMI. + enum: [0, 1] + + mode: + $ref: /schemas/types.yaml#/definitions/uint32 + description: 1 for HOST, 2 for PERIPHERAL, 3 for OTG. + enum: [1, 2, 3] + + power: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Indicates the maximum current the controller can supply when + operating in host mode. A value of 50 corresponds to 100 mA, and a + value of 150 corresponds to 300 mA. + enum: [50, 150] + + phys: + maxItems: 1 + + phy-names: + const: usb2-phy + + usb-phy: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: Phandle for the PHY device. + deprecated: true + + ctrl-module: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle of the control module this glue uses to write to mailbox. + +required: + - reg + - compatible + - interrupts + - interrupt-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + usb@4a0ab000 { + compatible = "ti,omap4-musb"; + reg = <0x4a0ab000 0x1000>; + interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "mc", "dma"; + multipoint = <1>; + num-eps = <16>; + ram-bits = <12>; + ctrl-module = <&omap_control_usb>; + phys = <&usb2_phy>; + phy-names = "usb2-phy"; + interface-type = <1>; + mode = <3>; + power = <50>; + }; +...
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index ee7fd3c..5e504ce 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -541,6 +541,8 @@ description: ESTeem Wireless Modems "^eswin,.*": description: Beijing ESWIN Technology Group Co. Ltd. + "^etekmicro,.*": + description: Wuxi ETEK Micro-Electronics Co.,Ltd. "^ettus,.*": description: NI Ettus Research "^eukrea,.*":
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 4137ab4..e00eaf9 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c
@@ -26,14 +26,6 @@ struct ep_device { #define to_ep_device(_dev) \ container_of(_dev, struct ep_device, dev) -struct ep_attribute { - struct attribute attr; - ssize_t (*show)(struct usb_device *, - struct usb_endpoint_descriptor *, char *); -}; -#define to_ep_attribute(_attr) \ - container_of(_attr, struct ep_attribute, attr) - #define usb_ep_attr(field, format_string) \ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 240b15b..1816972 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig
@@ -150,6 +150,18 @@ functionality. Say 'Y' or 'M' if you have one such device. +config USB_DWC3_IMX + tristate "NXP iMX Platform" + depends on OF && COMMON_CLK + depends on (ARCH_MXC && ARM64) || COMPILE_TEST + default USB_DWC3 + help + NXP iMX SoC use DesignWare Core IP for USB2/3 + functionality. + This driver also handles the wakeup feature outside + of DesignWare Core. + Say 'Y' or 'M' if you have one such device. + config USB_DWC3_XILINX tristate "Xilinx Platforms" depends on (ARCH_ZYNQMP || COMPILE_TEST) && OF
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 073bef5..f379711 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom-legacy.o obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o +obj-$(CONFIG_USB_DWC3_IMX) += dwc3-imx.o obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 161a4d5..58899b1 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c
@@ -782,6 +782,24 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index) return 0; } +static void dwc3_ulpi_setup(struct dwc3 *dwc) +{ + int index; + u32 reg; + + /* Don't do anything if there is no ULPI PHY */ + if (!dwc->ulpi) + return; + + if (dwc->enable_usb2_transceiver_delay) { + for (index = 0; index < dwc->num_usb2_ports; index++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); + reg |= DWC3_GUSB2PHYCFG_XCVRDLY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + } + } +} + /** * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * @dwc: Pointer to our controller context structure @@ -1363,6 +1381,8 @@ int dwc3_core_init(struct dwc3 *dwc) dwc->ulpi_ready = true; } + dwc3_ulpi_setup(dwc); + if (!dwc->phys_ready) { ret = dwc3_core_get_phy(dwc); if (ret) @@ -1675,6 +1695,9 @@ static void dwc3_get_software_properties(struct dwc3 *dwc, u16 gsbuscfg0_reqinfo; int ret; + if (properties->needs_full_reinit) + dwc->needs_full_reinit = true; + dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED; if (properties->gsbuscfg0_reqinfo != @@ -2479,7 +2502,8 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) dwc3_core_exit(dwc); break; case DWC3_GCTL_PRTCAP_HOST: - if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) { + if (!PMSG_IS_AUTO(msg) && + (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) { dwc3_core_exit(dwc); break; } @@ -2542,7 +2566,8 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) dwc3_gadget_resume(dwc); break; case DWC3_GCTL_PRTCAP_HOST: - if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) { + if (!PMSG_IS_AUTO(msg) && + (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) { ret = dwc3_core_init_for_resume(dwc); if (ret) return ret;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index a35b3db..e0dee9d 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h
@@ -302,6 +302,7 @@ #define DWC3_GUSB2PHYCFG_SUSPHY BIT(6) #define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4) #define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8) +#define DWC3_GUSB2PHYCFG_XCVRDLY BIT(9) #define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3) #define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1) #define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10) @@ -1119,6 +1120,8 @@ struct dwc3_glue_ops { * @usb3_lpm_capable: set if hadrware supports Link Power Management * @usb2_lpm_disable: set to disable usb2 lpm for host * @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget + * @needs_full_reinit: set to indicate the core may lose power and need full + * initialization during system pm * @disable_scramble_quirk: set if we enable the disable scramble quirk * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk @@ -1149,7 +1152,7 @@ struct dwc3_glue_ops { * VBUS with an external supply. * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed * instances in park mode. - * @parkmode_disable_hs_quirk: set if we need to disable all HishSpeed + * @parkmode_disable_hs_quirk: set if we need to disable all HighSpeed * instances in park mode. * @gfladj_refclk_lpm_sel: set if we need to enable SOF/ITP counter * running based on ref_clk @@ -1161,6 +1164,8 @@ struct dwc3_glue_ops { * 3 - Reserved * @dis_metastability_quirk: set to disable metastability quirk. * @dis_split_quirk: set to disable split boundary. + * @enable_usb2_transceiver_delay: Set to insert a delay before the + * assertion of the TxValid signal during a HS Chirp. * @sys_wakeup: set if the device may do system wakeup. * @wakeup_configured: set if the device is configured for remote wakeup. * @suspended: set to track suspend event due to U3/L2. @@ -1373,6 +1378,7 @@ struct dwc3 { unsigned usb3_lpm_capable:1; unsigned usb2_lpm_disable:1; unsigned usb2_gadget_lpm_disable:1; + unsigned needs_full_reinit:1; unsigned disable_scramble_quirk:1; unsigned u2exit_lfps_quirk:1; @@ -1403,6 +1409,7 @@ struct dwc3 { unsigned dis_metastability_quirk:1; unsigned dis_split_quirk:1; + unsigned enable_usb2_transceiver_delay:1; unsigned async_callbacks:1; unsigned sys_wakeup:1; unsigned wakeup_configured:1;
diff --git a/drivers/usb/dwc3/dwc3-imx.c b/drivers/usb/dwc3/dwc3-imx.c new file mode 100644 index 0000000..303708f --- /dev/null +++ b/drivers/usb/dwc3/dwc3-imx.c
@@ -0,0 +1,442 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dwc3-imx.c - NXP i.MX Soc USB3 Specific Glue layer + * + * Copyright 2026 NXP + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#include "core.h" +#include "glue.h" + +/* USB wakeup registers */ +#define USB_WAKEUP_CTRL 0x00 + +/* Global wakeup interrupt enable, also used to clear interrupt */ +#define USB_WAKEUP_EN BIT(31) +/* Wakeup from connect or disconnect, only for superspeed */ +#define USB_WAKEUP_SS_CONN BIT(5) +/* 0 select vbus_valid, 1 select sessvld */ +#define USB_WAKEUP_VBUS_SRC_SESS_VAL BIT(4) +/* Enable signal for wake up from u3 state */ +#define USB_WAKEUP_U3_EN BIT(3) +/* Enable signal for wake up from id change */ +#define USB_WAKEUP_ID_EN BIT(2) +/* Enable signal for wake up from vbus change */ +#define USB_WAKEUP_VBUS_EN BIT(1) +/* Enable signal for wake up from dp/dm change */ +#define USB_WAKEUP_DPDM_EN BIT(0) + +#define USB_WAKEUP_EN_MASK GENMASK(5, 0) + +/* USB glue registers */ +#define USB_CTRL0 0x00 +#define USB_CTRL1 0x04 + +#define USB_CTRL0_PORTPWR_EN BIT(12) /* 1 - PPC enabled (default) */ +#define USB_CTRL0_USB3_FIXED BIT(22) /* 1 - USB3 permanent attached */ +#define USB_CTRL0_USB2_FIXED BIT(23) /* 1 - USB2 permanent attached */ + +#define USB_CTRL1_OC_POLARITY BIT(16) /* 0 - HIGH / 1 - LOW */ +#define USB_CTRL1_PWR_POLARITY BIT(17) /* 0 - HIGH / 1 - LOW */ + +struct dwc3_imx { + struct dwc3 dwc; + struct device *dev; + void __iomem *blkctl_base; + void __iomem *glue_base; + struct clk *hsio_clk; + struct clk *suspend_clk; + int irq; + bool pm_suspended; + bool wakeup_pending; + unsigned permanent_attached:1; + unsigned disable_pwr_ctrl:1; + unsigned overcur_active_low:1; + unsigned power_active_low:1; +}; + +#define to_dwc3_imx(d) container_of((d), struct dwc3_imx, dwc) + +static void dwc3_imx_get_property(struct dwc3_imx *dwc_imx) +{ + struct device *dev = dwc_imx->dev; + + dwc_imx->permanent_attached = + device_property_read_bool(dev, "fsl,permanently-attached"); + dwc_imx->disable_pwr_ctrl = + device_property_read_bool(dev, "fsl,disable-port-power-control"); + dwc_imx->overcur_active_low = + device_property_read_bool(dev, "fsl,over-current-active-low"); + dwc_imx->power_active_low = + device_property_read_bool(dev, "fsl,power-active-low"); +} + +static void dwc3_imx_configure_glue(struct dwc3_imx *dwc_imx) +{ + u32 value; + + if (!dwc_imx->glue_base) + return; + + value = readl(dwc_imx->glue_base + USB_CTRL0); + + if (dwc_imx->permanent_attached) + value |= USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED; + else + value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED); + + if (dwc_imx->disable_pwr_ctrl) + value &= ~USB_CTRL0_PORTPWR_EN; + else + value |= USB_CTRL0_PORTPWR_EN; + + writel(value, dwc_imx->glue_base + USB_CTRL0); + + value = readl(dwc_imx->glue_base + USB_CTRL1); + if (dwc_imx->overcur_active_low) + value |= USB_CTRL1_OC_POLARITY; + else + value &= ~USB_CTRL1_OC_POLARITY; + + if (dwc_imx->power_active_low) + value |= USB_CTRL1_PWR_POLARITY; + else + value &= ~USB_CTRL1_PWR_POLARITY; + + writel(value, dwc_imx->glue_base + USB_CTRL1); +} + +static void dwc3_imx_wakeup_enable(struct dwc3_imx *dwc_imx, pm_message_t msg) +{ + struct dwc3 *dwc = &dwc_imx->dwc; + u32 val; + + val = readl(dwc_imx->blkctl_base + USB_WAKEUP_CTRL); + + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->xhci) { + val |= USB_WAKEUP_EN | USB_WAKEUP_DPDM_EN; + if (PMSG_IS_AUTO(msg)) + val |= USB_WAKEUP_SS_CONN | USB_WAKEUP_U3_EN; + } else { + val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN | + USB_WAKEUP_VBUS_SRC_SESS_VAL; + } + + writel(val, dwc_imx->blkctl_base + USB_WAKEUP_CTRL); +} + +static void dwc3_imx_wakeup_disable(struct dwc3_imx *dwc_imx) +{ + u32 val; + + val = readl(dwc_imx->blkctl_base + USB_WAKEUP_CTRL); + val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK); + writel(val, dwc_imx->blkctl_base + USB_WAKEUP_CTRL); +} + +static irqreturn_t dwc3_imx_interrupt(int irq, void *data) +{ + struct dwc3_imx *dwc_imx = data; + struct dwc3 *dwc = &dwc_imx->dwc; + + if (!dwc_imx->pm_suspended) + return IRQ_HANDLED; + + disable_irq_nosync(dwc_imx->irq); + dwc_imx->wakeup_pending = true; + + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->xhci) + pm_runtime_resume(&dwc->xhci->dev); + else if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) + pm_runtime_get(dwc->dev); + + return IRQ_HANDLED; +} + +static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role) +{ + if (role == USB_ROLE_HOST) + /* + * For xhci host, we need disable dwc core auto + * suspend, because during this auto suspend delay(5s), + * xhci host RUN_STOP is cleared and wakeup is not + * enabled, if device is inserted, xhci host can't + * response the connection. + */ + pm_runtime_dont_use_autosuspend(dwc->dev); + else + pm_runtime_use_autosuspend(dwc->dev); +} + +static struct dwc3_glue_ops dwc3_imx_glue_ops = { + .pre_set_role = dwc3_imx_pre_set_role, +}; + +static const struct property_entry dwc3_imx_properties[] = { + PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk"), + PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk"), + {}, +}; + +static const struct software_node dwc3_imx_swnode = { + .properties = dwc3_imx_properties, +}; + +static int dwc3_imx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3_imx *dwc_imx; + struct dwc3 *dwc; + struct resource *res; + const char *irq_name; + struct dwc3_probe_data probe_data = {}; + int ret, irq; + + dwc_imx = devm_kzalloc(dev, sizeof(*dwc_imx), GFP_KERNEL); + if (!dwc_imx) + return -ENOMEM; + + platform_set_drvdata(pdev, dwc_imx); + dwc_imx->dev = dev; + + dwc3_imx_get_property(dwc_imx); + + dwc_imx->blkctl_base = devm_platform_ioremap_resource_byname(pdev, "blkctl"); + if (IS_ERR(dwc_imx->blkctl_base)) + return PTR_ERR(dwc_imx->blkctl_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "glue"); + if (!res) { + dev_warn(dev, "Base address for glue layer missing\n"); + } else { + dwc_imx->glue_base = devm_ioremap_resource(dev, res); + if (IS_ERR(dwc_imx->glue_base)) + return PTR_ERR(dwc_imx->glue_base); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); + if (!res) + return dev_err_probe(dev, -ENODEV, "missing core memory resource\n"); + + dwc_imx->hsio_clk = devm_clk_get_enabled(dev, "hsio"); + if (IS_ERR(dwc_imx->hsio_clk)) + return dev_err_probe(dev, PTR_ERR(dwc_imx->hsio_clk), + "Failed to get hsio clk\n"); + + dwc_imx->suspend_clk = devm_clk_get_enabled(dev, "suspend"); + if (IS_ERR(dwc_imx->suspend_clk)) + return dev_err_probe(dev, PTR_ERR(dwc_imx->suspend_clk), + "Failed to get suspend clk\n"); + + irq = platform_get_irq_byname(pdev, "wakeup"); + if (irq < 0) + return irq; + dwc_imx->irq = irq; + + irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", dev_name(dev)); + if (!irq_name) + return dev_err_probe(dev, -ENOMEM, "failed to create irq_name\n"); + + ret = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx_interrupt, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + irq_name, dwc_imx); + if (ret) + return dev_err_probe(dev, ret, "failed to request IRQ #%d\n", irq); + + ret = device_add_software_node(dev, &dwc3_imx_swnode); + if (ret) + return dev_err_probe(dev, ret, "failed to add software node\n"); + + dwc3_imx_configure_glue(dwc_imx); + + dwc = &dwc_imx->dwc; + dwc->dev = dev; + dwc->glue_ops = &dwc3_imx_glue_ops; + + probe_data.res = res; + probe_data.dwc = dwc; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + probe_data.properties.needs_full_reinit = true; + + ret = dwc3_core_probe(&probe_data); + if (ret) { + device_remove_software_node(dev); + return ret; + } + + device_set_wakeup_capable(dev, true); + return 0; +} + +static void dwc3_imx_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3 *dwc = dev_get_drvdata(dev); + + dwc3_core_remove(dwc); + device_remove_software_node(dev); +} + +static void dwc3_imx_suspend(struct dwc3_imx *dwc_imx, pm_message_t msg) +{ + if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc_imx->dev)) + dwc3_imx_wakeup_enable(dwc_imx, msg); + + enable_irq(dwc_imx->irq); + dwc_imx->pm_suspended = true; +} + +static void dwc3_imx_resume(struct dwc3_imx *dwc_imx, pm_message_t msg) +{ + struct dwc3 *dwc = &dwc_imx->dwc; + + dwc_imx->pm_suspended = false; + if (!dwc_imx->wakeup_pending) + disable_irq_nosync(dwc_imx->irq); + + dwc3_imx_wakeup_disable(dwc_imx); + + /* Upon power loss any previous configuration is lost, restore it */ + dwc3_imx_configure_glue(dwc_imx); + + if (dwc_imx->wakeup_pending) { + dwc_imx->wakeup_pending = false; + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) + pm_runtime_put_autosuspend(dwc->dev); + else + /* + * Add wait for xhci switch from suspend + * clock to normal clock to detect connection. + */ + usleep_range(9000, 10000); + } +} + +static int dwc3_imx_runtime_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + int ret; + + ret = dwc3_runtime_suspend(dwc); + if (ret) + return ret; + + dwc3_imx_suspend(dwc_imx, PMSG_AUTO_SUSPEND); + return 0; +} + +static int dwc3_imx_runtime_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + + dwc3_imx_resume(dwc_imx, PMSG_AUTO_RESUME); + return dwc3_runtime_resume(dwc); +} + +static int dwc3_imx_runtime_idle(struct device *dev) +{ + return dwc3_runtime_idle(dev_get_drvdata(dev)); +} + +static int dwc3_imx_pm_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + int ret; + + ret = dwc3_pm_suspend(dwc); + if (ret) + return ret; + + dwc3_imx_suspend(dwc_imx, PMSG_SUSPEND); + + if (device_may_wakeup(dev)) { + enable_irq_wake(dwc_imx->irq); + device_set_out_band_wakeup(dev); + } else { + clk_disable_unprepare(dwc_imx->suspend_clk); + } + + clk_disable_unprepare(dwc_imx->hsio_clk); + + return 0; +} + +static int dwc3_imx_pm_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + int ret; + + if (device_may_wakeup(dwc_imx->dev)) { + disable_irq_wake(dwc_imx->irq); + } else { + ret = clk_prepare_enable(dwc_imx->suspend_clk); + if (ret) + return ret; + } + + ret = clk_prepare_enable(dwc_imx->hsio_clk); + if (ret) { + clk_disable_unprepare(dwc_imx->suspend_clk); + return ret; + } + + dwc3_imx_resume(dwc_imx, PMSG_RESUME); + + ret = dwc3_pm_resume(dwc); + if (ret) + return ret; + + return 0; +} + +static void dwc3_imx_complete(struct device *dev) +{ + dwc3_pm_complete(dev_get_drvdata(dev)); +} + +static int dwc3_imx_prepare(struct device *dev) +{ + return dwc3_pm_prepare(dev_get_drvdata(dev)); +} + +static const struct dev_pm_ops dwc3_imx_dev_pm_ops = { + SYSTEM_SLEEP_PM_OPS(dwc3_imx_pm_suspend, dwc3_imx_pm_resume) + RUNTIME_PM_OPS(dwc3_imx_runtime_suspend, dwc3_imx_runtime_resume, + dwc3_imx_runtime_idle) + .complete = pm_sleep_ptr(dwc3_imx_complete), + .prepare = pm_sleep_ptr(dwc3_imx_prepare), +}; + +static const struct of_device_id dwc3_imx_of_match[] = { + { .compatible = "nxp,imx8mp-dwc3", }, + {}, +}; +MODULE_DEVICE_TABLE(of, dwc3_imx_of_match); + +static struct platform_driver dwc3_imx_driver = { + .probe = dwc3_imx_probe, + .remove = dwc3_imx_remove, + .driver = { + .name = "imx-dwc3", + .pm = pm_ptr(&dwc3_imx_dev_pm_ops), + .of_match_table = dwc3_imx_of_match, + }, +}; + +module_platform_driver(dwc3_imx_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DesignWare USB3 i.MX Glue Layer");
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 9ac7554..f43f73a 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -526,14 +526,14 @@ static int dwc3_qcom_find_num_ports(struct platform_device *pdev) int irq; irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1"); - if (irq <= 0) + if (irq < 0) return 1; for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) { sprintf(irq_name, "dp_hs_phy_%d", port_num); irq = platform_get_irq_byname_optional(pdev, irq_name); - if (irq <= 0) + if (irq < 0) return port_num - 1; }
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0a68890..3d4ca68 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c
@@ -1688,7 +1688,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) * transfer, there's no need to update the transfer. */ if (!ret && !starting) - return ret; + return 0; req = next_request(&dep->started_list); if (!req) {
diff --git a/drivers/usb/dwc3/glue.h b/drivers/usb/dwc3/glue.h index df86e14..d738e17 100644 --- a/drivers/usb/dwc3/glue.h +++ b/drivers/usb/dwc3/glue.h
@@ -12,9 +12,12 @@ /** * dwc3_properties: DWC3 core properties * @gsbuscfg0_reqinfo: Value to be programmed in the GSBUSCFG0.REQINFO field + * @needs_full_reinit: indicate the controller may not remain power during system + * pm and need full initialization */ struct dwc3_properties { u32 gsbuscfg0_reqinfo; + unsigned needs_full_reinit:1; }; #define DWC3_DEFAULT_PROPERTIES ((struct dwc3_properties){ \
diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c index 57daad1..a256b7f 100644 --- a/drivers/usb/dwc3/ulpi.c +++ b/drivers/usb/dwc3/ulpi.c
@@ -10,10 +10,13 @@ #include <linux/delay.h> #include <linux/time64.h> #include <linux/ulpi/regs.h> +#include <linux/ulpi/driver.h> #include "core.h" #include "io.h" +#define USB_VENDOR_MICROCHIP 0x0424 + #define DWC3_ULPI_ADDR(a) \ ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ @@ -83,6 +86,26 @@ static const struct ulpi_ops dwc3_ulpi_ops = { .write = dwc3_ulpi_write, }; +static void dwc3_ulpi_detect_config(struct dwc3 *dwc) +{ + struct ulpi *ulpi = dwc->ulpi; + + switch (ulpi->id.vendor) { + case USB_VENDOR_MICROCHIP: + switch (ulpi->id.product) { + case 0x0009: + /* Microchip USB3340 ULPI PHY */ + dwc->enable_usb2_transceiver_delay = true; + break; + default: + break; + } + break; + default: + break; + } +} + int dwc3_ulpi_init(struct dwc3 *dwc) { /* Register the interface */ @@ -92,6 +115,8 @@ int dwc3_ulpi_init(struct dwc3 *dwc) return PTR_ERR(dwc->ulpi); } + dwc3_ulpi_detect_config(dwc); + return 0; }
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index b5f0def..19fdac0 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c
@@ -1541,9 +1541,9 @@ static int f_midi2_create_card(struct f_midi2 *midi2) return err; midi2->card = card; - strcpy(card->driver, "f_midi2"); - strcpy(card->shortname, "MIDI 2.0 Gadget"); - strcpy(card->longname, "MIDI 2.0 Gadget"); + strscpy(card->driver, "f_midi2"); + strscpy(card->shortname, "MIDI 2.0 Gadget"); + strscpy(card->longname, "MIDI 2.0 Gadget"); id = 0; for (i = 0; i < midi2->num_eps; i++) {
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index e43ad63..cdd1dfc 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c
@@ -1086,7 +1086,7 @@ static int gs_console_init(struct gs_port *port) if (!cons) return -ENOMEM; - strcpy(cons->console.name, "ttyGS"); + strscpy(cons->console.name, "ttyGS"); cons->console.write = gs_console_write; cons->console.device = gs_console_device; cons->console.flags = CON_PRINTBUFFER;
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index 5f95146..0e0db68 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -3151,7 +3151,7 @@ int udc_probe(struct udc *dev) tmp, dev->phys_addr, dev->chiprev, (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1"); - strcpy(tmp, UDC_DRIVER_VERSION_STRING); + strscpy(tmp, UDC_DRIVER_VERSION_STRING); if (dev->chiprev == UDC_HSA0_REV) { dev_err(dev->dev, "chip revision is A0; too old\n"); retval = -ENODEV;
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 34abff8..eaaa497 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c
@@ -12,7 +12,6 @@ #include <linux/clk.h> #include <linux/platform_data/usb-ehci-orion.h> #include <linux/of.h> -#include <linux/phy/phy.h> #include <linux/usb.h> #include <linux/usb/hcd.h> #include <linux/io.h> @@ -60,7 +59,6 @@ struct orion_ehci_hcd { struct clk *clk; - struct phy *phy; }; static struct hc_driver __read_mostly ehci_orion_hc_driver; @@ -276,13 +274,6 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) goto err_put_hcd; } - priv->phy = devm_phy_optional_get(&pdev->dev, "usb"); - if (IS_ERR(priv->phy)) { - err = PTR_ERR(priv->phy); - if (err != -ENOSYS) - goto err_dis_clk; - } - /* * (Re-)program MBUS remapping windows if we are asked to. */
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 3f6aa24..ed4b11f 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c
@@ -1357,15 +1357,11 @@ static void tegra_xhci_id_work(struct work_struct *work) dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode)); - mutex_lock(&tegra->lock); - if (tegra->host_mode) phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST); else phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); - mutex_unlock(&tegra->lock); - tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(tegra->padctl, tegra->otg_usb2_port);
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 45dc209..1f0b3c4 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c
@@ -55,7 +55,6 @@ * Status: * * Untested with multiple scanners. - * Untested on SMP. * Untested on a bigendian machine. * * History: @@ -170,25 +169,13 @@ static struct usb_driver mts_usb_driver = { #define MTS_VERSION "0.4.3" #define MTS_NAME "microtek usb (rev " MTS_VERSION "): " -#define MTS_WARNING(x...) \ - printk( KERN_WARNING MTS_NAME x ) -#define MTS_ERROR(x...) \ - printk( KERN_ERR MTS_NAME x ) -#define MTS_INT_ERROR(x...) \ - MTS_ERROR(x) -#define MTS_MESSAGE(x...) \ - printk( KERN_INFO MTS_NAME x ) - #if defined MTS_DO_DEBUG #define MTS_DEBUG(x...) \ printk( KERN_DEBUG MTS_NAME x ) -#define MTS_DEBUG_GOT_HERE() \ - MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __func__ ) #define MTS_DEBUG_INT() \ - do { MTS_DEBUG_GOT_HERE(); \ - MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \ + do { MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \ MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",transfer->status,(int)context->data_length, (int)transfer->actual_length ); \ mts_debug_dump(context->instance);\ } while(0) @@ -197,7 +184,6 @@ static struct usb_driver mts_usb_driver = { #define MTS_NUL_STATEMENT do { } while(0) #define MTS_DEBUG(x...) MTS_NUL_STATEMENT -#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT #define MTS_DEBUG_INT() MTS_NUL_STATEMENT #endif @@ -316,7 +302,6 @@ static inline void mts_debug_dump(struct mts_desc* dummy) #endif static inline void mts_urb_abort(struct mts_desc* desc) { - MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); usb_kill_urb( desc->urb ); @@ -332,8 +317,6 @@ static int mts_scsi_abort(struct scsi_cmnd *srb) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); - MTS_DEBUG_GOT_HERE(); - mts_urb_abort(desc); return FAILED; @@ -344,7 +327,6 @@ static int mts_scsi_host_reset(struct scsi_cmnd *srb) struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); int result; - MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); result = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf); @@ -386,7 +368,8 @@ void mts_int_submit_urb (struct urb* transfer, res = usb_submit_urb( transfer, GFP_ATOMIC ); if ( unlikely(res) ) { - MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res ); + dev_err(&context->instance->usb_dev->dev, + "could not submit URB! Error was %d\n",(int)res ); set_host_byte(context->srb, DID_ERROR); mts_transfer_cleanup(transfer); } @@ -452,12 +435,9 @@ static void mts_command_done( struct urb *transfer ) if ( unlikely(status) ) { if (status == -ENOENT) { /* We are being killed */ - MTS_DEBUG_GOT_HERE(); set_host_byte(context->srb, DID_ABORT); } else { /* A genuine error has occurred */ - MTS_DEBUG_GOT_HERE(); - set_host_byte(context->srb, DID_ERROR); } mts_transfer_cleanup(transfer); @@ -523,8 +503,6 @@ mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc) { int pipe; - MTS_DEBUG_GOT_HERE(); - desc->context.instance = desc; desc->context.srb = srb; @@ -565,7 +543,6 @@ static enum scsi_qc_status mts_scsi_queuecommand_lck(struct scsi_cmnd *srb) struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); int res; - MTS_DEBUG_GOT_HERE(); mts_show_command(srb); mts_debug_dump(desc); @@ -601,7 +578,7 @@ static enum scsi_qc_status mts_scsi_queuecommand_lck(struct scsi_cmnd *srb) res=usb_submit_urb(desc->urb, GFP_ATOMIC); if(unlikely(res)){ - MTS_ERROR("error %d submitting URB\n",(int)res); + dev_err(&desc->usb_dev->dev, "error %d submitting URB\n",(int)res); set_host_byte(srb, DID_ERROR); if(likely(callback != NULL)) @@ -666,59 +643,47 @@ static int mts_usb_probe(struct usb_interface *intf, /* the current altsetting on the interface we're probing */ struct usb_host_interface *altsetting; - MTS_DEBUG_GOT_HERE(); MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev ); MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n", le16_to_cpu(dev->descriptor.idProduct), le16_to_cpu(dev->descriptor.idVendor) ); - MTS_DEBUG_GOT_HERE(); - /* the current altsetting on the interface we're probing */ altsetting = intf->cur_altsetting; - /* Check if the config is sane */ if ( altsetting->desc.bNumEndpoints != MTS_EP_TOTAL ) { - MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n", + dev_warn(&dev->dev, "expecting %d got %d endpoints! Bailing out.\n", (int)MTS_EP_TOTAL, (int)altsetting->desc.bNumEndpoints ); return -ENODEV; } for( i = 0; i < altsetting->desc.bNumEndpoints; i++ ) { - if ((altsetting->endpoint[i].desc.bmAttributes & - USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { - - MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n", - (int)altsetting->endpoint[i].desc.bEndpointAddress ); - } else { - if (altsetting->endpoint[i].desc.bEndpointAddress & - USB_DIR_IN) - *ep_in_current++ - = altsetting->endpoint[i].desc.bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; - else { - if ( ep_out != -1 ) { - MTS_WARNING( "can only deal with one output endpoints. Bailing out." ); - return -ENODEV; - } - - ep_out = altsetting->endpoint[i].desc.bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; + if (usb_endpoint_is_bulk_in(&altsetting->endpoint[i].desc)) { + *ep_in_current++ = usb_endpoint_num(&altsetting->endpoint[i].desc); + } else if (usb_endpoint_is_bulk_out(&altsetting->endpoint[i].desc)) { + if (ep_out == -1) { + ep_out = usb_endpoint_num(&altsetting->endpoint[i].desc); + } else { + dev_warn(&dev->dev, "can only deal with bulk endpoints; endpoint %d is not bulk.\n", + usb_endpoint_num(&altsetting->endpoint[i].desc)); + return -ENODEV; } + } else { + dev_warn(&dev->dev, "can only deal with bulk endpoints; endpoint %d is not bulk.\n", + usb_endpoint_num(&altsetting->endpoint[i].desc)); } - } if (ep_in_current != &ep_in_set[2]) { - MTS_WARNING("couldn't find two input bulk endpoints. Bailing out.\n"); + dev_warn(&dev->dev, "couldn't find two input bulk endpoints. Bailing out.\n"); return -ENODEV; } if ( ep_out == -1 ) { - MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" ); + dev_warn(&dev->dev, "couldn't find an output bulk endpoint. Bailing out.\n" ); return -ENODEV; } @@ -744,15 +709,15 @@ static int mts_usb_probe(struct usb_interface *intf, new_desc->ep_image = ep_in_set[1]; if ( new_desc->ep_out != MTS_EP_OUT ) - MTS_WARNING( "will this work? Command EP is not usually %d\n", + dev_warn(&dev->dev, "will this work? Command EP is not usually %d\n", (int)new_desc->ep_out ); if ( new_desc->ep_response != MTS_EP_RESPONSE ) - MTS_WARNING( "will this work? Response EP is not usually %d\n", + dev_warn(&dev->dev, "will this work? Response EP is not usually %d\n", (int)new_desc->ep_response ); if ( new_desc->ep_image != MTS_EP_IMAGE ) - MTS_WARNING( "will this work? Image data EP is not usually %d\n", + dev_warn(&dev->dev, "will this work? Image data EP is not usually %d\n", (int)new_desc->ep_image ); new_desc->host = scsi_host_alloc(&mts_scsi_host_template,
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index 99185fc..4a7f955 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -215,7 +215,7 @@ static int cypress_probe(struct usb_interface *interface, if (!dev) goto error_mem; - dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->udev = interface_to_usbdev(interface); /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); @@ -239,8 +239,6 @@ static void cypress_disconnect(struct usb_interface *interface) * device files have been removed */ usb_set_intfdata(interface, NULL); - usb_put_dev(dev->udev); - dev_info(&interface->dev, "Cypress CY7C63xxx device now disconnected\n");
diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 2bf0824..b183df9 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c
@@ -311,7 +311,7 @@ static int cytherm_probe(struct usb_interface *interface, if (!dev) goto error_mem; - dev->udev = usb_get_dev(udev); + dev->udev = udev; usb_set_intfdata(interface, dev); @@ -334,8 +334,6 @@ static void cytherm_disconnect(struct usb_interface *interface) /* first remove the files, then NULL the pointer */ usb_set_intfdata(interface, NULL); - usb_put_dev(dev->udev); - kfree(dev); dev_info(&interface->dev, "Cypress thermometer now disconnected\n");
diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h index 1a1e86e..35d15b0 100644 --- a/drivers/usb/misc/onboard_usb_dev.h +++ b/drivers/usb/misc/onboard_usb_dev.h
@@ -108,6 +108,11 @@ static const struct onboard_dev_pdata genesys_gl852g_data = { .is_hub = true, }; +static const struct onboard_dev_pdata usb_a_conn_data = { + .num_supplies = 1, + .supply_names = { "vbus" }, +}; + static const struct onboard_dev_pdata vialab_vl817_data = { .reset_us = 10, .num_supplies = 1, @@ -130,6 +135,7 @@ static const struct onboard_dev_pdata xmos_xvf3500_data = { }; static const struct of_device_id onboard_dev_match[] = { + { .compatible = "usb-a-connector", .data = &usb_a_conn_data, }, { .compatible = "usb424,2412", .data = µchip_usb424_data, }, { .compatible = "usb424,2514", .data = µchip_usb2514_data, }, { .compatible = "usb424,2517", .data = µchip_usb424_data, },
diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c index 6aaec2d..37f6b79 100644 --- a/drivers/usb/misc/trancevibrator.c +++ b/drivers/usb/misc/trancevibrator.c
@@ -92,7 +92,7 @@ static int tv_probe(struct usb_interface *interface, goto error; } - dev->udev = usb_get_dev(udev); + dev->udev = udev; usb_set_intfdata(interface, dev); return 0; @@ -108,7 +108,6 @@ static void tv_disconnect(struct usb_interface *interface) dev = usb_get_intfdata (interface); usb_set_intfdata(interface, NULL); - usb_put_dev(dev->udev); kfree(dev); }
diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index 7e85fd1..c60121f 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c
@@ -776,7 +776,7 @@ static int ljca_probe(struct usb_interface *interface, init_completion(&adap->cmd_completion); INIT_LIST_HEAD(&adap->client_list); - adap->intf = usb_get_intf(interface); + adap->intf = interface; adap->usb_dev = usb_dev; adap->dev = dev; @@ -787,7 +787,7 @@ static int ljca_probe(struct usb_interface *interface, ret = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL); if (ret) { dev_err(dev, "bulk endpoints not found\n"); - goto err_put; + goto err_destroy_mutex; } adap->rx_pipe = usb_rcvbulkpipe(usb_dev, usb_endpoint_num(ep_in)); adap->tx_pipe = usb_sndbulkpipe(usb_dev, usb_endpoint_num(ep_out)); @@ -797,14 +797,14 @@ static int ljca_probe(struct usb_interface *interface, adap->rx_buf = devm_kzalloc(dev, adap->rx_len, GFP_KERNEL); if (!adap->rx_buf) { ret = -ENOMEM; - goto err_put; + goto err_destroy_mutex; } /* alloc rx urb */ adap->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!adap->rx_urb) { ret = -ENOMEM; - goto err_put; + goto err_destroy_mutex; } usb_fill_bulk_urb(adap->rx_urb, usb_dev, adap->rx_pipe, adap->rx_buf, adap->rx_len, ljca_recv, adap); @@ -836,10 +836,7 @@ static int ljca_probe(struct usb_interface *interface, err_free: usb_free_urb(adap->rx_urb); - -err_put: - usb_put_intf(adap->intf); - +err_destroy_mutex: mutex_destroy(&adap->mutex); return ret; @@ -864,8 +861,6 @@ static void ljca_disconnect(struct usb_interface *interface) usb_free_urb(adap->rx_urb); - usb_put_intf(adap->intf); - mutex_destroy(&adap->mutex); }
diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index b37bf53..89d25fc 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c
@@ -312,7 +312,7 @@ static int sevseg_probe(struct usb_interface *interface, if (!mydev) goto error_mem; - mydev->udev = usb_get_dev(udev); + mydev->udev = udev; mydev->intf = interface; usb_set_intfdata(interface, mydev); @@ -338,7 +338,6 @@ static void sevseg_disconnect(struct usb_interface *interface) mydev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); - usb_put_dev(mydev->udev); kfree(mydev); dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); }
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index a8af761..b7d3c44 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c
@@ -677,35 +677,32 @@ static int uss720_probe(struct usb_interface *intf, struct parport_uss720_private *priv; struct parport *pp; unsigned char reg; - int ret; + int ret = -ENODEV; dev_dbg(&intf->dev, "probe: vendor id 0x%x, device id 0x%x\n", le16_to_cpu(usbdev->descriptor.idVendor), le16_to_cpu(usbdev->descriptor.idProduct)); /* our known interfaces have 3 alternate settings */ - if (intf->num_altsetting != 3) { - usb_put_dev(usbdev); - return -ENODEV; - } + if (intf->num_altsetting != 3) + goto bail_out_early; + ret = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2); dev_dbg(&intf->dev, "set interface result %d\n", ret); interface = intf->cur_altsetting; - if (interface->desc.bNumEndpoints < 2) { - usb_put_dev(usbdev); - return -ENODEV; - } + if (interface->desc.bNumEndpoints < 2) + goto bail_out_early; /* * Allocate parport interface */ + ret = -ENOMEM; priv = kzalloc_obj(struct parport_uss720_private); - if (!priv) { - usb_put_dev(usbdev); - return -ENOMEM; - } + if (!priv) + goto bail_out_early; + priv->pp = NULL; priv->usbdev = usbdev; kref_init(&priv->ref_count); @@ -752,6 +749,10 @@ static int uss720_probe(struct usb_interface *intf, kill_all_async_requests_priv(priv); kref_put(&priv->ref_count, destroy_priv); return -ENODEV; + +bail_out_early: + usb_put_dev(usbdev); + return ret; } static void uss720_disconnect(struct usb_interface *intf)
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 0acc625..73ac25f 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c
@@ -1600,7 +1600,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb) /* log core options (read using indexed model) */ reg = musb_read_configdata(mbase); - strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); + strscpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); if (reg & MUSB_CONFIGDATA_DYNFIFO) { strcat(aInfo, ", dyn FIFOs"); musb->dyn_fifo = true;
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 658b54d8..b3591d6 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -1600,29 +1600,29 @@ static int ti_download_firmware(struct ti_device *tdev) if (le16_to_cpu(dev->descriptor.idVendor) == MTS_VENDOR_ID) { switch (le16_to_cpu(dev->descriptor.idProduct)) { case MTS_CDMA_PRODUCT_ID: - strcpy(buf, "mts_cdma.fw"); + strscpy(buf, "mts_cdma.fw"); break; case MTS_GSM_PRODUCT_ID: - strcpy(buf, "mts_gsm.fw"); + strscpy(buf, "mts_gsm.fw"); break; case MTS_EDGE_PRODUCT_ID: - strcpy(buf, "mts_edge.fw"); + strscpy(buf, "mts_edge.fw"); break; case MTS_MT9234MU_PRODUCT_ID: - strcpy(buf, "mts_mt9234mu.fw"); + strscpy(buf, "mts_mt9234mu.fw"); break; case MTS_MT9234ZBA_PRODUCT_ID: - strcpy(buf, "mts_mt9234zba.fw"); + strscpy(buf, "mts_mt9234zba.fw"); break; case MTS_MT9234ZBAOLD_PRODUCT_ID: - strcpy(buf, "mts_mt9234zba.fw"); + strscpy(buf, "mts_mt9234zba.fw"); break; } } if (buf[0] == '\0') { if (tdev->td_is_3410) - strcpy(buf, "ti_3410.fw"); + strscpy(buf, "ti_3410.fw"); else - strcpy(buf, "ti_5052.fw"); + strscpy(buf, "ti_5052.fw"); } status = request_firmware(&fw_p, buf, &dev->dev); }
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 58fb97e..db5e4a4 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c
@@ -35,7 +35,9 @@ static int switch_fwnode_match(struct device *dev, const void *fwnode) static void *typec_switch_match(const struct fwnode_handle *fwnode, const char *id, void *data) { + struct typec_switch_dev **sw_devs = data; struct device *dev; + int i; /* * Device graph (OF graph) does not give any means to identify the @@ -56,6 +58,13 @@ static void *typec_switch_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, switch_fwnode_match); + /* Skip duplicates */ + for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) + if (to_typec_switch_dev(dev) == sw_devs[i]) { + put_device(dev); + return NULL; + } + return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -80,7 +89,8 @@ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode) if (!sw) return ERR_PTR(-ENOMEM); - count = fwnode_connection_find_matches(fwnode, "orientation-switch", NULL, + count = fwnode_connection_find_matches(fwnode, "orientation-switch", + (void **)sw_devs, typec_switch_match, (void **)sw_devs, ARRAY_SIZE(sw_devs)); @@ -265,7 +275,9 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode) static void *typec_mux_match(const struct fwnode_handle *fwnode, const char *id, void *data) { + struct typec_mux_dev **mux_devs = data; struct device *dev; + int i; /* * Device graph (OF graph) does not give any means to identify the @@ -281,6 +293,14 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); + /* Skip duplicates */ + for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) + if (to_typec_mux_dev(dev) == mux_devs[i]) { + put_device(dev); + return NULL; + } + + return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -306,7 +326,8 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode) return ERR_PTR(-ENOMEM); count = fwnode_connection_find_matches(fwnode, "mode-switch", - NULL, typec_mux_match, + (void **)mux_devs, + typec_mux_match, (void **)mux_devs, ARRAY_SIZE(mux_devs)); if (count <= 0) {
diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig index 8cdd84c..00baa75 100644 --- a/drivers/usb/typec/tcpm/Kconfig +++ b/drivers/usb/typec/tcpm/Kconfig
@@ -58,6 +58,8 @@ tristate "Fairchild FUSB302 Type-C chip driver" depends on I2C depends on EXTCON || !EXTCON + depends on DRM || DRM=n + select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF help The Fairchild FUSB302 Type-C chip driver that works with Type-C Port Controller Manager to provide USB PD and USB
diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 19ff821..ce7069f 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -5,6 +5,7 @@ * Fairchild FUSB302 Type-C Chip Driver */ +#include <drm/bridge/aux-bridge.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/errno.h> @@ -1689,6 +1690,7 @@ static int fusb302_probe(struct i2c_client *client) { struct fusb302_chip *chip; struct i2c_adapter *adapter = client->adapter; + struct auxiliary_device *bridge_dev; struct device *dev = &client->dev; const char *name; int ret = 0; @@ -1747,6 +1749,13 @@ static int fusb302_probe(struct i2c_client *client) goto destroy_workqueue; } + bridge_dev = devm_drm_dp_hpd_bridge_alloc(chip->dev, to_of_node(chip->tcpc_dev.fwnode)); + if (IS_ERR(bridge_dev)) { + ret = PTR_ERR(bridge_dev); + dev_err_probe(chip->dev, ret, "failed to alloc bridge\n"); + goto destroy_workqueue; + } + chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev); if (IS_ERR(chip->tcpm_port)) { fwnode_handle_put(chip->tcpc_dev.fwnode); @@ -1764,6 +1773,10 @@ static int fusb302_probe(struct i2c_client *client) enable_irq_wake(chip->gpio_int_n_irq); i2c_set_clientdata(client, chip); + ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev); + if (ret) + return ret; + return ret; tcpm_unregister_port:
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 2a951c5..8b7e6eb 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -999,7 +999,7 @@ static int tcpci_resume(struct device *dev) return ret; } -DEFINE_SIMPLE_DEV_PM_OPS(tcpci_pm_ops, tcpci_suspend, tcpci_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tcpci_pm_ops, tcpci_suspend, tcpci_resume); static const struct i2c_device_id tcpci_id[] = { { "tcpci" },
diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index 88c50b9..37cf55a 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
@@ -19,9 +19,11 @@ #include <linux/regulator/consumer.h> #define RT1711H_VID 0x29CF +#define ET7304_VID 0x6DCF #define RT1711H_PID 0x1711 #define RT1711H_DID 0x2171 #define RT1715_DID 0x2173 +#define ET7304_DID 0x2173 #define RT1711H_PHYCTRL1 0x80 #define RT1711H_PHYCTRL2 0x81 @@ -55,6 +57,7 @@ struct rt1711h_chip_info { u32 rxdz_sel; + u16 vid; u16 did; bool enable_pd30_extended_message; }; @@ -308,7 +311,7 @@ static int rt1711h_check_revision(struct i2c_client *i2c, struct rt1711h_chip *c ret = i2c_smbus_read_word_data(i2c, TCPC_VENDOR_ID); if (ret < 0) return ret; - if (ret != RT1711H_VID) { + if (ret != chip->info->vid) { dev_err(&i2c->dev, "vid is not correct, 0x%04x\n", ret); return -ENODEV; } @@ -405,17 +408,27 @@ static void rt1711h_remove(struct i2c_client *client) tcpci_unregister_port(chip->tcpci); } +static const struct rt1711h_chip_info et7304 = { + .rxdz_sel = RT1711H_BMCIO_RXDZSEL, + .vid = ET7304_VID, + .did = ET7304_DID, + .enable_pd30_extended_message = true, +}; + static const struct rt1711h_chip_info rt1711h = { + .vid = RT1711H_VID, .did = RT1711H_DID, }; static const struct rt1711h_chip_info rt1715 = { .rxdz_sel = RT1711H_BMCIO_RXDZSEL, + .vid = RT1711H_VID, .did = RT1715_DID, .enable_pd30_extended_message = true, }; static const struct i2c_device_id rt1711h_id[] = { + { "et7304", (kernel_ulong_t)&et7304 }, { "rt1711h", (kernel_ulong_t)&rt1711h }, { "rt1715", (kernel_ulong_t)&rt1715 }, {} @@ -423,6 +436,7 @@ static const struct i2c_device_id rt1711h_id[] = { MODULE_DEVICE_TABLE(i2c, rt1711h_id); static const struct of_device_id rt1711h_of_match[] = { + { .compatible = "etekmicro,et7304", .data = &et7304 }, { .compatible = "richtek,rt1711h", .data = &rt1711h }, { .compatible = "richtek,rt1715", .data = &rt1715 }, {}
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 8e0e14a..63a75b94 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -12,6 +12,7 @@ #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/kthread.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/power_supply.h> @@ -188,7 +189,8 @@ S(STRUCTURED_VDMS), \ S(COUNTRY_INFO), \ S(COUNTRY_CODES), \ - S(REVISION_INFORMATION) + S(REVISION_INFORMATION), \ + S(GETTING_SINK_EXTENDED_CAPABILITIES) #define GENERATE_ENUM(e) e #define GENERATE_STRING(s) #s @@ -229,6 +231,7 @@ enum pd_msg_request { PD_MSG_DATA_SINK_CAP, PD_MSG_DATA_SOURCE_CAP, PD_MSG_DATA_REV, + PD_MSG_EXT_SINK_CAP_EXT }; enum adev_actions { @@ -337,6 +340,42 @@ struct pd_timings { u32 snk_bc12_cmpletion_time; }; +/* Convert microwatt to watt */ +#define UW_TO_W(pow) ((pow) / 1000000) + +/* + * struct pd_identifier - Contains info about PD identifiers + * @vid: Vendor ID (assigned by USB-IF) + * @pid: Product ID (assigned by manufacturer) + * @xid: Value assigned by USB-IF for product + */ +struct pd_identifier { + u16 vid; + u16 pid; + u32 xid; +}; + +/* + * struct sink_caps_ext_data - Sink extended capability data + * @load_step: Indicates the load step slew rate. Value of 0 indicates 150mA/us + * & 1 indicates 500 mA/us + * @load_char: Snk overload characteristics + * @compliance: Types of sources the sink has been tested & certified on + * @modes: Charging caps & power sources supported + * @spr_min_pdp: Sink Minimum PDP for SPR mode (in Watts) + * @spr_op_pdp: Sink Operational PDP for SPR mode (in Watts) + * @spr_max_pdp: Sink Maximum PDP for SPR mode (in Watts) + */ +struct sink_caps_ext_data { + u8 load_step; + u16 load_char; + u8 compliance; + u8 modes; + u8 spr_min_pdp; + u8 spr_op_pdp; + u8 spr_max_pdp; +}; + struct tcpm_port { struct device *dev; @@ -585,6 +624,9 @@ struct tcpm_port { /* Indicates maximum (revision, version) supported */ struct pd_revision_info pd_rev; + + struct pd_identifier pd_ident; + struct sink_caps_ext_data sink_caps_ext; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -605,9 +647,9 @@ struct altmode_vdm_event { struct kthread_work work; struct tcpm_port *port; u32 header; - u32 *data; int cnt; enum tcpm_transmit_type tx_sop_type; + u32 data[] __counted_by(cnt); }; static const char * const pd_rev[] = { @@ -725,7 +767,7 @@ static void _tcpm_log(struct tcpm_port *port, const char *fmt, va_list args) if (tcpm_log_full(port)) { port->logbuffer_head = max(port->logbuffer_head - 1, 0); - strcpy(tmpbuffer, "overflow"); + strscpy(tmpbuffer, "overflow"); } if (port->logbuffer_head < 0 || @@ -841,10 +883,10 @@ static void tcpm_log_source_caps(struct tcpm_port *port) pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo), pdo_spr_avs_apdo_src_peak_current(pdo)); else - strcpy(msg, "undefined APDO"); + strscpy(msg, "undefined APDO"); break; default: - strcpy(msg, "undefined"); + strscpy(msg, "undefined"); break; } tcpm_log(port, " PDO %d: type %d, %s", @@ -1367,6 +1409,64 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port) return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); } +static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) +{ + u16 operating_snk_watt = port->operating_snk_mw / 1000; + struct sink_caps_ext_data *data = &port->sink_caps_ext; + struct pd_identifier *pd_ident = &port->pd_ident; + struct sink_caps_ext_msg skedb = {0}; + struct pd_message msg; + u8 data_obj_cnt; + + if (!port->self_powered) + data->spr_op_pdp = operating_snk_watt; + + /* + * SPR Sink Minimum PDP indicates the minimum power required to operate + * a sink device in its lowest level of functionality without requiring + * power from the battery. We can use the operating_snk_watt value to + * populate it, as operating_snk_watt indicates device's min operating + * power. + */ + data->spr_min_pdp = operating_snk_watt; + + if (data->spr_op_pdp < data->spr_min_pdp || + data->spr_max_pdp < data->spr_op_pdp) { + tcpm_log(port, + "Invalid PDP values, Min PDP:%u, Op PDP:%u, Max PDP:%u", + data->spr_min_pdp, data->spr_op_pdp, data->spr_max_pdp); + return -EOPNOTSUPP; + } + + memset(&msg, 0, sizeof(msg)); + skedb.vid = cpu_to_le16(pd_ident->vid); + skedb.pid = cpu_to_le16(pd_ident->pid); + skedb.xid = cpu_to_le32(pd_ident->xid); + skedb.skedb_ver = SKEDB_VER_1_0; + skedb.load_step = data->load_step; + skedb.load_char = cpu_to_le16(data->load_char); + skedb.compliance = data->compliance; + skedb.modes = data->modes; + skedb.spr_min_pdp = data->spr_min_pdp; + skedb.spr_op_pdp = data->spr_op_pdp; + skedb.spr_max_pdp = data->spr_max_pdp; + memcpy(msg.ext_msg.data, &skedb, sizeof(skedb)); + msg.ext_msg.header = PD_EXT_HDR_LE(sizeof(skedb), + 0, /* Denotes if request chunk */ + 0, /* Chunk Number */ + 1 /* Chunked */); + + data_obj_cnt = count_chunked_data_objs(sizeof(skedb)); + msg.header = cpu_to_le16(PD_HEADER(PD_EXT_SINK_CAP_EXT, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + data_obj_cnt, + 1 /* Denotes if ext header */)); + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + static void mod_tcpm_delayed_work(struct tcpm_port *port, unsigned int delay_ms) { if (delay_ms) { @@ -1653,7 +1753,6 @@ static void tcpm_queue_vdm_work(struct kthread_work *work) tcpm_queue_vdm(port, event->header, event->data, event->cnt, event->tx_sop_type); port_unlock: - kfree(event->data); kfree(event); mutex_unlock(&port->lock); } @@ -1662,35 +1761,27 @@ static int tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header, const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type) { struct altmode_vdm_event *event; - u32 *data_cpy; int ret = -ENOMEM; - event = kzalloc_obj(*event); + event = kzalloc_flex(*event, data, cnt); if (!event) goto err_event; - data_cpy = kcalloc(cnt, sizeof(u32), GFP_KERNEL); - if (!data_cpy) - goto err_data; - kthread_init_work(&event->work, tcpm_queue_vdm_work); + event->cnt = cnt; event->port = port; event->header = header; - memcpy(data_cpy, data, sizeof(u32) * cnt); - event->data = data_cpy; - event->cnt = cnt; + memcpy(event->data, data, sizeof(u32) * cnt); event->tx_sop_type = tx_sop_type; ret = kthread_queue_work(port->wq, &event->work); if (!ret) { ret = -EBUSY; - goto err_queue; + goto err_data; } return 0; -err_queue: - kfree(data_cpy); err_data: kfree(event); err_event: @@ -3655,6 +3746,19 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); break; + case PD_CTRL_GET_SINK_CAP_EXT: + /* This is an unsupported message if port type is SRC */ + if (port->negotiated_rev >= PD_REV30 && + port->port_type != TYPEC_PORT_SRC) + tcpm_pd_handle_msg(port, PD_MSG_EXT_SINK_CAP_EXT, + GETTING_SINK_EXTENDED_CAPABILITIES); + else + tcpm_pd_handle_msg(port, + port->negotiated_rev < PD_REV30 ? + PD_MSG_CTRL_REJECT : + PD_MSG_CTRL_NOT_SUPP, + NONE_AMS); + break; default: tcpm_pd_handle_msg(port, port->negotiated_rev < PD_REV30 ? @@ -3907,6 +4011,16 @@ static bool tcpm_send_queued_message(struct tcpm_port *port) ret); tcpm_ams_finish(port); break; + case PD_MSG_EXT_SINK_CAP_EXT: + ret = tcpm_pd_send_sink_cap_ext(port); + if (ret == -EOPNOTSUPP) + tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP); + else if (ret < 0) + tcpm_log(port, + "Unable to transmit sink cap extended, ret=%d", + ret); + tcpm_ams_finish(port); + break; default: break; } @@ -7291,6 +7405,129 @@ static void tcpm_fw_get_timings(struct tcpm_port *port, struct fwnode_handle *fw port->timings.snk_bc12_cmpletion_time = val; } +static void tcpm_fw_get_pd_ident(struct tcpm_port *port) +{ + struct pd_identifier *pd_ident = &port->pd_ident; + u32 *vdo; + + /* First 3 vdo values contain info regarding USB PID, VID & XID */ + if (port->nr_snk_vdo >= 3) + vdo = port->snk_vdo; + else if (port->nr_snk_vdo_v1 >= 3) + vdo = port->snk_vdo_v1; + else + return; + + pd_ident->vid = PD_IDH_VID(vdo[0]); + pd_ident->pid = PD_PRODUCT_PID(vdo[2]); + pd_ident->xid = PD_CSTAT_XID(vdo[1]); + tcpm_log(port, "vid:%#x pid:%#x xid:%#x", + pd_ident->vid, pd_ident->pid, pd_ident->xid); +} + +static void tcpm_parse_snk_pdos(struct tcpm_port *port) +{ + struct sink_caps_ext_data *caps = &port->sink_caps_ext; + u32 max_mv, max_ma; + u8 avs_tier1_pdp, avs_tier2_pdp; + int i, pdo_itr; + u32 *snk_pdos; + + for (i = 0; i < port->pd_count; ++i) { + snk_pdos = port->pd_list[i]->sink_desc.pdo; + for (pdo_itr = 0; pdo_itr < PDO_MAX_OBJECTS && snk_pdos[pdo_itr]; + ++pdo_itr) { + u32 pdo = snk_pdos[pdo_itr]; + u8 curr_snk_pdp = 0; + + switch (pdo_type(pdo)) { + case PDO_TYPE_FIXED: + max_mv = pdo_fixed_voltage(pdo); + max_ma = pdo_fixed_current(pdo); + curr_snk_pdp = UW_TO_W(max_mv * max_ma); + break; + case PDO_TYPE_BATT: + curr_snk_pdp = UW_TO_W(pdo_max_power(pdo)); + break; + case PDO_TYPE_VAR: + max_mv = pdo_max_voltage(pdo); + max_ma = pdo_max_current(pdo); + curr_snk_pdp = UW_TO_W(max_mv * max_ma); + break; + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) { + max_mv = pdo_pps_apdo_max_voltage(pdo); + max_ma = pdo_pps_apdo_max_current(pdo); + curr_snk_pdp = UW_TO_W(max_mv * max_ma); + caps->modes |= SINK_MODE_PPS; + } else if (pdo_apdo_type(pdo) == + APDO_TYPE_SPR_AVS) { + avs_tier1_pdp = UW_TO_W(SPR_AVS_TIER1_MAX_VOLT_MV + * pdo_spr_avs_apdo_9v_to_15v_max_current_ma(pdo)); + avs_tier2_pdp = UW_TO_W(SPR_AVS_TIER2_MAX_VOLT_MV + * pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo)); + curr_snk_pdp = max(avs_tier1_pdp, avs_tier2_pdp); + caps->modes |= SINK_MODE_AVS; + } + break; + default: + tcpm_log(port, "Invalid source PDO type, ignoring"); + continue; + } + + caps->spr_max_pdp = max(caps->spr_max_pdp, + curr_snk_pdp); + } + } +} + +static void tcpm_fw_get_sink_caps_ext(struct tcpm_port *port, + struct fwnode_handle *fwnode) +{ + struct sink_caps_ext_data *caps = &port->sink_caps_ext; + int ret; + u32 val; + + /* + * Load step represents the change in current per usec that a given + * source can tolerate while maintaining Vbus within the vSrcValid + * range. For a sink this represents the "preferred" load-step value. It + * can only have 2 values (150 mA/usec or 500 mA/usec) with 150 mA/usec + * being the default. + */ + ret = fwnode_property_read_u32(fwnode, "sink-load-step", &val); + if (!ret) + caps->load_step = val == 500 ? 1 : 0; + + fwnode_property_read_u16(fwnode, "sink-load-characteristics", + &caps->load_char); + fwnode_property_read_u8(fwnode, "sink-compliance", &caps->compliance); + caps->modes = SINK_MODE_VBUS; + + /* + * As per "6.5.13.14" SPR Sink Operational PDP definition, for battery + * powered devices, this value will correspond to the PDP of the + * charging adapter either shipped or recommended for use with it. For + * batteryless sink devices SPR Operational PDP indicates the power + * required to operate all the device's functional modes. Hence, this + * value may be considered equal to port's operating_snk_mw. As + * operating_sink_mw can change as per the pd set used thus, OP PDP + * is determined when populating Sink Caps Extended Data Block. + */ + if (port->self_powered) { + fwnode_property_read_u32(fwnode, "charging-adapter-pdp-milliwatt", + &val); + caps->spr_op_pdp = (u8)(val / 1000); + caps->modes |= SINK_MODE_BATT; + } + + tcpm_parse_snk_pdos(port); + tcpm_log(port, + "load-step:%#x load-char:%#x compl:%#x op-pdp:%#x max-pdp:%#x", + caps->load_step, caps->load_char, caps->compliance, + caps->spr_op_pdp, caps->spr_max_pdp); +} + static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { struct fwnode_handle *capabilities, *caps = NULL; @@ -7464,6 +7701,9 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode } } + if (port->port_type != TYPEC_PORT_SRC) + tcpm_fw_get_sink_caps_ext(port, fwnode); + put_caps: if (caps != fwnode) fwnode_handle_put(caps); @@ -7506,6 +7746,8 @@ static int tcpm_fw_get_snk_vdos(struct tcpm_port *port, struct fwnode_handle *fw return ret; } + tcpm_fw_get_pd_ident(port); + return 0; }
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index e2b26af..43faec7 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c
@@ -820,8 +820,10 @@ static void cd321x_update_work(struct work_struct *work) desc.identity = &st.partner_identity; tps->partner = typec_register_partner(tps->port, &desc); - if (IS_ERR(tps->partner)) - dev_warn(tps->dev, "%s: failed to register partnet\n", __func__); + if (IS_ERR(tps->partner)) { + dev_warn(tps->dev, "%s: failed to register partner\n", __func__); + return; + } if (desc.identity) { typec_partner_set_identity(tps->partner);
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index f38a4d7..4efbe41 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -235,6 +235,8 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, if (cci & UCSI_CCI_ERROR) ret = ucsi_read_error(ucsi, connector_num); + trace_ucsi_run_command(cmd, ret); + mutex_unlock(&ucsi->ppm_lock); return ret; }
diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 11b3e24..c7878ea 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c
@@ -373,6 +373,8 @@ static unsigned long quirk_sc8280xp = UCSI_NO_PARTNER_PDOS | UCSI_DELAY_DEVICE_P static unsigned long quirk_sm8450 = UCSI_DELAY_DEVICE_PDOS; static const struct of_device_id pmic_glink_ucsi_of_quirks[] = { + { .compatible = "qcom,glymur-pmic-glink", .data = &quirk_sm8450, }, + { .compatible = "qcom,kaanapali-pmic-glink", .data = &quirk_sm8450, }, { .compatible = "qcom,qcm6490-pmic-glink", .data = &quirk_sc8280xp, }, { .compatible = "qcom,sc8180x-pmic-glink", .data = &quirk_sc8180x, }, { .compatible = "qcom,sc8280xp-pmic-glink", .data = &quirk_sc8280xp, },
diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c index c596565..ca749fd 100644 --- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c +++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c
@@ -193,7 +193,7 @@ static void gaokun_ucsi_connector_status(struct ucsi_connector *con) gaokun_set_orientation(con, &uec->ports[idx]); } -const struct ucsi_operations gaokun_ucsi_ops = { +static const struct ucsi_operations gaokun_ucsi_ops = { .read_version = gaokun_ucsi_read_version, .read_cci = gaokun_ucsi_read_cci, .poll_cci = gaokun_ucsi_read_cci,
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index bfc10f6..5bc8c47 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -463,7 +463,7 @@ static void set_status_attr(int id) status = status_attrs + id; if (id == 0) - strcpy(status->name, "status"); + strscpy(status->name, "status"); else snprintf(status->name, MAX_STATUS_NAME+1, "status.%d", id); status->attr.attr.name = status->name;
diff --git a/include/dt-bindings/usb/pd.h b/include/dt-bindings/usb/pd.h index e6526b1..6cff233 100644 --- a/include/dt-bindings/usb/pd.h +++ b/include/dt-bindings/usb/pd.h
@@ -465,4 +465,22 @@ | ((vbm) & 0x3) << 15 | (curr) << 14 | ((vbi) & 0x3f) << 7 \ | ((gi) & 0x3f) << 1 | (ct)) +/* + * Sink Load Characteristics + * ------------------------- + * <15> :: Can tolerate vbus voltage droop + * <11:14> :: Duty cycle in 5% increments when bits 4:0 are non-zero + * <10:5> :: Overload period in 20ms when bits 4:0 are non-zero + * <4:0> :: Percent overload in 10% increments. Values higher than 25 are + * clipped to 250% + */ +#define SINK_LOAD_CHAR(vdroop, duty_cycle, period, percent_ol) \ + (((vdroop) & 0x1) << 15 | ((duty_cycle) & 0xf) << 11 | \ + ((period) & 0x3f) << 5 | ((percent_ol) & 0x1f)) + +/* Compliance */ +#define COMPLIANCE_LPS (1 << 0) +#define COMPLIANCE_PS1 (1 << 1) +#define COMPLIANCE_PS2 (1 << 2) + #endif /* __DT_POWER_DELIVERY_H */
diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 4ac082a..97ef37a 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h
@@ -118,8 +118,8 @@ struct cdc_ncm_ctx { u32 timer_interval; u32 max_ndp_size; - u8 is_ndp16; - u8 filtering_supported; + bool is_ndp16; + bool filtering_supported; union { struct usb_cdc_ncm_ndp16 *delayed_ndp16; struct usb_cdc_ncm_ndp32 *delayed_ndp32;
diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 6ccd1b2..5a98983 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h
@@ -34,7 +34,8 @@ enum pd_ctrl_msg_type { PD_CTRL_FR_SWAP = 19, PD_CTRL_GET_PPS_STATUS = 20, PD_CTRL_GET_COUNTRY_CODES = 21, - /* 22-23 Reserved */ + PD_CTRL_GET_SINK_CAP_EXT = 22, + /* 23 Reserved */ PD_CTRL_GET_REVISION = 24, /* 25-31 Reserved */ }; @@ -72,7 +73,8 @@ enum pd_ext_msg_type { PD_EXT_PPS_STATUS = 12, PD_EXT_COUNTRY_INFO = 13, PD_EXT_COUNTRY_CODES = 14, - /* 15-31 Reserved */ + PD_EXT_SINK_CAP_EXT = 15, + /* 16-31 Reserved */ }; #define PD_REV10 0x0 @@ -205,6 +207,72 @@ struct pd_message { }; } __packed; +/* + * count_chunked_data_objs - Helper to calculate number of Data Objects on a 4 + * byte boundary. + * @size: Size of data block for extended message. Should *not* include extended + * header size. + */ +static inline u8 count_chunked_data_objs(u32 size) +{ + size += offsetof(struct pd_chunked_ext_message_data, data); + return ((size / 4) + (size % 4 ? 1 : 0)); +} + +/* Sink Caps Extended Data Block Version */ +#define SKEDB_VER_1_0 1 + +/* Sink Caps Extended Sink Modes */ +#define SINK_MODE_PPS BIT(0) +#define SINK_MODE_VBUS BIT(1) +#define SINK_MODE_AC_SUPPLY BIT(2) +#define SINK_MODE_BATT BIT(3) +#define SINK_MODE_BATT_UL BIT(4) /* Unlimited battery power supply */ +#define SINK_MODE_AVS BIT(5) + +/** + * struct sink_caps_ext_msg - Sink extended capability PD message + * @vid: Vendor ID + * @pid: Product ID + * @xid: Value assigned by USB-IF for product + * @fw: Firmware version + * @hw: Hardware version + * @skedb_ver: Sink Caps Extended Data Block (SKEDB) Version + * @load_step: Indicates the load step slew rate. + * @load_char: Sink overload characteristics + * @compliance: Types of sources the sink has been tested & certified on + * @touch_temp: Indicates the IEC standard to which the touch temperature + * conforms to (if applicable). + * @batt_info: Indicates number batteries and hot swappable ports + * @modes: Charging caps & power sources supported + * @spr_min_pdp: Sink Minimum PDP for SPR mode + * @spr_op_pdp: Sink Operational PDP for SPR mode + * @spr_max_pdp: Sink Maximum PDP for SPR mode + * @epr_min_pdp: Sink Minimum PDP for EPR mode + * @epr_op_pdp: Sink Operational PDP for EPR mode + * @epr_max_pdp: Sink Maximum PDP for EPR mode + */ +struct sink_caps_ext_msg { + __le16 vid; + __le16 pid; + __le32 xid; + u8 fw; + u8 hw; + u8 skedb_ver; + u8 load_step; + __le16 load_char; + u8 compliance; + u8 touch_temp; + u8 batt_info; + u8 modes; + u8 spr_min_pdp; + u8 spr_op_pdp; + u8 spr_max_pdp; + u8 epr_min_pdp; + u8 epr_op_pdp; + u8 epr_max_pdp; +} __packed; + /* PDO: Power Data Object */ #define PDO_MAX_OBJECTS 7 @@ -329,6 +397,11 @@ enum pd_apdo_type { #define PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR GENMASK(19, 10) /* 10mA unit */ #define PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR GENMASK(9, 0) /* 10mA unit */ +/* SPR AVS has two different current ranges 9V - 15V, 15V - 20V */ +#define SPR_AVS_TIER1_MIN_VOLT_MV 9000 +#define SPR_AVS_TIER1_MAX_VOLT_MV 15000 +#define SPR_AVS_TIER2_MAX_VOLT_MV 20000 + static inline enum pd_pdo_type pdo_type(u32 pdo) { return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK; @@ -339,6 +412,11 @@ static inline unsigned int pdo_fixed_voltage(u32 pdo) return ((pdo >> PDO_FIXED_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; } +static inline unsigned int pdo_fixed_current(u32 pdo) +{ + return ((pdo >> PDO_FIXED_CURR_SHIFT) & PDO_CURR_MASK) * 10; +} + static inline unsigned int pdo_min_voltage(u32 pdo) { return ((pdo >> PDO_VAR_MIN_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;