aboutsummaryrefslogtreecommitdiffstats
diff options
authorMark Brown <broonie@kernel.org>2026-05-29 23:00:26 +0100
committerMark Brown <broonie@kernel.org>2026-05-29 23:00:26 +0100
commit9394de50f7ac760fe5ae2eea0fa184185f2cdc3a (patch)
treebbc7006f9d0c4c574c1fd0ce4ae07f4c5200d1b6
parentffc4db0244867d349d1f62258b36885e4ef6155c (diff)
parent22d91cef94b5b86cff0d68ebfce7741740672704 (diff)
downloadlinux-next-history-9394de50f7ac760fe5ae2eea0fa184185f2cdc3a.tar.gz
Merge branch 'usb-next' of https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git
-rw-r--r--Documentation/devicetree/bindings/usb/cdns,usb3.yaml73
-rw-r--r--Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml25
-rw-r--r--Documentation/devicetree/bindings/usb/genesys,gl850g.yaml18
-rw-r--r--Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml9
-rw-r--r--Documentation/devicetree/bindings/usb/richtek,rt1711h.txt44
-rw-r--r--Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml3
-rw-r--r--Documentation/hwmon/index.rst1
-rw-r--r--Documentation/hwmon/prom21-xhci.rst101
-rw-r--r--Documentation/translations/zh_CN/subsystem-apis.rst2
-rw-r--r--Documentation/translations/zh_CN/usb/CREDITS163
-rw-r--r--Documentation/translations/zh_CN/usb/acm.rst147
-rw-r--r--Documentation/translations/zh_CN/usb/authorization.rst139
-rw-r--r--Documentation/translations/zh_CN/usb/chipidea.rst150
-rw-r--r--Documentation/translations/zh_CN/usb/dwc3.rst63
-rw-r--r--Documentation/translations/zh_CN/usb/ehci.rst261
-rw-r--r--Documentation/translations/zh_CN/usb/index.rst54
-rw-r--r--Documentation/translations/zh_CN/usb/usbmon.rst427
-rw-r--r--arch/arm/mach-pxa/devices.c7
-rw-r--r--arch/arm/mach-pxa/gumstix.c1
-rw-r--r--arch/arm/mach-pxa/udc.h8
-rw-r--r--arch/arm64/boot/dts/qcom/pm4125.dtsi1
-rw-r--r--arch/arm64/boot/dts/qcom/pm7250b.dtsi1
-rw-r--r--arch/arm64/boot/dts/qcom/pm8150b.dtsi1
-rw-r--r--arch/arm64/boot/dts/qcom/pmi632.dtsi1
-rw-r--r--arch/arm64/boot/dts/qcom/qrb2210-rb1.dts2
-rw-r--r--arch/arm64/boot/dts/qcom/qrb4210-rb2.dts2
-rw-r--r--arch/arm64/boot/dts/qcom/qrb5165-rb5.dts2
-rw-r--r--arch/arm64/boot/dts/qcom/sdm632-fairphone-fp3.dts2
-rw-r--r--arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts2
-rw-r--r--arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts2
-rw-r--r--arch/arm64/boot/dts/qcom/sm8150-hdk.dts2
-rw-r--r--arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi2
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/prom21-xhci.c239
-rw-r--r--drivers/usb/Makefile2
-rw-r--r--drivers/usb/atm/ueagle-atm.c58
-rw-r--r--drivers/usb/cdns3/Kconfig112
-rw-r--r--drivers/usb/cdns3/Makefile44
-rw-r--r--drivers/usb/cdns3/cdns3-gadget.c1
-rw-r--r--drivers/usb/cdns3/cdns3-plat.c58
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.c50
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.h1
-rw-r--r--drivers/usb/cdns3/cdnsp-mem.c73
-rw-r--r--drivers/usb/cdns3/cdnsp-pci.c243
-rw-r--r--drivers/usb/cdns3/cdnsp-ring.c9
-rw-r--r--drivers/usb/cdns3/core.c48
-rw-r--r--drivers/usb/cdns3/core.h9
-rw-r--r--drivers/usb/cdns3/drd.c45
-rw-r--r--drivers/usb/cdns3/gadget-export.h10
-rw-r--r--drivers/usb/cdns3/host-export.h4
-rw-r--r--drivers/usb/chipidea/core.c11
-rw-r--r--drivers/usb/chipidea/udc.c93
-rw-r--r--drivers/usb/class/cdc-acm.c6
-rw-r--r--drivers/usb/core/hcd.c14
-rw-r--r--drivers/usb/dwc2/hcd_intr.c10
-rw-r--r--drivers/usb/dwc3/core.c14
-rw-r--r--drivers/usb/dwc3/dwc3-google.c5
-rw-r--r--drivers/usb/dwc3/dwc3-octeon.c12
-rw-r--r--drivers/usb/gadget/udc/aspeed_udc.c7
-rw-r--r--drivers/usb/gadget/udc/core.c7
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c3
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c41
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.h2
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c35
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.h2
-rw-r--r--drivers/usb/host/Kconfig8
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/max3421-hcd.c8
-rw-r--r--drivers/usb/host/xhci-hub.c5
-rw-r--r--drivers/usb/host/xhci-mtk.c6
-rw-r--r--drivers/usb/host/xhci-pci-prom21.c137
-rw-r--r--drivers/usb/host/xhci-pci.c11
-rw-r--r--drivers/usb/host/xhci-pci.h3
-rw-r--r--drivers/usb/misc/Kconfig2
-rw-r--r--drivers/usb/misc/onboard_usb_dev.c1
-rw-r--r--drivers/usb/misc/onboard_usb_dev.h8
-rw-r--r--drivers/usb/misc/usb251xb.c18
-rw-r--r--drivers/usb/misc/usb3503.c2
-rw-r--r--drivers/usb/misc/usb4604.c2
-rw-r--r--drivers/usb/phy/phy-isp1301.c2
-rw-r--r--drivers/usb/typec/anx7411.c4
-rw-r--r--drivers/usb/typec/mux/fsa4480.c2
-rw-r--r--drivers/usb/typec/mux/intel_pmc_mux.c19
-rw-r--r--drivers/usb/typec/mux/it5205.c2
-rw-r--r--drivers/usb/typec/mux/nb7vpq904m.c2
-rw-r--r--drivers/usb/typec/mux/pi3usb30532.c2
-rw-r--r--drivers/usb/typec/mux/ps883x.c196
-rw-r--r--drivers/usb/typec/mux/ptn36502.c2
-rw-r--r--drivers/usb/typec/mux/wcd939x-usbss.c2
-rw-r--r--drivers/usb/typec/tcpm/fusb302.c4
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c5
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c12
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c13
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim_core.c2
-rw-r--r--drivers/usb/typec/tcpm/tcpci_rt1711h.c8
-rw-r--r--drivers/usb/typec/tipd/core.c2
-rw-r--r--drivers/usb/typec/ucsi/cros_ec_ucsi.c6
-rw-r--r--drivers/usb/typec/ucsi/debugfs.c31
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c60
-rw-r--r--drivers/usb/typec/ucsi/ucsi.h21
-rw-r--r--drivers/usb/typec/ucsi/ucsi_acpi.c22
-rw-r--r--drivers/usb/typec/ucsi/ucsi_ccg.c10
-rw-r--r--drivers/usb/typec/ucsi/ucsi_stm32g0.c4
-rw-r--r--drivers/usb/typec/ucsi/ucsi_yoga_c630.c6
-rw-r--r--drivers/usb/usbip/Kconfig2
-rw-r--r--drivers/usb/usbip/vhci_sysfs.c52
-rw-r--r--include/linux/platform_data/pxa2xx_udc.h15
-rw-r--r--include/linux/platform_data/usb-xhci-prom21.h22
109 files changed, 2926 insertions, 811 deletions
diff --git a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml
index a199e5ba64161..e8082c5c05a25 100644
--- a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml
+++ b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml
@@ -4,29 +4,37 @@
$id: http://devicetree.org/schemas/usb/cdns,usb3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Cadence USBSS-DRD controller
+title: Cadence USBSS and USBSSP DRD controller
maintainers:
- Pawel Laszczak <pawell@cadence.com>
+description:
+ Cadence USB dual-role controller. Covers USBSS (SuperSpeed, USB 3.0) and
+ USBSSP (SuperSpeed Plus, USB 3.1 gen2x1). Both variants share the same
+ DRD/OTG register interface, so the driver auto-detects the controller
+ version at runtime.
+
properties:
compatible:
- const: cdns,usb3
+ oneOf:
+ - const: cdns,usb3
+ - items:
+ - {}
+ - const: cdns,cdnsp
reg:
- items:
- - description: OTG controller registers
- - description: XHCI Host controller registers
- - description: DEVICE controller registers
+ minItems: 2
+ maxItems: 3
reg-names:
+ minItems: 2
+ maxItems: 3
items:
- - const: otg
- - const: xhci
- - const: dev
+ enum: [ otg, xhci, dev ]
interrupts:
- minItems: 3
+ minItems: 2
items:
- description: XHCI host controller interrupt
- description: Device controller interrupt
@@ -35,7 +43,7 @@ properties:
cleared by xhci core, this interrupt is optional
interrupt-names:
- minItems: 3
+ minItems: 2
items:
- const: host
- const: peripheral
@@ -49,7 +57,7 @@ properties:
cdns3 to type C connector.
maximum-speed:
- enum: [super-speed, high-speed, full-speed]
+ enum: [super-speed-plus, super-speed, high-speed, full-speed]
phys:
minItems: 1
@@ -87,6 +95,47 @@ allOf:
- $ref: usb-drd.yaml#
- $ref: usb-xhci.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: cdns,cdnsp
+ then:
+ properties:
+ reg:
+ items:
+ - description: XHCI Host controller registers
+ - description: DEVICE controller registers
+ reg-names:
+ items:
+ - const: xhci
+ - const: dev
+ interrupts:
+ maxItems: 2
+ interrupt-names:
+ items:
+ - const: host
+ - const: peripheral
+ dr_mode:
+ enum: [host, peripheral]
+ else:
+ properties:
+ reg:
+ items:
+ - description: OTG controller registers
+ - description: XHCI Host controller registers
+ - description: DEVICE controller registers
+ reg-names:
+ items:
+ - const: otg
+ - const: xhci
+ - const: dev
+ interrupts:
+ minItems: 3
+ maxItems: 4
+ interrupt-names:
+ minItems: 3
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
index fec04702f5300..620b564914d43 100644
--- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
+++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
@@ -45,11 +45,11 @@ properties:
clocks:
minItems: 1
- maxItems: 2
+ maxItems: 3
clock-names:
minItems: 1
- maxItems: 2
+ maxItems: 3
operating-points-v2:
description: A phandle to the OPP table containing the performance states.
@@ -92,6 +92,27 @@ allOf:
- $ref: chipidea,usb2-common.yaml#
- $ref: usb-hcd.yaml#
- $ref: usb-drd.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: qcom,ci-hdrc
+ then:
+ properties:
+ clocks:
+ minItems: 2
+ clock-names:
+ minItems: 2
+ items:
+ - const: iface
+ - const: core
+ - const: fs
+ else:
+ properties:
+ clocks:
+ maxItems: 2
+ clock-names:
+ maxItems: 2
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml
index 9a94b2a74a1eb..e8b8c03f87a0e 100644
--- a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml
+++ b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml
@@ -15,6 +15,7 @@ properties:
- usb5e3,608
- usb5e3,610
- usb5e3,620
+ - usb5e3,625
- usb5e3,626
reg: true
@@ -26,6 +27,10 @@ properties:
description:
The regulator that provides 3.3V or 5.0V core power to the hub.
+ vdd12-supply:
+ description:
+ The regulator that provides 1.2V power to the hub.
+
peer-hub: true
ports:
@@ -56,6 +61,7 @@ allOf:
properties:
peer-hub: false
vdd-supply: false
+ vdd12-supply: false
- if:
properties:
@@ -68,6 +74,18 @@ allOf:
properties:
peer-hub: true
vdd-supply: true
+ vdd12-supply: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - usb5e3,625
+ then:
+ properties:
+ peer-hub: true
+ vdd-supply: true
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml
index 6d3fa2bc9ceec..ba790c4488b76 100644
--- a/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml
+++ b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml
@@ -79,22 +79,17 @@ properties:
- const: fr-swap
vdd-vbus-supply:
- description: VBUS power supply.
+ deprecated: true
+ description: use connector/vbus-supply instead.
vdd-pdphy-supply:
description: VDD regulator supply to the PDPHY.
- port:
- $ref: /schemas/graph.yaml#/properties/port
- description:
- Contains a port which produces data-role switching messages.
-
required:
- compatible
- reg
- interrupts
- interrupt-names
- - vdd-vbus-supply
allOf:
- if:
diff --git a/Documentation/devicetree/bindings/usb/richtek,rt1711h.txt b/Documentation/devicetree/bindings/usb/richtek,rt1711h.txt
deleted file mode 100644
index 6f8115db2ea9b..0000000000000
--- a/Documentation/devicetree/bindings/usb/richtek,rt1711h.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-Richtek RT1711H TypeC PD Controller.
-
-Required properties:
- - compatible : Must be "richtek,rt1711h".
- - reg : Must be 0x4e, it's slave address of RT1711H.
- - interrupts : <a b> where a is the interrupt number and b represents an
- encoding of the sense and level information for the interrupt.
-
-Required sub-node:
-- connector: The "usb-c-connector" attached to the tcpci chip, the bindings
- of connector node are specified in
- Documentation/devicetree/bindings/connector/usb-connector.yaml
-
-Example :
-rt1711h@4e {
- compatible = "richtek,rt1711h";
- reg = <0x4e>;
- interrupt-parent = <&gpio26>;
- interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
-
- usb_con: connector {
- compatible = "usb-c-connector";
- label = "USB-C";
- data-role = "dual";
- power-role = "dual";
- try-power-role = "sink";
- source-pdos = <PDO_FIXED(5000, 2000, PDO_FIXED_USB_COMM)>;
- sink-pdos = <PDO_FIXED(5000, 2000, PDO_FIXED_USB_COMM)
- PDO_VAR(5000, 12000, 2000)>;
- op-sink-microwatt = <10000000>;
-
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
-
- port@1 {
- reg = <1>;
- usb_con_ss: endpoint {
- remote-endpoint = <&usb3_data_ss>;
- };
- };
- };
- };
-};
diff --git a/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml b/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml
index fd1b13c0ed6bb..0554dbc4b8541 100644
--- a/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml
+++ b/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml
@@ -26,6 +26,7 @@ select:
contains:
enum:
- rockchip,rk3328-dwc3
+ - rockchip,rk3528-dwc3
- rockchip,rk3562-dwc3
- rockchip,rk3568-dwc3
- rockchip,rk3576-dwc3
@@ -38,6 +39,7 @@ properties:
items:
- enum:
- rockchip,rk3328-dwc3
+ - rockchip,rk3528-dwc3
- rockchip,rk3562-dwc3
- rockchip,rk3568-dwc3
- rockchip,rk3576-dwc3
@@ -135,6 +137,7 @@ allOf:
compatible:
contains:
enum:
+ - rockchip,rk3528-dwc3
- rockchip,rk3568-dwc3
- rockchip,rk3576-dwc3
then:
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index e880c6ca84f03..bad3c7bb17dc1 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -222,6 +222,7 @@ Hardware Monitoring Kernel Drivers
pmbus
powerz
powr1220
+ prom21-xhci
pt5161l
pxe1610
pwm-fan
diff --git a/Documentation/hwmon/prom21-xhci.rst b/Documentation/hwmon/prom21-xhci.rst
new file mode 100644
index 0000000000000..7984fb187bd8a
--- /dev/null
+++ b/Documentation/hwmon/prom21-xhci.rst
@@ -0,0 +1,101 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver prom21-xhci
+=========================
+
+Supported chips:
+
+ * AMD Promontory 21 (PROM21) xHCI USB host controller
+
+ Prefix: 'prom21_xhci'
+
+ PCI IDs: 1022:43fc, 1022:43fd
+
+Author:
+
+ - Jihong Min <hurryman2212@gmail.com>
+
+Description
+-----------
+
+This driver exposes the temperature sensor in AMD PROM21 xHCI controllers.
+
+The driver binds to an auxiliary device created by the xHCI PCI driver for
+supported controllers. The sensor value is accessed through a vendor-specific
+index/data register pair in the controller's PCI MMIO BAR.
+The auxiliary device is created by the ``xhci-pci-prom21`` PCI glue driver.
+USB host operation is otherwise delegated to the common ``xhci-pci`` code.
+
+PROM21 is an AMD chipset IP used in single-chip or daisy-chained configurations
+to build AMD 6xx/8xx series chipsets. Since the xHCI controllers are
+integrated in PROM21, this temperature can also be used as a monitor for a
+temperature close to the AMD chipset temperature.
+
+Register access
+---------------
+
+The temperature value is read through a vendor-specific index/data register
+pair in the xHCI PCI MMIO BAR. The driver uses the following byte offsets from
+the MMIO BAR base:
+
+======================= =====================================================
+0x3000 Vendor index register
+0x3008 Vendor data register
+======================= =====================================================
+
+The driver saves the current vendor index register value, writes the
+temperature selector ``0x0001e520`` to the vendor index register, reads the
+vendor data register, and restores the previous vendor index value before
+returning. The raw temperature value is the low 8 bits of the vendor data
+register value.
+
+The hwmon core serializes this driver's callbacks, and the driver restores the
+previous index value after each read. This does not provide synchronization
+with firmware, SMM, ACPI AML, or any other user outside this driver.
+
+No public AMD reference is available for the register pair or the raw value.
+The register pair was identified on an X870E system with two PROM21 xHCI
+controllers. One controller was passed through to a Windows VM, and the same
+controller's PCI MMIO BAR was observed from the Linux host while HWiNFO64 was
+reporting the PROM21 xHCI temperature. In the test environment, the reported
+temperature was very stable at idle and the displayed sensor resolution was
+low, which made it possible to look for a consistently repeating MMIO response
+for the same reported temperature. During observation, offset 0x3000 repeatedly
+contained selector ``0x0001e520``. Writing the same selector to offset 0x3000
+from Linux and then reading offset 0x3008 reproduced the same raw value, so the
+offsets are treated as a vendor index/data register pair.
+
+The conversion formula was empirically inferred by matching observed raw
+8-bit values against HWiNFO64's reported PROM21 xHCI temperature for the same
+controller. The observed mapping is:
+
+ temp[C] = raw * 0.9066 - 78.624
+
+Runtime PM
+----------
+
+The driver does not wake the xHCI PCI device for hwmon reads. It reads the
+temperature only when the parent device is already active. A read from a
+suspended device returns ``-ENODATA``. After a successful read, the driver
+drops its active-only runtime PM reference and lets the PM core re-evaluate the
+idle state.
+
+Sysfs entries
+-------------
+
+======================= =====================================================
+temp1_input Temperature in millidegrees Celsius
+======================= =====================================================
+
+The hwmon device name is ``prom21_xhci``. The sysfs path depends on the hwmon
+device number assigned by the kernel. Userspace can locate the device by
+matching the ``name`` attribute:
+
+.. code-block:: sh
+
+ for hwmon in /sys/class/hwmon/hwmon*; do
+ [ "$(cat "$hwmon/name")" = "prom21_xhci" ] || continue
+ cat "$hwmon/temp1_input"
+ done
+
+If the raw register value is invalid, ``temp1_input`` returns ``-ENODATA``.
diff --git a/Documentation/translations/zh_CN/subsystem-apis.rst b/Documentation/translations/zh_CN/subsystem-apis.rst
index 830217140fb66..b52e1feb0167e 100644
--- a/Documentation/translations/zh_CN/subsystem-apis.rst
+++ b/Documentation/translations/zh_CN/subsystem-apis.rst
@@ -90,6 +90,7 @@ TODOList:
security/index
PCI/index
peci/index
+ usb/index
TODOList:
@@ -104,6 +105,5 @@ TODOList:
* accel/index
* crypto/index
* bpf/index
-* usb/index
* misc-devices/index
* wmi/index
diff --git a/Documentation/translations/zh_CN/usb/CREDITS b/Documentation/translations/zh_CN/usb/CREDITS
new file mode 100644
index 0000000000000..c133b1a5daff4
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/CREDITS
@@ -0,0 +1,163 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/CREDITS
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+简易 Linux USB 驱动的致谢名单:
+
+以下人员都为 Linux USB 驱动代码作出了贡献
+(按姓氏字母顺序排列)。我相信这份名单本应
+更长一些,但确实不容易维护。
+如需将自己加入名单,请提交补丁。
+
+ Georg Acher <acher@informatik.tu-muenchen.de>
+ David Brownell <dbrownell@users.sourceforge.net>
+ Alan Cox <alan@lxorguk.ukuu.org.uk>
+ Randy Dunlap <randy.dunlap@intel.com>
+ Johannes Erdfelt <johannes@erdfelt.com>
+ Deti Fliegl <deti@fliegl.de>
+ ham <ham@unsuave.com>
+ Bradley M Keryan <keryan@andrew.cmu.edu>
+ Greg Kroah-Hartman <greg@kroah.com>
+ Pavel Machek <pavel@suse.cz>
+ Paul Mackerras <paulus@cs.anu.edu.au>
+ Petko Manlolov <petkan@dce.bg>
+ David E. Nelson <dnelson@jump.net>
+ Vojtech Pavlik <vojtech@suse.cz>
+ Bill Ryder <bryder@sgi.com>
+ Thomas Sailer <sailer@ife.ee.ethz.ch>
+ Gregory P. Smith <greg@electricrain.com>
+ Linus Torvalds <torvalds@linux-foundation.org>
+ Roman Weissgaerber <weissg@vienna.at>
+ <Kazuki.Yasumatsu@fujixerox.co.jp>
+
+特别感谢:
+
+ Inaky Perez Gonzalez <inaky@peloncho.fis.ucm.es>
+ 感谢他发起了 Linux USB 驱动开发工作,并编写了体量较大的 uusbd
+ 驱动中的大部分代码。我们从那项工作中学到了很多。
+
+ NetBSD 和 FreeBSD 的 USB 开发者们
+ 感谢他们加入 Linux USB 邮件列表,提供建议并分享实现经验。
+
+附加感谢:
+ 还要感谢以下公司与个人在硬件、支持、时间投入和开发方面提供的捐赠与帮助
+ (摘自 Inaky 驱动原始的 THANKS 文件):
+
+ 以下公司曾帮助我们开发 Linux USB / UUSBD:
+
+ - 3Com GmbH 捐赠了一台 ISDN Pro TA,并在技术问题和测试设备方面为我
+ 提供支持。没想到能得到这么大的帮助。
+
+ - USAR Systems 向我们提供了他们出色的 USB 评估套件,
+ 使我们能够测试 Linux USB 驱动对最新 USB 规范的符合性。
+ USAR Systems 认识到保持开放操作系统与时俱进的重要性,
+ 并以硬件支持这个项目。感谢!
+
+ - 感谢英特尔提供的宝贵帮助。
+
+ - 我们与 Cherry 合作,使 Linux 成为首个内置 USB 支持的操作系统。
+ Cherry 是全球最大的键盘制造商之一。
+
+ - CMD Technology, Inc. 慷慨捐赠了一块 CSA-6700 PCI-to-USB
+ 控制卡,用于测试 OHCI 实现。
+
+ - 由于他们对我们的支持,Keytronic 可以放心,
+ 他们的键盘能卖给至少 300 万 Linux 用户中的一部分。
+
+ - ing büro h doran [http://www.ibhdoran.com]!
+ 在欧洲,想给主板买一个 PC 背板 USB 连接器几乎是不可能的
+ (我自己做的那个相当糟糕 :))。现在我知道该去哪里买漂亮的 USB
+ 配件了!
+
+ - Genius Germany 捐赠了一只 USB 鼠标,用于测试鼠标启动协议;
+ 他们还捐赠了 F-23 数字摇杆和 NetMouse Pro。感谢!
+
+ - AVM GmbH Berlin 支持我们开发 Linux 下的 AVM ISDN Controller B1 USB 驱动。
+ AVM 是领先的 ISDN 控制器制造商,其主动式设计对包括 Linux 在内的
+ 所有操作系统平台开放。
+
+ - 非常感谢 Y-E Data, Inc 捐赠的 FlashBuster-U USB 软驱,
+ 使我们能够测试批量传输代码。
+
+ - 感谢 Logitech 捐赠了一只三轴 USB 鼠标。
+
+ Logitech 负责设计、制造并销售各种人机接口设备,
+ 在键盘、鼠标、轨迹球、摄像头、扬声器,以及面向游戏和专业用途的
+ 控制设备方面拥有悠久历史和丰富经验。
+
+ 作为这些设备广为人知的供应商和销售商,他们捐赠了 USB 鼠标、
+ 摇杆和扫描仪,以表明 Linux 的重要性,也让 Logitech 的客户
+ 能在自己喜欢的操作系统上获得支持,并让所有 Linux 用户都能使用
+ Logitech 以及其他 USB 硬件。
+
+ Logitech 也是 1999 年 2 月 11 日维也纳 Linux 大会的官方赞助商,
+ 我们将在会上展示 Linux USB 工作的最新进展。
+
+ - 感谢 CATC 提供 USB Inspector,帮助我们揭开 UHCI 内部实现中
+ 那些不为人知的角落。
+
+ - 感谢 Entrega 为开发工作提供 PCI 转 USB 卡、集线器和转换器产品。
+
+ - 感谢 ConnectTech 提供 WhiteHEAT USB 转串口转换器以及相关文档,
+ 让这个驱动得以写成。
+
+ - 感谢 ADMtek 提供 Pegasus 和 Pegasus II 评估板、规格说明,
+ 以及驱动开发过程中的宝贵建议。
+
+ 另外还要感谢以下个人(嘿,顺序不分先后 :))
+
+ - Oren Tirosh <orenti@hishome.net>,
+ 他非常耐心地听我唠叨各种 USB 疑问,还给了很多很酷的想法。
+
+ - Jochen Karrer <karrer@wpfd25.physik.uni-wuerzburg.de>,
+ 指出了致命 bug,并给出了宝贵建议。
+
+ - Edmund Humemberger <ed@atnet.at>,他在公共关系与项目管理方面
+ 为 Linux-USB 项目付出了巨大的努力。
+
+ - Alberto Menegazzi <flash@flash.iol.it> 正在着手编写 UUSBD 文档,加油!
+
+ - Ric Klaren <ia_ric@cs.utwente.nl> 编写了很好的入门文档,
+ 与 Alberto 的作品形成良性竞争:)。
+
+ - Christian Groessler <cpg@aladdin.de>,感谢他在那些棘手细节上的帮助。
+
+ - Paul MacKerras 改进了 OHCI 实现,推动了对 iMac 的支持,
+ 并提供了大量的改进意见。
+
+ - Fernando Herrera <fherrera@eurielec.etsit.upm.es>
+ 负责撰写、维护并不断补充那份期待已久、独一无二又精彩的
+ UUSBD FAQ!太棒了!
+
+ - Rasca Gmelch <thron@gmx.de> 重新启用了 raw 驱动,
+ 指出了一些错误,并启动了 uusbd-utils 软件包。
+
+ - Peter Dettori <dettori@ozy.dec.com>,像疯了一样挖掘 bug,
+ 还提出了很多很酷的建议,太棒了!
+
+ - 自由软件与 Linux 社区的所有成员,包括 FSF、GNU 项目、
+ MIT X 联盟、TeX 社区等等,谢谢你们!
+
+ - 特别感谢 Richard Stallman 创造了 Emacs!
+
+ - 感谢 linux-usb 邮件列表的所有成员,读了那么多邮件——不开玩笑了,
+ 感谢你们提出的所有建议!
+
+ - 感谢 USB Implementers Forum 成员们的帮助与支持。
+
+ - Nathan Myers <ncm@cantrip.org>,感谢他的建议!
+ (希望你喜欢 Cibeles 的派对。)
+
+ - 感谢 Linus Torvalds 创建、开发并管理 Linux。
+
+ - Mike Smith、Craig Keithley、Thierry Giron 和 Janet Schank
+ 感谢他们让我认识到标准 USB 集线器其实也没那么“标准”,
+ 这有助于我们在标准集线器驱动中加入厂商特定的特殊处理。
diff --git a/Documentation/translations/zh_CN/usb/acm.rst b/Documentation/translations/zh_CN/usb/acm.rst
new file mode 100644
index 0000000000000..51d6eb8f5660e
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/acm.rst
@@ -0,0 +1,147 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/acm.rst
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+====================
+Linux ACM 驱动 v0.16
+====================
+
+版权所有 (c) 1999 Vojtech Pavlik <vojtech@suse.cz>
+
+由 SuSE 赞助
+
+0. 免责声明
+~~~~~~~~~~~
+本程序是自由软件;你可以在自由软件基金会发布的
+GNU 通用公共许可证第 2 版,或者(按你的选择)
+任何后续版本的条款下重新发布和/或修改它。
+
+发布本程序是希望它能发挥作用,但它不附带任何担保;
+甚至不包括对适销性或特定用途适用性的默示担保。
+详情见 GNU 通用公共许可证。
+
+你应该已经随本程序收到了 GNU 通用公共许可证的副本;
+如果没有,请致信:Free Software Foundation, Inc., 59
+Temple Place, Suite 330, Boston, MA 02111-1307 USA。
+
+如需联系作者,可发送电子邮件至 vojtech@suse.cz,
+或邮寄至:
+Vojtech Pavlik, Ucitelska 1576, Prague 8,
+182 00, Czech Republic。
+
+为方便起见,软件包中已附带 GNU 通用公共许可证
+第 2 版:见 COPYING 文件。
+
+1. 使用方法
+~~~~~~~~~~~
+``drivers/usb/class/cdc-acm.c`` 驱动可用于符合 USB
+通信设备类抽象控制模型(USB CDC ACM)规范的
+USB 调制解调器和 USB ISDN 终端适配器。
+
+许多调制解调器支持此驱动,以下是我所知道的一些型号:
+
+ - 3Com OfficeConnect 56k
+ - 3Com Voice FaxModem Pro
+ - 3Com Sportster
+ - MultiTech MultiModem 56k
+ - Zoom 2986L FaxModem
+ - Compaq 56k FaxModem
+ - ELSA Microlink 56k
+
+我知道有一款 ISDN 终端适配器可以与 ACM 驱动一起使用:
+
+ - 3Com USR ISDN Pro TA
+
+一些手机也可以通过 USB 连接。
+我知道以下机型可以正常工作:
+
+ - SonyEricsson K800i
+
+遗憾的是,许多调制解调器和大多数 ISDN TA
+都使用专有接口,因此无法与此驱动配合工作。
+购买前请先确认设备是否符合 ACM 规范。
+
+要使用这些调制解调器,需要加载以下模块::
+
+ usbcore.ko
+ uhci-hcd.ko ohci-hcd.ko or ehci-hcd.ko
+ cdc-acm.ko
+
+之后就应该可以访问这些调制解调器了。
+应当可以使用 ``minicom``、``ppp`` 和 ``mgetty``
+与它们通信。
+
+2. 验证驱动是否正常工作
+~~~~~~~~~~~~~~~~~~~~~~~
+
+第一步是检查 ``/sys/kernel/debug/usb/devices``,
+其内容应该类似如下::
+
+ T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2
+ B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0
+ D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
+ P: Vendor=0000 ProdID=0000 Rev= 0.00
+ S: Product=USB UHCI Root Hub
+ S: SerialNumber=6800
+ C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA
+ I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
+ E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms
+ T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
+ D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2
+ P: Vendor=04c1 ProdID=008f Rev= 2.07
+ S: Manufacturer=3Com Inc.
+ S: Product=3Com U.S. Robotics Pro ISDN TA
+ S: SerialNumber=UFT53A49BVT7
+ C: #Ifs= 1 Cfg#= 1 Atr=60 MxPwr= 0mA
+ I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=acm
+ E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
+ E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
+ E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
+ C:* #Ifs= 2 Cfg#= 2 Atr=60 MxPwr= 0mA
+ I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
+ E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
+ I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
+ E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
+ E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
+
+这三行的存在很关键(以及 ``Cls=`` 字段里出现的
+``comm`` 和 ``data`` 类);它说明这是一个 ACM
+设备。``Driver=acm`` 表示该设备正在使用 acm 驱动。
+如果只看到 ``Cls=ff(vend.)``,那就无能为力了:
+这说明你手上的设备使用的是厂商专有接口::
+
+ D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2
+ I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
+ I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
+
+在系统日志中应该可以看到::
+
+ usb.c: USB new device connect, assigned device number 2
+ usb.c: kmalloc IF c7691fa0, numif 1
+ usb.c: kmalloc IF c7b5f3e0, numif 2
+ usb.c: skipped 4 class/vendor specific interface descriptors
+ usb.c: new device strings: Mfr=1, Product=2, SerialNumber=3
+ usb.c: USB device number 2 default language ID 0x409
+ Manufacturer: 3Com Inc.
+ Product: 3Com U.S. Robotics Pro ISDN TA
+ SerialNumber: UFT53A49BVT7
+ acm.c: probing config 1
+ acm.c: probing config 2
+ ttyACM0: USB ACM device
+ acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0
+ acm.c: acm_control_msg: rq: 0x20 val: 0x0 len: 0x7 result: 7
+ usb.c: acm driver claimed interface c7b5f3e0
+ usb.c: acm driver claimed interface c7b5f3f8
+ usb.c: acm driver claimed interface c7691fa0
+
+如果以上都正常,请启动 ``minicom``,
+把它配置为连接 ``ttyACM`` 设备,然后
+尝试输入 ``at``。如果返回 ``OK``,说明一切工作正常。
diff --git a/Documentation/translations/zh_CN/usb/authorization.rst b/Documentation/translations/zh_CN/usb/authorization.rst
new file mode 100644
index 0000000000000..2aa311f6b9675
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/authorization.rst
@@ -0,0 +1,139 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/authorization.rst
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+=============================
+授权或禁止 USB 设备连接到系统
+=============================
+
+版权 (C) 2007 Inaky Perez-Gonzalez
+<inaky@linux.intel.com> 英特尔公司
+
+此功能允许你控制 USB 设备是否可以在系统中使用。
+借助它,你可以完全通过用户空间实现对 USB 设备的锁定。
+
+目前,当插入一个 USB 设备时,系统会对其进行配置,
+其接口会立即向用户开放。
+有了这项改动,只有在 root 授权设备完成配置后,
+设备才可被使用。
+
+
+用法
+====
+
+授权设备接入::
+
+ $ echo 1 > /sys/bus/usb/devices/DEVICE/authorized
+
+取消对设备的授权::
+
+ $ echo 0 > /sys/bus/usb/devices/DEVICE/authorized
+
+将新连接到 ``hostX`` 的设备默认设为未授权(即锁定)::
+
+ $ echo 0 > /sys/bus/usb/devices/usbX/authorized_default
+
+解除锁定::
+
+ $ echo 1 > /sys/bus/usb/devices/usbX/authorized_default
+
+默认情况下,所有 USB 设备都是授权的。
+向 ``authorized_default`` 属性写入 ``2`` 会使内核
+默认只授权连接到内部 USB 端口的设备。
+
+系统锁定示例(比较粗糙)
+------------------------
+
+假设你想实现一个锁定功能,只允许类型为 XYZ 的设备接入
+(例如某台带有外露 USB 端口的自助服务终端)::
+
+ 启动系统
+ rc.local ->
+
+ for host in /sys/bus/usb/devices/usb*
+ do
+ echo 0 > $host/authorized_default
+ done
+
+给 udev 挂一个脚本,用于处理新插入的 USB 设备::
+
+ if device_is_my_type $DEV
+ then
+ echo 1 > $device_path/authorized
+ done
+
+
+``device_is_my_type()`` 才是锁定方案真正见功夫的
+地方。仅仅检查 class、type 和 protocol 是否匹配
+某个值,是你能做出的最糟糕的安全验证之一;
+对想绕过它的人来说,这反而是最容易利用的方案。
+如果你需要真正安全的办法,那就该使用加密、
+证书认证之类的机制。把 USB 存储设备当作
+“钥匙”的一个简单例子可以是::
+
+ function device_is_my_type()
+ {
+ echo 1 > authorized # 暂时授权它
+ # FIXME: 确保没人能挂载它
+ mount DEVICENODE /mntpoint
+ sum=$(md5sum /mntpoint/.signature)
+ if [ $sum = $(cat /etc/lockdown/keysum) ]
+ then
+ echo "We are good, connected"
+ umount /mntpoint
+ # 再做一些额外处理,让其他人也能使用它
+ else
+ echo 0 > authorized
+ fi
+ }
+
+
+当然,这个例子很粗糙;真正要做的话,
+你会想用基于 PKI 的证书校验,这样就不必依赖
+共享密钥之类的东西。不过你应该已经明白意思了。
+任何拿到设备仿真工具包的��都能伪造描述符和设备信息。
+别信这个。
+
+接口授权
+--------
+
+也有类似的方法用于允许或拒绝特定 USB 接口。
+这使得你可以只阻止某个 USB 设备中的部分接口。
+
+授权接口::
+
+ $ echo 1 > /sys/bus/usb/devices/INTERFACE/authorized
+
+取消接口授权::
+
+ $ echo 0 > /sys/bus/usb/devices/INTERFACE/authorized
+
+也可以更改特定 USB 总线上新接口的默认授权值。
+
+默认允许接口::
+
+ $ echo 1 > /sys/bus/usb/devices/usbX/interface_authorized_default
+
+默认拒绝接口::
+
+ $ echo 0 > /sys/bus/usb/devices/usbX/interface_authorized_default
+
+默认情况下,
+``interface_authorized_default`` 位为 ``1``,
+因此所有接口默认都处于已授权状态。
+
+注意:
+ 如果把一个先前未授权的接口改为已授权,
+ 则必须通过将 ``INTERFACE`` 写入 ``/sys/bus/usb/drivers_probe``
+ 来手动触发驱动探测。
+
+对于需要多个接口的驱动程序,应先授权所有必需接口,
+然后再触发驱动探测。这样做可以避免副作用。
diff --git a/Documentation/translations/zh_CN/usb/chipidea.rst b/Documentation/translations/zh_CN/usb/chipidea.rst
new file mode 100644
index 0000000000000..ea0dc3043189f
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/chipidea.rst
@@ -0,0 +1,150 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/chipidea.rst
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+=============================
+ChipIdea 高速双角色控制器驱动
+=============================
+
+1. 如何测试 OTG FSM(HNP 和 SRP)
+---------------------------------
+
+下面以两块 Freescale i.MX6Q Sabre SD 开发板为例,
+说明如何通过 sysfs 输入文件演示 OTG 的 HNP 和 SRP 功能。
+
+1.1 如何使能 OTG FSM
+--------------------
+
+1.1.1 在 ``menuconfig`` 中选择 ``CONFIG_USB_OTG_FSM``,并重新编译内核
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+重新构建内核镜像和模块。如果想查看 OTG FSM 的
+一些内部变量,可以挂载 ``debugfs``;其中有两个文件
+可以显示 OTG FSM 变量以及部分控制器寄存器的值::
+
+ cat /sys/kernel/debug/ci_hdrc.0/otg
+ cat /sys/kernel/debug/ci_hdrc.0/registers
+
+1.1.2 在控制器节点对应的 ``dts`` 文件中添加以下条目
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ otg-rev = <0x0200>;
+ adp-disable;
+
+1.2 测试步骤
+------------
+
+1) 给两块 Freescale i.MX6Q Sabre SD 开发板上电,
+ 并加载 gadget 类驱动(例如 ``g_mass_storage``)。
+
+2) 用 USB 线连接两块开发板:
+ 一端是 micro A 插头,另一端是 micro B 插头。
+
+ 插入 micro A 插头的一端是 A 设备,它应枚举另一端的 B 设备。
+
+3) 角色切换
+
+ 在 B 设备上执行::
+
+ echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
+
+ B 设备应接管主机角色并枚举 A 设备。
+
+4) A 设备切回主机角色
+
+ 在 B 设备上执行::
+
+ echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
+
+ 或者,通过引入 HNP 轮询,B 端主机可以知道
+ A 端外设希望切换为主机角色,因此这次角色切换
+ 也可以通过 A 端外设响应 B 端主机的轮询,
+ 在 A 侧触发。
+ 这可以通过在 A 设备上执行下面的命令来完成::
+
+ echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req
+
+ A 设备应切回主机角色并枚举 B 设备。
+
+5) 拔掉 B 设备(拔掉 micro B 插头),
+ 并在 10 秒内重新插入;
+ A 设备应重新枚举 B 设备。
+
+6) 拔掉 B 设备(拔掉 micro B 插头),
+ 并在 10 秒后重新插入;
+ A 设备不应重新枚举 B 设备。
+
+ 如果 A 设备希望使用总线:
+
+ 在 A 设备上执行::
+
+ echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_drop
+ echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req
+
+ 如果 B 设备希望使用总线:
+
+ 在 B 设备上执行::
+
+ echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
+
+7) A 设备关闭总线供电
+
+ 在 A 设备上执行::
+
+ echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_drop
+
+ A 设备应断开与 B 设备的连接,并关闭总线供电。
+
+8) B 设备发出 SRP 数据脉冲
+
+ 在 B 设备上执行::
+
+ echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
+
+ A 设备应恢复 USB 总线并枚举 B 设备。
+
+1.3 参考文档
+------------
+《On-The-Go and Embedded Host Supplement
+to the USB Revision 2.0 Specification
+July 27, 2012 Revision 2.0 version 1.1a》
+
+2. 如何将 USB 用作系统唤醒源
+----------------------------
+下面是在 i.MX6 平台上把 USB 用作系统唤醒源的示例。
+
+2.1 使能核心控制器的唤醒功能::
+
+ echo enabled > /sys/bus/platform/devices/ci_hdrc.0/power/wakeup
+
+2.2 使能 glue 层的唤醒功能::
+
+ echo enabled > /sys/bus/platform/devices/2184000.usb/power/wakeup
+
+2.3 使能 PHY 的唤醒功能(可选)::
+
+ echo enabled > /sys/bus/platform/devices/20c9000.usbphy/power/wakeup
+
+2.4 使能根集线器的唤醒功能::
+
+ echo enabled > /sys/bus/usb/devices/usb1/power/wakeup
+
+2.5 使能相关设备的唤醒功能::
+
+ echo enabled > /sys/bus/usb/devices/1-1/power/wakeup
+
+如果系统只有一个 USB 端口,
+而你希望在该端口上启用 USB 唤醒功能,
+可以使用下面的脚本::
+
+ for i in $(find /sys -name wakeup | grep usb);do echo enabled > $i;done;
diff --git a/Documentation/translations/zh_CN/usb/dwc3.rst b/Documentation/translations/zh_CN/usb/dwc3.rst
new file mode 100644
index 0000000000000..3468ce50c5baa
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/dwc3.rst
@@ -0,0 +1,63 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/dwc3.rst
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+=========
+DWC3 驱动
+=========
+
+
+待办
+~~~~
+
+阅读时如果想顺手认领点任务,可以从下面挑一项 :)
+
+- 将中断处理程序改为每个端点各自使用线程化 IRQ
+
+ 事实证明,有些 DWC3 命令大约需要 ``~1 ms`` 才能完成。
+ 当前代码会一直自旋等待命令完成,这种设计并不好。
+
+ 实现思路:
+
+ - DWC 核心实现了一个按端点对中断进行解复用的 IRQ 控制器。
+ 中断号在探测(``probe``)阶段分配,并归属于该设备。
+ 如果硬件通过 ``MSI`` 为每个端点提供独立中断,
+ 那么这个“虚拟”IRQ 控制器就可以被真实的端点中断取代。
+
+ - 在调用 ``usb_ep_enable()`` 时请求并分配中断资源,
+ 在调用 ``usb_ep_disable()`` 时释放中断资源。
+ 最坏情况下需要 32 个中断,最少是 ``ep0/1`` 的两个中断。
+ - ``dwc3_send_gadget_ep_cmd()`` 将在 ``wait_for_completion_timeout()``
+ 中休眠,直到命令完成。
+ - 中断处理程序分为以下几个部分:
+
+ - 设备级主中断处理程序
+ 遍历每个事件,并对其调用 ``generic_handle_irq()``。
+ 从 ``generic_handle_irq()`` 返回后,确认事件计数器,使中断最终消失。
+
+ - 设备级线程化处理程序
+ 无。
+
+ - 端点中断的主处理程序
+ 读取事件并尽量处理它。凡是需要睡眠的操作都交给线程处理。
+ 事件保存在每个端点的数据结构中。
+ 还要注意,一旦把某项工作交给线程处理,
+ 就不要再在主处理程序里处理它,
+ 以免出现优先级反转之类的问题。
+
+ - 端点中断的线程化处理程序
+ 处理剩余的端点工作,这些工作可能会睡眠,例如等待命令完成。
+
+ 延迟:
+
+ 不应增加延迟,因为中断线程具有较高优先级,
+ 会在普通用户态任务之前运行
+ (除非用户更改了调度优先级)。
diff --git a/Documentation/translations/zh_CN/usb/ehci.rst b/Documentation/translations/zh_CN/usb/ehci.rst
new file mode 100644
index 0000000000000..e05e493a30d38
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/ehci.rst
@@ -0,0 +1,261 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/ehci.rst
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+=========
+EHCI 驱动
+=========
+
+2002年12月27日
+
+EHCI 驱动用于通过支持 USB 2.0 的主机控制器
+硬件与高速 USB 2.0 设备通信。USB 2.0 兼容
+USB 1.1 标准,它定义了三种传输速率:
+
+ - “高速”(High Speed)480 Mbit/sec(60 MByte/sec)
+ - “全速”(Full Speed)12 Mbit/sec(1.5 MByte/sec)
+ - “低速”(Low Speed)1.5 Mbit/sec
+
+USB 1.1 仅支持全速与低速。
+高速设备可以在 USB 1.1 系统上使用,
+但速度会降到 USB 1.1 的速率。
+
+USB 1.1 设备也可以在 USB 2.0 系统上使用。当它们
+插入 EHCI 控制器时,会被交由 USB 1.1 的伴随
+(companion)控制器处理,该控制器通常是 OHCI 或 UHCI。
+
+当 USB 1.1 设备插入 USB 2.0 集线器时,它们通过
+集线器中的事务转换器(Transaction Translator,TT)
+与 EHCI 控制器交互,该转换器将低速或全速事务转换为
+高速分割事务,从而避免浪费传输带宽。
+
+截至本文���写时,该驱动已在以下 EHCI 实现上成功运行
+(按字母顺序):Intel、NEC、Philips 和 VIA。
+其他供应商的 EHCI 实现正在陆续问世;
+预计该驱动在这些实现上也可正常运行。
+
+自 2001 年年中起,usb-storage 设备就已可用
+(在 2.4 版该驱动上速度相当不错),
+集线器则直到 2001 年底才开始可用,而其他类型的高速设备
+似乎要等到更多系统内置 USB 2.0 后才会出现。
+这类新系统从 2002 年初开始上市,
+并在 2002 年下半年变得更加常见。
+
+注意,USB 2.0 支持并不只是 EHCI 本身。
+它还需要对 Linux-USB 核心 API 作出其他修改,
+包括 hub 驱动;不过这些修改并不需要真正改变
+暴露给 USB 设备驱动的基本 ``usbcore`` API。
+
+- David Brownell
+ <dbrownell@users.sourceforge.net>
+
+
+功能
+====
+
+该驱动会定期在 x86 硬件上进行测试,
+也已在 PPC 硬件上使用,因此大小端问题应当已经解决。
+因此可以认为,它已经处理好了所有必要的 PCI 细节,
+所以即便在 DMA 映射有些特殊的系统上,
+I/O 也应能正常运行。
+
+传输类型
+--------
+
+截至本文撰写时,该驱动应当已经能够很好地处理
+所有控制传输、批量传输和中断传输,
+包括通过 USB 2.0 集线器中的事务转换器
+与 USB 1.1 设备通信;但仍可能存在 bug。
+
+高速等时(ISO)传输支持也已可用,但截至本文撰写时,
+还没有 Linux 驱动使用这项支持。
+
+目前尚不支持通过事务转换器实现全速等时传输。
+需要注意,ISO 传输的 split transaction 支持
+与高速 ISO 传输几乎无法共用代码,
+因为 EHCI 用不同的数据结构表示它们。
+因此,目前大多数 USB 音频和视频设备
+还不能通过高速总线连接使用。
+
+驱动行为
+--------
+
+所有类型的传输都可以排队。
+这意味着来自一个接口驱动的控制传输
+(或通过 usbfs 发出的控制传输)不会干扰
+另一个驱动的控制传输,而且中断传输可以使用 1 帧的周期,
+而不必担心中断处理开销导致的数据丢失。
+
+
+EHCI 根集线器代码会将 USB 1.1 设备移交给其伴随控制器。
+该驱动不需要了解那些驱动的任何细节;
+一个原本就能正常工作的 OHCI 或 UHCI 驱动,
+并不会因为 EHCI 驱动也存在而需要更改。
+
+电源管理方面还有一些问题;
+当前挂起/恢复的行为还不完全正确。
+
+此外,在调度周期性事务
+(中断和等时传输)时还采取了一些简化处理。
+这些简化会限制可调度的周期性事务数量,
+并且无法使用小于一帧的轮询间隔。
+
+使用方式
+========
+
+假设有一个 EHCI 控制器(位于 PCI 卡或主板上),
+并且已将此驱动编译为模块,可这样加载::
+
+ # modprobe ehci-hcd
+
+卸载方式::
+
+ # rmmod ehci-hcd
+
+还应加载一个伴随控制器驱动,
+例如 ``ohci-hcd`` 或 ``uhci-hcd``。
+如果 EHCI 驱动出现任何问题,只需卸载它的模块,
+随后该伴随控制器驱动就会接手
+此前由 EHCI 驱动处理的所有设备
+(但速度会降低)。
+
+模块参数(传给 ``modprobe``)包括:
+
+ log2_irq_thresh(默认值 0):
+ 默认中断延迟的 log2 值,单位是微帧。默认值 0 表示 1 个微帧
+ (125 微秒)。最大值 6 表示 2^6 = 64 个微帧。
+ 该值控制 EHCI 控制器发出中断的频率。
+
+如果在 2.5 内核上使用此驱动,并且启用了 USB 调试支持,
+则会在任一 EHCI 控制器的 ``sysfs`` 目录中看到三个文件:
+
+ ``async``
+ 转储异步调度,用于控制传输和批量传输。它会显示每个活动的 ``qh``
+ 以及待处理的 ``qtd``,通常每个 ``urb`` 对应一个 ``qtd``。
+ (可以在 ``usb-storage`` 做磁盘 I/O 时看它;顺便观察请求队列!)
+
+ ``periodic``
+ 转储周期性调度,用于中断传输和等时传输。不显示 ``qtd``。
+
+ ``registers``
+ 显示控制器寄存器状态。
+
+这些文件的内容有助于定位驱动问题。
+
+
+设备驱动通常不需要关心自己是否运行在 EHCI 之上,
+但它们可能想检查
+``usb_device->speed == USB_SPEED_HIGH``。
+高速设备能做到全速(或低速)设备做不到的事,
+例如高带宽的周期性传输(中断或 ISO 传输)。
+另外,设备描述符中的某些值
+(例如周期性传输的轮询间隔)
+在高速模式下使用不同的编码方式。
+
+不过,一定要让设备驱动经过 USB 2.0 集线器的测试。
+当使用事务转换器时,这些集线器报告某些故障
+(例如断开连接)的方式会不同;
+已经见过一些驱动在遇到与 OHCI 或 UHCI
+所报告的不同故障时表现不佳。
+
+性能
+====
+
+USB 2.0 吞吐量主要受两个因素制约:
+主机控制器处理请求的速度,以及设备响应这些请求的速度。
+480 Mbit/sec 的“原始传输率”对所有设备都成立,
+但总吞吐量还会受到诸如单个高速包之间的延迟、
+驱动是否足够聪明,以及系统整体负载等因素的影响。
+延迟也是性能考量因素。
+
+批量传输最常用于关注吞吐量的场景。
+需要记住的是,批量传输总是以 512 字节包为单位,
+而一个 USB 2.0 微帧中最多只能容纳 13 个这样的包。
+8 个 USB 2.0 微帧构成一个 USB 1.1 帧;
+一个微帧的时长是 1 毫秒 / 8 = 125 微秒。
+
+因此,只要硬件和设备驱动软件都允许,
+批量传输可提供超过 50 MByte/sec 的带宽。
+周期性传输模式(等时和中断)允许使用更大的包大小,
+从而可以逼近所宣称的 480 Mbit/sec 传输率。
+
+硬件性能
+--------
+
+截至本文撰写时,单个 USB 2.0 设备的最大传输速率
+通常约为 20 MByte/sec。
+这当然会随着时间改变:一些设备现在更快,一些更慢。
+
+第一代 NEC EHCI 实现似乎存在
+大约 28 MByte/sec 的硬件瓶颈。
+虽然这对单个 20 MByte/sec 的设备显然已经够用,
+但把三个这样的设备挂到同一总线上,
+并不能得到 60 MByte/sec。
+问题似乎在于控制器硬件无法并发进行 USB 与 PCI 访问,
+因此它每个微帧只会尝试 6 次(也许是 7 次)
+USB 事务,而不是 13 次。
+(对一个比其他产品早上市一年的芯片来说,
+这是个合理的妥协!)
+
+
+预计较新的实现会在这方面做得更好,
+通过投入更多芯片面积来解决这个问题,
+使新的主板芯片组更接近 60 MByte/sec 的目标。
+这既包括 NEC 的更新实现,也包括其他厂商的芯片。
+
+
+主机从 EHCI 控制器收到“请求已完成”中断的最小延迟
+为一个微帧(125 微秒)。该延迟可以调节;
+驱动提供了一个模块选项。默认情况下,
+``ehci-hcd`` 使用最小延迟,这意味着当发出一个控制
+或批量请求时,通常可以在不到 250 微秒内得知它已完成
+(具体取决于传输大小)。
+
+软件性能
+--------
+
+即便只是要达到 20 MByte/sec 的传输速率,
+Linux-USB 设备驱动也必须让 EHCI 队列始终保持满载。
+这意味着要发出较大的请求,
+或者在需要发出一连串小请求时使用批量请求排队。
+如果驱动未做到这一点,那么会直接从性能结果上表现出来。
+
+
+在典型情况下,使用 ``usb_bulk_msg()``
+以 4 KB 块循环写出,
+会浪费超过一半的 USB 2.0 带宽。
+I/O 完成与驱动发出下一次请求之间的延迟,
+通常会比一次 I/O 本身耗时更长。
+如果同样的循环改用 16 KB 块,会好一些;
+若使用一连串 128 KB 块,则浪费会少得多。
+
+
+但与其依赖这么大的 I/O 缓冲区来让同步 I/O 高效,
+不如直接向主机控制器排入多个(批量)请求,
+然后等待它们全部完成(或在出错时取消)。
+这种 URB 排队方式对所有 USB 1.1
+主机控制器驱动也同样适用。
+
+
+在 Linux 2.5 内核中,定义了新的 ``usb_sg_*()`` API;
+它们会把 scatterlist 中的所有缓冲区都排入队列。
+它们还使用 scatterlist 的 DMA 映射
+(其中可能应用 IOMMU)并减少中断次数,
+这些都有助于让高速传输尽可能快地运行。
+
+待办:
+ 中断传输和等时(ISO)传输的性能问题。
+ 这些周期性传输都是完全调度的,因此,主要问题可能在于如何触发高带宽模式。
+
+待办:
+ 通过 ``sysfs`` 中的 ``uframe_periodic_max`` 参数,
+ 可以分配超过标准 80% 的周期性带宽。
+ 后续将对此进行说明。
diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst
new file mode 100644
index 0000000000000..eb5aca0c13ecc
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/index.rst
@@ -0,0 +1,54 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/index.rst
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+========
+USB 支持
+========
+
+.. toctree::
+ :maxdepth: 1
+
+ acm
+ authorization
+ chipidea
+ dwc3
+ ehci
+ usbmon
+
+Todolist:
+
+* functionfs
+* functionfs-desc
+* gadget_configfs
+* gadget_hid
+* gadget_multi
+* gadget_printer
+* gadget_serial
+* gadget_uvc
+* gadget-testing
+* iuu_phoenix
+* mass-storage
+* misc_usbsevseg
+* mtouchusb
+* ohci
+* raw-gadget
+* usbip_protocol
+* usb-serial
+* usb-help
+* text_files
+
+.. only:: subproject and html
+
+ 索引
+ ====
+
+ * :ref:`genindex`
diff --git a/Documentation/translations/zh_CN/usb/usbmon.rst b/Documentation/translations/zh_CN/usb/usbmon.rst
new file mode 100644
index 0000000000000..11b6d5b59dce2
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/usbmon.rst
@@ -0,0 +1,427 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/usbmon.rst
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+======
+usbmon
+======
+
+简介
+====
+小写形式的 ``usbmon`` 指的是内核中的一项功能,
+用于收集 USB 总线上的 I/O 跟踪信息。它类似于网络监控工具
+``tcpdump(1)`` 或 Ethereal 所使用的数据包套接字。
+类似地,人们希望使用 usbdump 或 USBMon
+(首字母大写)之类的工具来检查
+usbmon 生成的原始跟踪数据。
+
+usbmon 报告的是各个外设驱动
+向主机控制器驱动(HCD)发出的请求。
+因此,如果 HCD 本身有 bug,那么 usbmon 报告的跟踪信息
+可能无法精确对应实际的总线事务。
+这和 tcpdump 的情况是一样的。
+
+目前实现了两种 API: ``text`` 和 ``binary``。
+二进制 API 通过 ``/dev`` 命名空间中的字符设备提供,
+并且属于 ABI。文本 API 自内核 2.6.35 起已废弃,
+但为了方便仍然可用。
+
+如何使用 usbmon 收集原始文本跟踪信息
+====================================
+
+与数据包套接字不同,usbmon 提供了一种接口,
+可以输出文本格式的跟踪信息。这样做有两个目的:
+第一,在更完善的格式最终确定之前,
+它作为工具间通用的跟踪交换格式;
+第二,在不使用工具的情况下,人们也可以直接阅读这些信息。
+
+要收集原始文本跟踪信息,请按以下步骤进行操作。
+
+1. 准备
+-------
+
+挂载 debugfs(内核配置中必须启用它),并加载 usbmon 模块
+(如果��是作为模块构建的)。如果 usbmon 已经编入内核,
+那么第二步可以省略。
+
+命令示例::
+
+ # mount -t debugfs none_debugs /sys/kernel/debug
+ # modprobe usbmon
+ #
+
+确认总线套接字是否存在::
+
+ # ls /sys/kernel/debug/usb/usbmon
+ 0s 0u 1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u
+ #
+
+现在,你可以选择使用 ``0u`` 捕获所有总线上的数据包,
+并跳到第 3 步;
+也可以先按第 2 步找到目标设备所在的总线。
+这样可以过滤掉那些持续输出数据的烦人设备。
+
+2. 查找目标设备连接的是哪条总线
+-------------------------------
+
+运行 ``cat /sys/kernel/debug/usb/devices``,
+找到对应设备的 T 行。通常可以通过厂商字符串来查找。
+如果有许多类似设备,可以拔掉其中一个,
+再比较前后两次 ``/sys/kernel/debug/usb/devices``
+的输出。T 行里会包含总线编号。
+
+示例::
+
+ T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
+ D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
+ P: Vendor=0557 ProdID=2004 Rev= 1.00
+ S: Manufacturer=ATEN
+ S: Product=UC100KM V2.00
+
+``Bus=03`` 表示它位于 3 号总线上。或者,
+也可以查看 ``lsusb`` 的输出,并从对应行得到总线编号。
+
+示例如下::
+
+ Bus 003 Device 002: ID 0557:2004 ATEN UC100KM V2.00
+
+
+3. 启动 cat 命令
+----------------
+
+如果只监听单条总线,可执行::
+
+ # cat /sys/kernel/debug/usb/usbmon/3u > /tmp/1.mon.out
+
+否则,如果要监听所有总线,则执行::
+
+ # cat /sys/kernel/debug/usb/usbmon/0u > /tmp/1.mon.out
+
+此进程会一直读取,直到被终止。
+由于输出通常会很长,因此更推荐将输出重定向到某个位置。
+
+
+4. 在 USB 总线上执行期望的操作
+------------------------------
+
+此处需要执行一些会产生 USB 流量的动作,
+比如插入 U 盘、拷贝文件、操作摄像头等。
+
+
+5. 停止 cat
+-----------
+
+这一步通常通过键盘中断(Control-C)完成。
+
+此时输出文件(本例中为 ``/tmp/1.mon.out``)
+可以保存、通过电子邮件发送,或使用文本编辑器查看。
+如果使用最后一种方式,请确保文件不会大到编辑器无法打开。
+
+
+原始文本数据格式
+================
+
+目前支持两种格式:原始格式,也就是 ``1t`` 格式,
+以及 ``1u`` 格式。``1t`` 格式在内核 2.6.21 中已被废弃。
+``1u`` 格式增加了一些字段,例如 ISO 帧描述符、
+``interval`` 等。它生成的行会稍长一些,
+但在其他方面是 ``1t`` 格式的完整超集。
+
+如果程序需要区分上述两种格式,
+可以查看 ``address`` 字段(见下文)。
+如果其中有两个冒号,就是 ``1t`` 格式;
+否则是 ``1u`` 格式。
+
+任何文本格式的数据由一系列事件组成,
+如 URB 提交、URB 回调、提交错误等。
+每个事件对应单独的一行文本,
+由使用空白符间隔的若干字段组成。
+字段的数量与位置可能取决于事件类型,
+但以下字段对所有类型都通用:
+
+下面按从左到右的顺序列出这些共有字段:
+
+- URB Tag。用于标识 URB,通常是 URB 结构体在内核中的地址
+ (以十六进制表示),
+ 但也可能是序号或其他合理的唯一字符串。
+
+- 时间戳(微秒),十进制数字。
+ 时间戳的精度取决于可用时钟,
+ 因此可能远差于
+ 1 微秒(例如实现使用的是 jiffies)。
+
+- 事件类型。它表示的是事件的格式,而不是 URB 的类型。
+ 可用值为:``S`` 表示提交,``C`` 表示回调,``E`` 表示提交错误。
+
+- ``Address`` 字段(以前称作 ``pipe``)。
+ 它包含四个由冒号分隔的字段:
+ URB 类型及方向、总线号、设备地址和端点号。类型与方向的编码如下:
+
+ == == ==========================
+ Ci Co 控制输入和输出
+ Zi Zo 等时输入和输出
+ Ii Io 中断输入和输出
+ Bi Bo 批量输入和输出
+ == == ==========================
+
+ 总线号、设备地址和端点号使用十进制,但可能有前导零。
+
+- URB 状态字段。这个字段要么是一个字母,
+ 要么是几个由冒号分隔的数字:
+ URB 状态、``interval``、``start frame`` 和 ``error count``。
+ 与 ``address`` 字段不同,除了状态外,其余字段都是可选的。
+ ``interval`` 只会为中断和等时 URB 打印;``start frame`` 只会为
+ 等时 URB 打印;错误计数只会在等时回调事件中打印。
+
+ 状态字段是一个十进制数字,有时为负数,
+ 对应 URB 的 ``status`` 字段。
+ 对于提交事件,这个字段本身没有实际意义,
+ 但为了便于脚本解析,它仍然存在。
+ 当发生错误时,该字段包含错误码。
+
+ 在提交控制包时,这个字段包含的是 ``Setup Tag``,
+ 而不是一组数字。
+ 判断 ``Setup Tag`` 是否存在很容易,因为它从来不是数字。
+ 因此,如果脚本在这个字段里发现的是一组数字,
+ 就会继续读取数据长度(等时 URB 除外)。
+ 如果发现的是其他内容,比如一个字母,
+ 那么脚本会先读取 ``Setup`` 包,再读取数据长度或等时描述符。
+
+- ``Setup`` 包由 5 个字段组成:
+ ``bmRequestType``、``bRequest``、``wValue``、
+ ``wIndex`` 和 ``wLength``。这些字段由 USB 2.0 规范定义。
+ 如果 ``Setup Tag`` 为 ``s``,就可以安全地解码这些字段。
+ 否则,说明 Setup 包虽然存在,但并未被捕获,此时各字段中会填入占位内容。
+
+- 等时传输帧描述符的数量及其内容:
+ 如果一个等时传输事件带有一组描述符,首先打印该 URB 中描述符的总数,
+ 然后为每个描述符打印一个字段,最多打印 5 个字段。
+ 每个字段由三个用冒号分隔的十进制数字组成,
+ 分别表示状态(status)、偏移(offset)和长度(length)。
+ 对于提交(submission),报告的是初始长度;
+ 对于回调(callback),报告的是实际长度。
+
+- 数据长度:
+ 对于提交,表示请求的长度;对于回调,表示实际传输的长度。
+
+- 数据标签:
+ 即使数据长度非零,usbmon 也不一定会捕获数据。
+ 仅当标签为 ``=`` 时,才会有数据字段。
+
+- 数据字段:
+ 以大端十六进制格式显示。注意,这些并不是真正的机器字,
+ 而只是把字节流拆成若干“字”以便阅读。因此最后一个字可能只包含
+ 1 到 4 个字节。
+ 收集的数据长度是有限的,可能小于数据长度字段中报告的值。
+ 因为数据长度字段只统计实际接收到的字节,而数据字段包含整个传输缓冲区,
+ 所以,在等时输入(Zi)完成且缓冲区中接收到的数据稀疏的情况下,
+ 收集的数据长度可能大于数据长度字段的值。
+
+
+
+示例:
+
+获取端口状态的输入控制传输::
+
+ d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
+ d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000
+
+向地址为 5 的存储设备发送
+31 字节 Bulk 包装的 SCSI 命令 ``0x28``
+(``READ_10``)的输出批量传输::
+
+ dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 ad000000 00800000 80010a28 20000000 20000040 00000000 000000
+ dd65f0e8 4128379808 C Bo:1:005:2 0 31 >
+
+原始二进制格式与 API
+====================
+API 的整体架构与前文大体相同,只是事件以二进制格式传递。
+每个事件都通过下面的结构发送
+(这个名字是为了叙述方便而虚构的)::
+
+
+ struct usbmon_packet {
+ u64 id; /* 0: URB ID - 从提交到回调 */
+ unsigned char type; /* 8: 与文本相同;可扩展 */
+ unsigned char xfer_type; /* ISO (0)、中断、控制、批量 (3) */
+ unsigned char epnum; /* 端点号和传输方向 */
+ unsigned char devnum; /* 设备地址 */
+ u16 busnum; /* 12: 总线号 */
+ char flag_setup; /* 14: 与文本相同 */
+ char flag_data; /* 15: 与文本相同;二进制零也可 */
+ s64 ts_sec; /* 16: gettimeofday */
+ s32 ts_usec; /* 24: gettimeofday */
+ int status; /* 28: */
+ unsigned int length; /* 32: 数据长度(提交或实际) */
+ unsigned int len_cap; /* 36: 已捕获的数据长度 */
+ union { /* 40: */
+ unsigned char setup[SETUP_LEN]; /* 仅用于控制类 S 事件 */
+ struct iso_rec { /* 仅用于 ISO */
+ int error_count;
+ int numdesc;
+ } iso;
+ } s;
+ int interval; /* 48: 仅用于中断和 ISO */
+ int start_frame; /* 52: 仅用于 ISO */
+ unsigned int xfer_flags; /* 56: URB 的 transfer_flags 副本 */
+ unsigned int ndesc; /* 60: 实际 ISO 描述符数量 */
+ }; /* 64 总长度 */
+
+可以用 ``read(2)``、``ioctl(2)``,
+或者通过 ``mmap`` 访问缓冲区,
+从字符设备接收这些事件。
+不过,出于兼容性原因,``read(2)``
+只返回前 48 个字节。
+
+字符设备通常命名为 ``/dev/usbmonN``,
+其中 ``N`` 是 USB 总线号。
+编号为零的设备(``/dev/usbmon0``)比较特殊,
+表示“所有总线”。
+请注意,具体命名策略由 Linux 发行版决定。
+
+如果你手动创建 ``/dev/usbmon0``,
+请确保它归 root 所有,并且权限为 ``0600``。
+否则,非特权用户将能够窃听键盘流量。
+
+以下 ``MON_IOC_MAGIC`` 为 ``0x92`` 的 ioctl 调用可用:
+
+``MON_IOCQ_URB_LEN``,定义为 ``_IO(MON_IOC_MAGIC, 1)``
+
+该调用返回下一个事件的数据长度。
+注意大多数事件不包含数据,
+因此如果该调用返回零,并不意味着没有事件。
+
+``MON_IOCG_STATS``,定义为
+``_IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats)``
+
+参数是指向以下结构的指针::
+
+ struct mon_bin_stats {
+ u32 queued;
+ u32 dropped;
+ };
+
+成员 ``queued`` 表示当前缓冲区中已经排队的事件数量,
+而不是自上次重置以来处理过的事件数量。
+
+成员 ``dropped`` 表示自上次调用
+``MON_IOCG_STATS`` 以来丢失的事件数量。
+
+``MON_IOCT_RING_SIZE``,定义为 ``_IO(MON_IOC_MAGIC, 4)``
+
+此调用设置缓冲区大小。参数为以字节为单位的缓冲区大小。
+大小可能会向下取整到下一个块(或页)。
+如果请���的大小超出该内核的 [未指定] 范围,
+则调用会失败并返回 ``-EINVAL``。
+
+``MON_IOCQ_RING_SIZE``,定义为 ``_IO(MON_IOC_MAGIC, 5)``
+
+该调用返回缓冲区当前大小(以字节为单位)。
+
+``MON_IOCX_GET``,定义为
+``_IOW(MON_IOC_MAGIC, 6, struct mon_get_arg)``
+``MON_IOCX_GETX``,定义为
+``_IOW(MON_IOC_MAGIC, 10, struct mon_get_arg)``
+
+如果内核缓冲区中没有事件,
+这些调用就会一直等待,直到有事件到达,
+然后返回第一个事件。
+参数是指向以下结构的指针::
+
+ struct mon_get_arg {
+ struct usbmon_packet *hdr;
+ void *data;
+ size_t alloc; /* 数据长度可以为零 */
+ };
+
+
+调用前,应填好 ``hdr``、``data`` 和 ``alloc``。
+调用返回后,``hdr`` 指向的区域中包含下一个事件的结构;
+如果存在数据,那么数据缓冲区中也会包含相应数据。
+该事件会从内核缓冲区中移除。
+
+``MON_IOCX_GET`` 会将 48 字节的数据复制到 ``hdr`` 区域,
+``MON_IOCX_GETX`` 会复制 64 字节。
+
+``MON_IOCX_MFETCH``,定义为
+``_IOWR(MON_IOC_MAGIC, 7, struct mon_mfetch_arg)``
+
+当应用程序通过 ``mmap(2)`` 访问缓冲区时,
+主要使用这个 ioctl。
+其参数是指向以下结构的指针::
+
+ struct mon_mfetch_arg {
+ uint32_t *offvec; /* 获取的事件偏移向量 */
+ uint32_t nfetch; /* 要获取的事件数量(输出:已获取) */
+ uint32_t nflush; /* 要刷新的事件数量 */
+ };
+
+
+该 ioctl 的操作分为三个阶段:
+
+首先,从内核缓冲区移除并丢弃最多 ``nflush`` 个事件。
+实际丢弃的事件数量会写回 ``nflush``。
+
+其次,除非伪设备以 ``O_NONBLOCK`` 打开,否则会一直等待,
+直到缓冲区中出现事件。
+
+第三,将最多 ``nfetch`` 个偏移量提取到 mmap
+缓冲区,并存入 ``offvec`` 中。
+实际提取到的事件偏移数量会存回 ``nfetch``。
+
+``MON_IOCH_MFLUSH``,定义为 ``_IO(MON_IOC_MAGIC, 8)``
+
+此调用从内核缓冲区移除若干事件。
+其参数为要移除的事件数量。
+如果缓冲区中的事件少于请求数量,
+则移除所有事件,且不报告错误。
+当没有事件时也可使用。
+
+``FIONBIO``
+
+如果有需要,将来可能会实现 ``FIONBIO`` ioctl。
+
+除了 ``ioctl(2)`` 和 ``read(2)`` 之外,
+二进制 API 的特殊文件也可以用 ``select(2)`` 和
+``poll(2)`` 轮询。
+但 ``lseek(2)`` 不起作用。
+
+* 二进制 API 的内核缓冲区内存映射访问
+
+基本思想很简单:
+
+准备时,先获取当前大小,再用 ``mmap(2)`` 映射缓冲区。
+然后执行类似下面伪代码的循环::
+
+ struct mon_mfetch_arg fetch;
+ struct usbmon_packet *hdr;
+ int nflush = 0;
+ for (;;) {
+ fetch.offvec = vec; // 有 N 个 32 位字
+ fetch.nfetch = N; // 或者少于 N
+ fetch.nflush = nflush;
+ ioctl(fd, MON_IOCX_MFETCH, &fetch); // 同时处理错误
+ nflush = fetch.nfetch; // 完成后要刷新这么多包
+ for (i = 0; i < nflush; i++) {
+ hdr = (struct ubsmon_packet *) &mmap_area[vec[i]];
+ if (hdr->type == '@') // 填充包
+ continue;
+ caddr_t data = &mmap_area[vec[i]] + 64;
+ process_packet(hdr, data);
+ }
+ }
+
+
+
+因此,主要思想是每 N 个事件只执行一次 ioctl。
+
+虽然缓冲区是环形的,但返回的头和数据不会跨越缓冲区末端,
+因此上面的伪代码无需任何合并操作。
diff --git a/arch/arm/mach-pxa/devices.c b/arch/arm/mach-pxa/devices.c
index 7695cfce01a16..edad956a1483c 100644
--- a/arch/arm/mach-pxa/devices.c
+++ b/arch/arm/mach-pxa/devices.c
@@ -11,7 +11,6 @@
#include <linux/platform_data/i2c-pxa.h>
#include <linux/soc/pxa/cpu.h>
-#include "udc.h"
#include <linux/platform_data/video-pxafb.h>
#include <linux/platform_data/mmc-pxamci.h>
#include "irqs.h"
@@ -83,10 +82,6 @@ void __init pxa_set_mci_info(const struct pxamci_platform_data *info,
pr_err("Unable to create mci device: %d\n", err);
}
-static struct pxa2xx_udc_mach_info pxa_udc_info = {
- .gpio_pullup = -1,
-};
-
static struct resource pxa2xx_udc_resources[] = {
[0] = {
.start = 0x40600000,
@@ -108,7 +103,6 @@ struct platform_device pxa25x_device_udc = {
.resource = pxa2xx_udc_resources,
.num_resources = ARRAY_SIZE(pxa2xx_udc_resources),
.dev = {
- .platform_data = &pxa_udc_info,
.dma_mask = &udc_dma_mask,
}
};
@@ -119,7 +113,6 @@ struct platform_device pxa27x_device_udc = {
.resource = pxa2xx_udc_resources,
.num_resources = ARRAY_SIZE(pxa2xx_udc_resources),
.dev = {
- .platform_data = &pxa_udc_info,
.dma_mask = &udc_dma_mask,
}
};
diff --git a/arch/arm/mach-pxa/gumstix.c b/arch/arm/mach-pxa/gumstix.c
index 1713bdf3b71e4..6074815a4bcae 100644
--- a/arch/arm/mach-pxa/gumstix.c
+++ b/arch/arm/mach-pxa/gumstix.c
@@ -39,7 +39,6 @@
#include "pxa25x.h"
#include <linux/platform_data/mmc-pxamci.h>
-#include "udc.h"
#include "gumstix.h"
#include "devices.h"
diff --git a/arch/arm/mach-pxa/udc.h b/arch/arm/mach-pxa/udc.h
deleted file mode 100644
index 9a827e32db98b..0000000000000
--- a/arch/arm/mach-pxa/udc.h
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * arch/arm/mach-pxa/include/mach/udc.h
- *
- */
-#include <linux/platform_data/pxa2xx_udc.h>
-
-extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info);
-
diff --git a/arch/arm64/boot/dts/qcom/pm4125.dtsi b/arch/arm64/boot/dts/qcom/pm4125.dtsi
index cf8c822e80ce8..542e8fe030da4 100644
--- a/arch/arm64/boot/dts/qcom/pm4125.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm4125.dtsi
@@ -61,7 +61,6 @@
"attach-detach",
"legacy-cable-detect",
"try-snk-src-detect";
- vdd-vbus-supply = <&pm4125_vbus>;
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/pm7250b.dtsi b/arch/arm64/boot/dts/qcom/pm7250b.dtsi
index 0761e6b5fd8d1..43cab07126c5c 100644
--- a/arch/arm64/boot/dts/qcom/pm7250b.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm7250b.dtsi
@@ -86,7 +86,6 @@
"msg-tx-discarded",
"msg-rx-discarded",
"fr-swap";
- vdd-vbus-supply = <&pm7250b_vbus>;
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/pm8150b.dtsi b/arch/arm64/boot/dts/qcom/pm8150b.dtsi
index 3f7b0b6a1d109..b83be4b6cb1c7 100644
--- a/arch/arm64/boot/dts/qcom/pm8150b.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm8150b.dtsi
@@ -95,7 +95,6 @@
"msg-tx-discarded",
"msg-rx-discarded",
"fr-swap";
- vdd-vbus-supply = <&pm8150b_vbus>;
};
pm8150b_temp: temp-alarm@2400 {
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index 8c899d148e461..b0ed35094a98d 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -69,7 +69,6 @@
"attach-detach",
"legacy-cable-detect",
"try-snk-src-detect";
- vdd-vbus-supply = <&pmi632_vbus>;
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts b/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts
index da46e9d655280..4ace2d6c06cee 100644
--- a/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts
+++ b/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts
@@ -409,6 +409,8 @@
typec-power-opmode = "default";
pd-disable;
+ vbus-supply = <&pm4125_vbus>;
+
ports {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts b/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts
index 1203172729faa..22baee407fbee 100644
--- a/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts
+++ b/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts
@@ -323,6 +323,8 @@
typec-power-opmode = "default";
pd-disable;
+ vbus-supply = <&pmi632_vbus>;
+
ports {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
index 54da0d759a671..690b484352edf 100644
--- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
+++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
@@ -1499,6 +1499,8 @@
data-role = "dual";
self-powered;
+ vbus-supply = <&pm8150b_vbus>;
+
source-pdos = <PDO_FIXED(5000, 3000,
PDO_FIXED_DUAL_ROLE |
PDO_FIXED_USB_COMM |
diff --git a/arch/arm64/boot/dts/qcom/sdm632-fairphone-fp3.dts b/arch/arm64/boot/dts/qcom/sdm632-fairphone-fp3.dts
index 0edb2992b902e..3223884f9cd68 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-fairphone-fp3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-fairphone-fp3.dts
@@ -252,6 +252,8 @@
typec-power-opmode = "default";
pd-disable;
+ vbus-supply = <&pmi632_vbus>;
+
port {
pmi632_hs_in: endpoint {
remote-endpoint = <&usb_dwc3_hs>;
diff --git a/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts b/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts
index 466ad409e9240..0f23eaef01f21 100644
--- a/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts
+++ b/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts
@@ -251,6 +251,8 @@
typec-power-opmode = "default";
pd-disable;
+ vbus-supply = <&pmi632_vbus>;
+
ports {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
index 3964aae47fd4e..23f950067a08b 100644
--- a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
+++ b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
@@ -953,6 +953,8 @@
typec-power-opmode = "default";
pd-disable;
+ vbus-supply = <&pm7250b_vbus>;
+
ports {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sm8150-hdk.dts b/arch/arm64/boot/dts/qcom/sm8150-hdk.dts
index 6ae6e07c37dff..8fb04ce72f91e 100644
--- a/arch/arm64/boot/dts/qcom/sm8150-hdk.dts
+++ b/arch/arm64/boot/dts/qcom/sm8150-hdk.dts
@@ -572,6 +572,8 @@
data-role = "dual";
self-powered;
+ vbus-supply = <&pm8150b_vbus>;
+
source-pdos = <PDO_FIXED(5000, 3000,
PDO_FIXED_DUAL_ROLE |
PDO_FIXED_USB_COMM |
diff --git a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi
index c017399297b9d..51b57c697a753 100644
--- a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi
@@ -725,6 +725,8 @@
data-role = "dual";
self-powered;
+ vbus-supply = <&pm8150b_vbus>;
+
source-pdos = <PDO_FIXED(5000, 3000,
PDO_FIXED_DUAL_ROLE |
PDO_FIXED_USB_COMM |
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 997bba56fedfd..ba1bdde49587b 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -963,6 +963,16 @@ config SENSORS_POWR1220
This driver can also be built as a module. If so, the module
will be called powr1220.
+config SENSORS_PROM21_XHCI
+ tristate "AMD Promontory 21 xHCI temperature sensor"
+ depends on USB_XHCI_PCI
+ help
+ If you say yes here you get support for the AMD Promontory 21
+ (PROM21) xHCI temperature sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called prom21-xhci.
+
config SENSORS_LAN966X
tristate "Microchip LAN966x Hardware Monitoring"
depends on SOC_LAN966 || COMPILE_TEST
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index efbd1cb81816f..4e99640af849e 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -197,6 +197,7 @@ obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_POWERZ) += powerz.o
obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o
+obj-$(CONFIG_SENSORS_PROM21_XHCI) += prom21-xhci.o
obj-$(CONFIG_SENSORS_PT5161L) += pt5161l.o
obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
obj-$(CONFIG_SENSORS_QNAP_MCU_HWMON) += qnap-mcu-hwmon.o
diff --git a/drivers/hwmon/prom21-xhci.c b/drivers/hwmon/prom21-xhci.c
new file mode 100644
index 0000000000000..d40d0c53ce456
--- /dev/null
+++ b/drivers/hwmon/prom21-xhci.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Promontory 21 xHCI Hwmon Implementation
+ * (only temperature monitoring is supported)
+ *
+ * This can be effectively used as the alternative chipset temperature monitor.
+ *
+ * Copyright (C) 2026 Jihong Min <hurryman2212@gmail.com>
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/hwmon.h>
+#include <linux/io.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_data/usb-xhci-prom21.h>
+#include <linux/pm_runtime.h>
+
+#define PROM21_XHCI_INDEX_OFFSET 0x3000
+#define PROM21_XHCI_DATA_OFFSET 0x3008
+#define PROM21_XHCI_TEMP_SELECTOR 0x0001e520
+
+struct prom21_xhci {
+ struct pci_dev *pdev;
+ struct device *hwmon_dev;
+ void __iomem *regs;
+};
+
+static int prom21_xhci_pm_get(struct prom21_xhci *hwmon)
+{
+ struct device *dev = &hwmon->pdev->dev;
+ int ret;
+
+ /*
+ * PROM21 temperature register access does not return a valid value while
+ * the parent xHCI PCI function is suspended. Do not wake the device from
+ * a hwmon read. On success, hold a usage reference without changing the
+ * runtime PM state; if runtime PM is disabled, allow the read unless the
+ * device is still marked suspended.
+ */
+ ret = pm_runtime_get_if_active(dev);
+ if (ret > 0)
+ return 0;
+
+ if (ret == -EINVAL) {
+ if (pm_runtime_status_suspended(dev))
+ return -ENODATA;
+
+ pm_runtime_get_noresume(dev);
+ return 0;
+ }
+
+ if (!ret)
+ return -ENODATA;
+
+ return ret;
+}
+
+/*
+ * This is not a pure MMIO read. The PROM21 vendor data register is selected
+ * by temporarily writing PROM21_XHCI_TEMP_SELECTOR to the vendor index
+ * register.
+ * The hwmon core already serializes this driver's callbacks, so this driver
+ * does not need an additional private lock. That does not synchronize with
+ * firmware, SMM, ACPI, or other possible users. Keep the sequence short and
+ * restore the previous index before returning.
+ */
+static int prom21_xhci_read_temp_raw_restore_index(struct prom21_xhci *hwmon,
+ u8 *raw)
+{
+ struct device *dev = &hwmon->pdev->dev;
+ u32 index;
+ u8 data;
+ int ret;
+
+ ret = prom21_xhci_pm_get(hwmon);
+ if (ret)
+ return ret;
+
+ index = readl(hwmon->regs + PROM21_XHCI_INDEX_OFFSET);
+ /* Select the PROM21 temperature register through the vendor index. */
+ writel(PROM21_XHCI_TEMP_SELECTOR,
+ hwmon->regs + PROM21_XHCI_INDEX_OFFSET);
+ /* Use a 32-bit read for PCI MMIO register access. */
+ data = readl(hwmon->regs + PROM21_XHCI_DATA_OFFSET) & 0xff;
+ /* Restore the previous vendor index register value. */
+ writel(index, hwmon->regs + PROM21_XHCI_INDEX_OFFSET);
+ readl(hwmon->regs + PROM21_XHCI_INDEX_OFFSET);
+
+ /*
+ * Drop the usage reference taken by prom21_xhci_pm_get(). This is
+ * enough because the read path never resumes the device; use the normal
+ * put path so the PM core can re-evaluate idle state after the read.
+ * Otherwise, a racing xHCI autosuspend attempt can see a nonzero
+ * runtime PM usage count and skip autosuspend, and a later
+ * pm_runtime_put_noidle(), which does not check for an idle device,
+ * would leave the device active.
+ */
+ pm_runtime_put(dev);
+
+ if (!data)
+ return -ENODATA;
+
+ *raw = data;
+ return 0;
+}
+
+static long prom21_xhci_raw_to_millicelsius(u8 raw)
+{
+ /*
+ * No public AMD reference is available for this value.
+ * The scale was derived from observed PROM21 xHCI temperature readings:
+ * temp[C] = raw * 0.9066 - 78.624
+ */
+ return DIV_ROUND_CLOSEST(raw * 9066, 10) - 78624;
+}
+
+static umode_t prom21_xhci_is_visible(const void *drvdata,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ return 0;
+ }
+}
+
+static int prom21_xhci_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct prom21_xhci *hwmon = dev_get_drvdata(dev);
+ u8 raw;
+ int ret;
+
+ if (type != hwmon_temp || attr != hwmon_temp_input)
+ return -EOPNOTSUPP;
+
+ ret = prom21_xhci_read_temp_raw_restore_index(hwmon, &raw);
+ if (ret)
+ return ret;
+
+ *val = prom21_xhci_raw_to_millicelsius(raw);
+ return 0;
+}
+
+static const struct hwmon_ops prom21_xhci_ops = {
+ .is_visible = prom21_xhci_is_visible,
+ .read = prom21_xhci_read,
+};
+
+static const struct hwmon_channel_info *const prom21_xhci_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL,
+};
+
+static const struct hwmon_chip_info prom21_xhci_chip_info = {
+ .ops = &prom21_xhci_ops,
+ .info = prom21_xhci_info,
+};
+
+static int prom21_xhci_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct device *dev = &auxdev->dev;
+ const struct prom21_xhci_pdata *pdata = dev_get_platdata(dev);
+ struct prom21_xhci *hwmon;
+
+ if (!pdata)
+ return dev_err_probe(dev, -ENODEV,
+ "platform data unavailable\n");
+
+ if (!pdata->regs ||
+ pdata->rsrc_len < PROM21_XHCI_DATA_OFFSET + sizeof(u32))
+ return dev_err_probe(dev, -ENODEV, "invalid MMIO resource\n");
+
+ hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ hwmon->pdev = pdata->pdev;
+ hwmon->regs = pdata->regs;
+ auxiliary_set_drvdata(auxdev, hwmon);
+
+ /*
+ * Parent the hwmon device to the PCI function because the temperature
+ * value is read from that function's MMIO BAR, and systems may contain
+ * multiple PROM21 xHCI functions. This lets userspace identify the PCI
+ * endpoint for each reading. The auxiliary driver still owns the hwmon
+ * lifetime and unregisters it before HCD teardown.
+ */
+ hwmon->hwmon_dev =
+ hwmon_device_register_with_info(&pdata->pdev->dev, "prom21_xhci",
+ hwmon, &prom21_xhci_chip_info,
+ NULL);
+ if (IS_ERR(hwmon->hwmon_dev))
+ return PTR_ERR(hwmon->hwmon_dev);
+
+ return 0;
+}
+
+static void prom21_xhci_remove(struct auxiliary_device *auxdev)
+{
+ struct prom21_xhci *hwmon = auxiliary_get_drvdata(auxdev);
+
+ /*
+ * The PROM21 PCI glue destroys the auxiliary device before HCD teardown.
+ * Unregister the hwmon device here so sysfs removes the attributes,
+ * stops new reads, and drains active hwmon callbacks before the xHCI
+ * MMIO mapping is released.
+ */
+ hwmon_device_unregister(hwmon->hwmon_dev);
+}
+
+static const struct auxiliary_device_id prom21_xhci_id_table[] = {
+ { .name = "xhci_pci_prom21.hwmon" },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, prom21_xhci_id_table);
+
+static struct auxiliary_driver prom21_xhci_driver = {
+ .name = "prom21-xhci",
+ .probe = prom21_xhci_probe,
+ .remove = prom21_xhci_remove,
+ .id_table = prom21_xhci_id_table,
+};
+module_auxiliary_driver(prom21_xhci_driver);
+
+MODULE_AUTHOR("Jihong Min <hurryman2212@gmail.com>");
+MODULE_DESCRIPTION("AMD Promontory 21 xHCI temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 6f3c861498875..eecbd631fdab5 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -14,8 +14,6 @@ obj-$(CONFIG_USB_DWC2) += dwc2/
obj-$(CONFIG_USB_ISP1760) += isp1760/
obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns3/
-obj-$(CONFIG_USB_CDNS3) += cdns3/
-obj-$(CONFIG_USB_CDNSP_PCI) += cdns3/
obj-$(CONFIG_USB_FOTG210) += fotg210/
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index f3ae72feb5bfc..ed2611aacb252 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -51,12 +51,6 @@
"[ueagle-atm vdbg] " format, ##args); \
} while (0)
-#define uea_enters(usb_dev) \
- uea_vdbg(usb_dev, "entering %s\n" , __func__)
-
-#define uea_leaves(usb_dev) \
- uea_vdbg(usb_dev, "leaving %s\n" , __func__)
-
#define uea_err(usb_dev, format, args...) \
dev_err(&(usb_dev)->dev , "[UEAGLE-ATM] " format , ##args)
@@ -606,7 +600,6 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry,
u32 crc = 0;
int ret, size;
- uea_enters(usb);
if (!fw_entry) {
uea_err(usb, "firmware is not available\n");
goto err;
@@ -670,7 +663,6 @@ err_fw_corrupted:
uea_err(usb, "firmware is corrupted\n");
err:
release_firmware(fw_entry);
- uea_leaves(usb);
}
/*
@@ -681,7 +673,6 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
int ret;
char *fw_name = EAGLE_FIRMWARE;
- uea_enters(usb);
uea_info(usb, "pre-firmware device, uploading firmware\n");
switch (ver) {
@@ -710,7 +701,6 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
else
uea_info(usb, "loading firmware %s\n", fw_name);
- uea_leaves(usb);
return ret;
}
@@ -1137,7 +1127,6 @@ static int uea_cmv_e1(struct uea_softc *sc,
struct cmv_e1 cmv;
int ret;
- uea_enters(INS_TO_USBDEV(sc));
uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, "
"offset : 0x%04x, data : 0x%08x\n",
E1_FUNCTION_TYPE(function),
@@ -1164,9 +1153,8 @@ static int uea_cmv_e1(struct uea_softc *sc,
sizeof(cmv), &cmv);
if (ret < 0)
return ret;
- ret = wait_cmv_ack(sc);
- uea_leaves(INS_TO_USBDEV(sc));
- return ret;
+
+ return wait_cmv_ack(sc);
}
static int uea_cmv_e4(struct uea_softc *sc,
@@ -1175,7 +1163,6 @@ static int uea_cmv_e4(struct uea_softc *sc,
struct cmv_e4 cmv;
int ret;
- uea_enters(INS_TO_USBDEV(sc));
memset(&cmv, 0, sizeof(cmv));
uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, "
@@ -1199,9 +1186,8 @@ static int uea_cmv_e4(struct uea_softc *sc,
sizeof(cmv), &cmv);
if (ret < 0)
return ret;
- ret = wait_cmv_ack(sc);
- uea_leaves(INS_TO_USBDEV(sc));
- return ret;
+
+ return wait_cmv_ack(sc);
}
static inline int uea_read_cmv_e1(struct uea_softc *sc,
@@ -1295,7 +1281,6 @@ static int uea_stat_e1(struct uea_softc *sc)
u32 data;
int ret;
- uea_enters(INS_TO_USBDEV(sc));
data = sc->stats.phy.state;
ret = uea_read_cmv_e1(sc, E1_SA_STAT, 0, &sc->stats.phy.state);
@@ -1438,7 +1423,6 @@ static int uea_stat_e4(struct uea_softc *sc)
u32 tmp_arr[2];
int ret;
- uea_enters(INS_TO_USBDEV(sc));
data = sc->stats.phy.state;
/* XXX only need to be done before operationnal... */
@@ -1805,7 +1789,6 @@ static int uea_start_reset(struct uea_softc *sc)
u16 zero = 0; /* ;-) */
int ret;
- uea_enters(INS_TO_USBDEV(sc));
uea_info(INS_TO_USBDEV(sc), "(re)booting started\n");
/* mask interrupt */
@@ -1873,7 +1856,6 @@ static int uea_start_reset(struct uea_softc *sc)
return ret;
sc->reset = 0;
- uea_leaves(INS_TO_USBDEV(sc));
return ret;
}
@@ -1889,7 +1871,6 @@ static int uea_kthread(void *data)
int ret = -EAGAIN;
set_freezable();
- uea_enters(INS_TO_USBDEV(sc));
while (!kthread_should_stop()) {
if (ret < 0 || sc->reset)
ret = uea_start_reset(sc);
@@ -1898,7 +1879,7 @@ static int uea_kthread(void *data)
if (ret != -EAGAIN)
uea_wait(sc, 0, msecs_to_jiffies(1000));
}
- uea_leaves(INS_TO_USBDEV(sc));
+
return ret;
}
@@ -1911,8 +1892,6 @@ static int load_XILINX_firmware(struct uea_softc *sc)
u8 value;
char *fw_name = FPGA930_FIRMWARE;
- uea_enters(INS_TO_USBDEV(sc));
-
ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev);
if (ret) {
uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n",
@@ -1956,7 +1935,6 @@ static int load_XILINX_firmware(struct uea_softc *sc)
err1:
release_firmware(fw_entry);
err0:
- uea_leaves(INS_TO_USBDEV(sc));
return ret;
}
@@ -1966,7 +1944,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr)
struct cmv_dsc_e1 *dsc = &sc->cmv_dsc.e1;
struct cmv_e1 *cmv = &intr->u.e1.s2.cmv;
- uea_enters(INS_TO_USBDEV(sc));
if (le16_to_cpu(cmv->wPreamble) != E1_PREAMBLE)
goto bad1;
@@ -1990,7 +1967,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr)
if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE,
E1_MODEMREADY)) {
wake_up_cmv_ack(sc);
- uea_leaves(INS_TO_USBDEV(sc));
return;
}
@@ -2004,7 +1980,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr)
sc->data = sc->data << 16 | sc->data >> 16;
wake_up_cmv_ack(sc);
- uea_leaves(INS_TO_USBDEV(sc));
return;
bad2:
@@ -2012,14 +1987,12 @@ bad2:
"Function : %d, Subfunction : %d\n",
E1_FUNCTION_TYPE(cmv->bFunction),
E1_FUNCTION_SUBTYPE(cmv->bFunction));
- uea_leaves(INS_TO_USBDEV(sc));
return;
bad1:
uea_err(INS_TO_USBDEV(sc), "invalid cmv received, "
"wPreamble %d, bDirection %d\n",
le16_to_cpu(cmv->wPreamble), cmv->bDirection);
- uea_leaves(INS_TO_USBDEV(sc));
}
/* The modem send us an ack. First with check if it right */
@@ -2028,7 +2001,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr)
struct cmv_dsc_e4 *dsc = &sc->cmv_dsc.e4;
struct cmv_e4 *cmv = &intr->u.e4.s2.cmv;
- uea_enters(INS_TO_USBDEV(sc));
uea_dbg(INS_TO_USBDEV(sc), "cmv %x %x %x %x %x %x\n",
be16_to_cpu(cmv->wGroup), be16_to_cpu(cmv->wFunction),
be16_to_cpu(cmv->wOffset), be16_to_cpu(cmv->wAddress),
@@ -2040,7 +2012,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr)
if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE,
E4_MODEMREADY, 1)) {
wake_up_cmv_ack(sc);
- uea_leaves(INS_TO_USBDEV(sc));
return;
}
@@ -2053,7 +2024,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr)
sc->data = be32_to_cpu(cmv->dwData[0]);
sc->data1 = be32_to_cpu(cmv->dwData[1]);
wake_up_cmv_ack(sc);
- uea_leaves(INS_TO_USBDEV(sc));
return;
bad2:
@@ -2061,7 +2031,6 @@ bad2:
"Function : %d, Subfunction : %d\n",
E4_FUNCTION_TYPE(cmv->wFunction),
E4_FUNCTION_SUBTYPE(cmv->wFunction));
- uea_leaves(INS_TO_USBDEV(sc));
return;
}
@@ -2089,8 +2058,6 @@ static void uea_intr(struct urb *urb)
struct intr_pkt *intr = urb->transfer_buffer;
int status = urb->status;
- uea_enters(INS_TO_USBDEV(sc));
-
if (unlikely(status < 0)) {
uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n",
status);
@@ -2130,8 +2097,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf)
int ret = -ENOMEM;
int size;
- uea_enters(INS_TO_USBDEV(sc));
-
if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
size = E4_INTR_PKT_SIZE;
sc->dispatch_cmv = uea_dispatch_cmv_e4;
@@ -2188,7 +2153,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf)
goto err2;
}
- uea_leaves(INS_TO_USBDEV(sc));
return 0;
err2:
@@ -2198,7 +2162,6 @@ err1:
sc->urb_int = NULL;
kfree(intr);
err0:
- uea_leaves(INS_TO_USBDEV(sc));
return ret;
}
@@ -2208,7 +2171,7 @@ err0:
static void uea_stop(struct uea_softc *sc)
{
int ret;
- uea_enters(INS_TO_USBDEV(sc));
+
ret = kthread_stop(sc->kthread);
uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret);
@@ -2222,7 +2185,6 @@ static void uea_stop(struct uea_softc *sc)
flush_work(&sc->task);
release_firmware(sc->dsp_firm);
- uea_leaves(INS_TO_USBDEV(sc));
}
/* syfs interface */
@@ -2495,8 +2457,6 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
int ret, ifnum = intf->altsetting->desc.bInterfaceNumber;
unsigned int alt;
- uea_enters(usb);
-
/* interface 0 is for firmware/monitoring */
if (ifnum != UEA_INTR_IFACE_NO)
return -ENODEV;
@@ -2589,8 +2549,7 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
struct usb_device *usb = interface_to_usbdev(intf);
int ret;
- uea_enters(usb);
- uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) Rev (%#X): %s\n",
+ uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n",
le16_to_cpu(usb->descriptor.idVendor),
le16_to_cpu(usb->descriptor.idProduct),
le16_to_cpu(usb->descriptor.bcdDevice),
@@ -2620,7 +2579,6 @@ static void uea_disconnect(struct usb_interface *intf)
{
struct usb_device *usb = interface_to_usbdev(intf);
int ifnum = intf->altsetting->desc.bInterfaceNumber;
- uea_enters(usb);
/* ADI930 has 2 interfaces and eagle 3 interfaces.
* Pre-firmware device has one interface
@@ -2631,8 +2589,6 @@ static void uea_disconnect(struct usb_interface *intf)
mutex_unlock(&uea_mutex);
uea_info(usb, "ADSL device removed\n");
}
-
- uea_leaves(usb);
}
/*
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
index 0a514b5915272..39ad23d1ada89 100644
--- a/drivers/usb/cdns3/Kconfig
+++ b/drivers/usb/cdns3/Kconfig
@@ -1,6 +1,9 @@
config USB_CDNS_SUPPORT
tristate "Cadence USB Support"
- depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
+ depends on USB_SUPPORT && HAS_DMA
+ depends on USB || USB_GADGET
+ depends on USB if !USB_GADGET
+ depends on USB_GADGET if !USB
select USB_XHCI_PLATFORM if USB_XHCI_HCD
select USB_ROLE_SWITCH
help
@@ -8,44 +11,49 @@ config USB_CDNS_SUPPORT
dual-role controller.
It supports: dual-role switch, Host-only, and Peripheral-only.
-config USB_CDNS_HOST
- bool
-
if USB_CDNS_SUPPORT
config USB_CDNS3
- tristate "Cadence USB3 Dual-Role Controller"
+ tristate "Cadence USB dual-role controller (USBSS and USBSSP)"
depends on USB_CDNS_SUPPORT
help
- Say Y here if your system has a Cadence USB3 dual-role controller.
- It supports: dual-role switch, Host-only, and Peripheral-only.
+ Say Y or M here if your system has an on-chip Cadence USB
+ dual-role controller. This covers both USBSS (USB 3.0) and
+ USBSSP (SuperSpeed Plus) IP; the driver detects the variant at
+ runtime.
- If you choose to build this driver is a dynamically linked
- as module, the module will be called cdns3.ko.
-endif
+ The core driver (core, DRD, generic platform binding for the
+ "cdns,usb3" device tree compatible, optional host and gadget)
+ builds as one module named cdns.ko when built as a loadable
+ module.
+
+ It supports: dual-role switch, Host-only, and Peripheral-only.
if USB_CDNS3
-config USB_CDNS3_GADGET
- bool "Cadence USB3 device controller"
- depends on USB_GADGET=y || USB_GADGET=USB_CDNS3
+config USB_CDNS3_HOST
+ bool "Cadence USB host controller (xHCI)"
+ depends on USB=y || USB=USB_CDNS3
help
- Say Y here to enable device controller functionality of the
- Cadence USBSS-DEV driver.
+ Say Y here to enable host controller functionality for Cadence
+ USBSS and USBSSP dual-role controllers.
- This controller supports FF, HS and SS mode. It doesn't support
- LS and SSP mode.
+ The host controller is xHCI compliant and uses the standard
+ xHCI driver.
-config USB_CDNS3_HOST
- bool "Cadence USB3 host controller"
- depends on USB=y || USB=USB_CDNS3
- select USB_CDNS_HOST
+config USB_CDNS3_GADGET
+ bool "Cadence USB device controller (USBSS and USBSSP)"
+ depends on USB_GADGET=y || USB_GADGET=USB_CDNS3
help
- Say Y here to enable host controller functionality of the
- Cadence driver.
+ Say Y here to include Cadence USB device (gadget) support for
+ both USBSS (USB 3.0) and USBSSP (SuperSpeed Plus) IP in the
+ cdns.ko module. The implementation is selected at runtime from
+ the detected controller version.
+
+ USBSS gadget supports FF, HS and SS mode (not LS or SSP).
+ USBSSP gadget supports FF, HS, SS and SSP mode (not LS).
- Host controller is compliant with XHCI so it will use
- standard XHCI driver.
+comment "Platform glue driver support"
config USB_CDNS3_PCI_WRAP
tristate "Cadence USB3 support on PCIe-based platforms"
@@ -58,6 +66,17 @@ config USB_CDNS3_PCI_WRAP
If you choose to build this driver as module it will
be dynamically linked and module will be called cdns3-pci.ko
+config USB_CDNSP_PCI
+ tristate "Cadence USBSSP support on PCIe-based platforms"
+ depends on USB_PCI && ACPI
+ default USB_CDNS3
+ help
+ If you're using the USBSSP Core IP with a PCIe, please say
+ 'Y' or 'M' here.
+
+ If you choose to build this driver as module it will
+ be dynamically linked and module will be called cdnsp-pci.ko
+
config USB_CDNS3_TI
tristate "Cadence USB3 support on TI platforms"
depends on ARCH_K3 || COMPILE_TEST
@@ -81,6 +100,7 @@ config USB_CDNS3_IMX
config USB_CDNS3_STARFIVE
tristate "Cadence USB3 support on StarFive SoC platforms"
depends on ARCH_STARFIVE || COMPILE_TEST
+ default USB_CDNS3
help
Say 'Y' or 'M' here if you are building for StarFive SoCs
platforms that contain Cadence USB3 controller core.
@@ -89,45 +109,7 @@ config USB_CDNS3_STARFIVE
If you choose to build this driver as module it will
be dynamically linked and module will be called cdns3-starfive.ko
-endif
-
-if USB_CDNS_SUPPORT
-
-config USB_CDNSP_PCI
- tristate "Cadence CDNSP Dual-Role Controller"
- depends on USB_CDNS_SUPPORT && USB_PCI && ACPI
- help
- Say Y here if your system has a Cadence CDNSP dual-role controller.
- It supports: dual-role switch Host-only, and Peripheral-only.
-
- If you choose to build this driver is a dynamically linked
- module, the module will be called cdnsp.ko.
-endif
-
-if USB_CDNSP_PCI
-
-config USB_CDNSP_GADGET
- bool "Cadence CDNSP device controller"
- depends on USB_GADGET=y || USB_GADGET=USB_CDNSP_PCI
- help
- Say Y here to enable device controller functionality of the
- Cadence CDNSP-DEV driver.
-
- Cadence CDNSP Device Controller in device mode is
- very similar to XHCI controller. Therefore some algorithms
- used has been taken from host driver.
- This controller supports FF, HS, SS and SSP mode.
- It doesn't support LS.
-
-config USB_CDNSP_HOST
- bool "Cadence CDNSP host controller"
- depends on USB=y || USB=USB_CDNSP_PCI
- select USB_CDNS_HOST
- help
- Say Y here to enable host controller functionality of the
- Cadence driver.
- Host controller is compliant with XHCI so it uses
- standard XHCI driver.
+endif # USB_CDNS3
-endif
+endif # USB_CDNS_SUPPORT
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
index 48dfae75b5aaf..b2e4ba6a49a36 100644
--- a/drivers/usb/cdns3/Makefile
+++ b/drivers/usb/cdns3/Makefile
@@ -3,42 +3,28 @@
CFLAGS_cdns3-trace.o := -I$(src)
CFLAGS_cdnsp-trace.o := -I$(src)
-cdns-usb-common-y := core.o drd.o
-cdns3-y := cdns3-plat.o
+obj-$(CONFIG_USB_CDNS3) += cdns.o
-ifeq ($(CONFIG_USB),m)
-obj-m += cdns-usb-common.o
-obj-m += cdns3.o
-else
-obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns-usb-common.o
-obj-$(CONFIG_USB_CDNS3) += cdns3.o
-endif
+cdns-y := core.o drd.o cdns3-plat.o
+cdns-$(CONFIG_USB_CDNS3_HOST) += host.o
-cdns-usb-common-$(CONFIG_USB_CDNS_HOST) += host.o
-cdns3-$(CONFIG_USB_CDNS3_GADGET) += cdns3-gadget.o cdns3-ep0.o
+ifneq ($(CONFIG_USB_CDNS3_GADGET),)
+cdns-y += cdns3-gadget.o cdns3-ep0.o \
+ cdnsp-ring.o cdnsp-gadget.o \
+ cdnsp-mem.o cdnsp-ep0.o
+endif
+ifneq ($(CONFIG_TRACING),)
ifneq ($(CONFIG_USB_CDNS3_GADGET),)
-cdns3-$(CONFIG_TRACING) += cdns3-trace.o
+cdns-y += cdns3-trace.o cdnsp-trace.o
+endif
endif
+##
+# Platform-specific glue layers (PCI wrappers, SoC integration)
+##
obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
+obj-$(CONFIG_USB_CDNSP_PCI) += cdnsp-pci.o
obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o
obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o
obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o
-
-cdnsp-udc-pci-y := cdnsp-pci.o
-
-ifdef CONFIG_USB_CDNSP_PCI
-ifeq ($(CONFIG_USB),m)
-obj-m += cdnsp-udc-pci.o
-else
-obj-$(CONFIG_USB_CDNSP_PCI) += cdnsp-udc-pci.o
-endif
-endif
-
-cdnsp-udc-pci-$(CONFIG_USB_CDNSP_GADGET) += cdnsp-ring.o cdnsp-gadget.o \
- cdnsp-mem.o cdnsp-ep0.o
-
-ifneq ($(CONFIG_USB_CDNSP_GADGET),)
-cdnsp-udc-pci-$(CONFIG_TRACING) += cdnsp-trace.o
-endif
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
index 1db8db1b7cc36..42311c1bfada1 100644
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -3522,3 +3522,4 @@ int cdns3_gadget_init(struct cdns *cdns)
return 0;
}
+EXPORT_SYMBOL_GPL(cdns3_gadget_init);
diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c
index 94e9706a1806b..bb5405460035e 100644
--- a/drivers/usb/cdns3/cdns3-plat.c
+++ b/drivers/usb/cdns3/cdns3-plat.c
@@ -21,6 +21,7 @@
#include "core.h"
#include "gadget-export.h"
+#include "host-export.h"
#include "drd.h"
static int set_phy_power_on(struct cdns *cdns)
@@ -44,6 +45,19 @@ static void set_phy_power_off(struct cdns *cdns)
phy_power_off(cdns->usb2_phy);
}
+static int cdns3_plat_gadget_init(struct cdns *cdns)
+{
+ if (cdns->version < CDNSP_CONTROLLER_V2)
+ return cdns3_gadget_init(cdns);
+ else
+ return cdnsp_gadget_init(cdns);
+}
+
+static int cdns3_plat_host_init(struct cdns *cdns)
+{
+ return cdns_host_init(cdns);
+}
+
/**
* cdns3_plat_probe - probe for cdns3 core device
* @pdev: Pointer to cdns3 core platform device
@@ -64,6 +78,14 @@ static int cdns3_plat_probe(struct platform_device *pdev)
cdns->dev = dev;
cdns->pdata = dev_get_platdata(dev);
+ if (cdns->pdata && cdns->pdata->override_apb_timeout)
+ cdns->override_apb_timeout = cdns->pdata->override_apb_timeout;
+
+ if (device_is_compatible(dev, "cdns,cdnsp")) {
+ cdns->no_drd = true;
+ cdns->version = CDNSP_CONTROLLER_V2;
+ dev_dbg(dev, "No DRD support\n");
+ }
platform_set_drvdata(pdev, cdns);
@@ -97,21 +119,22 @@ static int cdns3_plat_probe(struct platform_device *pdev)
cdns->dev_regs = regs;
- cdns->otg_irq = platform_get_irq_byname(pdev, "otg");
- if (cdns->otg_irq < 0)
- return dev_err_probe(dev, cdns->otg_irq,
- "Failed to get otg IRQ\n");
+ if (!cdns->no_drd) {
+ cdns->otg_irq = platform_get_irq_byname(pdev, "otg");
+ if (cdns->otg_irq < 0)
+ return dev_err_probe(dev, cdns->otg_irq,
+ "Failed to get otg IRQ\n");
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
- if (!res) {
- dev_err(dev, "couldn't get otg resource\n");
- return -ENXIO;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
+ if (!res) {
+ dev_err(dev, "couldn't get otg resource\n");
+ return -ENXIO;
+ }
+ cdns->otg_res = *res;
}
cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable");
- cdns->otg_res = *res;
-
cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
if (cdns->wakeup_irq == -EPROBE_DEFER)
return cdns->wakeup_irq;
@@ -143,12 +166,16 @@ static int cdns3_plat_probe(struct platform_device *pdev)
if (ret)
goto err_phy_power_on;
- cdns->gadget_init = cdns3_gadget_init;
-
ret = cdns_init(cdns);
if (ret)
goto err_cdns_init;
+ cdns->gadget_init = cdns3_plat_gadget_init;
+ cdns->host_init = cdns3_plat_host_init;
+ ret = cdns_core_init_role(cdns);
+ if (ret)
+ goto err_cdns_init_role;
+
device_set_wakeup_capable(dev, true);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -166,6 +193,9 @@ static int cdns3_plat_probe(struct platform_device *pdev)
return 0;
+err_cdns_init_role:
+ if (cdns->role_sw)
+ usb_role_switch_unregister(cdns->role_sw);
err_cdns_init:
set_phy_power_off(cdns);
err_phy_power_on:
@@ -321,6 +351,7 @@ static const struct dev_pm_ops cdns3_pm_ops = {
#ifdef CONFIG_OF
static const struct of_device_id of_cdns3_match[] = {
{ .compatible = "cdns,usb3" },
+ { .compatible = "cdns,cdnsp" },
{ },
};
MODULE_DEVICE_TABLE(of, of_cdns3_match);
@@ -339,6 +370,3 @@ static struct platform_driver cdns3_driver = {
module_platform_driver(cdns3_driver);
MODULE_ALIAS("platform:cdns3");
-MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c
index 6b3815f8a6e5b..a5275c2fb43b8 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.c
+++ b/drivers/usb/cdns3/cdnsp-gadget.c
@@ -124,20 +124,28 @@ void cdnsp_set_link_state(struct cdnsp_device *pdev,
}
static void cdnsp_disable_port(struct cdnsp_device *pdev,
- __le32 __iomem *port_regs)
+ struct cdnsp_port *port)
{
- u32 temp = cdnsp_port_state_to_neutral(readl(port_regs));
+ u32 temp;
+
+ if (!port->exist)
+ return;
- writel(temp | PORT_PED, port_regs);
+ temp = cdnsp_port_state_to_neutral(readl(&port->regs->portsc));
+ writel(temp | PORT_PED, &port->regs->portsc);
}
static void cdnsp_clear_port_change_bit(struct cdnsp_device *pdev,
- __le32 __iomem *port_regs)
+ struct cdnsp_port *port)
{
- u32 portsc = readl(port_regs);
+ u32 portsc;
+
+ if (!port->exist)
+ return;
+ portsc = readl(&port->regs->portsc);
writel(cdnsp_port_state_to_neutral(portsc) |
- (portsc & PORT_CHANGE_BITS), port_regs);
+ (portsc & PORT_CHANGE_BITS), &port->regs->portsc);
}
static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev)
@@ -944,7 +952,7 @@ void cdnsp_set_usb2_hardware_lpm(struct cdnsp_device *pdev,
struct usb_request *req,
int enable)
{
- if (pdev->active_port != &pdev->usb2_port || !pdev->gadget.lpm_capable)
+ if (pdev->active_port == &pdev->usb3_port || !pdev->gadget.lpm_capable)
return;
trace_cdnsp_lpm(enable);
@@ -1310,20 +1318,26 @@ static int cdnsp_run(struct cdnsp_device *pdev,
break;
}
- if (speed >= USB_SPEED_SUPER) {
+ if (pdev->usb3_port.exist && speed >= USB_SPEED_SUPER) {
writel(temp, &pdev->port3x_regs->mode_addr);
cdnsp_set_link_state(pdev, &pdev->usb3_port.regs->portsc,
XDEV_RXDETECT);
} else {
- cdnsp_disable_port(pdev, &pdev->usb3_port.regs->portsc);
+ cdnsp_disable_port(pdev, &pdev->usb3_port);
}
- cdnsp_set_link_state(pdev, &pdev->usb2_port.regs->portsc,
- XDEV_RXDETECT);
+ if (pdev->usb2_port.exist) {
+ cdnsp_set_link_state(pdev, &pdev->usb2_port.regs->portsc,
+ XDEV_RXDETECT);
+ writel(PORT_REG6_L1_L0_HW_EN | fs_speed, &pdev->port20_regs->port_reg6);
+ }
+
+ if (pdev->eusb_port.exist)
+ cdnsp_set_link_state(pdev, &pdev->eusb_port.regs->portsc,
+ XDEV_RXDETECT);
cdnsp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- writel(PORT_REG6_L1_L0_HW_EN | fs_speed, &pdev->port20_regs->port_reg6);
ret = cdnsp_start(pdev);
if (ret) {
@@ -1469,8 +1483,10 @@ static void cdnsp_stop(struct cdnsp_device *pdev)
cdnsp_ep_dequeue(&pdev->eps[0], req);
}
- cdnsp_disable_port(pdev, &pdev->usb2_port.regs->portsc);
- cdnsp_disable_port(pdev, &pdev->usb3_port.regs->portsc);
+ cdnsp_disable_port(pdev, &pdev->usb2_port);
+ cdnsp_disable_port(pdev, &pdev->usb3_port);
+ cdnsp_disable_port(pdev, &pdev->eusb_port);
+
cdnsp_disable_slot(pdev);
cdnsp_halt(pdev);
@@ -1479,8 +1495,9 @@ static void cdnsp_stop(struct cdnsp_device *pdev)
temp = readl(&pdev->ir_set->irq_pending);
writel(IMAN_IE_CLEAR(temp), &pdev->ir_set->irq_pending);
- cdnsp_clear_port_change_bit(pdev, &pdev->usb2_port.regs->portsc);
- cdnsp_clear_port_change_bit(pdev, &pdev->usb3_port.regs->portsc);
+ cdnsp_clear_port_change_bit(pdev, &pdev->usb2_port);
+ cdnsp_clear_port_change_bit(pdev, &pdev->eusb_port);
+ cdnsp_clear_port_change_bit(pdev, &pdev->usb3_port);
/* Clear interrupt line */
temp = readl(&pdev->ir_set->irq_pending);
@@ -2075,3 +2092,4 @@ int cdnsp_gadget_init(struct cdns *cdns)
return 0;
}
+EXPORT_SYMBOL_GPL(cdnsp_gadget_init);
diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h
index a91cca509db08..c44bca348a419 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.h
+++ b/drivers/usb/cdns3/cdnsp-gadget.h
@@ -1474,6 +1474,7 @@ struct cdnsp_device {
unsigned int link_state;
struct cdnsp_port usb2_port;
+ struct cdnsp_port eusb_port;
struct cdnsp_port usb3_port;
struct cdnsp_port *active_port;
u16 test_mode;
diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c
index a2a1b21f2ef87..5d8cdc91927dd 100644
--- a/drivers/usb/cdns3/cdnsp-mem.c
+++ b/drivers/usb/cdns3/cdnsp-mem.c
@@ -1088,11 +1088,9 @@ void cdnsp_mem_cleanup(struct cdnsp_device *pdev)
pdev->dcbaa, pdev->dcbaa->dma);
pdev->dcbaa = NULL;
-
- pdev->usb2_port.exist = 0;
- pdev->usb3_port.exist = 0;
- pdev->usb2_port.port_num = 0;
- pdev->usb3_port.port_num = 0;
+ memset(&pdev->usb2_port, 0, sizeof(struct cdnsp_port));
+ memset(&pdev->eusb_port, 0, sizeof(struct cdnsp_port));
+ memset(&pdev->usb3_port, 0, sizeof(struct cdnsp_port));
pdev->active_port = NULL;
}
@@ -1133,6 +1131,18 @@ static void cdnsp_add_in_port(struct cdnsp_device *pdev,
port_offset = CDNSP_EXT_PORT_OFF(temp);
port_count = CDNSP_EXT_PORT_COUNT(temp);
+ if (port == &pdev->eusb_port) {
+ /*
+ * If controller has usb2 + eusb port then eusb is as
+ * second port
+ */
+ if (port_count == 2)
+ port_offset++;
+
+ if (port_count == 1 && pdev->usb2_port.exist)
+ return;
+ }
+
trace_cdnsp_port_info(addr, port_offset, port_count, port->maj_rev);
port->port_num = port_offset;
@@ -1152,13 +1162,10 @@ static int cdnsp_setup_port_arrays(struct cdnsp_device *pdev)
base = &pdev->cap_regs->hc_capbase;
offset = cdnsp_find_next_ext_cap(base, 0,
EXT_CAP_CFG_DEV_20PORT_CAP_ID);
- pdev->port20_regs = base + offset;
-
- offset = cdnsp_find_next_ext_cap(base, 0, D_XEC_CFG_3XPORT_CAP);
- pdev->port3x_regs = base + offset;
+ if (offset)
+ pdev->port20_regs = base + offset;
offset = 0;
- base = &pdev->cap_regs->hc_capbase;
/* Driver expects max 2 extended protocol capability. */
for (i = 0; i < 2; i++) {
@@ -1173,26 +1180,46 @@ static int cdnsp_setup_port_arrays(struct cdnsp_device *pdev)
cdnsp_add_in_port(pdev, &pdev->usb3_port,
base + offset);
- if (CDNSP_EXT_PORT_MAJOR(temp) == 0x02 &&
- !pdev->usb2_port.port_num)
- cdnsp_add_in_port(pdev, &pdev->usb2_port,
- base + offset);
+ if (CDNSP_EXT_PORT_MAJOR(temp) == 0x02) {
+ if (!pdev->usb2_port.port_num && pdev->port20_regs)
+ cdnsp_add_in_port(pdev, &pdev->usb2_port,
+ base + offset);
+
+ if (!pdev->eusb_port.port_num)
+ cdnsp_add_in_port(pdev, &pdev->eusb_port,
+ base + offset);
+ }
}
- if (!pdev->usb2_port.exist || !pdev->usb3_port.exist) {
- dev_err(pdev->dev, "Error: Only one port detected\n");
+ if (!pdev->usb2_port.exist && !pdev->eusb_port.exist &&
+ !pdev->usb3_port.exist) {
+ dev_err(pdev->dev, "Error: No port detected\n");
return -ENODEV;
}
- trace_cdnsp_init("Found USB 2.0 ports and USB 3.0 ports.");
+ if (pdev->usb2_port.exist) {
+ pdev->usb2_port.regs = (struct cdnsp_port_regs __iomem *)
+ (&pdev->op_regs->port_reg_base + NUM_PORT_REGS *
+ (pdev->usb2_port.port_num - 1));
+ trace_cdnsp_init("Found USB 2.0 port.");
+ }
- pdev->usb2_port.regs = (struct cdnsp_port_regs __iomem *)
- (&pdev->op_regs->port_reg_base + NUM_PORT_REGS *
- (pdev->usb2_port.port_num - 1));
+ if (pdev->eusb_port.exist) {
+ pdev->eusb_port.regs = (struct cdnsp_port_regs __iomem *)
+ (&pdev->op_regs->port_reg_base + NUM_PORT_REGS *
+ (pdev->eusb_port.port_num - 1));
+ trace_cdnsp_init("Found eUSB 2.0 port.");
+ }
+
+ if (pdev->usb3_port.exist) {
+ offset = cdnsp_find_next_ext_cap(base, 0, D_XEC_CFG_3XPORT_CAP);
+ pdev->port3x_regs = base + offset;
- pdev->usb3_port.regs = (struct cdnsp_port_regs __iomem *)
- (&pdev->op_regs->port_reg_base + NUM_PORT_REGS *
- (pdev->usb3_port.port_num - 1));
+ pdev->usb3_port.regs = (struct cdnsp_port_regs __iomem *)
+ (&pdev->op_regs->port_reg_base + NUM_PORT_REGS *
+ (pdev->usb3_port.port_num - 1));
+ trace_cdnsp_init("Found USB 3.x port.");
+ }
return 0;
}
diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c
index 566d94e49102e..c38a3dc7111a7 100644
--- a/drivers/usb/cdns3/cdnsp-pci.c
+++ b/drivers/usb/cdns3/cdnsp-pci.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Cadence PCI Glue driver.
+ * Cadence USBSSP PCI Glue driver.
*
* Copyright (C) 2019 Cadence.
*
@@ -16,26 +16,45 @@
#include <linux/pci.h>
#include "core.h"
-#include "gadget-export.h"
+struct cdnsp_wrap {
+ struct platform_device *plat_dev;
+ struct property_entry prop[3];
+ struct resource dev_res[6];
+ int devfn;
+};
+
+#define RES_IRQ_HOST_ID 0
+#define RES_IRQ_PERIPHERAL_ID 1
+#define RES_IRQ_OTG_ID 2
+#define RES_HOST_ID 3
+#define RES_DEV_ID 4
+#define RES_DRD_ID 5
+/* DRD PCI configuration - 64-bit addressing */
+/* First PCI function */
#define PCI_BAR_HOST 0
-#define PCI_BAR_OTG 0
#define PCI_BAR_DEV 2
+/* Second PCI function */
+#define PCI_BAR_OTG 0
+/* Device only PCI configuration - 32-bit addressing */
+/* First PCI function */
+#define PCI_BAR_ONLY_DEV 1
#define PCI_DEV_FN_HOST_DEVICE 0
#define PCI_DEV_FN_OTG 1
#define PCI_DRIVER_NAME "cdns-pci-usbssp"
-#define PLAT_DRIVER_NAME "cdns-usbssp"
+#define PLAT_DRIVER_NAME "cdns-usb3"
-#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20
+#define PCI_DEVICE_ID_CDNS_UDC_USBSSP 0x0400
+#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20
static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
{
/*
* Gets the second function.
- * Platform has two function. The fist keeps resources for
- * Host/Device while the secon keeps resources for DRD/OTG.
+ * Platform has two function. The first keeps resources for
+ * Host/Device while the second keeps resources for DRD/OTG.
*/
if (pdev->device == PCI_DEVICE_ID_CDNS_USBSSP)
return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USBSS, NULL);
@@ -48,11 +67,13 @@ static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
static int cdnsp_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
- struct device *dev = &pdev->dev;
- struct pci_dev *func;
+ struct platform_device_info plat_info;
+ static struct cdns3_platform_data pdata;
+ struct cdnsp_wrap *wrap;
struct resource *res;
- struct cdns *cdnsp;
- int ret;
+ struct pci_dev *func;
+ bool no_drd = false;
+ int ret = 0;
/*
* For GADGET/HOST PCI (devfn) function number is 0,
@@ -62,11 +83,14 @@ static int cdnsp_pci_probe(struct pci_dev *pdev,
pdev->devfn != PCI_DEV_FN_OTG))
return -EINVAL;
+ if (pdev->device == PCI_DEVICE_ID_CDNS_UDC_USBSSP)
+ no_drd = true;
+
func = cdnsp_get_second_fun(pdev);
- if (!func)
+ if (!func && !no_drd)
return -EINVAL;
- if (func->class == PCI_CLASS_SERIAL_USB_XHCI ||
+ if ((func && func->class == PCI_CLASS_SERIAL_USB_XHCI) ||
pdev->class == PCI_CLASS_SERIAL_USB_XHCI) {
ret = -EINVAL;
goto put_pci;
@@ -79,147 +103,125 @@ static int cdnsp_pci_probe(struct pci_dev *pdev,
}
pci_set_master(pdev);
- if (pci_is_enabled(func)) {
- cdnsp = pci_get_drvdata(func);
+
+ if (func && pci_is_enabled(func)) {
+ wrap = pci_get_drvdata(func);
} else {
- cdnsp = kzalloc_obj(*cdnsp);
- if (!cdnsp) {
+ wrap = kzalloc_obj(*wrap);
+ if (!wrap) {
ret = -ENOMEM;
goto put_pci;
}
}
- /* For GADGET device function number is 0. */
- if (pdev->devfn == 0) {
- resource_size_t rsrc_start, rsrc_len;
+ res = wrap->dev_res;
- /* Function 0: host(BAR_0) + device(BAR_1).*/
- dev_dbg(dev, "Initialize resources\n");
- rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV);
- rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV);
- res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev");
- if (!res) {
- dev_dbg(dev, "controller already in use\n");
- ret = -EBUSY;
- goto free_cdnsp;
- }
+ if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
+ int bar_dev = no_drd ? PCI_BAR_ONLY_DEV : PCI_BAR_DEV;
- cdnsp->dev_regs = devm_ioremap(dev, rsrc_start, rsrc_len);
- if (!cdnsp->dev_regs) {
- dev_dbg(dev, "error mapping memory\n");
- ret = -EFAULT;
- goto free_cdnsp;
- }
+ /* Function 0: host(BAR_0) + device(BAR_2). */
+ dev_dbg(&pdev->dev, "Initialize Device resources\n");
+ res[RES_DEV_ID].start = pci_resource_start(pdev, bar_dev);
+ res[RES_DEV_ID].end = pci_resource_end(pdev, bar_dev);
+ res[RES_DEV_ID].name = "dev";
+ res[RES_DEV_ID].flags = IORESOURCE_MEM;
+ dev_dbg(&pdev->dev, "USBSSP-DEV physical base addr: %pa\n",
+ &res[RES_DEV_ID].start);
- cdnsp->dev_irq = pdev->irq;
- dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n",
- &rsrc_start);
+ res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
+ res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
+ res[RES_HOST_ID].name = "xhci";
+ res[RES_HOST_ID].flags = IORESOURCE_MEM;
+ dev_dbg(&pdev->dev, "USBSSP-XHCI physical base addr: %pa\n",
+ &res[RES_HOST_ID].start);
- res = &cdnsp->xhci_res[0];
- res->start = pci_resource_start(pdev, PCI_BAR_HOST);
- res->end = pci_resource_end(pdev, PCI_BAR_HOST);
- res->name = "xhci";
- res->flags = IORESOURCE_MEM;
- dev_dbg(dev, "USBSS-XHCI physical base addr: %pa\n",
- &res->start);
+ /* Interrupt for XHCI */
+ wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
+ wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
+ wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
- /* Interrupt for XHCI, */
- res = &cdnsp->xhci_res[1];
- res->start = pdev->irq;
- res->name = "host";
- res->flags = IORESOURCE_IRQ;
+ /* Interrupt for device. It's the same as for HOST. */
+ wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
+ wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
+ wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
} else {
- res = &cdnsp->otg_res;
- res->start = pci_resource_start(pdev, PCI_BAR_OTG);
- res->end = pci_resource_end(pdev, PCI_BAR_OTG);
- res->name = "otg";
- res->flags = IORESOURCE_MEM;
- dev_dbg(dev, "CDNSP-DRD physical base addr: %pa\n",
- &res->start);
+ res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
+ res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG);
+ res[RES_DRD_ID].name = "otg";
+ res[RES_DRD_ID].flags = IORESOURCE_MEM;
+ dev_dbg(&pdev->dev, "CDNSP-DRD physical base addr: %pa\n",
+ &res[RES_DRD_ID].start);
/* Interrupt for OTG/DRD. */
- cdnsp->otg_irq = pdev->irq;
+ wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
+ wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
+ wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
}
- /*
- * Cadence PCI based platform require some longer timeout for APB
- * to fixes domain clock synchronization issue after resuming
- * controller from L1 state.
- */
- cdnsp->override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE;
- pci_set_drvdata(pdev, cdnsp);
-
- if (pci_is_enabled(func)) {
- cdnsp->dev = dev;
- cdnsp->gadget_init = cdnsp_gadget_init;
-
- ret = cdns_init(cdnsp);
- if (ret)
- goto free_cdnsp;
- }
+ if (no_drd || pci_is_enabled(func)) {
+ u8 idx = 0;
- device_wakeup_enable(&pdev->dev);
- if (pci_dev_run_wake(pdev))
- pm_runtime_put_noidle(&pdev->dev);
+ /* set up platform device info */
+ pdata.override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE;
- return 0;
+ if (no_drd) {
+ wrap->prop[idx++] = PROPERTY_ENTRY_STRING("compatible",
+ "cdns,cdnsp");
+ wrap->prop[idx++] = PROPERTY_ENTRY_STRING("dr_mode", "peripheral");
+ } else {
+ wrap->prop[idx++] = PROPERTY_ENTRY_STRING("dr_mode", "otg");
+ wrap->prop[idx++] = PROPERTY_ENTRY_BOOL("usb-role-switch");
+ }
-free_cdnsp:
- if (!pci_is_enabled(func))
- kfree(cdnsp);
+ memset(&plat_info, 0, sizeof(plat_info));
+ plat_info.parent = &pdev->dev;
+ plat_info.fwnode = pdev->dev.fwnode;
+ plat_info.name = PLAT_DRIVER_NAME;
+ plat_info.id = pdev->devfn;
+ plat_info.res = wrap->dev_res;
+ plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
+ plat_info.dma_mask = pdev->dma_mask;
+ plat_info.data = &pdata;
+ plat_info.size_data = sizeof(pdata);
+ plat_info.properties = wrap->prop;
+ wrap->devfn = pdev->devfn;
+ /* register platform device */
+ wrap->plat_dev = platform_device_register_full(&plat_info);
+ if (IS_ERR(wrap->plat_dev)) {
+ ret = PTR_ERR(wrap->plat_dev);
+ kfree(wrap);
+ goto put_pci;
+ }
+ }
+ pci_set_drvdata(pdev, wrap);
put_pci:
pci_dev_put(func);
-
return ret;
}
static void cdnsp_pci_remove(struct pci_dev *pdev)
{
- struct cdns *cdnsp;
+ struct cdnsp_wrap *wrap;
struct pci_dev *func;
func = cdnsp_get_second_fun(pdev);
- cdnsp = (struct cdns *)pci_get_drvdata(pdev);
+ wrap = pci_get_drvdata(pdev);
- if (pci_dev_run_wake(pdev))
- pm_runtime_get_noresume(&pdev->dev);
+ if (wrap->devfn == pdev->devfn)
+ platform_device_unregister(wrap->plat_dev);
- if (pci_is_enabled(func)) {
- cdns_remove(cdnsp);
- } else {
- kfree(cdnsp);
- }
+ if (!func || !pci_is_enabled(func))
+ kfree(wrap);
pci_dev_put(func);
}
-static int __maybe_unused cdnsp_pci_suspend(struct device *dev)
-{
- struct cdns *cdns = dev_get_drvdata(dev);
-
- return cdns_suspend(cdns);
-}
-
-static int __maybe_unused cdnsp_pci_resume(struct device *dev)
-{
- struct cdns *cdns = dev_get_drvdata(dev);
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&cdns->lock, flags);
- ret = cdns_resume(cdns);
- spin_unlock_irqrestore(&cdns->lock, flags);
- cdns_set_active(cdns, 1);
-
- return ret;
-}
-
-static const struct dev_pm_ops cdnsp_pci_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(cdnsp_pci_suspend, cdnsp_pci_resume)
-};
-
static const struct pci_device_id cdnsp_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC_USBSSP),
+ .class = PCI_CLASS_SERIAL_USB_DEVICE },
+ { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC_USBSSP),
+ .class = PCI_CLASS_SERIAL_USB_CDNS },
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP),
.class = PCI_CLASS_SERIAL_USB_DEVICE },
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP),
@@ -230,13 +232,10 @@ static const struct pci_device_id cdnsp_pci_ids[] = {
};
static struct pci_driver cdnsp_pci_driver = {
- .name = "cdnsp-pci",
+ .name = PCI_DRIVER_NAME,
.id_table = cdnsp_pci_ids,
.probe = cdnsp_pci_probe,
.remove = cdnsp_pci_remove,
- .driver = {
- .pm = &cdnsp_pci_pm_ops,
- }
};
module_pci_driver(cdnsp_pci_driver);
@@ -245,4 +244,4 @@ MODULE_DEVICE_TABLE(pci, cdnsp_pci_ids);
MODULE_ALIAS("pci:cdnsp");
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Cadence CDNSP PCI driver");
+MODULE_DESCRIPTION("Cadence CDNSP PCI wrapper");
diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c
index 0758f171f73ec..715658c981ff8 100644
--- a/drivers/usb/cdns3/cdnsp-ring.c
+++ b/drivers/usb/cdns3/cdnsp-ring.c
@@ -259,7 +259,7 @@ static bool cdnsp_room_on_ring(struct cdnsp_device *pdev,
*/
static void cdnsp_force_l0_go(struct cdnsp_device *pdev)
{
- if (pdev->active_port == &pdev->usb2_port && pdev->gadget.lpm_capable)
+ if (pdev->active_port != &pdev->usb3_port && pdev->gadget.lpm_capable)
cdnsp_set_link_state(pdev, &pdev->active_port->regs->portsc, XDEV_U0);
}
@@ -763,6 +763,8 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id)
if (port_id == pdev->usb2_port.port_num) {
port = &pdev->usb2_port;
+ } else if (port_id == pdev->eusb_port.port_num) {
+ port = &pdev->eusb_port;
} else if (port_id == pdev->usb3_port.port_num) {
port = &pdev->usb3_port;
} else {
@@ -779,7 +781,8 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id)
cdnsp_enable_slot(pdev);
}
- if (port_id == pdev->usb2_port.port_num)
+ if ((pdev->usb2_port.exist && port_id == pdev->usb2_port.port_num) ||
+ (pdev->eusb_port.exist && port_id == pdev->eusb_port.port_num))
cdnsp_set_usb2_hardware_lpm(pdev, NULL, 1);
else
writel(PORT_U1_TIMEOUT(1) | PORT_U2_TIMEOUT(1),
@@ -808,7 +811,7 @@ static void cdnsp_handle_port_status(struct cdnsp_device *pdev,
port_regs = pdev->active_port->regs;
- if (port_id == pdev->usb2_port.port_num)
+ if (port_id == pdev->usb2_port.port_num || port_id == pdev->eusb_port.port_num)
port2 = true;
new_event:
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index f0e32227c0b79..504bdf13ea804 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -21,7 +21,6 @@
#include <linux/pm_runtime.h>
#include "core.h"
-#include "host-export.h"
#include "drd.h"
static int cdns_idle_init(struct cdns *cdns);
@@ -71,7 +70,8 @@ static void cdns_role_stop(struct cdns *cdns)
static void cdns_exit_roles(struct cdns *cdns)
{
cdns_role_stop(cdns);
- cdns_drd_exit(cdns);
+ if (!cdns->no_drd)
+ cdns_drd_exit(cdns);
}
/**
@@ -80,7 +80,7 @@ static void cdns_exit_roles(struct cdns *cdns)
*
* Returns 0 on success otherwise negative errno
*/
-static int cdns_core_init_role(struct cdns *cdns)
+int cdns_core_init_role(struct cdns *cdns)
{
struct device *dev = cdns->dev;
enum usb_dr_mode best_dr_mode;
@@ -96,23 +96,13 @@ static int cdns_core_init_role(struct cdns *cdns)
* can be restricted later depending on strap pin configuration.
*/
if (dr_mode == USB_DR_MODE_UNKNOWN) {
- if (cdns->version == CDNSP_CONTROLLER_V2) {
- if (IS_ENABLED(CONFIG_USB_CDNSP_HOST) &&
- IS_ENABLED(CONFIG_USB_CDNSP_GADGET))
- dr_mode = USB_DR_MODE_OTG;
- else if (IS_ENABLED(CONFIG_USB_CDNSP_HOST))
- dr_mode = USB_DR_MODE_HOST;
- else if (IS_ENABLED(CONFIG_USB_CDNSP_GADGET))
- dr_mode = USB_DR_MODE_PERIPHERAL;
- } else {
- if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
- IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
- dr_mode = USB_DR_MODE_OTG;
- else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
- dr_mode = USB_DR_MODE_HOST;
- else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
- dr_mode = USB_DR_MODE_PERIPHERAL;
- }
+ if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
+ IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
+ dr_mode = USB_DR_MODE_OTG;
+ else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
+ dr_mode = USB_DR_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
+ dr_mode = USB_DR_MODE_PERIPHERAL;
}
/*
@@ -137,11 +127,8 @@ static int cdns_core_init_role(struct cdns *cdns)
dr_mode = best_dr_mode;
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
- if ((cdns->version == CDNSP_CONTROLLER_V2 &&
- IS_ENABLED(CONFIG_USB_CDNSP_HOST)) ||
- (cdns->version < CDNSP_CONTROLLER_V2 &&
- IS_ENABLED(CONFIG_USB_CDNS3_HOST)))
- ret = cdns_host_init(cdns);
+ if (cdns->host_init)
+ ret = cdns->host_init(cdns);
else
ret = -ENXIO;
@@ -197,11 +184,14 @@ static int cdns_core_init_role(struct cdns *cdns)
goto err;
}
+ dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
+
return 0;
err:
cdns_exit_roles(cdns);
return ret;
}
+EXPORT_SYMBOL_GPL(cdns_core_init_role);
/**
* cdns_hw_role_state_machine - role switch state machine based on hw events.
@@ -469,14 +459,8 @@ int cdns_init(struct cdns *cdns)
if (ret)
goto init_failed;
- ret = cdns_core_init_role(cdns);
- if (ret)
- goto init_failed;
-
spin_lock_init(&cdns->lock);
- dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
-
return 0;
init_failed:
cdns_drd_exit(cdns);
@@ -576,5 +560,5 @@ EXPORT_SYMBOL_GPL(cdns_set_active);
MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
-MODULE_DESCRIPTION("Cadence USBSS and USBSSP DRD Driver");
+MODULE_DESCRIPTION("Cadence USBSS/USBSSP DRD driver (core, DRD, platform, optional host/gadget)");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
index 801be9e61340e..8c492fda924c5 100644
--- a/drivers/usb/cdns3/core.h
+++ b/drivers/usb/cdns3/core.h
@@ -45,6 +45,7 @@ struct cdns3_platform_data {
unsigned long quirks;
#define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0)
#define CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE BIT(1)
+ u32 override_apb_timeout; /* 0 = use default (e.g. for PCI) */
};
/**
@@ -82,6 +83,10 @@ struct cdns3_platform_data {
* @override_apb_timeout: hold value of APB timeout. For value 0 the default
* value in CHICKEN_BITS_3 will be preserved.
* @gadget_init: pointer to gadget initialization function
+ * @host_init: pointer to host initialization function
+ * @no_drd: DRD register block is inaccessible. The controller is hardwired to
+ * single role (host or device) or the logic for role switching is
+ * missing.
*/
struct cdns {
struct device *dev;
@@ -120,13 +125,15 @@ struct cdns {
spinlock_t lock;
struct xhci_plat_priv *xhci_plat_data;
u32 override_apb_timeout;
-
int (*gadget_init)(struct cdns *cdns);
+ int (*host_init)(struct cdns *cdns);
+ bool no_drd;
};
int cdns_hw_role_switch(struct cdns *cdns);
int cdns_init(struct cdns *cdns);
int cdns_remove(struct cdns *cdns);
+int cdns_core_init_role(struct cdns *cdns);
#ifdef CONFIG_PM_SLEEP
int cdns_resume(struct cdns *cdns);
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
index 84fb38a5723a3..d2bb682e4552c 100644
--- a/drivers/usb/cdns3/drd.c
+++ b/drivers/usb/cdns3/drd.c
@@ -87,6 +87,9 @@ int cdns_get_id(struct cdns *cdns)
{
int id;
+ if (cdns->no_drd)
+ return 0;
+
id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
dev_dbg(cdns->dev, "OTG ID: %d", id);
@@ -107,7 +110,7 @@ void cdns_clear_vbus(struct cdns *cdns)
{
u32 reg;
- if (cdns->version != CDNSP_CONTROLLER_V2)
+ if (cdns->version != CDNSP_CONTROLLER_V2 || cdns->no_drd)
return;
reg = readl(&cdns->otg_cdnsp_regs->override);
@@ -120,7 +123,7 @@ void cdns_set_vbus(struct cdns *cdns)
{
u32 reg;
- if (cdns->version != CDNSP_CONTROLLER_V2)
+ if (cdns->version != CDNSP_CONTROLLER_V2 || cdns->no_drd)
return;
reg = readl(&cdns->otg_cdnsp_regs->override);
@@ -179,7 +182,10 @@ static void cdns_otg_enable_irq(struct cdns *cdns)
int cdns_drd_host_on(struct cdns *cdns)
{
u32 val, ready_bit;
- int ret;
+ int ret = 0;
+
+ if (cdns->no_drd)
+ goto phy_set;
/* Enable host mode. */
writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS,
@@ -197,6 +203,7 @@ int cdns_drd_host_on(struct cdns *cdns)
if (ret)
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
+phy_set:
phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_HOST);
phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST);
return ret;
@@ -210,6 +217,9 @@ void cdns_drd_host_off(struct cdns *cdns)
{
u32 val;
+ if (cdns->no_drd)
+ goto phy_set;
+
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
&cdns->otg_regs->cmd);
@@ -218,6 +228,8 @@ void cdns_drd_host_off(struct cdns *cdns)
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_HOST_STATE_MASK),
1, 2000000);
+
+phy_set:
phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID);
phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
}
@@ -234,6 +246,9 @@ int cdns_drd_gadget_on(struct cdns *cdns)
u32 ready_bit;
int ret, val;
+ if (cdns->no_drd)
+ goto phy_set;
+
/* switch OTG core */
writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
@@ -251,6 +266,7 @@ int cdns_drd_gadget_on(struct cdns *cdns)
return ret;
}
+phy_set:
phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_DEVICE);
phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE);
return 0;
@@ -265,6 +281,9 @@ void cdns_drd_gadget_off(struct cdns *cdns)
{
u32 val;
+ if (cdns->no_drd)
+ goto phy_set;
+
/*
* Driver should wait at least 10us after disabling Device
* before turning-off Device (DEV_BUS_DROP).
@@ -277,6 +296,8 @@ void cdns_drd_gadget_off(struct cdns *cdns)
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_DEV_STATE_MASK),
1, 2000000);
+
+phy_set:
phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID);
phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
}
@@ -392,6 +413,18 @@ int cdns_drd_init(struct cdns *cdns)
u32 state, reg;
int ret;
+ if (cdns->no_drd) {
+ cdns->dr_mode = usb_get_dr_mode(cdns->dev);
+
+ if (cdns->dr_mode != USB_DR_MODE_HOST &&
+ cdns->dr_mode != USB_DR_MODE_PERIPHERAL) {
+ dev_err(cdns->dev, "Incorrect dr_mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
if (IS_ERR(regs))
return PTR_ERR(regs);
@@ -492,6 +525,9 @@ int cdns_drd_init(struct cdns *cdns)
int cdns_drd_exit(struct cdns *cdns)
{
+ if (cdns->no_drd)
+ return 0;
+
cdns_otg_disable_irq(cdns);
return 0;
@@ -500,6 +536,9 @@ int cdns_drd_exit(struct cdns *cdns)
/* Indicate the cdns3 core was power lost before */
bool cdns_power_is_lost(struct cdns *cdns)
{
+ if (cdns->no_drd)
+ return false;
+
if (cdns->version == CDNS3_CONTROLLER_V0) {
if (!(readl(&cdns->otg_v0_regs->simulate) & BIT(0)))
return true;
diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
index c37b6269b0010..60c3177db62c1 100644
--- a/drivers/usb/cdns3/gadget-export.h
+++ b/drivers/usb/cdns3/gadget-export.h
@@ -10,9 +10,10 @@
#ifndef __LINUX_CDNS3_GADGET_EXPORT
#define __LINUX_CDNS3_GADGET_EXPORT
-#if IS_ENABLED(CONFIG_USB_CDNSP_GADGET)
+#if IS_ENABLED(CONFIG_USB_CDNS3_GADGET)
int cdnsp_gadget_init(struct cdns *cdns);
+int cdns3_gadget_init(struct cdns *cdns);
#else
static inline int cdnsp_gadget_init(struct cdns *cdns)
@@ -20,13 +21,6 @@ static inline int cdnsp_gadget_init(struct cdns *cdns)
return -ENXIO;
}
-#endif /* CONFIG_USB_CDNSP_GADGET */
-
-#if IS_ENABLED(CONFIG_USB_CDNS3_GADGET)
-
-int cdns3_gadget_init(struct cdns *cdns);
-#else
-
static inline int cdns3_gadget_init(struct cdns *cdns)
{
return -ENXIO;
diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
index cf92173ecf008..34fd1f1ad59da 100644
--- a/drivers/usb/cdns3/host-export.h
+++ b/drivers/usb/cdns3/host-export.h
@@ -9,7 +9,7 @@
#ifndef __LINUX_CDNS3_HOST_EXPORT
#define __LINUX_CDNS3_HOST_EXPORT
-#if IS_ENABLED(CONFIG_USB_CDNS_HOST)
+#if IS_ENABLED(CONFIG_USB_CDNS3_HOST)
int cdns_host_init(struct cdns *cdns);
@@ -22,6 +22,6 @@ static inline int cdns_host_init(struct cdns *cdns)
static inline void cdns_host_exit(struct cdns *cdns) { }
-#endif /* USB_CDNS_HOST */
+#endif /* CONFIG_USB_CDNS3_HOST */
#endif /* __LINUX_CDNS3_HOST_EXPORT */
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 2ab3db3c10151..07563be0013f4 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -1187,19 +1187,16 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci->role = ci_get_role(ci);
if (!ci_otg_is_fsm_mode(ci)) {
- /* only update vbus status for peripheral */
- if (ci->role == CI_ROLE_GADGET) {
- /* Pull down DP for possible charger detection */
- hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
- ci_handle_vbus_change(ci);
- }
-
ret = ci_role_start(ci, ci->role);
if (ret) {
dev_err(dev, "can't start %s role\n",
ci_role(ci)->name);
goto stop;
}
+
+ /* only update vbus status for peripheral */
+ if (ci->role == CI_ROLE_GADGET)
+ ci_handle_vbus_change(ci);
}
ret = devm_request_irq(dev, ci->irq, ci_irq_handler, IRQF_SHARED,
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index f2de86d0ce403..d52f894898934 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1835,6 +1835,20 @@ static const struct usb_ep_ops usb_ep_ops = {
* GADGET block
*****************************************************************************/
+static void ci_udc_enable_vbus_irq(struct ci_hdrc *ci, bool enable)
+{
+ u32 reg = OTGSC_BSVIS;
+
+ if (!ci->is_otg)
+ return;
+
+ if (enable)
+ reg |= OTGSC_BSVIE;
+
+ /* Clear pending BSVIS and enable/disable BSVIE */
+ hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, reg);
+}
+
static int ci_udc_get_frame(struct usb_gadget *_gadget)
{
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
@@ -2030,6 +2044,8 @@ static int init_eps(struct ci_hdrc *ci)
{
int retval = 0, i, j;
+ memset(ci->ci_hw_ep, 0, sizeof(ci->ci_hw_ep));
+
for (i = 0; i < ci->hw_ep_max/2; i++)
for (j = RX; j <= TX; j++) {
int k = i + j * ci->hw_ep_max/2;
@@ -2275,6 +2291,8 @@ static int udc_start(struct ci_hdrc *ci)
struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps;
int retval = 0;
+ memset(&ci->gadget, 0, sizeof(ci->gadget));
+
ci->gadget.ops = &usb_gadget_ops;
ci->gadget.speed = USB_SPEED_UNKNOWN;
ci->gadget.max_speed = USB_SPEED_HIGH;
@@ -2313,10 +2331,15 @@ static int udc_start(struct ci_hdrc *ci)
ci->gadget.ep0 = &ci->ep0in->ep;
+ if (ci->platdata->pins_device)
+ pinctrl_select_state(ci->platdata->pctl,
+ ci->platdata->pins_device);
+
retval = usb_add_gadget_udc(dev, &ci->gadget);
if (retval)
goto destroy_eps;
+ ci_udc_enable_vbus_irq(ci, true);
return retval;
destroy_eps:
@@ -2328,48 +2351,20 @@ free_qh_pool:
return retval;
}
-/*
- * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC
- *
- * No interrupts active, the IRQ has been released
+/**
+ * udc_stop: deinitialize gadget role
+ * @ci: chipidea controller
*/
-void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
+static void udc_stop(struct ci_hdrc *ci)
{
- if (!ci->roles[CI_ROLE_GADGET])
- return;
-
+ ci_udc_enable_vbus_irq(ci, false);
usb_del_gadget_udc(&ci->gadget);
+ ci->vbus_active = 0;
destroy_eps(ci);
dma_pool_destroy(ci->td_pool);
dma_pool_destroy(ci->qh_pool);
-}
-
-static int udc_id_switch_for_device(struct ci_hdrc *ci)
-{
- if (ci->platdata->pins_device)
- pinctrl_select_state(ci->platdata->pctl,
- ci->platdata->pins_device);
-
- if (ci->is_otg)
- /* Clear and enable BSV irq */
- hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
- OTGSC_BSVIS | OTGSC_BSVIE);
-
- return 0;
-}
-
-static void udc_id_switch_for_host(struct ci_hdrc *ci)
-{
- /*
- * host doesn't care B_SESSION_VALID event
- * so clear and disable BSV irq
- */
- if (ci->is_otg)
- hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
-
- ci->vbus_active = 0;
if (ci->platdata->pins_device && ci->platdata->pins_default)
pinctrl_select_state(ci->platdata->pctl,
@@ -2395,9 +2390,7 @@ static void udc_suspend(struct ci_hdrc *ci)
static void udc_resume(struct ci_hdrc *ci, bool power_lost)
{
if (power_lost) {
- if (ci->is_otg)
- hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
- OTGSC_BSVIS | OTGSC_BSVIE);
+ ci_udc_enable_vbus_irq(ci, true);
if (ci->vbus_active)
usb_gadget_vbus_disconnect(&ci->gadget);
} else if (ci->vbus_active && ci->driver &&
@@ -2420,7 +2413,6 @@ static void udc_resume(struct ci_hdrc *ci, bool power_lost)
int ci_hdrc_gadget_init(struct ci_hdrc *ci)
{
struct ci_role_driver *rdrv;
- int ret;
if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC))
return -ENXIO;
@@ -2429,8 +2421,8 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci)
if (!rdrv)
return -ENOMEM;
- rdrv->start = udc_id_switch_for_device;
- rdrv->stop = udc_id_switch_for_host;
+ rdrv->start = udc_start;
+ rdrv->stop = udc_stop;
#ifdef CONFIG_PM_SLEEP
rdrv->suspend = udc_suspend;
rdrv->resume = udc_resume;
@@ -2438,9 +2430,22 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci)
rdrv->irq = udc_irq;
rdrv->name = "gadget";
- ret = udc_start(ci);
- if (!ret)
- ci->roles[CI_ROLE_GADGET] = rdrv;
+ ci->roles[CI_ROLE_GADGET] = rdrv;
- return ret;
+ /* Pull down DP for possible charger detection */
+ hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+ return 0;
+}
+
+/*
+ * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC
+ *
+ * No interrupts active, the IRQ has been released
+ */
+void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
+{
+ struct device *dev = &ci->gadget.dev;
+
+ if (ci->roles[CI_ROLE_GADGET] && device_is_registered(dev))
+ udc_stop(ci);
}
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index ddf0b59638595..49ab02f258722 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -797,6 +797,9 @@ static void acm_port_shutdown(struct tty_port *port)
"ctrl polling restart failed after port close\n");
/* port_shutdown() cleared DTR/RTS; restore them */
acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS);
+ if (acm_submit_read_urbs(acm, GFP_KERNEL))
+ dev_dbg(&acm->control->dev,
+ "read urb restart failed after port close\n");
}
}
@@ -1564,6 +1567,9 @@ skip_countries:
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL))
dev_warn(&intf->dev,
"failed to start persistent ctrl polling\n");
+ if (acm_submit_read_urbs(acm, GFP_KERNEL))
+ dev_warn(&intf->dev,
+ "failed to start persistent bulk read polling\n");
}
return 0;
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index b181b43a35dc0..8e8a5f59b319b 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -446,7 +446,8 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
/* Root hub control transfers execute synchronously */
-static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
+static int rh_call_control(struct usb_hcd *hcd,
+ struct urb *urb, gfp_t mem_flags)
{
struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength;
@@ -481,8 +482,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
* tbuf should be at least as big as the
* USB hub descriptor.
*/
- tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength);
- tbuf = kzalloc(tbuf_size, GFP_KERNEL);
+ tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength);
+ tbuf = kzalloc(tbuf_size, mem_flags);
if (!tbuf) {
status = -ENOMEM;
goto err_alloc;
@@ -807,12 +808,13 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
return retval;
}
-static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
+static int rh_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb, gfp_t mem_flags)
{
if (usb_endpoint_xfer_int(&urb->ep->desc))
return rh_queue_status (hcd, urb);
if (usb_endpoint_xfer_control(&urb->ep->desc))
- return rh_call_control (hcd, urb);
+ return rh_call_control(hcd, urb, mem_flags);
return -EINVAL;
}
@@ -1533,7 +1535,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
*/
if (is_root_hub(urb->dev)) {
- status = rh_urb_enqueue(hcd, urb);
+ status = rh_urb_enqueue(hcd, urb, mem_flags);
} else {
status = map_urb_for_dma(hcd, urb, mem_flags);
if (likely(status == 0)) {
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 5c7538d498dd1..f380275ac696a 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -515,18 +515,20 @@ void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
u32 pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT;
if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL) {
- if (WARN(!chan || !chan->qh,
- "chan->qh must be specified for non-control eps\n"))
+ if (!chan->qh) {
+ dev_err(hsotg->dev, "chan->qh must be specified for non-control eps\n");
return;
+ }
if (pid == TSIZ_SC_MC_PID_DATA0)
chan->qh->data_toggle = DWC2_HC_PID_DATA0;
else
chan->qh->data_toggle = DWC2_HC_PID_DATA1;
} else {
- if (WARN(!qtd,
- "qtd must be specified for control eps\n"))
+ if (!qtd) {
+ dev_err(hsotg->dev, "qtd must be specified for control eps\n");
return;
+ }
if (pid == TSIZ_SC_MC_PID_DATA0)
qtd->data_toggle = DWC2_HC_PID_DATA0;
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 65213896de998..517aa7f1486da 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -425,8 +425,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
}
reg = dwc3_readl(dwc, DWC3_GUCTL);
- reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
- reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
+ FIELD_MODIFY(DWC3_GUCTL_REFCLKPER_MASK, &reg, period);
dwc3_writel(dwc, DWC3_GUCTL, reg);
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
@@ -456,12 +455,9 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
decr = 480000000 / rate;
reg = dwc3_readl(dwc, DWC3_GFLADJ);
- reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
- & ~DWC3_GFLADJ_240MHZDECR
- & ~DWC3_GFLADJ_240MHZDECR_PLS1;
- reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj)
- | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1)
- | FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1);
+ FIELD_MODIFY(DWC3_GFLADJ_REFCLK_FLADJ_MASK, &reg, fladj);
+ FIELD_MODIFY(DWC3_GFLADJ_240MHZDECR, &reg, decr >> 1);
+ FIELD_MODIFY(DWC3_GFLADJ_240MHZDECR_PLS1, &reg, decr & 1);
if (dwc->gfladj_refclk_lpm_sel)
reg |= DWC3_GFLADJ_REFCLK_LPM_SEL;
@@ -525,7 +521,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
}
/**
- * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length
+ * dwc3_alloc_event_buffers - Allocate one event buffer of size @length
* @dwc: pointer to our controller context structure
* @length: size of event buffer
*
diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c
index 4ca567ec01d07..60ee4cc99b286 100644
--- a/drivers/usb/dwc3/dwc3-google.c
+++ b/drivers/usb/dwc3/dwc3-google.c
@@ -104,9 +104,8 @@ static int dwc3_google_set_pmu_state(struct dwc3_google *google, int state)
regmap_read(google->usb_cfg_regmap,
google->host_cfg_offset + HOST_CFG1_OFFSET, &reg);
- reg &= ~HOST_CFG1_PM_POWER_STATE_REQUEST;
- reg |= (FIELD_PREP(HOST_CFG1_PM_POWER_STATE_REQUEST, state) |
- HOST_CFG1_PME_EN);
+ FIELD_MODIFY(HOST_CFG1_PM_POWER_STATE_REQUEST, &reg, state);
+ reg |= HOST_CFG1_PME_EN;
regmap_write(google->usb_cfg_regmap,
google->host_cfg_offset + HOST_CFG1_OFFSET, reg);
diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c
index 42bfc14ae0c46..2201f0f34abb7 100644
--- a/drivers/usb/dwc3/dwc3-octeon.c
+++ b/drivers/usb/dwc3/dwc3-octeon.c
@@ -296,8 +296,7 @@ static int dwc3_octeon_setup(struct dwc3_octeon *octeon,
return div;
}
val = dwc3_octeon_readq(uctl_ctl_reg);
- val &= ~USBDRD_UCTL_CTL_H_CLKDIV_SEL;
- val |= FIELD_PREP(USBDRD_UCTL_CTL_H_CLKDIV_SEL, div);
+ FIELD_MODIFY(USBDRD_UCTL_CTL_H_CLKDIV_SEL, &val, div);
val |= USBDRD_UCTL_CTL_H_CLK_EN;
dwc3_octeon_writeq(uctl_ctl_reg, val);
val = dwc3_octeon_readq(uctl_ctl_reg);
@@ -314,14 +313,11 @@ static int dwc3_octeon_setup(struct dwc3_octeon *octeon,
/* Step 5a: Reference clock configuration. */
val = dwc3_octeon_readq(uctl_ctl_reg);
val &= ~USBDRD_UCTL_CTL_REF_CLK_DIV2;
- val &= ~USBDRD_UCTL_CTL_REF_CLK_SEL;
- val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_SEL, ref_clk_sel);
+ FIELD_MODIFY(USBDRD_UCTL_CTL_REF_CLK_SEL, &val, ref_clk_sel);
- val &= ~USBDRD_UCTL_CTL_REF_CLK_FSEL;
- val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_FSEL, ref_clk_fsel);
+ FIELD_MODIFY(USBDRD_UCTL_CTL_REF_CLK_FSEL, &val, ref_clk_fsel);
- val &= ~USBDRD_UCTL_CTL_MPLL_MULTIPLIER;
- val |= FIELD_PREP(USBDRD_UCTL_CTL_MPLL_MULTIPLIER, mpll_mul);
+ FIELD_MODIFY(USBDRD_UCTL_CTL_MPLL_MULTIPLIER, &val, mpll_mul);
/* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */
val |= USBDRD_UCTL_CTL_SSC_EN;
diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c
index 7fc6696b7694b..75f9c831b21a6 100644
--- a/drivers/usb/gadget/udc/aspeed_udc.c
+++ b/drivers/usb/gadget/udc/aspeed_udc.c
@@ -694,7 +694,7 @@ static int ast_udc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
struct ast_udc_dev *udc = ep->udc;
struct ast_udc_request *req;
unsigned long flags;
- int rc = 0;
+ int rc = -EINVAL;
spin_lock_irqsave(&udc->lock, flags);
@@ -704,14 +704,11 @@ static int ast_udc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
list_del_init(&req->queue);
ast_udc_done(ep, req, -ESHUTDOWN);
_req->status = -ECONNRESET;
+ rc = 0;
break;
}
}
- /* dequeue request not found */
- if (&req->req != _req)
- rc = -EINVAL;
-
spin_unlock_irqrestore(&udc->lock, flags);
return rc;
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index e8861eaad9077..60340ff9edbf0 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -712,6 +712,9 @@ static int usb_gadget_connect_locked(struct usb_gadget *gadget)
goto out;
}
+ if (gadget->connected)
+ goto out;
+
if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) {
/*
* If the gadget isn't usable (because it is deactivated,
@@ -885,8 +888,10 @@ int usb_gadget_activate(struct usb_gadget *gadget)
* If gadget has been connected before deactivation, or became connected
* while it was being deactivated, we call usb_gadget_connect().
*/
- if (gadget->connected)
+ if (gadget->connected) {
+ gadget->connected = false;
ret = usb_gadget_connect_locked(gadget);
+ }
unlock:
mutex_unlock(&gadget->udc->connect_lock);
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index db42a5e3e805f..ac2a984c2f87e 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -1616,7 +1616,8 @@ pm_next:
if (stat & INT_USBRESET) { /* hub reset done */
ACK(INT_USBRESET);
INFO(dev, "USB reset done, gadget %s\n",
- dev->driver->driver.name);
+ dev->driver ? dev->driver->driver.name :
+ "<not bound>");
}
// and INT_ERR on some endpoint's crc/bitstuff/... problem
}
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index b3d58d7c3a774..594d671937634 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -12,7 +12,7 @@
/* #define VERBOSE_DEBUG */
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
@@ -261,24 +261,12 @@ static void nuke (struct pxa25x_ep *, int status);
/* one GPIO should control a D+ pullup, so host sees this device (or not) */
static void pullup_off(void)
{
- struct pxa2xx_udc_mach_info *mach = the_controller->mach;
- int off_level = mach->gpio_pullup_inverted;
-
- if (gpio_is_valid(mach->gpio_pullup))
- gpio_set_value(mach->gpio_pullup, off_level);
- else if (mach->udc_command)
- mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+ gpiod_set_value(the_controller->pullup_gpio, 0);
}
static void pullup_on(void)
{
- struct pxa2xx_udc_mach_info *mach = the_controller->mach;
- int on_level = !mach->gpio_pullup_inverted;
-
- if (gpio_is_valid(mach->gpio_pullup))
- gpio_set_value(mach->gpio_pullup, on_level);
- else if (mach->udc_command)
- mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+ gpiod_set_value(the_controller->pullup_gpio, 1);
}
#if defined(CONFIG_CPU_BIG_ENDIAN)
@@ -1190,8 +1178,7 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active)
udc = container_of(_gadget, struct pxa25x_udc, gadget);
- /* not all boards support pullup control */
- if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command)
+ if (!udc->pullup_gpio)
return -EOPNOTSUPP;
udc->pullup = (is_active != 0);
@@ -2343,19 +2330,17 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
/* other non-static parts of init */
dev->dev = &pdev->dev;
- dev->mach = dev_get_platdata(&pdev->dev);
dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
- if (gpio_is_valid(dev->mach->gpio_pullup)) {
- retval = devm_gpio_request_one(&pdev->dev, dev->mach->gpio_pullup,
- GPIOF_OUT_INIT_LOW, "pca25x_udc GPIO PULLUP");
- if (retval) {
- dev_dbg(&pdev->dev,
- "can't get pullup gpio %d, err: %d\n",
- dev->mach->gpio_pullup, retval);
- goto err;
- }
+ dev->pullup_gpio = devm_gpiod_get_index_optional(&pdev->dev, "pullup", 0,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(dev->pullup_gpio)) {
+ dev_dbg(&pdev->dev,
+ "can't get pullup gpio err: %ld\n",
+ PTR_ERR(dev->pullup_gpio));
+ retval = PTR_ERR(dev->pullup_gpio);
+ goto err;
}
timer_setup(&dev->timer, udc_watchdog, 0);
@@ -2439,7 +2424,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state)
struct pxa25x_udc *udc = platform_get_drvdata(dev);
unsigned long flags;
- if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command)
+ if (!udc->pullup_gpio)
WARNING("USB host won't detect disconnect!\n");
udc->suspended = 1;
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h
index 6ab6047edc834..3452cf54286c8 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.h
+++ b/drivers/usb/gadget/udc/pxa25x_udc.h
@@ -112,7 +112,7 @@ struct pxa25x_udc {
struct device *dev;
struct clk *clk;
- struct pxa2xx_udc_mach_info *mach;
+ struct gpio_desc *pullup_gpio;
struct usb_phy *transceiver;
u64 dma_mask;
struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS];
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index 1abea0d48c35d..640f81988c041 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -17,7 +17,6 @@
#include <linux/proc_fs.h>
#include <linux/clk.h>
#include <linux/irq.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
@@ -1423,14 +1422,7 @@ static const struct usb_ep_ops pxa_ep_ops = {
*/
static void dplus_pullup(struct pxa_udc *udc, int on)
{
- if (udc->gpiod) {
- gpiod_set_value(udc->gpiod, on);
- } else if (udc->udc_command) {
- if (on)
- udc->udc_command(PXA2XX_UDC_CMD_CONNECT);
- else
- udc->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
- }
+ gpiod_set_value(udc->gpiod, on);
udc->pullup_on = on;
}
@@ -1521,7 +1513,7 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
struct pxa_udc *udc = to_gadget_udc(_gadget);
int ret;
- if (!udc->gpiod && !udc->udc_command)
+ if (!udc->gpiod)
return -EOPNOTSUPP;
dplus_pullup(udc, is_active);
@@ -2380,26 +2372,11 @@ MODULE_DEVICE_TABLE(of, udc_pxa_dt_ids);
static int pxa_udc_probe(struct platform_device *pdev)
{
struct pxa_udc *udc = &memory;
- int retval = 0, gpio;
- struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev);
+ int retval = 0;
- if (mach) {
- gpio = mach->gpio_pullup;
- if (gpio_is_valid(gpio)) {
- retval = devm_gpio_request_one(&pdev->dev, gpio,
- GPIOF_OUT_INIT_LOW,
- "USB D+ pullup");
- if (retval)
- return retval;
- udc->gpiod = gpio_to_desc(mach->gpio_pullup);
-
- if (mach->gpio_pullup_inverted ^ gpiod_is_active_low(udc->gpiod))
- gpiod_toggle_active_low(udc->gpiod);
- }
- udc->udc_command = mach->udc_command;
- } else {
- udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS);
- }
+ udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS);
+ if (IS_ERR(udc->gpiod))
+ return PTR_ERR(udc->gpiod);
udc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(udc->regs))
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h
index 31bf79ce931c6..2c28b691010a4 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.h
+++ b/drivers/usb/gadget/udc/pxa27x_udc.h
@@ -426,7 +426,6 @@ struct udc_stats {
* @usb_gadget: udc gadget structure
* @driver: bound gadget (zero, g_ether, g_mass_storage, ...)
* @dev: device
- * @udc_command: machine specific function to activate D+ pullup
* @gpiod: gpio descriptor of gpio for D+ pullup (or NULL if none)
* @transceiver: external transceiver to handle vbus sense and D+ pullup
* @ep0state: control endpoint state machine state
@@ -452,7 +451,6 @@ struct pxa_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct device *dev;
- void (*udc_command)(int);
struct gpio_desc *gpiod;
struct usb_phy *transceiver;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 0a277a07cf706..b3b1ec696bf50 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -42,6 +42,12 @@ config USB_XHCI_PCI
depends on USB_PCI
default y
+config USB_XHCI_PCI_PROM21
+ tristate
+ depends on USB_XHCI_PCI
+ default USB_XHCI_PCI if SENSORS_PROM21_XHCI != n
+ select AUXILIARY_BUS
+
config USB_XHCI_PCI_RENESAS
tristate "Support for additional Renesas xHCI controller with firmware"
depends on USB_XHCI_PCI
@@ -71,7 +77,7 @@ config USB_XHCI_HISTB
config USB_XHCI_MTK
tristate "xHCI support for MediaTek SoCs"
select MFD_SYSCON
- depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || COMPILE_TEST
+ depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || ARCH_AIROHA || COMPILE_TEST
help
Say 'Y' to enable the support for the xHCI host controller
found in MediaTek SoCs.
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index a07e7ba9cd533..174580c1281a2 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_FHCI_HCD) += fhci.o
obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o
obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
+obj-$(CONFIG_USB_XHCI_PCI_PROM21) += xhci-pci-prom21.o
obj-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o
obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 0e17c988d36a2..73e76d0e69738 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -1685,6 +1685,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
case ClearHubFeature:
break;
case ClearPortFeature:
+ if (index != 1)
+ goto error;
switch (value) {
case USB_PORT_FEAT_SUSPEND:
break;
@@ -1694,6 +1696,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
!pdata->vbus_active_level);
fallthrough;
default:
+ if (value >= 32)
+ goto error;
max3421_hcd->port_status &= ~(1 << value);
}
break;
@@ -1726,6 +1730,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
break;
case SetPortFeature:
+ if (index != 1)
+ goto error;
switch (value) {
case USB_PORT_FEAT_LINK_STATE:
case USB_PORT_FEAT_U1_TIMEOUT:
@@ -1747,6 +1753,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
max3421_reset_port(hcd);
fallthrough;
default:
+ if (value >= 32)
+ goto error;
if ((max3421_hcd->port_status & USB_PORT_STAT_POWER)
!= 0)
max3421_hcd->port_status |= (1 << value);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index bacd0ddd0d09b..3830d41239612 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -226,9 +226,8 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf,
USB_SSP_SUBLINK_SPEED_ST_SYM_RX);
ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr);
- attr &= ~USB_SSP_SUBLINK_SPEED_ST;
- attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST,
- USB_SSP_SUBLINK_SPEED_ST_SYM_TX);
+ FIELD_MODIFY(USB_SSP_SUBLINK_SPEED_ST, &attr,
+ USB_SSP_SUBLINK_SPEED_ST_SYM_TX);
ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr);
break;
case PLT_ASYM_RX:
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 06043c7c31006..d9b865546a67b 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -185,9 +185,9 @@ static void xhci_mtk_rxfifo_depth_set(struct xhci_hcd_mtk *mtk)
return;
value = readl(hcd->regs + HSCH_CFG1);
- value &= ~SCH3_RXFIFO_DEPTH_MASK;
- value |= FIELD_PREP(SCH3_RXFIFO_DEPTH_MASK,
- SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1);
+ FIELD_MODIFY(SCH3_RXFIFO_DEPTH_MASK, &value,
+ SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1);
+
writel(value, hcd->regs + HSCH_CFG1);
}
diff --git a/drivers/usb/host/xhci-pci-prom21.c b/drivers/usb/host/xhci-pci-prom21.c
new file mode 100644
index 0000000000000..6486f4a093455
--- /dev/null
+++ b/drivers/usb/host/xhci-pci-prom21.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Promontory 21 xHCI host controller PCI Bus Glue.
+ *
+ * This does not add any PROM21-specific USB or xHCI operation. It exists only
+ * to publish an auxiliary device for integrated temperature sensor support.
+ *
+ * Copyright (C) 2026 Jihong Min <hurryman2212@gmail.com>
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device/devres.h>
+#include <linux/errno.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_data/usb-xhci-prom21.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "xhci-pci.h"
+
+struct prom21_xhci_auxdev {
+ struct auxiliary_device *auxdev;
+ struct prom21_xhci_pdata pdata;
+ int id;
+};
+
+static DEFINE_IDA(prom21_xhci_auxdev_ida);
+
+static void prom21_xhci_auxdev_release(struct device *dev, void *res)
+{
+ struct prom21_xhci_auxdev *prom21_auxdev = res;
+
+ auxiliary_device_destroy(prom21_auxdev->auxdev);
+ ida_free(&prom21_xhci_auxdev_ida, prom21_auxdev->id);
+}
+
+static int prom21_xhci_create_auxdev(struct pci_dev *pdev)
+{
+ struct prom21_xhci_auxdev *prom21_auxdev;
+ struct usb_hcd *hcd = pci_get_drvdata(pdev);
+ int ret;
+
+ prom21_auxdev = devres_alloc(prom21_xhci_auxdev_release,
+ sizeof(*prom21_auxdev), GFP_KERNEL);
+ if (!prom21_auxdev)
+ return -ENOMEM;
+
+ prom21_auxdev->pdata.pdev = pdev;
+ prom21_auxdev->pdata.regs = hcd->regs;
+ prom21_auxdev->pdata.rsrc_len = hcd->rsrc_len;
+
+ prom21_auxdev->id = ida_alloc(&prom21_xhci_auxdev_ida, GFP_KERNEL);
+ if (prom21_auxdev->id < 0) {
+ ret = prom21_auxdev->id;
+ goto err_free_devres;
+ }
+
+ prom21_auxdev->auxdev = auxiliary_device_create(&pdev->dev,
+ KBUILD_MODNAME, "hwmon",
+ &prom21_auxdev->pdata,
+ prom21_auxdev->id);
+ if (!prom21_auxdev->auxdev) {
+ ret = -ENOMEM;
+ goto err_free_ida;
+ }
+
+ devres_add(&pdev->dev, prom21_auxdev);
+ return 0;
+
+err_free_ida:
+ ida_free(&prom21_xhci_auxdev_ida, prom21_auxdev->id);
+err_free_devres:
+ devres_free(prom21_auxdev);
+ return ret;
+}
+
+static void prom21_xhci_destroy_auxdev(struct pci_dev *pdev)
+{
+ devres_release(&pdev->dev, prom21_xhci_auxdev_release, NULL, NULL);
+}
+
+static int prom21_xhci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ int retval;
+
+ retval = xhci_pci_common_probe(dev, id);
+ if (retval)
+ return retval;
+
+ retval = prom21_xhci_create_auxdev(dev);
+ if (retval) {
+ /*
+ * The auxiliary device only provides optional temperature sensor
+ * support. Keep the xHCI controller usable if it fails.
+ */
+ dev_err(&dev->dev,
+ "failed to create PROM21 hwmon auxiliary device: %d\n",
+ retval);
+ }
+
+ return 0;
+}
+
+static void prom21_xhci_remove(struct pci_dev *dev)
+{
+ prom21_xhci_destroy_auxdev(dev);
+ xhci_pci_remove(dev);
+}
+
+static const struct pci_device_id pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD) },
+ { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static struct pci_driver prom21_xhci_driver = {
+ .name = "xhci-pci-prom21",
+ .id_table = pci_ids,
+
+ .probe = prom21_xhci_probe,
+ .remove = prom21_xhci_remove,
+
+ .shutdown = usb_hcd_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&usb_hcd_pci_pm_ops),
+ },
+};
+module_pci_driver(prom21_xhci_driver);
+
+MODULE_AUTHOR("Jihong Min <hurryman2212@gmail.com>");
+MODULE_DESCRIPTION("AMD Promontory 21 xHCI PCI Host Controller Driver");
+MODULE_IMPORT_NS("xhci");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 585b2f3117b08..039c26b241d08 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -696,12 +696,23 @@ static const struct pci_device_id pci_ids_renesas[] = {
{ /* end: all zeroes */ }
};
+/* handled by xhci-pci-prom21 if enabled */
+static const struct pci_device_id pci_ids_prom21[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD) },
+ { /* end: all zeroes */ }
+};
+
static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS) &&
pci_match_id(pci_ids_renesas, dev))
return -ENODEV;
+ if (IS_ENABLED(CONFIG_USB_XHCI_PCI_PROM21) &&
+ pci_match_id(pci_ids_prom21, dev))
+ return -ENODEV;
+
return xhci_pci_common_probe(dev, id);
}
diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h
index e87c7d9d76b8e..11f435f94322a 100644
--- a/drivers/usb/host/xhci-pci.h
+++ b/drivers/usb/host/xhci-pci.h
@@ -4,6 +4,9 @@
#ifndef XHCI_PCI_H
#define XHCI_PCI_H
+#define PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC 0x43fc
+#define PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD 0x43fd
+
int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id);
void xhci_pci_remove(struct pci_dev *dev);
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 0b56b773dbdf7..91b62acb537b3 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -145,7 +145,7 @@ config USB_APPLEDISPLAY
Displays over USB. This driver provides a sysfs interface.
config USB_QCOM_EUD
- tristate "QCOM Embedded USB Debugger(EUD) Driver"
+ tristate "Qualcomm Embedded USB Debugger(EUD) Driver"
depends on ARCH_QCOM || COMPILE_TEST
select QCOM_SCM
select USB_ROLE_SWITCH
diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c
index 7cdbdfe07a763..11508ed4df251 100644
--- a/drivers/usb/misc/onboard_usb_dev.c
+++ b/drivers/usb/misc/onboard_usb_dev.c
@@ -665,6 +665,7 @@ static const struct usb_device_id onboard_dev_id_table[] = {
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 HUB */
+ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0625) }, /* Genesys Logic GL3590 USB 3.2 HUB */
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2412) }, /* USB2412 USB 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 HUB */
diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h
index ac1aa3e122ad9..3523f8f8a1496 100644
--- a/drivers/usb/misc/onboard_usb_dev.h
+++ b/drivers/usb/misc/onboard_usb_dev.h
@@ -101,6 +101,13 @@ static const struct onboard_dev_pdata genesys_gl852g_data = {
.is_hub = true,
};
+static const struct onboard_dev_pdata genesys_gl3590_data = {
+ .reset_us = 50,
+ .num_supplies = 2,
+ .supply_names = { "vdd", "vdd12" },
+ .is_hub = true,
+};
+
static const struct onboard_dev_pdata usb_a_conn_data = {
.num_supplies = 1,
.supply_names = { "vbus" },
@@ -146,6 +153,7 @@ static const struct of_device_id onboard_dev_match[] = {
{ .compatible = "usb5e3,608", .data = &genesys_gl850g_data, },
{ .compatible = "usb5e3,610", .data = &genesys_gl852g_data, },
{ .compatible = "usb5e3,620", .data = &genesys_gl852g_data, },
+ { .compatible = "usb5e3,625", .data = &genesys_gl3590_data, },
{ .compatible = "usb5e3,626", .data = &genesys_gl852g_data, },
{ .compatible = "usbbda,179", .data = &realtek_rtl8188etv_data, },
{ .compatible = "usbbda,411", .data = &realtek_rts5411_data, },
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index 7c0778631bea8..fb0693742f018 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -746,15 +746,15 @@ static int usb251xb_i2c_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(usb251xb_i2c_pm_ops, usb251xb_i2c_suspend, usb251xb_i2c_resume);
static const struct i2c_device_id usb251xb_id[] = {
- { "usb2422" },
- { "usb2512b" },
- { "usb2512bi" },
- { "usb2513b" },
- { "usb2513bi" },
- { "usb2514b" },
- { "usb2514bi" },
- { "usb2517" },
- { "usb2517i" },
+ { .name = "usb2422" },
+ { .name = "usb2512b" },
+ { .name = "usb2512bi" },
+ { .name = "usb2513b" },
+ { .name = "usb2513bi" },
+ { .name = "usb2514b" },
+ { .name = "usb2514bi" },
+ { .name = "usb2517" },
+ { .name = "usb2517i" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, usb251xb_id);
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
index 322e59381b783..759770a132606 100644
--- a/drivers/usb/misc/usb3503.c
+++ b/drivers/usb/misc/usb3503.c
@@ -390,7 +390,7 @@ static SIMPLE_DEV_PM_OPS(usb3503_platform_pm_ops, usb3503_platform_suspend,
usb3503_platform_resume);
static const struct i2c_device_id usb3503_id[] = {
- { USB3503_I2C_NAME },
+ { .name = USB3503_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, usb3503_id);
diff --git a/drivers/usb/misc/usb4604.c b/drivers/usb/misc/usb4604.c
index c9a2fb3518ae4..2ae9656715e01 100644
--- a/drivers/usb/misc/usb4604.c
+++ b/drivers/usb/misc/usb4604.c
@@ -135,7 +135,7 @@ static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
usb4604_i2c_resume);
static const struct i2c_device_id usb4604_id[] = {
- { "usb4604" },
+ { .name = "usb4604" },
{ }
};
MODULE_DEVICE_TABLE(i2c, usb4604_id);
diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c
index 2940f0c84e1b7..73cc70e958dea 100644
--- a/drivers/usb/phy/phy-isp1301.c
+++ b/drivers/usb/phy/phy-isp1301.c
@@ -25,7 +25,7 @@ struct isp1301 {
#define phy_to_isp(p) (container_of((p), struct isp1301, phy))
static const struct i2c_device_id isp1301_id[] = {
- { "isp1301" },
+ { .name = "isp1301" },
{ }
};
MODULE_DEVICE_TABLE(i2c, isp1301_id);
diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c
index 2e8ae1d2faf98..604868ebf4221 100644
--- a/drivers/usb/typec/anx7411.c
+++ b/drivers/usb/typec/anx7411.c
@@ -1577,8 +1577,8 @@ static void anx7411_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id anx7411_id[] = {
- { "anx7411" },
- {}
+ { .name = "anx7411" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, anx7411_id);
diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c
index c54e42c7e6a16..bea0c1deec94d 100644
--- a/drivers/usb/typec/mux/fsa4480.c
+++ b/drivers/usb/typec/mux/fsa4480.c
@@ -336,7 +336,7 @@ static void fsa4480_remove(struct i2c_client *client)
}
static const struct i2c_device_id fsa4480_table[] = {
- { "fsa4480" },
+ { .name = "fsa4480" },
{ }
};
MODULE_DEVICE_TABLE(i2c, fsa4480_table);
diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c
index 1698428654abb..219a32da13480 100644
--- a/drivers/usb/typec/mux/intel_pmc_mux.c
+++ b/drivers/usb/typec/mux/intel_pmc_mux.c
@@ -151,13 +151,14 @@ struct pmc_usb {
u8 num_ports;
struct device *dev;
struct intel_scu_ipc_dev *ipc;
- struct pmc_usb_port *port;
struct acpi_device *iom_adev;
void __iomem *iom_base;
u32 iom_port_status_offset;
u8 iom_port_status_size;
struct dentry *dentry;
+
+ struct pmc_usb_port port[] __counted_by(num_ports);
};
static struct dentry *pmc_mux_debugfs_root;
@@ -731,27 +732,25 @@ static int pmc_usb_probe(struct platform_device *pdev)
{
struct fwnode_handle *fwnode = NULL;
struct pmc_usb *pmc;
+ u8 num_ports = 0;
int i = 0;
int ret;
- pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
- if (!pmc)
- return -ENOMEM;
-
device_for_each_child_node(&pdev->dev, fwnode)
- pmc->num_ports++;
+ num_ports++;
/* The IOM microcontroller has a limitation of max 4 ports. */
- if (pmc->num_ports > 4) {
+ if (num_ports > 4) {
dev_err(&pdev->dev, "driver limited to 4 ports\n");
return -ERANGE;
}
- pmc->port = devm_kcalloc(&pdev->dev, pmc->num_ports,
- sizeof(struct pmc_usb_port), GFP_KERNEL);
- if (!pmc->port)
+ pmc = devm_kzalloc(&pdev->dev, struct_size(pmc, port, num_ports), GFP_KERNEL);
+ if (!pmc)
return -ENOMEM;
+ pmc->num_ports = num_ports;
+
pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev);
if (!pmc->ipc)
return -EPROBE_DEFER;
diff --git a/drivers/usb/typec/mux/it5205.c b/drivers/usb/typec/mux/it5205.c
index 4357cc67a8672..5e1a120b2e3b4 100644
--- a/drivers/usb/typec/mux/it5205.c
+++ b/drivers/usb/typec/mux/it5205.c
@@ -266,7 +266,7 @@ static void it5205_remove(struct i2c_client *client)
}
static const struct i2c_device_id it5205_table[] = {
- { "it5205" },
+ { .name = "it5205" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, it5205_table);
diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c
index b57b6c9c40fe7..d1fa26ff442c2 100644
--- a/drivers/usb/typec/mux/nb7vpq904m.c
+++ b/drivers/usb/typec/mux/nb7vpq904m.c
@@ -499,7 +499,7 @@ static void nb7vpq904m_remove(struct i2c_client *client)
}
static const struct i2c_device_id nb7vpq904m_table[] = {
- { "nb7vpq904m" },
+ { .name = "nb7vpq904m" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nb7vpq904m_table);
diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c
index 8eeec135dcdbd..985683fe49e91 100644
--- a/drivers/usb/typec/mux/pi3usb30532.c
+++ b/drivers/usb/typec/mux/pi3usb30532.c
@@ -169,7 +169,7 @@ static void pi3usb30532_remove(struct i2c_client *client)
}
static const struct i2c_device_id pi3usb30532_table[] = {
- { "pi3usb30532" },
+ { .name = "pi3usb30532" },
{ }
};
MODULE_DEVICE_TABLE(i2c, pi3usb30532_table);
diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c
index 1256252eceedc..f52443638ee28 100644
--- a/drivers/usb/typec/mux/ps883x.c
+++ b/drivers/usb/typec/mux/ps883x.c
@@ -61,19 +61,110 @@ struct ps883x_retimer {
struct mutex lock; /* protect non-concurrent retimer & switch */
enum typec_orientation orientation;
- u8 cfg0;
- u8 cfg1;
- u8 cfg2;
+ bool in_reset;
};
+static int ps883x_enable_vregs(struct ps883x_retimer *retimer)
+{
+ struct device *dev = &retimer->client->dev;
+ int ret;
+
+ ret = regulator_enable(retimer->vdd33_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD 3.3V regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(retimer->vdd33_cap_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD 3.3V CAP regulator: %d\n", ret);
+ goto err_vdd33_disable;
+ }
+
+ usleep_range(4000, 10000);
+
+ ret = regulator_enable(retimer->vdd_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD regulator: %d\n", ret);
+ goto err_vdd33_cap_disable;
+ }
+
+ ret = regulator_enable(retimer->vddar_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD AR regulator: %d\n", ret);
+ goto err_vdd_disable;
+ }
+
+ ret = regulator_enable(retimer->vddat_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD AT regulator: %d\n", ret);
+ goto err_vddar_disable;
+ }
+
+ ret = regulator_enable(retimer->vddio_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD IO regulator: %d\n", ret);
+ goto err_vddat_disable;
+ }
+
+ return 0;
+
+err_vddat_disable:
+ regulator_disable(retimer->vddat_supply);
+err_vddar_disable:
+ regulator_disable(retimer->vddar_supply);
+err_vdd_disable:
+ regulator_disable(retimer->vdd_supply);
+err_vdd33_cap_disable:
+ regulator_disable(retimer->vdd33_cap_supply);
+err_vdd33_disable:
+ regulator_disable(retimer->vdd33_supply);
+
+ return ret;
+}
+
+static void ps883x_disable_vregs(struct ps883x_retimer *retimer)
+{
+ regulator_disable(retimer->vddio_supply);
+ regulator_disable(retimer->vddat_supply);
+ regulator_disable(retimer->vddar_supply);
+ regulator_disable(retimer->vdd_supply);
+ regulator_disable(retimer->vdd33_cap_supply);
+ regulator_disable(retimer->vdd33_supply);
+}
+
+static void ps883x_reset(struct ps883x_retimer *retimer)
+{
+ if (retimer->in_reset)
+ return;
+
+ gpiod_set_value(retimer->reset_gpio, 1);
+ ps883x_disable_vregs(retimer);
+ retimer->in_reset = true;
+}
+
static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0,
- int cfg1, int cfg2)
+ int cfg1, int cfg2, bool reset)
{
struct device *dev = &retimer->client->dev;
int ret;
- if (retimer->cfg0 == cfg0 && retimer->cfg1 == cfg1 && retimer->cfg2 == cfg2)
+ if (reset) {
+ ps883x_reset(retimer);
+
return 0;
+ } else if (retimer->in_reset) {
+ ret = ps883x_enable_vregs(retimer);
+ if (ret)
+ return ret;
+
+ gpiod_set_value(retimer->reset_gpio, 0);
+
+ /* firmware initialization delay */
+ msleep(60);
+
+ retimer->in_reset = false;
+ }
ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0);
if (ret) {
@@ -93,10 +184,6 @@ static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0,
return ret;
}
- retimer->cfg0 = cfg0;
- retimer->cfg1 = cfg1;
- retimer->cfg2 = cfg2;
-
return 0;
}
@@ -107,6 +194,7 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state
int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT;
int cfg1 = 0x00;
int cfg2 = 0x00;
+ bool reset = false;
if (retimer->orientation == TYPEC_ORIENTATION_REVERSE)
cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED;
@@ -148,9 +236,13 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state
}
} else {
switch (state->mode) {
+ /* SAFE can be transient or point to an actual disconnect */
case TYPEC_STATE_SAFE:
+ reset = retimer->orientation == TYPEC_ORIENTATION_NONE;
+ break;
/* USB2 pins don't even go through this chip */
case TYPEC_MODE_USB2:
+ reset = true;
break;
case TYPEC_STATE_USB:
case TYPEC_MODE_USB3:
@@ -171,7 +263,7 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state
}
}
- return ps883x_configure(retimer, cfg0, cfg1, cfg2);
+ return ps883x_configure(retimer, cfg0, cfg1, cfg2, reset);
}
static int ps883x_sw_set(struct typec_switch_dev *sw,
@@ -184,11 +276,19 @@ static int ps883x_sw_set(struct typec_switch_dev *sw,
if (ret)
return ret;
- mutex_lock(&retimer->lock);
+ guard(mutex)(&retimer->lock);
if (retimer->orientation != orientation) {
retimer->orientation = orientation;
+ /*
+ * Orientation notifications usually come prior to mode switch
+ * events. If the retimer is already in reset, we still want to
+ * cache the new orientation value for the subsequent ps883x_set().
+ */
+ if (retimer->in_reset)
+ return 0;
+
ret = regmap_assign_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0,
CONN_STATUS_0_ORIENTATION_REVERSED,
orientation == TYPEC_ORIENTATION_REVERSE);
@@ -196,8 +296,6 @@ static int ps883x_sw_set(struct typec_switch_dev *sw,
dev_err(&retimer->client->dev, "failed to set orientation: %d\n", ret);
}
- mutex_unlock(&retimer->lock);
-
return ret;
}
@@ -222,75 +320,6 @@ static int ps883x_retimer_set(struct typec_retimer *rtmr,
return typec_mux_set(retimer->typec_mux, &mux_state);
}
-static int ps883x_enable_vregs(struct ps883x_retimer *retimer)
-{
- struct device *dev = &retimer->client->dev;
- int ret;
-
- ret = regulator_enable(retimer->vdd33_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD 3.3V regulator: %d\n", ret);
- return ret;
- }
-
- ret = regulator_enable(retimer->vdd33_cap_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD 3.3V CAP regulator: %d\n", ret);
- goto err_vdd33_disable;
- }
-
- usleep_range(4000, 10000);
-
- ret = regulator_enable(retimer->vdd_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD regulator: %d\n", ret);
- goto err_vdd33_cap_disable;
- }
-
- ret = regulator_enable(retimer->vddar_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD AR regulator: %d\n", ret);
- goto err_vdd_disable;
- }
-
- ret = regulator_enable(retimer->vddat_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD AT regulator: %d\n", ret);
- goto err_vddar_disable;
- }
-
- ret = regulator_enable(retimer->vddio_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD IO regulator: %d\n", ret);
- goto err_vddat_disable;
- }
-
- return 0;
-
-err_vddat_disable:
- regulator_disable(retimer->vddat_supply);
-err_vddar_disable:
- regulator_disable(retimer->vddar_supply);
-err_vdd_disable:
- regulator_disable(retimer->vdd_supply);
-err_vdd33_cap_disable:
- regulator_disable(retimer->vdd33_cap_supply);
-err_vdd33_disable:
- regulator_disable(retimer->vdd33_supply);
-
- return ret;
-}
-
-static void ps883x_disable_vregs(struct ps883x_retimer *retimer)
-{
- regulator_disable(retimer->vddio_supply);
- regulator_disable(retimer->vddat_supply);
- regulator_disable(retimer->vddar_supply);
- regulator_disable(retimer->vdd_supply);
- regulator_disable(retimer->vdd33_cap_supply);
- regulator_disable(retimer->vdd33_supply);
-}
-
static int ps883x_get_vregs(struct ps883x_retimer *retimer)
{
struct device *dev = &retimer->client->dev;
@@ -422,6 +451,9 @@ static int ps883x_retimer_probe(struct i2c_client *client)
}
}
+ /* Keep the retimer in reset until a Type-C notification comes */
+ ps883x_reset(retimer);
+
sw_desc.drvdata = retimer;
sw_desc.fwnode = dev_fwnode(dev);
sw_desc.set = ps883x_sw_set;
diff --git a/drivers/usb/typec/mux/ptn36502.c b/drivers/usb/typec/mux/ptn36502.c
index 129d9d24b9323..afd16775dbafa 100644
--- a/drivers/usb/typec/mux/ptn36502.c
+++ b/drivers/usb/typec/mux/ptn36502.c
@@ -404,7 +404,7 @@ static void ptn36502_remove(struct i2c_client *client)
}
static const struct i2c_device_id ptn36502_table[] = {
- { "ptn36502" },
+ { .name = "ptn36502" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ptn36502_table);
diff --git a/drivers/usb/typec/mux/wcd939x-usbss.c b/drivers/usb/typec/mux/wcd939x-usbss.c
index d46c353dfaf2f..73db3aa3cec4e 100644
--- a/drivers/usb/typec/mux/wcd939x-usbss.c
+++ b/drivers/usb/typec/mux/wcd939x-usbss.c
@@ -753,7 +753,7 @@ static void wcd939x_usbss_remove(struct i2c_client *client)
}
static const struct i2c_device_id wcd939x_usbss_table[] = {
- { "wcd9390-usbss" },
+ { .name = "wcd9390-usbss" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wcd939x_usbss_table);
diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index 9ab1277b7ed1e..3319f6a2b0c9b 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -1841,8 +1841,8 @@ static const struct of_device_id fusb302_dt_match[] __maybe_unused = {
MODULE_DEVICE_TABLE(of, fusb302_dt_match);
static const struct i2c_device_id fusb302_i2c_device_id[] = {
- { "typec_fusb302" },
- {}
+ { .name = "typec_fusb302" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, fusb302_i2c_device_id);
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
index 3766790c1548c..35320f89dad2d 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
@@ -4,19 +4,14 @@
*/
#include <linux/err.h>
-#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
-#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-#include <linux/usb/role.h>
#include <linux/usb/tcpm.h>
-#include <linux/usb/typec_mux.h>
#include <drm/bridge/aux-bridge.h>
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
index 8051eaa469913..429bd42a0e628 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -704,6 +705,7 @@ int qcom_pmic_typec_port_probe(struct platform_device *pdev,
struct device *dev = &pdev->dev;
struct pmic_typec_port_irq_data *irq_data;
struct pmic_typec_port *pmic_typec_port;
+ struct fwnode_handle *connector;
int i, ret, irq;
pmic_typec_port = devm_kzalloc(dev, sizeof(*pmic_typec_port), GFP_KERNEL);
@@ -720,7 +722,15 @@ int qcom_pmic_typec_port_probe(struct platform_device *pdev,
mutex_init(&pmic_typec_port->vbus_lock);
- pmic_typec_port->vdd_vbus = devm_regulator_get(dev, "vdd-vbus");
+ connector = device_get_named_child_node(dev, "connector");
+ if (!connector)
+ return -EINVAL;
+
+ pmic_typec_port->vdd_vbus = devm_of_regulator_get_optional(dev,
+ to_of_node(connector),
+ "vbus");
+ if (pmic_typec_port->vdd_vbus == ERR_PTR(-ENODEV))
+ pmic_typec_port->vdd_vbus = devm_regulator_get(dev, "vdd-vbus");
if (IS_ERR(pmic_typec_port->vdd_vbus))
return PTR_ERR(pmic_typec_port->vdd_vbus);
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 0148b8f50412b..7ac7000b2d139 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -141,13 +141,10 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
}
if (vconn_pres) {
- if (polarity == TYPEC_POLARITY_CC2) {
- reg &= ~TCPC_ROLE_CTRL_CC1;
- reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN);
- } else {
- reg &= ~TCPC_ROLE_CTRL_CC2;
- reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN);
- }
+ if (polarity == TYPEC_POLARITY_CC2)
+ FIELD_MODIFY(TCPC_ROLE_CTRL_CC1, &reg, TCPC_ROLE_CTRL_CC_OPEN);
+ else
+ FIELD_MODIFY(TCPC_ROLE_CTRL_CC2, &reg, TCPC_ROLE_CTRL_CC_OPEN);
}
ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
@@ -1020,7 +1017,7 @@ static int tcpci_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(tcpci_pm_ops, tcpci_suspend, tcpci_resume);
static const struct i2c_device_id tcpci_id[] = {
- { "tcpci" },
+ { .name = "tcpci" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tcpci_id);
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
index 7324139d51c8e..998693a618393 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
@@ -579,7 +579,7 @@ static int max_tcpci_suspend(struct device *dev)
static SIMPLE_DEV_PM_OPS(max_tcpci_pm_ops, max_tcpci_suspend, max_tcpci_resume);
static const struct i2c_device_id max_tcpci_id[] = {
- { "maxtcpc" },
+ { .name = "maxtcpc" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max_tcpci_id);
diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
index 4b3e4e22a82e8..a8726da6fc713 100644
--- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c
+++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
@@ -373,10 +373,10 @@ static const struct rt1711h_chip_info rt1715 = {
};
static const struct i2c_device_id rt1711h_id[] = {
- { "et7304", (kernel_ulong_t)&rt1715 },
- { "rt1711h", (kernel_ulong_t)&rt1711h },
- { "rt1715", (kernel_ulong_t)&rt1715 },
- {}
+ { .name = "et7304", .driver_data = (kernel_ulong_t)&rt1715 },
+ { .name = "rt1711h", .driver_data = (kernel_ulong_t)&rt1711h },
+ { .name = "rt1715", .driver_data = (kernel_ulong_t)&rt1715 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rt1711h_id);
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index d0b769333bd99..d5ee0af9058b9 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -2028,7 +2028,7 @@ static const struct of_device_id tps6598x_of_match[] = {
MODULE_DEVICE_TABLE(of, tps6598x_of_match);
static const struct i2c_device_id tps6598x_id[] = {
- { "tps6598x", (kernel_ulong_t)&tps6598x_data },
+ { .name = "tps6598x", .driver_data = (kernel_ulong_t)&tps6598x_data },
{ }
};
MODULE_DEVICE_TABLE(i2c, tps6598x_id);
diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c
index 251aa7251ce6a..c192d42d449ee 100644
--- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c
+++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c
@@ -114,12 +114,14 @@ static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd)
}
static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci,
- void *data, size_t size)
+ void *data, size_t size, void *msg_out,
+ size_t msg_out_size)
{
struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi);
int ret;
- ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size);
+ ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size, msg_out,
+ msg_out_size);
switch (ret) {
case -EBUSY:
/* EC may return -EBUSY if CCI.busy is set.
diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c
index a4b9a6b51649d..ff33a5e7c6b0d 100644
--- a/drivers/usb/typec/ucsi/debugfs.c
+++ b/drivers/usb/typec/ucsi/debugfs.c
@@ -40,6 +40,11 @@ static int ucsi_cmd(void *data, u64 val)
case UCSI_READ_POWER_LEVEL:
ret = ucsi_send_command(ucsi, val, NULL, 0);
break;
+ case UCSI_SET_PDOS:
+ ret = ucsi_write_message_out_command(ucsi, val, NULL, 0,
+ ucsi->debugfs->message_out,
+ UCSI_COMMAND_DATA_LEN(val));
+ break;
case UCSI_GET_CAPABILITY:
case UCSI_GET_CONNECTOR_CAPABILITY:
case UCSI_GET_ALTERNATE_MODES:
@@ -110,6 +115,30 @@ static int ucsi_vbus_volt_show(struct seq_file *m, void *v)
}
DEFINE_SHOW_ATTRIBUTE(ucsi_vbus_volt);
+static ssize_t ucsi_message_out_write(struct file *file,
+ const char __user *data, size_t count, loff_t *ppos)
+{
+ struct ucsi *ucsi = file->private_data;
+ int ret;
+
+ char *buf __free(kfree) = memdup_user_nul(data, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ ret = hex2bin(ucsi->debugfs->message_out, buf,
+ min(count / 2, sizeof(ucsi->debugfs->message_out)));
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations ucsi_message_out_fops = {
+ .open = simple_open,
+ .write = ucsi_message_out_write,
+ .llseek = generic_file_llseek,
+};
+
void ucsi_debugfs_register(struct ucsi *ucsi)
{
ucsi->debugfs = kzalloc_obj(*ucsi->debugfs);
@@ -122,6 +151,8 @@ void ucsi_debugfs_register(struct ucsi *ucsi)
debugfs_create_file("peak_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_peak_curr_fops);
debugfs_create_file("avg_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_avg_curr_fops);
debugfs_create_file("vbus_voltage", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_vbus_volt_fops);
+ debugfs_create_file("message_out", 0200, ucsi->debugfs->dentry, ucsi,
+ &ucsi_message_out_fops);
}
void ucsi_debugfs_unregister(struct ucsi *ucsi)
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 61cb24ed820f8..92166a3725b16 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -63,7 +63,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
EXPORT_SYMBOL_GPL(ucsi_notify_common);
int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
- void *data, size_t size)
+ void *data, size_t size, void *msg_out,
+ size_t msg_out_size)
{
bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI;
int ret;
@@ -75,6 +76,17 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
reinit_completion(&ucsi->complete);
+ if (msg_out && msg_out_size) {
+ if (!ucsi->ops->write_message_out) {
+ ret = -EOPNOTSUPP;
+ goto out_clear_bit;
+ }
+
+ ret = ucsi->ops->write_message_out(ucsi, msg_out, msg_out_size);
+ if (ret)
+ goto out_clear_bit;
+ }
+
ret = ucsi->ops->async_control(ucsi, command);
if (ret)
goto out_clear_bit;
@@ -110,11 +122,12 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack)
ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
}
- return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0);
+ return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0, NULL, 0);
}
static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
- void *data, size_t size, bool conn_ack)
+ void *data, size_t size, void *msg_out,
+ size_t msg_out_size, bool conn_ack)
{
int ret, err;
@@ -123,10 +136,12 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
if (size > UCSI_MAX_DATA_LENGTH(ucsi))
return -EINVAL;
- ret = ucsi->ops->sync_control(ucsi, command, cci, data, size);
+ ret = ucsi->ops->sync_control(ucsi, command, cci, data, size,
+ msg_out, msg_out_size);
if (*cci & UCSI_CCI_BUSY)
- return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
+ return ucsi_run_command(ucsi, UCSI_CANCEL, cci,
+ NULL, 0, NULL, 0, false) ?: -EBUSY;
if (ret)
return ret;
@@ -158,7 +173,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
int ret;
command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num);
- ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false);
+ ret = ucsi_run_command(ucsi, command, &cci, &error,
+ sizeof(error), NULL, 0, false);
if (ret < 0)
return ret;
@@ -208,7 +224,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
}
static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
- void *data, size_t size, bool conn_ack)
+ void *data, size_t size, void *msg_out,
+ size_t msg_out_size, bool conn_ack)
{
u8 connector_num;
u32 cci;
@@ -236,7 +253,8 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
mutex_lock(&ucsi->ppm_lock);
- ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack);
+ ret = ucsi_run_command(ucsi, cmd, &cci, data, size,
+ msg_out, msg_out_size, conn_ack);
if (cci & UCSI_CCI_ERROR)
ret = ucsi_read_error(ucsi, connector_num);
@@ -250,10 +268,23 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
int ucsi_send_command(struct ucsi *ucsi, u64 command,
void *data, size_t size)
{
- return ucsi_send_command_common(ucsi, command, data, size, false);
+ return ucsi_send_command_common(ucsi, command, data,
+ size, NULL, 0, false);
}
EXPORT_SYMBOL_GPL(ucsi_send_command);
+int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command,
+ void *data, size_t size, void *msg_out,
+ size_t msg_out_size)
+{
+ if (msg_out_size > UCSI_MAX_MSG_OUT_DATA_LEN(ucsi))
+ return -EINVAL;
+
+ return ucsi_send_command_common(ucsi, command, data,
+ size, msg_out, msg_out_size, false);
+}
+EXPORT_SYMBOL_GPL(ucsi_write_message_out_command);
+
/* -------------------------------------------------------------------------- */
struct ucsi_work {
@@ -681,7 +712,8 @@ static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack)
UCSI_MAX_DATA_LENGTH(con->ucsi));
int ret;
- ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack);
+ ret = ucsi_send_command_common(con->ucsi, command, &con->status, size,
+ NULL, 0, conn_ack);
return ret < 0 ? ret : 0;
}
@@ -1658,6 +1690,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
INIT_WORK(&con->work, ucsi_handle_connector_change);
init_completion(&con->complete);
mutex_init(&con->lock);
+ lockdep_set_class(&con->lock, &con->lock_key);
INIT_LIST_HEAD(&con->partner_tasks);
con->ucsi = ucsi;
@@ -1903,6 +1936,9 @@ static int ucsi_init(struct ucsi *ucsi)
goto err_reset;
}
+ for (i = 0; i < ucsi->cap.num_connectors; i++)
+ lockdep_register_key(&connector[i].lock_key);
+
/* Register all connectors */
for (i = 0; i < ucsi->cap.num_connectors; i++) {
connector[i].num = i + 1;
@@ -1932,6 +1968,9 @@ static int ucsi_init(struct ucsi *ucsi)
return 0;
err_unregister:
+ for (i = 0; i < ucsi->cap.num_connectors; i++)
+ lockdep_unregister_key(&connector[i].lock_key);
+
for (con = connector; con->port; con++) {
if (con->wq)
destroy_workqueue(con->wq);
@@ -2182,6 +2221,7 @@ void ucsi_unregister(struct ucsi *ucsi)
usb_power_delivery_unregister(ucsi->connector[i].pd);
ucsi->connector[i].pd = NULL;
typec_unregister_port(ucsi->connector[i].port);
+ lockdep_unregister_key(&ucsi->connector[i].lock_key);
}
kfree(ucsi->connector);
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index cff9ddc2ae215..325ed1e5ca804 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -65,6 +65,7 @@ struct dentry;
* @read_cci: Read CCI register
* @poll_cci: Read CCI register while polling with notifications disabled
* @read_message_in: Read message data from UCSI
+ * @write_message_out: Write message data to UCSI
* @sync_control: Blocking control operation
* @async_control: Non-blocking control operation
* @update_altmodes: Squashes duplicate DP altmodes
@@ -82,8 +83,9 @@ struct ucsi_operations {
int (*read_cci)(struct ucsi *ucsi, u32 *cci);
int (*poll_cci)(struct ucsi *ucsi, u32 *cci);
int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len);
+ int (*write_message_out)(struct ucsi *ucsi, void *data, size_t data_len);
int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci,
- void *data, size_t size);
+ void *data, size_t size, void *msg_out, size_t msg_out_size);
int (*async_control)(struct ucsi *ucsi, u64 command);
bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient,
struct ucsi_altmode *orig,
@@ -136,6 +138,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_GET_PD_MESSAGE 0x15
#define UCSI_GET_CAM_CS 0x18
#define UCSI_SET_SINK_PATH 0x1c
+#define UCSI_SET_PDOS 0x1d
#define UCSI_READ_POWER_LEVEL 0x1e
#define UCSI_SET_USB 0x21
#define UCSI_GET_LPM_PPM_INFO 0x22
@@ -213,6 +216,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_GET_PD_MESSAGE_TYPE_IDENTITY 4
#define UCSI_GET_PD_MESSAGE_TYPE_REVISION 5
+/* Data length bits */
+#define UCSI_COMMAND_DATA_LEN(_cmd_) (((_cmd_) >> 8) & GENMASK(7, 0))
+
/* -------------------------------------------------------------------------- */
/* Error information returned by PPM in response to GET_ERROR_STATUS command. */
@@ -453,6 +459,8 @@ struct ucsi_bitfield {
/* -------------------------------------------------------------------------- */
+#define MESSAGE_OUT_MAX_LEN 256
+
struct ucsi_debugfs_entry {
u64 command;
struct ucsi_data {
@@ -460,6 +468,7 @@ struct ucsi_debugfs_entry {
u64 high;
} response;
int status;
+ u8 message_out[MESSAGE_OUT_MAX_LEN];
struct dentry *dentry;
};
@@ -503,6 +512,9 @@ struct ucsi {
};
#define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff)
+#define UCSI_MAX_MSG_OUT_DATA_LEN(u) \
+ (((u)->version >= UCSI_VERSION_3_0) ? 255 : \
+ (((u)->version >= UCSI_VERSION_2_0) ? 256 : 16))
#define UCSI_MAX_SVID 5
#define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6)
@@ -517,6 +529,7 @@ struct ucsi_connector {
struct ucsi *ucsi;
struct mutex lock; /* port lock */
+ struct lock_class_key lock_key;
struct work_struct work;
struct completion complete;
struct workqueue_struct *wq;
@@ -564,13 +577,17 @@ struct ucsi_connector {
int ucsi_send_command(struct ucsi *ucsi, u64 command,
void *retval, size_t size);
+int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command,
+ void *retval, size_t size,
+ void *msg_out, size_t msg_out_size);
void ucsi_altmode_update_active(struct ucsi_connector *con);
int ucsi_resume(struct ucsi *ucsi);
void ucsi_notify_common(struct ucsi *ucsi, u32 cci);
int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
- void *data, size_t size);
+ void *data, size_t size, void *msg_out,
+ size_t msg_out_size);
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
int ucsi_register_port_psy(struct ucsi_connector *con);
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index 6b92f296e9850..60b12961e1a47 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -86,6 +86,21 @@ static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_le
return 0;
}
+static int ucsi_acpi_write_message_out(struct ucsi *ucsi, void *data, size_t data_len)
+{
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+
+ if (!data || !data_len)
+ return -EINVAL;
+
+ if (ucsi->version <= UCSI_VERSION_1_2)
+ memcpy(ua->base + UCSI_MESSAGE_OUT, data, data_len);
+ else
+ memcpy(ua->base + UCSIv2_MESSAGE_OUT, data, data_len);
+
+ return 0;
+}
+
static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
@@ -101,19 +116,22 @@ static const struct ucsi_operations ucsi_acpi_ops = {
.read_cci = ucsi_acpi_read_cci,
.poll_cci = ucsi_acpi_poll_cci,
.read_message_in = ucsi_acpi_read_message_in,
+ .write_message_out = ucsi_acpi_write_message_out,
.sync_control = ucsi_sync_control_common,
.async_control = ucsi_acpi_async_control
};
static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
- void *val, size_t len)
+ void *val, size_t len, void *msg_out,
+ size_t msg_out_size)
{
u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE |
UCSI_CONSTAT_PDOS_CHANGE;
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
- ret = ucsi_sync_control_common(ucsi, command, cci, val, len);
+ ret = ucsi_sync_control_common(ucsi, command, cci, val, len,
+ msg_out, msg_out_size);
if (ret < 0)
return ret;
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index 4463c1ae96bd4..d46ca942026e5 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -608,7 +608,8 @@ static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command)
}
static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
- void *data, size_t size)
+ void *data, size_t size, void *msg_out,
+ size_t msg_out_size)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
struct ucsi_connector *con;
@@ -630,7 +631,8 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
ucsi_ccg_update_set_new_cam_cmd(uc, con, &command);
}
- ret = ucsi_sync_control_common(ucsi, command, cci, data, size);
+ ret = ucsi_sync_control_common(ucsi, command, cci, data, size,
+ msg_out, msg_out_size);
switch (UCSI_COMMAND(command)) {
case UCSI_GET_CURRENT_CAM:
@@ -1530,8 +1532,8 @@ static const struct of_device_id ucsi_ccg_of_match_table[] = {
MODULE_DEVICE_TABLE(of, ucsi_ccg_of_match_table);
static const struct i2c_device_id ucsi_ccg_device_id[] = {
- { "ccgx-ucsi" },
- {}
+ { .name = "ccgx-ucsi" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c
index 838ac0185082c..848ed459a6deb 100644
--- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c
+++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c
@@ -737,8 +737,8 @@ static const struct of_device_id __maybe_unused ucsi_stm32g0_typec_of_match[] =
MODULE_DEVICE_TABLE(of, ucsi_stm32g0_typec_of_match);
static const struct i2c_device_id ucsi_stm32g0_typec_i2c_devid[] = {
- { "stm32g0-typec" },
- {}
+ { .name = "stm32g0-typec" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ucsi_stm32g0_typec_i2c_devid);
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
index 0187c1c4b21ab..1be18d1018426 100644
--- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -89,7 +89,8 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
u64 command,
u32 *cci,
- void *data, size_t size)
+ void *data, size_t size,
+ void *msg_out, size_t msg_out_size)
{
int ret;
@@ -126,7 +127,8 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
return 0;
}
- ret = ucsi_sync_control_common(ucsi, command, cci, data, size);
+ ret = ucsi_sync_control_common(ucsi, command, cci,
+ data, size, msg_out, msg_out_size);
if (ret < 0)
return ret;
diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig
index b9f94e2e278d8..50945b6fae1a4 100644
--- a/drivers/usb/usbip/Kconfig
+++ b/drivers/usb/usbip/Kconfig
@@ -40,7 +40,7 @@ config USBIP_VHCI_HC_PORTS
config USBIP_VHCI_NR_HCS
int "Number of USB/IP virtual host controllers"
- range 1 128
+ range 1 32
default 1
depends on USBIP_VHCI_HCD
help
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index b98d14c43d13d..a7ede6fb3da9c 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -59,6 +59,29 @@ static void port_show_vhci(char **out, int hub, int port, struct vhci_device *vd
*out += sprintf(*out, "\n");
}
+static ssize_t status_show_not_ready(int pdev_nr, char *out)
+{
+ char *s = out;
+ int i = 0;
+
+ for (i = 0; i < VHCI_HC_PORTS; i++) {
+ out += sprintf(out, "hs %04u %03u ",
+ (pdev_nr * VHCI_PORTS) + i,
+ VDEV_ST_NOTASSIGNED);
+ out += sprintf(out, "000 00000000 0000000000000000 0-0");
+ out += sprintf(out, "\n");
+ }
+
+ for (i = 0; i < VHCI_HC_PORTS; i++) {
+ out += sprintf(out, "ss %04u %03u ",
+ (pdev_nr * VHCI_PORTS) + VHCI_HC_PORTS + i,
+ VDEV_ST_NOTASSIGNED);
+ out += sprintf(out, "000 00000000 0000000000000000 0-0");
+ out += sprintf(out, "\n");
+ }
+ return out - s;
+}
+
/* Sysfs entry to show port status */
static ssize_t status_show_vhci(int pdev_nr, char *out)
{
@@ -76,6 +99,12 @@ static ssize_t status_show_vhci(int pdev_nr, char *out)
}
hcd = platform_get_drvdata(pdev);
+
+ if (!hcd) {
+ usbip_dbg_vhci_sysfs("show status error (hcd is NULL)\n");
+ return status_show_not_ready(pdev_nr, out);
+ }
+
vhci_hcd = hcd_to_vhci_hcd(hcd);
vhci = vhci_hcd->vhci;
@@ -104,29 +133,6 @@ static ssize_t status_show_vhci(int pdev_nr, char *out)
return out - s;
}
-static ssize_t status_show_not_ready(int pdev_nr, char *out)
-{
- char *s = out;
- int i = 0;
-
- for (i = 0; i < VHCI_HC_PORTS; i++) {
- out += sprintf(out, "hs %04u %03u ",
- (pdev_nr * VHCI_PORTS) + i,
- VDEV_ST_NOTASSIGNED);
- out += sprintf(out, "000 00000000 0000000000000000 0-0");
- out += sprintf(out, "\n");
- }
-
- for (i = 0; i < VHCI_HC_PORTS; i++) {
- out += sprintf(out, "ss %04u %03u ",
- (pdev_nr * VHCI_PORTS) + VHCI_HC_PORTS + i,
- VDEV_ST_NOTASSIGNED);
- out += sprintf(out, "000 00000000 0000000000000000 0-0");
- out += sprintf(out, "\n");
- }
- return out - s;
-}
-
static int status_name_to_id(const char *name)
{
char *c;
diff --git a/include/linux/platform_data/pxa2xx_udc.h b/include/linux/platform_data/pxa2xx_udc.h
index bc99cc6a3c5f8..c1e4d03bae2c4 100644
--- a/include/linux/platform_data/pxa2xx_udc.h
+++ b/include/linux/platform_data/pxa2xx_udc.h
@@ -10,21 +10,6 @@
#ifndef PXA2XX_UDC_H
#define PXA2XX_UDC_H
-struct pxa2xx_udc_mach_info {
- int (*udc_is_connected)(void); /* do we see host? */
- void (*udc_command)(int cmd);
-#define PXA2XX_UDC_CMD_CONNECT 0 /* let host see us */
-#define PXA2XX_UDC_CMD_DISCONNECT 1 /* so host won't see us */
-
- /* Boards following the design guidelines in the developer's manual,
- * with on-chip GPIOs not Lubbock's weird hardware, can have a sane
- * VBUS IRQ and omit the methods above. Store the GPIO number
- * here. Note that sometimes the signals go through inverters...
- */
- bool gpio_pullup_inverted;
- int gpio_pullup; /* high == pullup activated */
-};
-
#ifdef CONFIG_PXA27x
extern void pxa27x_clear_otgph(void);
#else
diff --git a/include/linux/platform_data/usb-xhci-prom21.h b/include/linux/platform_data/usb-xhci-prom21.h
new file mode 100644
index 0000000000000..ee672ad452a87
--- /dev/null
+++ b/include/linux/platform_data/usb-xhci-prom21.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AMD Promontory 21 xHCI auxiliary device platform data.
+ *
+ * Copyright (C) 2026 Jihong Min <hurryman2212@gmail.com>
+ */
+
+#ifndef _LINUX_PLATFORM_DATA_USB_XHCI_PROM21_H
+#define _LINUX_PLATFORM_DATA_USB_XHCI_PROM21_H
+
+#include <linux/compiler_types.h>
+#include <linux/types.h>
+
+struct pci_dev;
+
+struct prom21_xhci_pdata {
+ struct pci_dev *pdev;
+ void __iomem *regs;
+ resource_size_t rsrc_len;
+};
+
+#endif