diff options
| author | Mark Brown <broonie@kernel.org> | 2026-05-29 23:00:26 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-05-29 23:00:26 +0100 |
| commit | 9394de50f7ac760fe5ae2eea0fa184185f2cdc3a (patch) | |
| tree | bbc7006f9d0c4c574c1fd0ce4ae07f4c5200d1b6 | |
| parent | ffc4db0244867d349d1f62258b36885e4ef6155c (diff) | |
| parent | 22d91cef94b5b86cff0d68ebfce7741740672704 (diff) | |
| download | linux-next-history-9394de50f7ac760fe5ae2eea0fa184185f2cdc3a.tar.gz | |
Merge branch 'usb-next' of https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git
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, ®, 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, ®, fladj); + FIELD_MODIFY(DWC3_GFLADJ_240MHZDECR, ®, decr >> 1); + FIELD_MODIFY(DWC3_GFLADJ_240MHZDECR_PLS1, ®, 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 &= ~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, ®, 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, ®, TCPC_ROLE_CTRL_CC_OPEN); + else + FIELD_MODIFY(TCPC_ROLE_CTRL_CC2, ®, 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 |
