Merge tag 'usb-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 7 Oct 2022 23:48:26 +0000 (16:48 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 7 Oct 2022 23:48:26 +0000 (16:48 -0700)
Pull USB / Thunderbolt updates from Greg KH:
 "Here is the big set of USB and Thunderbolt driver changes for 6.1-rc1.

  Nothing major in here, lots of little things with new devices
  supported and updates for a few drivers. Highlights include:

   - thunderbolt/USB4 devices supported a bit better than before, and
     some new ids to enable new hardware devices

   - USB gadget uvc updates for newer video formats and better v4l
     integration (the v4l portions were acked by those maintainers)

   - typec updates for tiny issues and more typec drivers for new chips.

   - xhci tiny updates for minor issues

   - big usb-serial ftdi_sio driver update to handle new devices better

   - lots of tiny dwc3 fixes and updates for the IP block that is
     showing up everywhere these days

   - dts updates for new devices being supported

   - other tiny janitorial and cleanups fixes for lots of different USB
     drivers. Full details are in the shortlog.

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (169 commits)
  usb: gadget: uvc: don't put item still in use
  usb: gadget: uvc: Fix argument to sizeof() in uvc_register_video()
  usb: host: ehci-exynos: switch to using gpiod API
  Revert "usb: dwc3: Don't switch OTG -> peripheral if extcon is present"
  Revert "USB: fixup for merge issue with "usb: dwc3: Don't switch OTG -> peripheral if extcon is present""
  dt-bindings: usb: Convert FOTG210 to dt schema
  usb: mtu3: fix failed runtime suspend in host only mode
  USB: omap_udc: Fix spelling mistake: "tranceiver_ctrl" -> "transceiver_ctrl"
  usb: typec: ucsi_ccg: Disable UCSI ALT support on Tegra
  usb: typec: Replace custom implementation of device_match_fwnode()
  usb: typec: ucsi: Don't warn on probe deferral
  usb: add quirks for Lenovo OneLink+ Dock
  MAINTAINERS: switch dwc3 to Thinh
  usb: idmouse: fix an uninit-value in idmouse_open
  USB: PHY: JZ4770: Switch to use dev_err_probe() helper
  usb: phy: generic: Switch to use dev_err_probe() helper
  usb: ulpi: use DEFINE_SHOW_ATTRIBUTE to simplify ulpi_regs
  usb: cdns3: remove dead code
  usb: cdc-wdm: Use skb_put_data() instead of skb_put/memcpy pair
  usb: musb: sunxi: Switch to use dev_err_probe() helper
  ...

21 files changed:
1  2 
MAINTAINERS
arch/arm64/boot/dts/freescale/imx8mp.dtsi
drivers/media/usb/uvc/uvc_ctrl.c
drivers/media/usb/uvc/uvc_driver.c
drivers/phy/tegra/xusb-tegra186.c
drivers/phy/tegra/xusb.c
drivers/thunderbolt/nhi.c
drivers/thunderbolt/switch.c
drivers/thunderbolt/tb.c
drivers/thunderbolt/tb.h
drivers/thunderbolt/usb4.c
drivers/usb/misc/usb3503.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/typec/anx7411.c
drivers/usb/typec/stusb160x.c
drivers/usb/typec/tcpm/fusb302.c
drivers/usb/typec/tcpm/tcpci.c
drivers/usb/typec/tcpm/tcpci_rt1711h.c
drivers/usb/typec/ucsi/ucsi.c
drivers/usb/typec/ucsi/ucsi_ccg.c
drivers/usb/typec/ucsi/ucsi_stm32g0.c

diff --combined MAINTAINERS
@@@ -348,6 -348,7 +348,6 @@@ M: "Rafael J. Wysocki" <rafael@kernel.o
  R:    Len Brown <lenb@kernel.org>
  L:    linux-acpi@vger.kernel.org
  S:    Supported
 -W:    https://01.org/linux-acpi
  Q:    https://patchwork.kernel.org/project/linux-acpi/list/
  B:    https://bugzilla.kernel.org
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
@@@ -426,6 -427,7 +426,6 @@@ M: Rafael J. Wysocki <rafael@kernel.org
  R:    Zhang Rui <rui.zhang@intel.com>
  L:    linux-acpi@vger.kernel.org
  S:    Supported
 -W:    https://01.org/linux-acpi
  B:    https://bugzilla.kernel.org
  F:    drivers/acpi/*thermal*
  
@@@ -748,12 -750,6 +748,12 @@@ S:       Supporte
  F:    drivers/infiniband/hw/erdma
  F:    include/uapi/rdma/erdma-abi.h
  
 +ALIBABA PMU DRIVER
 +M:    Shuai Xue <xueshuai@linux.alibaba.com>
 +S:    Supported
 +F:    Documentation/admin-guide/perf/alibaba_pmu.rst
 +F:    drivers/perf/alibaba_uncore_dwr_pmu.c
 +
  ALIENWARE WMI DRIVER
  L:    Dell.Client.Kernel@dell.com
  S:    Maintained
@@@ -825,13 -821,6 +825,13 @@@ L:       linux-media@vger.kernel.or
  S:    Maintained
  F:    drivers/staging/media/sunxi/cedrus/
  
 +ALLWINNER DMIC DRIVERS
 +M:    Ban Tao <fengzheng923@gmail.com>
 +L:    alsa-devel@alsa-project.org (moderated for non-subscribers)
 +S:    Maintained
 +F:    Documentation/devicetree/bindings/sound/allwinner,sun50i-h6-dmic.yaml
 +F:    sound/soc/sunxi/sun50i-dmic.c
 +
  ALPHA PORT
  M:    Richard Henderson <richard.henderson@linaro.org>
  M:    Ivan Kokshaysky <ink@jurassic.park.msu.ru>
@@@ -890,13 -879,6 +890,13 @@@ L:       netdev@vger.kernel.or
  S:    Maintained
  F:    drivers/net/ethernet/altera/
  
 +ALTERA TSE PCS
 +M:    Maxime Chevallier <maxime.chevallier@bootlin.com>
 +L:    netdev@vger.kernel.org
 +S:    Supported
 +F:    drivers/net/pcs/pcs-altera-tse.c
 +F:    include/linux/pcs-altera-tse.h
 +
  ALTERA UART/JTAG UART SERIAL DRIVERS
  M:    Tobias Klauser <tklauser@distanz.ch>
  L:    linux-serial@vger.kernel.org
@@@ -1040,13 -1022,6 +1040,13 @@@ L:    platform-driver-x86@vger.kernel.or
  S:    Maintained
  F:    drivers/platform/x86/amd/pmc.c
  
 +AMD PMF DRIVER
 +M:    Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
 +L:    platform-driver-x86@vger.kernel.org
 +S:    Maintained
 +F:    Documentation/ABI/testing/sysfs-amd-pmf
 +F:    drivers/platform/x86/amd/pmf/
 +
  AMD HSMP DRIVER
  M:    Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
  R:    Carlos Bilbao <carlos.bilbao@amd.com>
@@@ -1070,7 -1045,6 +1070,7 @@@ L:      linux-pm@vger.kernel.or
  S:    Supported
  F:    Documentation/admin-guide/pm/amd-pstate.rst
  F:    drivers/cpufreq/amd-pstate*
 +F:    include/linux/amd-pstate.h
  F:    tools/power/x86/amd_pstate_tracer/amd_pstate_trace.py
  
  AMD PTDMA DRIVER
@@@ -1360,15 -1334,6 +1360,15 @@@ F:    drivers/iio/amplifiers/hmc425a.
  F:    drivers/staging/iio/*/ad*
  X:    drivers/iio/*/adjd*
  
 +ANALOG DEVICES INC MAX31760 DRIVER
 +M:    Ibrahim Tilki <Ibrahim.Tilki@analog.com>
 +S:    Maintained
 +W:    http://wiki.analog.com/
 +W:    https://ez.analog.com/linux-software-drivers
 +F:    Documentation/devicetree/bindings/hwmon/adi,max31760.yaml
 +F:    Documentation/hwmon/max31760.rst
 +F:    drivers/hwmon/max31760.c
 +
  ANALOGBITS PLL LIBRARIES
  M:    Paul Walmsley <paul.walmsley@sifive.com>
  S:    Supported
@@@ -1934,15 -1899,6 +1934,15 @@@ F:    include/dt-bindings/pinctrl/apple.
  F:    include/linux/apple-mailbox.h
  F:    include/linux/soc/apple/*
  
 +ARM/APPLE MACHINE SOUND DRIVERS
 +M:    Martin Povišer <povik+lin@cutebit.org>
 +L:    asahi@lists.linux.dev
 +L:    alsa-devel@alsa-project.org (moderated for non-subscribers)
 +S:    Maintained
 +F:    Documentation/devicetree/bindings/sound/apple,*
 +F:    sound/soc/apple/*
 +F:    sound/soc/codecs/cs42l83-i2c.c
 +
  ARM/ARTPEC MACHINE SUPPORT
  M:    Jesper Nilsson <jesper.nilsson@axis.com>
  M:    Lars Persson <lars.persson@axis.com>
@@@ -2438,7 -2394,6 +2438,7 @@@ N:      atme
  ARM/Microchip Sparx5 SoC support
  M:    Lars Povlsen <lars.povlsen@microchip.com>
  M:    Steen Hegelund <Steen.Hegelund@microchip.com>
 +M:    Daniel Machon <daniel.machon@microchip.com>
  M:    UNGLinuxDriver@microchip.com
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  S:    Supported
@@@ -2624,7 -2579,7 +2624,7 @@@ W:      http://www.armlinux.org.uk
  
  ARM/QUALCOMM SUPPORT
  M:    Andy Gross <agross@kernel.org>
 -M:    Bjorn Andersson <bjorn.andersson@linaro.org>
 +M:    Bjorn Andersson <andersson@kernel.org>
  R:    Konrad Dybcio <konrad.dybcio@somainline.org>
  L:    linux-arm-msm@vger.kernel.org
  S:    Maintained
@@@ -2633,7 -2588,6 +2633,7 @@@ F:      Documentation/devicetree/bindings/*/
  F:    Documentation/devicetree/bindings/soc/qcom/
  F:    arch/arm/boot/dts/qcom-*.dts
  F:    arch/arm/boot/dts/qcom-*.dtsi
 +F:    arch/arm/configs/qcom_defconfig
  F:    arch/arm/mach-qcom/
  F:    arch/arm64/boot/dts/qcom/
  F:    drivers/*/*/qcom*
@@@ -2696,7 -2650,7 +2696,7 @@@ F:      arch/arm/boot/dts/rtd
  F:    arch/arm/mach-realtek/
  F:    arch/arm64/boot/dts/realtek/
  
 -ARM/RENESAS ARM64 ARCHITECTURE
 +ARM/RENESAS ARCHITECTURE
  M:    Geert Uytterhoeven <geert+renesas@glider.be>
  M:    Magnus Damm <magnus.damm@gmail.com>
  L:    linux-renesas-soc@vger.kernel.org
@@@ -2707,16 -2661,6 +2707,16 @@@ T:    git git://git.kernel.org/pub/scm/lin
  F:    Documentation/devicetree/bindings/arm/renesas.yaml
  F:    Documentation/devicetree/bindings/hwinfo/renesas,prr.yaml
  F:    Documentation/devicetree/bindings/soc/renesas/
 +F:    arch/arm/boot/dts/emev2*
 +F:    arch/arm/boot/dts/gr-peach*
 +F:    arch/arm/boot/dts/iwg20d-q7*
 +F:    arch/arm/boot/dts/r7s*
 +F:    arch/arm/boot/dts/r8a*
 +F:    arch/arm/boot/dts/r9a*
 +F:    arch/arm/boot/dts/sh*
 +F:    arch/arm/configs/shmobile_defconfig
 +F:    arch/arm/include/debug/renesas-scif.S
 +F:    arch/arm/mach-shmobile/
  F:    arch/arm64/boot/dts/renesas/
  F:    drivers/soc/renesas/
  F:    include/linux/soc/renesas/
@@@ -2726,6 -2670,7 +2726,6 @@@ M:      Russell King <linux@armlinux.org.uk
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  S:    Maintained
  W:    http://www.armlinux.org.uk/
 -F:    arch/arm/include/asm/hardware/entry-macro-iomd.S
  F:    arch/arm/include/asm/hardware/ioc.h
  F:    arch/arm/include/asm/hardware/iomd.h
  F:    arch/arm/include/asm/hardware/memc.h
@@@ -2827,6 -2772,29 +2827,6 @@@ L:     linux-media@vger.kernel.or
  S:    Maintained
  F:    drivers/media/platform/samsung/s5p-mfc/
  
 -ARM/SHMOBILE ARM ARCHITECTURE
 -M:    Geert Uytterhoeven <geert+renesas@glider.be>
 -M:    Magnus Damm <magnus.damm@gmail.com>
 -L:    linux-renesas-soc@vger.kernel.org
 -S:    Supported
 -Q:    http://patchwork.kernel.org/project/linux-renesas-soc/list/
 -C:    irc://irc.libera.chat/renesas-soc
 -T:    git git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-devel.git next
 -F:    Documentation/devicetree/bindings/arm/renesas.yaml
 -F:    Documentation/devicetree/bindings/soc/renesas/
 -F:    arch/arm/boot/dts/emev2*
 -F:    arch/arm/boot/dts/gr-peach*
 -F:    arch/arm/boot/dts/iwg20d-q7*
 -F:    arch/arm/boot/dts/r7s*
 -F:    arch/arm/boot/dts/r8a*
 -F:    arch/arm/boot/dts/r9a*
 -F:    arch/arm/boot/dts/sh*
 -F:    arch/arm/configs/shmobile_defconfig
 -F:    arch/arm/include/debug/renesas-scif.S
 -F:    arch/arm/mach-shmobile/
 -F:    drivers/soc/renesas/
 -F:    include/linux/soc/renesas/
 -
  ARM/SOCFPGA ARCHITECTURE
  M:    Dinh Nguyen <dinguyen@kernel.org>
  S:    Maintained
@@@ -3119,8 -3087,6 +3119,8 @@@ W:      http://wiki.xilinx.co
  T:    git https://github.com/Xilinx/linux-xlnx.git
  F:    Documentation/devicetree/bindings/i2c/cdns,i2c-r1p10.yaml
  F:    Documentation/devicetree/bindings/i2c/xlnx,xps-iic-2.00.a.yaml
 +F:    Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml
 +F:    Documentation/devicetree/bindings/memory-controllers/xlnx,zynq-ddrc-a05.yaml
  F:    Documentation/devicetree/bindings/spi/xlnx,zynq-qspi.yaml
  F:    arch/arm/mach-zynq/
  F:    drivers/clocksource/timer-cadence-ttc.c
@@@ -3267,6 -3233,13 +3267,6 @@@ L:     linux-hwmon@vger.kernel.or
  S:    Maintained
  F:    drivers/hwmon/asus_wmi_sensors.c
  
 -ASUS WMI EC HARDWARE MONITOR DRIVER
 -M:    Eugene Shalygin <eugene.shalygin@gmail.com>
 -M:    Denis Pauk <pauk.denis@gmail.com>
 -L:    linux-hwmon@vger.kernel.org
 -S:    Maintained
 -F:    drivers/hwmon/asus_wmi_ec_sensors.c
 -
  ASUS EC HARDWARE MONITOR DRIVER
  M:    Eugene Shalygin <eugene.shalygin@gmail.com>
  L:    linux-hwmon@vger.kernel.org
@@@ -3852,7 -3825,6 +3852,7 @@@ F:      kernel/bpf/dispatcher.
  F:    kernel/bpf/trampoline.c
  F:    include/linux/bpf*
  F:    include/linux/filter.h
 +F:    include/linux/tnum.h
  
  BPF [BTF]
  M:    Martin KaFai Lau <martin.lau@linux.dev>
@@@ -3978,7 -3950,6 +3978,7 @@@ M:      William Zhang <william.zhang@broadco
  M:    Anand Gore <anand.gore@broadcom.com>
  M:    Kursad Oney <kursad.oney@broadcom.com>
  M:    Florian Fainelli <f.fainelli@gmail.com>
 +M:    Rafał Miłecki <rafal@milecki.pl>
  R:    Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com>
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  S:    Maintained
@@@ -4925,7 -4896,6 +4925,7 @@@ M:      Prashant Malani <pmalani@chromium.or
  L:    chrome-platform@lists.linux.dev
  S:    Maintained
  F:    drivers/platform/chrome/cros_ec_typec.c
 +F:    drivers/platform/chrome/cros_typec_switch.c
  
  CHROMEOS EC USB PD NOTIFY DRIVER
  M:    Prashant Malani <pmalani@chromium.org>
@@@ -5404,8 -5374,8 +5404,8 @@@ T:      git git://git.kernel.org/pub/scm/lin
  F:    drivers/cpuidle/cpuidle-big_little.c
  
  CPUIDLE DRIVER - ARM EXYNOS
 -M:    Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
  M:    Daniel Lezcano <daniel.lezcano@linaro.org>
 +R:    Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
  M:    Kukjin Kim <kgene@kernel.org>
  L:    linux-pm@vger.kernel.org
  L:    linux-samsung-soc@vger.kernel.org
@@@ -5753,6 -5723,13 +5753,6 @@@ F:     include/linux/tfrc.
  F:    include/uapi/linux/dccp.h
  F:    net/dccp/
  
 -DECnet NETWORK LAYER
 -L:    linux-decnet-user@lists.sourceforge.net
 -S:    Orphan
 -W:    http://linux-decnet.sourceforge.net
 -F:    Documentation/networking/decnet.rst
 -F:    net/decnet/
 -
  DECSTATION PLATFORM SUPPORT
  M:    "Maciej W. Rozycki" <macro@orcam.me.uk>
  L:    linux-mips@vger.kernel.org
@@@ -5921,10 -5898,9 +5921,9 @@@ T:     git git://git.kernel.org/pub/scm/lin
  F:    drivers/usb/dwc2/
  
  DESIGNWARE USB3 DRD IP DRIVER
- M:    Felipe Balbi <balbi@kernel.org>
+ M:    Thinh Nguyen <Thinh.Nguyen@synopsys.com>
  L:    linux-usb@vger.kernel.org
  S:    Maintained
- T:    git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
  F:    drivers/usb/dwc3/
  
  DEVANTECH SRF ULTRASONIC RANGER IIO DRIVER
@@@ -6205,7 -6181,7 +6204,7 @@@ F:      Documentation/devicetree/bindings/me
  F:    drivers/memory/samsung/exynos5422-dmc.c
  
  DME1737 HARDWARE MONITOR DRIVER
 -M:    Juerg Haefliger <juergh@gmail.com>
 +M:    Juerg Haefliger <juergh@proton.me>
  L:    linux-hwmon@vger.kernel.org
  S:    Maintained
  F:    Documentation/hwmon/dme1737.rst
@@@ -6445,11 -6421,6 +6444,11 @@@ S:    Maintaine
  F:    Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.yaml
  F:    drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
  
 +DRM DRIVER FOR GENERIC EDP PANELS
 +R:    Douglas Anderson <dianders@chromium.org>
 +F:    Documentation/devicetree/bindings/display/panel/panel-edp.yaml
 +F:    drivers/gpu/drm/panel/panel-edp.c
 +
  DRM DRIVER FOR GENERIC USB DISPLAY
  M:    Noralf Trønnes <noralf@tronnes.org>
  S:    Maintained
@@@ -7563,7 -7534,7 +7562,7 @@@ M:      Adrian Hunter <adrian.hunter@intel.c
  M:    Ritesh Harjani <riteshh@codeaurora.org>
  M:    Asutosh Das <asutoshd@codeaurora.org>
  L:    linux-mmc@vger.kernel.org
 -S:    Maintained
 +S:    Supported
  F:    drivers/mmc/host/cqhci*
  
  EMULEX 10Gbps iSCSI - OneConnect DRIVER
@@@ -7716,6 -7687,7 +7715,6 @@@ R:      Kees Cook <keescook@chromium.org
  L:    linux-mm@kvack.org
  S:    Supported
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/execve
 -F:    arch/alpha/kernel/binfmt_loader.c
  F:    fs/*binfmt_*.c
  F:    fs/exec.c
  F:    include/linux/binfmts.h
@@@ -8032,7 -8004,6 +8031,7 @@@ L:      linux-hardening@vger.kernel.or
  S:    Supported
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
  F:    include/linux/fortify-string.h
 +F:    lib/fortify_kunit.c
  F:    lib/test_fortify/*
  F:    scripts/test_fortify.sh
  K:    \b__NO_FORTIFY\b
@@@ -8475,6 -8446,7 +8474,6 @@@ F:      tools/testing/selftests/futex
  
  GATEWORKS SYSTEM CONTROLLER (GSC) DRIVER
  M:    Tim Harvey <tharvey@gateworks.com>
 -M:    Robert Jones <rjones@gateworks.com>
  S:    Maintained
  F:    Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml
  F:    drivers/mfd/gateworks-gsc.c
@@@ -8935,7 -8907,7 +8934,7 @@@ S:      Maintaine
  F:    Documentation/devicetree/bindings/media/nxp,imx8mq-vpu.yaml
  F:    Documentation/devicetree/bindings/media/rockchip,rk3568-vepu.yaml
  F:    Documentation/devicetree/bindings/media/rockchip-vpu.yaml
 -F:    drivers/staging/media/hantro/
 +F:    drivers/media/platform/verisilicon/
  
  HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER
  M:    Frank Seidel <frank@f-seidel.de>
@@@ -8971,7 -8943,7 +8970,7 @@@ F:      include/linux/hw_random.
  
  HARDWARE SPINLOCK CORE
  M:    Ohad Ben-Cohen <ohad@wizery.com>
 -M:    Bjorn Andersson <bjorn.andersson@linaro.org>
 +M:    Bjorn Andersson <andersson@kernel.org>
  R:    Baolin Wang <baolin.wang7@gmail.com>
  L:    linux-remoteproc@vger.kernel.org
  S:    Maintained
@@@ -9081,12 -9053,6 +9080,12 @@@ L:    linux-input@vger.kernel.or
  S:    Supported
  F:    drivers/hid/hid-playstation.c
  
 +HID PHOENIX RC FLIGHT CONTROLLER
 +M:    Marcus Folkesson <marcus.folkesson@gmail.com>
 +L:    linux-input@vger.kernel.org
 +S:    Maintained
 +F:    drivers/hid/hid-pxrc.c
 +
  HID SENSOR HUB DRIVERS
  M:    Jiri Kosina <jikos@kernel.org>
  M:    Jonathan Cameron <jic23@kernel.org>
@@@ -9099,12 -9065,6 +9098,12 @@@ F:    drivers/hid/hid-sensor-
  F:    drivers/iio/*/hid-*
  F:    include/linux/hid-sensor-*
  
 +HID VRC-2 CAR CONTROLLER DRIVER
 +M:    Marcus Folkesson <marcus.folkesson@gmail.com>
 +L:    linux-input@vger.kernel.org
 +S:    Maintained
 +F:    drivers/hid/hid-vrc2.c
 +
  HID WACOM DRIVER
  M:    Ping Cheng <ping.cheng@wacom.com>
  M:    Jason Gerecke  <jason.gerecke@wacom.com>
@@@ -9157,7 -9117,6 +9156,7 @@@ F:      net/dsa/tag_hellcreek.
  
  HISILICON DMA DRIVER
  M:    Zhou Wang <wangzhou1@hisilicon.com>
 +M:    Jie Hai <haijie1@hisilicon.com>
  L:    dmaengine@vger.kernel.org
  S:    Maintained
  F:    drivers/dma/hisi_dma.c
@@@ -10419,6 -10378,7 +10418,6 @@@ INTEL MENLOW THERMAL DRIVE
  M:    Sujith Thomas <sujith.thomas@intel.com>
  L:    linux-pm@vger.kernel.org
  S:    Supported
 -W:    https://01.org/linux-acpi
  F:    drivers/thermal/intel/intel_menlow.c
  
  INTEL P-Unit IPC DRIVER
@@@ -11113,8 -11073,8 +11112,8 @@@ F:   tools/testing/selftests
  KERNEL SMB3 SERVER (KSMBD)
  M:    Namjae Jeon <linkinjeon@kernel.org>
  M:    Steve French <sfrench@samba.org>
 -M:    Hyunchul Lee <hyc.lee@gmail.com>
  R:    Sergey Senozhatsky <senozhatsky@chromium.org>
 +R:    Tom Talpey <tom@talpey.com>
  L:    linux-cifs@vger.kernel.org
  S:    Maintained
  T:    git git://git.samba.org/ksmbd.git
@@@ -11596,15 -11556,6 +11595,15 @@@ F: drivers/ata/ahci_platform.
  F:    drivers/ata/libahci_platform.c
  F:    include/linux/ahci_platform.h
  
 +LIBATA SATA AHCI SYNOPSYS DWC CONTROLLER DRIVER
 +M:    Serge Semin <fancer.lancer@gmail.com>
 +L:    linux-ide@vger.kernel.org
 +S:    Maintained
 +T:    git git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata.git
 +F:    Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml
 +F:    Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml
 +F:    drivers/ata/ahci_dwc.c
 +
  LIBATA SATA PROMISE TX2/TX4 CONTROLLER DRIVER
  M:    Mikael Pettersson <mikpelinux@gmail.com>
  L:    linux-ide@vger.kernel.org
@@@ -12459,6 -12410,7 +12458,6 @@@ F:   drivers/power/supply/max77976_charge
  
  MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
  M:    Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
 -M:    Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
  L:    linux-pm@vger.kernel.org
  S:    Supported
  B:    mailto:linux-samsung-soc@vger.kernel.org
@@@ -12470,6 -12422,7 +12469,6 @@@ F:   drivers/power/supply/max77693_charge
  MAXIM PMIC AND MUIC DRIVERS FOR EXYNOS BASED BOARDS
  M:    Chanwoo Choi <cw00.choi@samsung.com>
  M:    Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
 -M:    Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
  L:    linux-kernel@vger.kernel.org
  S:    Supported
  B:    mailto:linux-samsung-soc@vger.kernel.org
@@@ -13030,9 -12983,9 +13029,9 @@@ F:   drivers/input/touchscreen/melfas_mip
  
  MELLANOX BLUEFIELD I2C DRIVER
  M:    Khalil Blaiech <kblaiech@nvidia.com>
 +M:    Asmaa Mnebhi <asmaa@nvidia.com>
  L:    linux-i2c@vger.kernel.org
  S:    Supported
 -F:    Documentation/devicetree/bindings/i2c/mellanox,i2c-mlxbf.yaml
  F:    drivers/i2c/busses/i2c-mlxbf.c
  
  MELLANOX ETHERNET DRIVER (mlx4_en)
@@@ -13339,7 -13292,7 +13338,7 @@@ F:   include/dt-bindings/dma/at91.
  MICROCHIP AT91 SERIAL DRIVER
  M:    Richard Genoud <richard.genoud@gmail.com>
  S:    Maintained
 -F:    Documentation/devicetree/bindings/mfd/atmel-usart.txt
 +F:    Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml
  F:    drivers/tty/serial/atmel_serial.c
  F:    drivers/tty/serial/atmel_serial.h
  
@@@ -13347,7 -13300,7 +13346,7 @@@ MICROCHIP AT91 USART MFD DRIVE
  M:    Radu Pirea <radu_nicolae.pirea@upb.ro>
  L:    linux-kernel@vger.kernel.org
  S:    Supported
 -F:    Documentation/devicetree/bindings/mfd/atmel-usart.txt
 +F:    Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml
  F:    drivers/mfd/at91-usart.c
  F:    include/dt-bindings/mfd/at91-usart.h
  
@@@ -13355,7 -13308,7 +13354,7 @@@ MICROCHIP AT91 USART SPI DRIVE
  M:    Radu Pirea <radu_nicolae.pirea@upb.ro>
  L:    linux-spi@vger.kernel.org
  S:    Supported
 -F:    Documentation/devicetree/bindings/mfd/atmel-usart.txt
 +F:    Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml
  F:    drivers/spi/spi-at91-usart.c
  
  MICROCHIP AUDIO ASOC DRIVERS
@@@ -13481,14 -13434,6 +13480,14 @@@ F: Documentation/devicetree/bindings/nv
  F:    drivers/nvmem/microchip-otpc.c
  F:    include/dt-bindings/nvmem/microchip,sama7g5-otpc.h
  
 +MICROCHIP PCI1XXXX I2C DRIVER
 +M:    Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>
 +M:    Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
 +M:    Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
 +L:    linux-i2c@vger.kernel.org
 +S:    Maintained
 +F:    drivers/i2c/busses/i2c-mchp-pci1xxxx.c
 +
  MICROCHIP PWM DRIVER
  M:    Claudiu Beznea <claudiu.beznea@microchip.com>
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@@ -13548,7 -13493,6 +13547,7 @@@ M:   UNGLinuxDriver@microchip.co
  L:    linux-mips@vger.kernel.org
  S:    Supported
  F:    Documentation/devicetree/bindings/mips/mscc.txt
 +F:    Documentation/devicetree/bindings/phy/mscc,vsc7514-serdes.yaml
  F:    Documentation/devicetree/bindings/power/reset/ocelot-reset.txt
  F:    arch/mips/boot/dts/mscc/
  F:    arch/mips/configs/generic/board-ocelot.config
@@@ -13836,7 -13780,7 +13835,7 @@@ MOTION EYE VAIO PICTUREBOOK CAMERA DRIV
  S:    Orphan
  W:    http://popies.net/meye/
  F:    Documentation/userspace-api/media/drivers/meye*
 -F:    drivers/media/pci/meye/
 +F:    drivers/staging/media/deprecated/meye/
  F:    include/uapi/linux/meye.h
  
  MOTORCOMM PHY DRIVER
@@@ -14505,7 -14449,6 +14504,7 @@@ M:   Willy Tarreau <w@1wt.eu
  S:    Maintained
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/wtarreau/nolibc.git
  F:    tools/include/nolibc/
 +F:    tools/testing/selftests/nolibc/
  
  NSDEPS
  M:    Matthias Maennich <maennich@google.com>
@@@ -14588,7 -14531,6 +14587,7 @@@ M:   Daniel Dadap <ddadap@nvidia.com
  L:    platform-driver-x86@vger.kernel.org
  S:    Supported
  F:    drivers/platform/x86/nvidia-wmi-ec-backlight.c
 +F:    include/linux/platform_data/x86/nvidia-wmi-ec-backlight.h
  
  NVM EXPRESS DRIVER
  M:    Keith Busch <kbusch@kernel.org>
@@@ -14604,15 -14546,6 +14603,15 @@@ F: drivers/nvme/common
  F:    include/linux/nvme*
  F:    include/uapi/linux/nvme_ioctl.h
  
 +NVM EXPRESS FABRICS AUTHENTICATION
 +M:    Hannes Reinecke <hare@suse.de>
 +L:    linux-nvme@lists.infradead.org
 +S:    Supported
 +F:    drivers/nvme/host/auth.c
 +F:    drivers/nvme/target/auth.c
 +F:    drivers/nvme/target/fabrics-cmd-auth.c
 +F:    include/linux/nvme-auth.h
 +
  NVM EXPRESS FC TRANSPORT DRIVERS
  M:    James Smart <james.smart@broadcom.com>
  L:    linux-nvme@lists.infradead.org
@@@ -14762,15 -14695,6 +14761,15 @@@ S: Orpha
  F:    Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml
  F:    drivers/nfc/nxp-nci
  
 +NXP i.MX 8MP DW100 V4L2 DRIVER
 +M:    Xavier Roumegue <xavier.roumegue@oss.nxp.com>
 +L:    linux-media@vger.kernel.org
 +S:    Maintained
 +F:    Documentation/devicetree/bindings/media/nxp,dw100.yaml
 +F:    Documentation/userspace-api/media/drivers/dw100.rst
 +F:    drivers/media/platform/nxp/dw100/
 +F:    include/uapi/linux/dw100.h
 +
  NXP i.MX 8QXP/8QM JPEG V4L2 DRIVER
  M:    Mirela Rabulea <mirela.rabulea@nxp.com>
  R:    NXP Linux Team <linux-imx@nxp.com>
@@@ -14822,13 -14746,6 +14821,13 @@@ F: net/dsa/tag_ocelot.
  F:    net/dsa/tag_ocelot_8021q.c
  F:    tools/testing/selftests/drivers/net/ocelot/*
  
 +OCELOT EXTERNAL SWITCH CONTROL
 +M:    Colin Foster <colin.foster@in-advantage.com>
 +S:    Supported
 +F:    Documentation/devicetree/bindings/mfd/mscc,ocelot.yaml
 +F:    drivers/mfd/ocelot*
 +F:    include/linux/mfd/ocelot.h
 +
  OCXL (Open Coherent Accelerator Processor Interface OpenCAPI) DRIVER
  M:    Frederic Barrat <fbarrat@linux.ibm.com>
  M:    Andrew Donnellan <ajd@linux.ibm.com>
@@@ -15023,7 -14940,6 +15022,7 @@@ F:   drivers/regulator/palmas-regulator*.
  F:    drivers/regulator/pbias-regulator.c
  F:    drivers/regulator/tps65217-regulator.c
  F:    drivers/regulator/tps65218-regulator.c
 +F:    drivers/regulator/tps65219-regulator.c
  F:    drivers/regulator/tps65910-regulator.c
  F:    drivers/regulator/twl-regulator.c
  F:    drivers/regulator/twl6030-regulator.c
@@@ -16209,7 -16125,7 +16208,7 @@@ F:   drivers/gpio/gpio-sama5d2-piobu.
  F:    drivers/pinctrl/pinctrl-at91*
  
  PIN CONTROLLER - QUALCOMM
 -M:    Bjorn Andersson <bjorn.andersson@linaro.org>
 +M:    Bjorn Andersson <andersson@kernel.org>
  L:    linux-arm-msm@vger.kernel.org
  S:    Maintained
  F:    Documentation/devicetree/bindings/pinctrl/qcom,*.txt
@@@ -16622,6 -16538,14 +16621,6 @@@ T:  git git://linuxtv.org/media_tree.gi
  F:    drivers/media/usb/pwc/*
  F:    include/trace/events/pwc.h
  
 -PWM FAN DRIVER
 -M:    Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
 -L:    linux-hwmon@vger.kernel.org
 -S:    Supported
 -F:    Documentation/devicetree/bindings/hwmon/pwm-fan.txt
 -F:    Documentation/hwmon/pwm-fan.rst
 -F:    drivers/hwmon/pwm-fan.c
 -
  PWM IR Transmitter
  M:    Sean Young <sean@mess.org>
  L:    linux-media@vger.kernel.org
@@@ -16690,9 -16614,6 +16689,9 @@@ M:   Srinivas Kandagatla <srinivas.kandag
  M:    Banajit Goswami <bgoswami@quicinc.com>
  L:    alsa-devel@alsa-project.org (moderated for non-subscribers)
  S:    Supported
 +F:    Documentation/devicetree/bindings/soc/qcom/qcom,apr.yaml
 +F:    Documentation/devicetree/bindings/sound/qcom,*
 +F:    drivers/soc/qcom/apr.c
  F:    include/dt-bindings/sound/qcom,wcd9335.h
  F:    sound/soc/codecs/lpass-rx-macro.*
  F:    sound/soc/codecs/lpass-tx-macro.*
@@@ -16897,7 -16818,7 +16896,7 @@@ F:   Documentation/devicetree/bindings/me
  F:    drivers/media/platform/qcom/camss/
  
  QUALCOMM CLOCK DRIVERS
 -M:    Bjorn Andersson <bjorn.andersson@linaro.org>
 +M:    Bjorn Andersson <andersson@kernel.org>
  L:    linux-arm-msm@vger.kernel.org
  S:    Supported
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux.git
@@@ -17387,7 -17308,7 +17386,7 @@@ S:   Supporte
  F:    fs/reiserfs/
  
  REMOTE PROCESSOR (REMOTEPROC) SUBSYSTEM
 -M:    Bjorn Andersson <bjorn.andersson@linaro.org>
 +M:    Bjorn Andersson <andersson@kernel.org>
  M:    Mathieu Poirier <mathieu.poirier@linaro.org>
  L:    linux-remoteproc@vger.kernel.org
  S:    Maintained
@@@ -17400,7 -17321,7 +17399,7 @@@ F:   include/linux/remoteproc.
  F:    include/linux/remoteproc/
  
  REMOTE PROCESSOR MESSAGING (RPMSG) SUBSYSTEM
 -M:    Bjorn Andersson <bjorn.andersson@linaro.org>
 +M:    Bjorn Andersson <andersson@kernel.org>
  M:    Mathieu Poirier <mathieu.poirier@linaro.org>
  L:    linux-remoteproc@vger.kernel.org
  S:    Maintained
@@@ -17629,7 -17550,6 +17628,7 @@@ F:   drivers/mailbox/mailbox-mpfs.
  F:    drivers/pci/controller/pcie-microchip-host.c
  F:    drivers/rtc/rtc-mpfs.c
  F:    drivers/soc/microchip/
 +F:    drivers/spi/spi-microchip-core-qspi.c
  F:    drivers/spi/spi-microchip-core.c
  F:    drivers/usb/musb/mpfs.c
  F:    include/soc/microchip/mpfs.h
@@@ -17837,24 -17757,6 +17836,24 @@@ F: include/rv
  F:    kernel/trace/rv/
  F:    tools/verification/
  
 +RUST
 +M:    Miguel Ojeda <ojeda@kernel.org>
 +M:    Alex Gaynor <alex.gaynor@gmail.com>
 +M:    Wedson Almeida Filho <wedsonaf@gmail.com>
 +R:    Boqun Feng <boqun.feng@gmail.com>
 +R:    Gary Guo <gary@garyguo.net>
 +R:    Björn Roy Baron <bjorn3_gh@protonmail.com>
 +L:    rust-for-linux@vger.kernel.org
 +S:    Supported
 +W:    https://github.com/Rust-for-Linux/linux
 +B:    https://github.com/Rust-for-Linux/linux/issues
 +T:    git https://github.com/Rust-for-Linux/linux.git rust-next
 +F:    Documentation/rust/
 +F:    rust/
 +F:    samples/rust/
 +F:    scripts/*rust*
 +K:    \b(?i:rust)\b
 +
  RXRPC SOCKETS (AF_RXRPC)
  M:    David Howells <dhowells@redhat.com>
  M:    Marc Dionne <marc.dionne@auristor.com>
@@@ -18026,7 -17928,8 +18025,7 @@@ M:   Hans Verkuil <hverkuil@xs4all.nl
  L:    linux-media@vger.kernel.org
  S:    Maintained
  T:    git git://linuxtv.org/media_tree.git
 -F:    drivers/media/common/saa7146/
 -F:    drivers/media/pci/saa7146/
 +F:    drivers/staging/media/deprecated/saa7146/
  F:    include/media/drv-intf/saa7146*
  
  SAFESETID SECURITY MODULE
@@@ -18081,6 -17984,7 +18080,6 @@@ F:   drivers/platform/x86/samsung-laptop.
  
  SAMSUNG MULTIFUNCTION PMIC DEVICE DRIVERS
  M:    Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
 -M:    Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
  L:    linux-kernel@vger.kernel.org
  L:    linux-samsung-soc@vger.kernel.org
  S:    Supported
@@@ -18375,7 -18279,7 +18374,7 @@@ F:   drivers/mmc/host/sdhci-brcmstb
  SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
  M:    Adrian Hunter <adrian.hunter@intel.com>
  L:    linux-mmc@vger.kernel.org
 -S:    Maintained
 +S:    Supported
  F:    drivers/mmc/host/sdhci*
  
  SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) MICROCHIP DRIVER
@@@ -18398,7 -18302,7 +18397,7 @@@ S:   Maintaine
  F:    drivers/mmc/host/sdhci-spear.c
  
  SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) TI OMAP DRIVER
 -M:    Kishon Vijay Abraham I <kishon@ti.com>
 +M:    Vignesh Raghavendra <vigneshr@ti.com>
  L:    linux-mmc@vger.kernel.org
  S:    Maintained
  F:    drivers/mmc/host/sdhci-omap.c
@@@ -19599,11 -19503,6 +19598,11 @@@ L: netdev@vger.kernel.or
  S:    Maintained
  F:    drivers/net/ethernet/dlink/sundance.c
  
 +SUN HAPPY MEAL ETHERNET DRIVER
 +M:    Sean Anderson <seanga2@gmail.com>
 +S:    Maintained
 +F:    drivers/net/ethernet/sun/sunhme.*
 +
  SUNPLUS ETHERNET DRIVER
  M:    Wells Lu <wellslutw@gmail.com>
  L:    netdev@vger.kernel.org
@@@ -19618,15 -19517,6 +19617,15 @@@ S: Maintaine
  F:    Documentation/devicetree/bindings/nvmem/sunplus,sp7021-ocotp.yaml
  F:    drivers/nvmem/sunplus-ocotp.c
  
 +SUNPLUS USB2 PHY DRIVER
 +M:    Vincent Shih <vincent.sunplus@gmail.com>
 +L:    linux-usb@vger.kernel.org
 +S:    Maintained
 +F:    Documentation/devicetree/bindings/phy/sunplus,sp7021-usb2-phy.yaml
 +F:    drivers/phy/sunplus/Kconfig
 +F:    drivers/phy/sunplus/Makefile
 +F:    drivers/phy/sunplus/phy-sunplus-usb2.c
 +
  SUNPLUS PWM DRIVER
  M:    Hammer Hsieh <hammerh0314@gmail.com>
  S:    Maintained
@@@ -20070,7 -19960,7 +20069,7 @@@ S:   Supporte
  F:    drivers/net/team/
  F:    include/linux/if_team.h
  F:    include/uapi/linux/if_team.h
 -F:    tools/testing/selftests/net/team/
 +F:    tools/testing/selftests/drivers/net/team/
  
  TECHNOLOGIC SYSTEMS TS-5500 PLATFORM SUPPORT
  M:    "Savoir-faire Linux Inc." <kernel@savoirfairelinux.com>
@@@ -20458,7 -20348,6 +20457,7 @@@ W:   https://linuxtv.or
  Q:    http://patchwork.linuxtv.org/project/linux-media/list/
  T:    git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
  F:    drivers/media/platform/ti/davinci/
 +F:    drivers/staging/media/deprecated/vpfe_capture/
  F:    include/media/davinci/
  
  TI ENHANCED QUADRATURE ENCODER PULSE (eQEP) DRIVER
@@@ -20599,7 -20488,7 +20598,7 @@@ S:   Odd fixe
  W:    https://linuxtv.org
  T:    git git://linuxtv.org/media_tree.git
  F:    Documentation/admin-guide/media/tm6000*
 -F:    drivers/media/usb/tm6000/
 +F:    drivers/staging/media/deprecated/tm6000/
  
  TMIO/SDHI MMC DRIVER
  M:    Wolfram Sang <wsa+renesas@sang-engineering.com>
@@@ -20699,10 -20588,9 +20698,10 @@@ F: include/linux/toshiba.
  F:    include/uapi/linux/toshiba.h
  
  TOSHIBA TC358743 DRIVER
 -M:    Mats Randgaard <matrandg@cisco.com>
 +M:    Hans Verkuil <hverkuil-cisco@xs4all.nl>
  L:    linux-media@vger.kernel.org
  S:    Maintained
 +F:    Documentation/devicetree/bindings/media/i2c/tc358743.txt
  F:    drivers/media/i2c/tc358743*
  F:    include/media/i2c/tc358743.h
  
@@@ -20723,13 -20611,6 +20722,13 @@@ Q: https://patchwork.kernel.org/project
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git
  F:    drivers/char/tpm/
  
 +TPS546D24 DRIVER
 +M:    Duke Du <dukedu83@gmail.com>
 +L:    linux-hwmon@vger.kernel.org
 +S:    Maintained
 +F:    Documentation/hwmon/tps546d24.rst
 +F:    drivers/hwmon/pmbus/tps546d24.c
 +
  TRACING
  M:    Steven Rostedt <rostedt@goodmis.org>
  M:    Ingo Molnar <mingo@redhat.com>
@@@ -21358,7 -21239,7 +21357,7 @@@ S:   Maintaine
  W:    http://royale.zerezo.com/zr364xx/
  T:    git git://linuxtv.org/media_tree.git
  F:    Documentation/admin-guide/media/zr364xx*
 -F:    drivers/media/usb/zr364xx/
 +F:    drivers/staging/media/deprecated/zr364xx/
  
  USER-MODE LINUX (UML)
  M:    Richard Weinberger <richard@nod.at>
@@@ -21933,7 -21814,7 +21932,7 @@@ F:   lib/test_scanf.
  F:    lib/vsprintf.c
  
  VT1211 HARDWARE MONITOR DRIVER
 -M:    Juerg Haefliger <juergh@gmail.com>
 +M:    Juerg Haefliger <juergh@proton.me>
  L:    linux-hwmon@vger.kernel.org
  S:    Maintained
  F:    Documentation/hwmon/vt1211.rst
@@@ -21992,11 -21873,9 +21991,11 @@@ F: drivers/input/tablet/wacom_serial4.
  
  WANGXUN ETHERNET DRIVER
  M:    Jiawen Wu <jiawenwu@trustnetic.com>
 +M:    Mengyuan Lou <mengyuanlou@net-swift.com>
 +W:    https://www.net-swift.com
  L:    netdev@vger.kernel.org
  S:    Maintained
 -F:    Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
 +F:    Documentation/networking/device_drivers/ethernet/wangxun/*
  F:    drivers/net/ethernet/wangxun/
  
  WATCHDOG DEVICE DRIVERS
@@@ -5,10 -5,8 +5,10 @@@
  
  #include <dt-bindings/clock/imx8mp-clock.h>
  #include <dt-bindings/power/imx8mp-power.h>
 +#include <dt-bindings/reset/imx8mp-reset.h>
  #include <dt-bindings/gpio/gpio.h>
  #include <dt-bindings/input/input.h>
 +#include <dt-bindings/interconnect/fsl,imx8mp.h>
  #include <dt-bindings/interrupt-controller/arm-gic.h>
  #include <dt-bindings/thermal/thermal.h>
  
                                        wakeup-source;
                                        status = "disabled";
                                };
 +
 +                              snvs_lpgpr: snvs-lpgpr {
 +                                      compatible = "fsl,imx8mp-snvs-lpgpr",
 +                                                   "fsl,imx7d-snvs-lpgpr";
 +                              };
                        };
  
                        clk: clock-controller@30380000 {
                                                reg = <IMX8MP_POWER_DOMAIN_MEDIAMIX_ISPDWP>;
                                                clocks = <&clk IMX8MP_CLK_MEDIA_ISP_ROOT>;
                                        };
 +
 +                                      pgc_vpumix: power-domain@19 {
 +                                              #power-domain-cells = <0>;
 +                                              reg = <IMX8MP_POWER_DOMAIN_VPUMIX>;
 +                                              clocks =<&clk IMX8MP_CLK_VPU_ROOT>;
 +                                      };
 +
 +                                      pgc_vpu_g1: power-domain@20 {
 +                                              #power-domain-cells = <0>;
 +                                              power-domains = <&pgc_vpumix>;
 +                                              reg = <IMX8MP_POWER_DOMAIN_VPU_G1>;
 +                                              clocks = <&clk IMX8MP_CLK_VPU_G1_ROOT>;
 +                                      };
 +
 +                                      pgc_vpu_g2: power-domain@21 {
 +                                              #power-domain-cells = <0>;
 +                                              power-domains = <&pgc_vpumix>;
 +                                              reg = <IMX8MP_POWER_DOMAIN_VPU_G2>;
 +                                              clocks = <&clk IMX8MP_CLK_VPU_G2_ROOT>;
 +                                      };
 +
 +                                      pgc_vpu_vc8000e: power-domain@22 {
 +                                              #power-domain-cells = <0>;
 +                                              power-domains = <&pgc_vpumix>;
 +                                              reg = <IMX8MP_POWER_DOMAIN_VPU_VC8000E>;
 +                                              clocks = <&clk IMX8MP_CLK_VPU_VC8KE_ROOT>;
 +                                      };
                                };
                        };
                };
                                                     "lcdif1", "isi", "mipi-csi2",
                                                     "lcdif2", "isp", "dwe",
                                                     "mipi-dsi2";
 +                              interconnects =
 +                                      <&noc IMX8MP_ICM_LCDIF_RD &noc IMX8MP_ICN_MEDIA>,
 +                                      <&noc IMX8MP_ICM_LCDIF_WR &noc IMX8MP_ICN_MEDIA>,
 +                                      <&noc IMX8MP_ICM_ISI0 &noc IMX8MP_ICN_MEDIA>,
 +                                      <&noc IMX8MP_ICM_ISI1 &noc IMX8MP_ICN_MEDIA>,
 +                                      <&noc IMX8MP_ICM_ISI2 &noc IMX8MP_ICN_MEDIA>,
 +                                      <&noc IMX8MP_ICM_ISP0 &noc IMX8MP_ICN_MEDIA>,
 +                                      <&noc IMX8MP_ICM_ISP1 &noc IMX8MP_ICN_MEDIA>,
 +                                      <&noc IMX8MP_ICM_DWE &noc IMX8MP_ICN_MEDIA>;
 +                              interconnect-names = "lcdif-rd", "lcdif-wr", "isi0",
 +                                                   "isi1", "isi2", "isp0", "isp1",
 +                                                   "dwe";
                                clocks = <&clk IMX8MP_CLK_MEDIA_APB_ROOT>,
                                         <&clk IMX8MP_CLK_MEDIA_AXI_ROOT>,
                                         <&clk IMX8MP_CLK_MEDIA_CAM1_PIX_ROOT>,
                                #power-domain-cells = <1>;
                        };
  
 +                      pcie_phy: pcie-phy@32f00000 {
 +                              compatible = "fsl,imx8mp-pcie-phy";
 +                              reg = <0x32f00000 0x10000>;
 +                              resets = <&src IMX8MP_RESET_PCIEPHY>,
 +                                       <&src IMX8MP_RESET_PCIEPHY_PERST>;
 +                              reset-names = "pciephy", "perst";
 +                              power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_PCIE_PHY>;
 +                              #phy-cells = <0>;
 +                              status = "disabled";
 +                      };
 +
                        hsio_blk_ctrl: blk-ctrl@32f10000 {
                                compatible = "fsl,imx8mp-hsio-blk-ctrl", "syscon";
                                reg = <0x32f10000 0x24>;
                                                <&pgc_hsiomix>, <&pgc_pcie_phy>;
                                power-domain-names = "bus", "usb", "usb-phy1",
                                                     "usb-phy2", "pcie", "pcie-phy";
 +                              interconnects = <&noc IMX8MP_ICM_NOC_PCIE &noc IMX8MP_ICN_HSIO>,
 +                                              <&noc IMX8MP_ICM_USB1 &noc IMX8MP_ICN_HSIO>,
 +                                              <&noc IMX8MP_ICM_USB2 &noc IMX8MP_ICN_HSIO>,
 +                                              <&noc IMX8MP_ICM_PCIE &noc IMX8MP_ICN_HSIO>;
 +                              interconnect-names = "noc-pcie", "usb1", "usb2", "pcie";
                                #power-domain-cells = <1>;
                        };
                };
  
 +              pcie: pcie@33800000 {
 +                      compatible = "fsl,imx8mp-pcie";
 +                      reg = <0x33800000 0x400000>, <0x1ff00000 0x80000>;
 +                      reg-names = "dbi", "config";
 +                      #address-cells = <3>;
 +                      #size-cells = <2>;
 +                      device_type = "pci";
 +                      bus-range = <0x00 0xff>;
 +                      ranges =  <0x81000000 0 0x00000000 0x1ff80000 0 0x00010000>, /* downstream I/O 64KB */
 +                                <0x82000000 0 0x18000000 0x18000000 0 0x07f00000>; /* non-prefetchable memory */
 +                      num-lanes = <1>;
 +                      num-viewport = <4>;
 +                      interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
 +                      interrupt-names = "msi";
 +                      #interrupt-cells = <1>;
 +                      interrupt-map-mask = <0 0 0 0x7>;
 +                      interrupt-map = <0 0 0 1 &gic GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>,
 +                                      <0 0 0 2 &gic GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>,
 +                                      <0 0 0 3 &gic GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>,
 +                                      <0 0 0 4 &gic GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
 +                      fsl,max-link-speed = <3>;
 +                      linux,pci-domain = <0>;
 +                      power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_PCIE>;
 +                      resets = <&src IMX8MP_RESET_PCIE_CTRL_APPS_EN>,
 +                               <&src IMX8MP_RESET_PCIE_CTRL_APPS_TURNOFF>;
 +                      reset-names = "apps", "turnoff";
 +                      phys = <&pcie_phy>;
 +                      phy-names = "pcie-phy";
 +                      status = "disabled";
 +              };
 +
                gpu3d: gpu@38000000 {
                        compatible = "vivante,gc";
                        reg = <0x38000000 0x8000>;
                        power-domains = <&pgc_gpu2d>;
                };
  
 +              vpumix_blk_ctrl: blk-ctrl@38330000 {
 +                      compatible = "fsl,imx8mp-vpu-blk-ctrl", "syscon";
 +                      reg = <0x38330000 0x100>;
 +                      #power-domain-cells = <1>;
 +                      power-domains = <&pgc_vpumix>, <&pgc_vpu_g1>,
 +                                      <&pgc_vpu_g2>, <&pgc_vpu_vc8000e>;
 +                      power-domain-names = "bus", "g1", "g2", "vc8000e";
 +                      clocks = <&clk IMX8MP_CLK_VPU_G1_ROOT>,
 +                               <&clk IMX8MP_CLK_VPU_G2_ROOT>,
 +                               <&clk IMX8MP_CLK_VPU_VC8KE_ROOT>;
 +                      clock-names = "g1", "g2", "vc8000e";
 +                      interconnects = <&noc IMX8MP_ICM_VPU_G1 &noc IMX8MP_ICN_VIDEO>,
 +                                      <&noc IMX8MP_ICM_VPU_G2 &noc IMX8MP_ICN_VIDEO>,
 +                                      <&noc IMX8MP_ICM_VPU_H1 &noc IMX8MP_ICN_VIDEO>;
 +                      interconnect-names = "g1", "g2", "vc8000e";
 +              };
 +
                gic: interrupt-controller@38800000 {
                        compatible = "arm,gic-v3";
                        reg = <0x38800000 0x10000>,
                                interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
                                phys = <&usb3_phy0>, <&usb3_phy0>;
                                phy-names = "usb2-phy", "usb3-phy";
-                               snps,dis-u2-freeclk-exists-quirk;
+                               snps,gfladj-refclk-lpm-sel-quirk;
                        };
  
                };
                                interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
                                phys = <&usb3_phy1>, <&usb3_phy1>;
                                phy-names = "usb2-phy", "usb3-phy";
-                               snps,dis-u2-freeclk-exists-quirk;
+                               snps,gfladj-refclk-lpm-sel-quirk;
                        };
                };
  
@@@ -18,6 -18,7 +18,7 @@@
  #include <linux/workqueue.h>
  #include <linux/atomic.h>
  #include <media/v4l2-ctrls.h>
+ #include <media/v4l2-uvc.h>
  
  #include "uvcvideo.h"
  
@@@ -985,56 -986,36 +986,56 @@@ static s32 __uvc_ctrl_get_value(struct 
        return value;
  }
  
 -static int __uvc_ctrl_get(struct uvc_video_chain *chain,
 -      struct uvc_control *ctrl, struct uvc_control_mapping *mapping,
 -      s32 *value)
 +static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
 +                             struct uvc_control *ctrl)
  {
 +      u8 *data;
        int ret;
  
 -      if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
 -              return -EACCES;
 +      if (ctrl->loaded)
 +              return 0;
  
 -      if (!ctrl->loaded) {
 -              if (ctrl->entity->get_cur) {
 -                      ret = ctrl->entity->get_cur(chain->dev,
 -                              ctrl->entity,
 -                              ctrl->info.selector,
 -                              uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
 -                              ctrl->info.size);
 -              } else {
 -                      ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
 -                              ctrl->entity->id,
 -                              chain->dev->intfnum,
 -                              ctrl->info.selector,
 -                              uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
 -                              ctrl->info.size);
 -              }
 -              if (ret < 0)
 -                      return ret;
 +      data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT);
  
 +      if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) {
 +              memset(data, 0, ctrl->info.size);
                ctrl->loaded = 1;
 +
 +              return 0;
        }
  
 +      if (ctrl->entity->get_cur)
 +              ret = ctrl->entity->get_cur(chain->dev, ctrl->entity,
 +                                          ctrl->info.selector, data,
 +                                          ctrl->info.size);
 +      else
 +              ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
 +                                   ctrl->entity->id, chain->dev->intfnum,
 +                                   ctrl->info.selector, data,
 +                                   ctrl->info.size);
 +
 +      if (ret < 0)
 +              return ret;
 +
 +      ctrl->loaded = 1;
 +
 +      return ret;
 +}
 +
 +static int __uvc_ctrl_get(struct uvc_video_chain *chain,
 +                        struct uvc_control *ctrl,
 +                        struct uvc_control_mapping *mapping,
 +                        s32 *value)
 +{
 +      int ret;
 +
 +      if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
 +              return -EACCES;
 +
 +      ret = __uvc_ctrl_load_cur(chain, ctrl);
 +      if (ret < 0)
 +              return ret;
 +
        *value = __uvc_ctrl_get_value(mapping,
                                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
  
@@@ -1830,10 -1811,21 +1831,10 @@@ int uvc_ctrl_set(struct uvc_fh *handle
         * needs to be loaded from the device to perform the read-modify-write
         * operation.
         */
 -      if (!ctrl->loaded && (ctrl->info.size * 8) != mapping->size) {
 -              if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) {
 -                      memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
 -                              0, ctrl->info.size);
 -              } else {
 -                      ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
 -                              ctrl->entity->id, chain->dev->intfnum,
 -                              ctrl->info.selector,
 -                              uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
 -                              ctrl->info.size);
 -                      if (ret < 0)
 -                              return ret;
 -              }
 -
 -              ctrl->loaded = 1;
 +      if ((ctrl->info.size * 8) != mapping->size) {
 +              ret = __uvc_ctrl_load_cur(chain, ctrl);
 +              if (ret < 0)
 +                      return ret;
        }
  
        /* Backup the current value in case we need to rollback later. */
@@@ -2420,9 -2412,10 +2421,9 @@@ static void uvc_ctrl_prune_entity(struc
  static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
                               struct uvc_control *ctrl)
  {
 -      const struct uvc_control_info *info = uvc_ctrls;
 -      const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls);
 -      const struct uvc_control_mapping *mapping;
 -      const struct uvc_control_mapping *mend;
 +      const struct uvc_control_mapping *mappings;
 +      unsigned int num_mappings;
 +      unsigned int i;
  
        /*
         * XU controls initialization requires querying the device for control
        if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT)
                return;
  
 -      for (; info < iend; ++info) {
 +      for (i = 0; i < ARRAY_SIZE(uvc_ctrls); ++i) {
 +              const struct uvc_control_info *info = &uvc_ctrls[i];
 +
                if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
                    ctrl->index == info->index) {
                        uvc_ctrl_add_info(chain->dev, ctrl, info);
         */
        if (chain->dev->info->mappings) {
                bool custom = false;
 -              unsigned int i;
  
 -              for (i = 0; (mapping = chain->dev->info->mappings[i]); ++i) {
 +              for (i = 0; chain->dev->info->mappings[i]; ++i) {
 +                      const struct uvc_control_mapping *mapping =
 +                              chain->dev->info->mappings[i];
 +
                        if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
                            ctrl->info.selector == mapping->selector) {
                                __uvc_ctrl_add_mapping(chain, ctrl, mapping);
        }
  
        /* Process common mappings next. */
 -      mapping = uvc_ctrl_mappings;
 -      mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings);
 +      for (i = 0; i < ARRAY_SIZE(uvc_ctrl_mappings); ++i) {
 +              const struct uvc_control_mapping *mapping = &uvc_ctrl_mappings[i];
  
 -      for (; mapping < mend; ++mapping) {
                if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
                    ctrl->info.selector == mapping->selector)
                        __uvc_ctrl_add_mapping(chain, ctrl, mapping);
  
        /* Finally process version-specific mappings. */
        if (chain->dev->uvc_version < 0x0150) {
 -              mapping = uvc_ctrl_mappings_uvc11;
 -              mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings_uvc11);
 +              mappings = uvc_ctrl_mappings_uvc11;
 +              num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc11);
        } else {
 -              mapping = uvc_ctrl_mappings_uvc15;
 -              mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings_uvc15);
 +              mappings = uvc_ctrl_mappings_uvc15;
 +              num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc15);
        }
  
 -      for (; mapping < mend; ++mapping) {
 +      for (i = 0; i < num_mappings; ++i) {
 +              const struct uvc_control_mapping *mapping = &mappings[i];
 +
                if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
                    ctrl->info.selector == mapping->selector)
                        __uvc_ctrl_add_mapping(chain, ctrl, mapping);
@@@ -20,6 -20,7 +20,7 @@@
  
  #include <media/v4l2-common.h>
  #include <media/v4l2-ioctl.h>
+ #include <media/v4l2-uvc.h>
  
  #include "uvcvideo.h"
  
@@@ -35,198 -36,6 +36,6 @@@ unsigned int uvc_dbg_param
  unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
  
  /* ------------------------------------------------------------------------
-  * Video formats
-  */
- static struct uvc_format_desc uvc_fmts[] = {
-       {
-               .name           = "YUV 4:2:2 (YUYV)",
-               .guid           = UVC_GUID_FORMAT_YUY2,
-               .fcc            = V4L2_PIX_FMT_YUYV,
-       },
-       {
-               .name           = "YUV 4:2:2 (YUYV)",
-               .guid           = UVC_GUID_FORMAT_YUY2_ISIGHT,
-               .fcc            = V4L2_PIX_FMT_YUYV,
-       },
-       {
-               .name           = "YUV 4:2:0 (NV12)",
-               .guid           = UVC_GUID_FORMAT_NV12,
-               .fcc            = V4L2_PIX_FMT_NV12,
-       },
-       {
-               .name           = "MJPEG",
-               .guid           = UVC_GUID_FORMAT_MJPEG,
-               .fcc            = V4L2_PIX_FMT_MJPEG,
-       },
-       {
-               .name           = "YVU 4:2:0 (YV12)",
-               .guid           = UVC_GUID_FORMAT_YV12,
-               .fcc            = V4L2_PIX_FMT_YVU420,
-       },
-       {
-               .name           = "YUV 4:2:0 (I420)",
-               .guid           = UVC_GUID_FORMAT_I420,
-               .fcc            = V4L2_PIX_FMT_YUV420,
-       },
-       {
-               .name           = "YUV 4:2:0 (M420)",
-               .guid           = UVC_GUID_FORMAT_M420,
-               .fcc            = V4L2_PIX_FMT_M420,
-       },
-       {
-               .name           = "YUV 4:2:2 (UYVY)",
-               .guid           = UVC_GUID_FORMAT_UYVY,
-               .fcc            = V4L2_PIX_FMT_UYVY,
-       },
-       {
-               .name           = "Greyscale 8-bit (Y800)",
-               .guid           = UVC_GUID_FORMAT_Y800,
-               .fcc            = V4L2_PIX_FMT_GREY,
-       },
-       {
-               .name           = "Greyscale 8-bit (Y8  )",
-               .guid           = UVC_GUID_FORMAT_Y8,
-               .fcc            = V4L2_PIX_FMT_GREY,
-       },
-       {
-               .name           = "Greyscale 8-bit (D3DFMT_L8)",
-               .guid           = UVC_GUID_FORMAT_D3DFMT_L8,
-               .fcc            = V4L2_PIX_FMT_GREY,
-       },
-       {
-               .name           = "IR 8-bit (L8_IR)",
-               .guid           = UVC_GUID_FORMAT_KSMEDIA_L8_IR,
-               .fcc            = V4L2_PIX_FMT_GREY,
-       },
-       {
-               .name           = "Greyscale 10-bit (Y10 )",
-               .guid           = UVC_GUID_FORMAT_Y10,
-               .fcc            = V4L2_PIX_FMT_Y10,
-       },
-       {
-               .name           = "Greyscale 12-bit (Y12 )",
-               .guid           = UVC_GUID_FORMAT_Y12,
-               .fcc            = V4L2_PIX_FMT_Y12,
-       },
-       {
-               .name           = "Greyscale 16-bit (Y16 )",
-               .guid           = UVC_GUID_FORMAT_Y16,
-               .fcc            = V4L2_PIX_FMT_Y16,
-       },
-       {
-               .name           = "BGGR Bayer (BY8 )",
-               .guid           = UVC_GUID_FORMAT_BY8,
-               .fcc            = V4L2_PIX_FMT_SBGGR8,
-       },
-       {
-               .name           = "BGGR Bayer (BA81)",
-               .guid           = UVC_GUID_FORMAT_BA81,
-               .fcc            = V4L2_PIX_FMT_SBGGR8,
-       },
-       {
-               .name           = "GBRG Bayer (GBRG)",
-               .guid           = UVC_GUID_FORMAT_GBRG,
-               .fcc            = V4L2_PIX_FMT_SGBRG8,
-       },
-       {
-               .name           = "GRBG Bayer (GRBG)",
-               .guid           = UVC_GUID_FORMAT_GRBG,
-               .fcc            = V4L2_PIX_FMT_SGRBG8,
-       },
-       {
-               .name           = "RGGB Bayer (RGGB)",
-               .guid           = UVC_GUID_FORMAT_RGGB,
-               .fcc            = V4L2_PIX_FMT_SRGGB8,
-       },
-       {
-               .name           = "RGB565",
-               .guid           = UVC_GUID_FORMAT_RGBP,
-               .fcc            = V4L2_PIX_FMT_RGB565,
-       },
-       {
-               .name           = "BGR 8:8:8 (BGR3)",
-               .guid           = UVC_GUID_FORMAT_BGR3,
-               .fcc            = V4L2_PIX_FMT_BGR24,
-       },
-       {
-               .name           = "H.264",
-               .guid           = UVC_GUID_FORMAT_H264,
-               .fcc            = V4L2_PIX_FMT_H264,
-       },
-       {
-               .name           = "H.265",
-               .guid           = UVC_GUID_FORMAT_H265,
-               .fcc            = V4L2_PIX_FMT_HEVC,
-       },
-       {
-               .name           = "Greyscale 8 L/R (Y8I)",
-               .guid           = UVC_GUID_FORMAT_Y8I,
-               .fcc            = V4L2_PIX_FMT_Y8I,
-       },
-       {
-               .name           = "Greyscale 12 L/R (Y12I)",
-               .guid           = UVC_GUID_FORMAT_Y12I,
-               .fcc            = V4L2_PIX_FMT_Y12I,
-       },
-       {
-               .name           = "Depth data 16-bit (Z16)",
-               .guid           = UVC_GUID_FORMAT_Z16,
-               .fcc            = V4L2_PIX_FMT_Z16,
-       },
-       {
-               .name           = "Bayer 10-bit (SRGGB10P)",
-               .guid           = UVC_GUID_FORMAT_RW10,
-               .fcc            = V4L2_PIX_FMT_SRGGB10P,
-       },
-       {
-               .name           = "Bayer 16-bit (SBGGR16)",
-               .guid           = UVC_GUID_FORMAT_BG16,
-               .fcc            = V4L2_PIX_FMT_SBGGR16,
-       },
-       {
-               .name           = "Bayer 16-bit (SGBRG16)",
-               .guid           = UVC_GUID_FORMAT_GB16,
-               .fcc            = V4L2_PIX_FMT_SGBRG16,
-       },
-       {
-               .name           = "Bayer 16-bit (SRGGB16)",
-               .guid           = UVC_GUID_FORMAT_RG16,
-               .fcc            = V4L2_PIX_FMT_SRGGB16,
-       },
-       {
-               .name           = "Bayer 16-bit (SGRBG16)",
-               .guid           = UVC_GUID_FORMAT_GR16,
-               .fcc            = V4L2_PIX_FMT_SGRBG16,
-       },
-       {
-               .name           = "Depth data 16-bit (Z16)",
-               .guid           = UVC_GUID_FORMAT_INVZ,
-               .fcc            = V4L2_PIX_FMT_Z16,
-       },
-       {
-               .name           = "Greyscale 10-bit (Y10 )",
-               .guid           = UVC_GUID_FORMAT_INVI,
-               .fcc            = V4L2_PIX_FMT_Y10,
-       },
-       {
-               .name           = "IR:Depth 26-bit (INZI)",
-               .guid           = UVC_GUID_FORMAT_INZI,
-               .fcc            = V4L2_PIX_FMT_INZI,
-       },
-       {
-               .name           = "4-bit Depth Confidence (Packed)",
-               .guid           = UVC_GUID_FORMAT_CNF4,
-               .fcc            = V4L2_PIX_FMT_CNF4,
-       },
-       {
-               .name           = "HEVC",
-               .guid           = UVC_GUID_FORMAT_HEVC,
-               .fcc            = V4L2_PIX_FMT_HEVC,
-       },
- };
- /* ------------------------------------------------------------------------
   * Utility functions
   */
  
@@@ -245,19 -54,6 +54,6 @@@ struct usb_host_endpoint *uvc_find_endp
        return NULL;
  }
  
- static struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16])
- {
-       unsigned int len = ARRAY_SIZE(uvc_fmts);
-       unsigned int i;
-       for (i = 0; i < len; ++i) {
-               if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
-                       return &uvc_fmts[i];
-       }
-       return NULL;
- }
  static enum v4l2_colorspace uvc_colorspace(const u8 primaries)
  {
        static const enum v4l2_colorspace colorprimaries[] = {
@@@ -329,90 -125,6 +125,6 @@@ static enum v4l2_ycbcr_encoding uvc_ycb
        return V4L2_YCBCR_ENC_DEFAULT;  /* Reserved */
  }
  
- /*
-  * Simplify a fraction using a simple continued fraction decomposition. The
-  * idea here is to convert fractions such as 333333/10000000 to 1/30 using
-  * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
-  * arbitrary parameters to remove non-significative terms from the simple
-  * continued fraction decomposition. Using 8 and 333 for n_terms and threshold
-  * respectively seems to give nice results.
-  */
- void uvc_simplify_fraction(u32 *numerator, u32 *denominator,
-               unsigned int n_terms, unsigned int threshold)
- {
-       u32 *an;
-       u32 x, y, r;
-       unsigned int i, n;
-       an = kmalloc_array(n_terms, sizeof(*an), GFP_KERNEL);
-       if (an == NULL)
-               return;
-       /*
-        * Convert the fraction to a simple continued fraction. See
-        * https://en.wikipedia.org/wiki/Continued_fraction
-        * Stop if the current term is bigger than or equal to the given
-        * threshold.
-        */
-       x = *numerator;
-       y = *denominator;
-       for (n = 0; n < n_terms && y != 0; ++n) {
-               an[n] = x / y;
-               if (an[n] >= threshold) {
-                       if (n < 2)
-                               n++;
-                       break;
-               }
-               r = x - an[n] * y;
-               x = y;
-               y = r;
-       }
-       /* Expand the simple continued fraction back to an integer fraction. */
-       x = 0;
-       y = 1;
-       for (i = n; i > 0; --i) {
-               r = y;
-               y = an[i-1] * y + x;
-               x = r;
-       }
-       *numerator = y;
-       *denominator = x;
-       kfree(an);
- }
- /*
-  * Convert a fraction to a frame interval in 100ns multiples. The idea here is
-  * to compute numerator / denominator * 10000000 using 32 bit fixed point
-  * arithmetic only.
-  */
- u32 uvc_fraction_to_interval(u32 numerator, u32 denominator)
- {
-       u32 multiplier;
-       /* Saturate the result if the operation would overflow. */
-       if (denominator == 0 ||
-           numerator/denominator >= ((u32)-1)/10000000)
-               return (u32)-1;
-       /*
-        * Divide both the denominator and the multiplier by two until
-        * numerator * multiplier doesn't overflow. If anyone knows a better
-        * algorithm please let me know.
-        */
-       multiplier = 10000000;
-       while (numerator > ((u32)-1)/multiplier) {
-               multiplier /= 2;
-               denominator /= 2;
-       }
-       return denominator ? numerator * multiplier / denominator : 0;
- }
  /* ------------------------------------------------------------------------
   * Terminal and unit management
   */
@@@ -1553,6 -1265,10 +1265,6 @@@ static int uvc_gpio_parse(struct uvc_de
        if (IS_ERR_OR_NULL(gpio_privacy))
                return PTR_ERR_OR_ZERO(gpio_privacy);
  
 -      unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1);
 -      if (!unit)
 -              return -ENOMEM;
 -
        irq = gpiod_to_irq(gpio_privacy);
        if (irq < 0) {
                if (irq != EPROBE_DEFER)
                return irq;
        }
  
 +      unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1);
 +      if (!unit)
 +              return -ENOMEM;
 +
        unit->gpio.gpio_privacy = gpio_privacy;
        unit->gpio.irq = irq;
        unit->gpio.bControlSize = 1;
@@@ -2740,7 -2452,7 +2452,7 @@@ static const struct usb_device_id uvc_i
          .idProduct            = 0x4034,
          .bInterfaceClass      = USB_CLASS_VIDEO,
          .bInterfaceSubClass   = 1,
 -        .bInterfaceProtocol   = 0,
 +        .bInterfaceProtocol   = UVC_PC_PROTOCOL_15,
          .driver_info          = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
        /* LogiLink Wireless Webcam */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) },
 +      /* Sonix Technology USB 2.0 Camera */
 +      { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
 +                              | USB_DEVICE_ID_MATCH_INT_INFO,
 +        .idVendor             = 0x3277,
 +        .idProduct            = 0x0072,
 +        .bInterfaceClass      = USB_CLASS_VIDEO,
 +        .bInterfaceSubClass   = 1,
 +        .bInterfaceProtocol   = 0,
 +        .driver_info          = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
        /* Acer EasyCamera */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@@ -1,6 -1,6 +1,6 @@@
  // SPDX-License-Identifier: GPL-2.0
  /*
-  * Copyright (c) 2016-2020, NVIDIA CORPORATION.  All rights reserved.
+  * Copyright (c) 2016-2022, NVIDIA CORPORATION.  All rights reserved.
   */
  
  #include <linux/delay.h>
@@@ -638,7 -638,7 +638,7 @@@ static void tegra186_utmi_bias_pad_powe
        mutex_unlock(&padctl->lock);
  }
  
- static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
+ static void tegra186_utmi_pad_power_on(struct phy *phy)
  {
        struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
        struct tegra_xusb_padctl *padctl = lane->pad->padctl;
                return;
        }
  
+       dev_dbg(dev, "power on UTMI pad %u\n", index);
        tegra186_utmi_bias_pad_power_on(padctl);
  
        udelay(2);
        padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
  }
  
- static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
+ static void tegra186_utmi_pad_power_down(struct phy *phy)
  {
        struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
        struct tegra_xusb_padctl *padctl = lane->pad->padctl;
        if (!phy)
                return;
  
+       dev_dbg(padctl->dev, "power down UTMI pad %u\n", index);
        value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
        value |= USB2_OTG_PD;
        padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
@@@ -849,15 -853,14 +853,14 @@@ static int tegra186_utmi_phy_power_on(s
        value |= RPD_CTRL(priv->calib.rpd_ctrl);
        padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
  
-       /* TODO: pad power saving */
-       tegra_phy_xusb_utmi_pad_power_on(phy);
+       tegra186_utmi_pad_power_on(phy);
        return 0;
  }
  
  static int tegra186_utmi_phy_power_off(struct phy *phy)
  {
-       /* TODO: pad power saving */
-       tegra_phy_xusb_utmi_pad_power_down(phy);
+       tegra186_utmi_pad_power_down(phy);
  
        return 0;
  }
@@@ -1381,9 -1384,12 +1384,9 @@@ tegra186_xusb_read_fuse_calibration(str
                return -ENOMEM;
  
        err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
 -      if (err) {
 -              if (err != -EPROBE_DEFER)
 -                      dev_err(dev, "failed to read calibration fuse: %d\n",
 -                              err);
 -              return err;
 -      }
 +      if (err)
 +              return dev_err_probe(dev, err,
 +                                   "failed to read calibration fuse\n");
  
        dev_dbg(dev, "FUSE_USB_CALIB_0 %#x\n", value);
  
@@@ -1483,6 -1489,8 +1486,8 @@@ static const struct tegra_xusb_padctl_o
        .suspend_noirq = tegra186_xusb_padctl_suspend_noirq,
        .resume_noirq = tegra186_xusb_padctl_resume_noirq,
        .vbus_override = tegra186_xusb_padctl_vbus_override,
+       .utmi_pad_power_on = tegra186_utmi_pad_power_on,
+       .utmi_pad_power_down = tegra186_utmi_pad_power_down,
  };
  
  #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
diff --combined drivers/phy/tegra/xusb.c
@@@ -1,6 -1,6 +1,6 @@@
  // SPDX-License-Identifier: GPL-2.0-only
  /*
-  * Copyright (c) 2014-2020, NVIDIA CORPORATION.  All rights reserved.
+  * Copyright (c) 2014-2022, NVIDIA CORPORATION.  All rights reserved.
   */
  
  #include <linux/delay.h>
@@@ -656,7 -656,6 +656,7 @@@ static int tegra_xusb_setup_usb_role_sw
        struct usb_role_switch_desc role_sx_desc = {
                .fwnode = dev_fwnode(&port->dev),
                .set = tegra_xusb_role_sw_set,
 +              .allow_userspace_control = true,
        };
        int err = 0;
  
@@@ -1271,7 -1270,7 +1271,7 @@@ static int tegra_xusb_padctl_remove(str
  
        padctl->soc->ops->remove(padctl);
  
 -      return err;
 +      return 0;
  }
  
  static __maybe_unused int tegra_xusb_padctl_suspend_noirq(struct device *dev)
@@@ -1459,6 -1458,26 +1459,26 @@@ int tegra_phy_xusb_utmi_port_reset(stru
  }
  EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset);
  
+ void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
+ {
+       struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+       struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+       if (padctl->soc->ops->utmi_pad_power_on)
+               padctl->soc->ops->utmi_pad_power_on(phy);
+ }
+ EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_on);
+ void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
+ {
+       struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+       struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+       if (padctl->soc->ops->utmi_pad_power_down)
+               padctl->soc->ops->utmi_pad_power_down(phy);
+ }
+ EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_down);
  int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl,
                                    unsigned int port)
  {
  #define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring")
  
  #define RING_FIRST_USABLE_HOPID       1
 -
 +/*
 + * Used with QUIRK_E2E to specify an unused HopID the Rx credits are
 + * transferred.
 + */
 +#define RING_E2E_RESERVED_HOPID       RING_FIRST_USABLE_HOPID
  /*
   * Minimal number of vectors when we use MSI-X. Two for control channel
   * Rx/Tx and the rest four are for cross domain DMA paths.
@@@ -42,9 -38,7 +42,9 @@@
  
  #define NHI_MAILBOX_TIMEOUT   500 /* ms */
  
 +/* Host interface quirks */
  #define QUIRK_AUTO_CLEAR_INT  BIT(0)
 +#define QUIRK_E2E             BIT(1)
  
  static int ring_interrupt_index(struct tb_ring *ring)
  {
@@@ -464,18 -458,8 +464,18 @@@ static void ring_release_msix(struct tb
  
  static int nhi_alloc_hop(struct tb_nhi *nhi, struct tb_ring *ring)
  {
 +      unsigned int start_hop = RING_FIRST_USABLE_HOPID;
        int ret = 0;
  
 +      if (nhi->quirks & QUIRK_E2E) {
 +              start_hop = RING_FIRST_USABLE_HOPID + 1;
 +              if (ring->flags & RING_FLAG_E2E && !ring->is_tx) {
 +                      dev_dbg(&nhi->pdev->dev, "quirking E2E TX HopID %u -> %u\n",
 +                              ring->e2e_tx_hop, RING_E2E_RESERVED_HOPID);
 +                      ring->e2e_tx_hop = RING_E2E_RESERVED_HOPID;
 +              }
 +      }
 +
        spin_lock_irq(&nhi->lock);
  
        if (ring->hop < 0) {
                 * Automatically allocate HopID from the non-reserved
                 * range 1 .. hop_count - 1.
                 */
 -              for (i = RING_FIRST_USABLE_HOPID; i < nhi->hop_count; i++) {
 +              for (i = start_hop; i < nhi->hop_count; i++) {
                        if (ring->is_tx) {
                                if (!nhi->tx_rings[i]) {
                                        ring->hop = i;
                }
        }
  
 +      if (ring->hop > 0 && ring->hop < start_hop) {
 +              dev_warn(&nhi->pdev->dev, "invalid hop: %d\n", ring->hop);
 +              ret = -EINVAL;
 +              goto err_unlock;
 +      }
        if (ring->hop < 0 || ring->hop >= nhi->hop_count) {
                dev_warn(&nhi->pdev->dev, "invalid hop: %d\n", ring->hop);
                ret = -EINVAL;
@@@ -1118,26 -1097,12 +1118,26 @@@ static void nhi_shutdown(struct tb_nhi 
  
  static void nhi_check_quirks(struct tb_nhi *nhi)
  {
 -      /*
 -       * Intel hardware supports auto clear of the interrupt status
 -       * reqister right after interrupt is being issued.
 -       */
 -      if (nhi->pdev->vendor == PCI_VENDOR_ID_INTEL)
 +      if (nhi->pdev->vendor == PCI_VENDOR_ID_INTEL) {
 +              /*
 +               * Intel hardware supports auto clear of the interrupt
 +               * status register right after interrupt is being
 +               * issued.
 +               */
                nhi->quirks |= QUIRK_AUTO_CLEAR_INT;
 +
 +              switch (nhi->pdev->device) {
 +              case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
 +              case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
 +                      /*
 +                       * Falcon Ridge controller needs the end-to-end
 +                       * flow control workaround to avoid losing Rx
 +                       * packets when RING_FLAG_E2E is set.
 +                       */
 +                      nhi->quirks |= QUIRK_E2E;
 +                      break;
 +              }
 +      }
  }
  
  static int nhi_check_iommu_pdev(struct pci_dev *pdev, void *data)
@@@ -1184,6 -1149,7 +1184,7 @@@ static void nhi_check_iommu(struct tb_n
  static int nhi_init_msi(struct tb_nhi *nhi)
  {
        struct pci_dev *pdev = nhi->pdev;
+       struct device *dev = &pdev->dev;
        int res, irq, nvec;
  
        /* In case someone left them on. */
  
                res = devm_request_irq(&pdev->dev, irq, nhi_msi,
                                       IRQF_NO_SUSPEND, "thunderbolt", nhi);
-               if (res) {
-                       dev_err(&pdev->dev, "request_irq failed, aborting\n");
-                       return res;
-               }
+               if (res)
+                       return dev_err_probe(dev, res, "request_irq failed, aborting\n");
        }
  
        return 0;
@@@ -1258,26 -1222,21 +1257,21 @@@ static struct tb *nhi_select_cm(struct 
  
  static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
  {
+       struct device *dev = &pdev->dev;
        struct tb_nhi *nhi;
        struct tb *tb;
        int res;
  
-       if (!nhi_imr_valid(pdev)) {
-               dev_warn(&pdev->dev, "firmware image not valid, aborting\n");
-               return -ENODEV;
-       }
+       if (!nhi_imr_valid(pdev))
+               return dev_err_probe(dev, -ENODEV, "firmware image not valid, aborting\n");
  
        res = pcim_enable_device(pdev);
-       if (res) {
-               dev_err(&pdev->dev, "cannot enable PCI device, aborting\n");
-               return res;
-       }
+       if (res)
+               return dev_err_probe(dev, res, "cannot enable PCI device, aborting\n");
  
        res = pcim_iomap_regions(pdev, 1 << 0, "thunderbolt");
-       if (res) {
-               dev_err(&pdev->dev, "cannot obtain PCI resources, aborting\n");
-               return res;
-       }
+       if (res)
+               return dev_err_probe(dev, res, "cannot obtain PCI resources, aborting\n");
  
        nhi = devm_kzalloc(&pdev->dev, sizeof(*nhi), GFP_KERNEL);
        if (!nhi)
        /* cannot fail - table is allocated in pcim_iomap_regions */
        nhi->iobase = pcim_iomap_table(pdev)[0];
        nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff;
-       dev_dbg(&pdev->dev, "total paths: %d\n", nhi->hop_count);
+       dev_dbg(dev, "total paths: %d\n", nhi->hop_count);
  
        nhi->tx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count,
                                     sizeof(*nhi->tx_rings), GFP_KERNEL);
        nhi_check_iommu(nhi);
  
        res = nhi_init_msi(nhi);
-       if (res) {
-               dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
-               return res;
-       }
+       if (res)
+               return dev_err_probe(dev, res, "cannot enable MSI, aborting\n");
  
        spin_lock_init(&nhi->lock);
  
        res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
-       if (res) {
-               dev_err(&pdev->dev, "failed to set DMA mask\n");
-               return res;
-       }
+       if (res)
+               return dev_err_probe(dev, res, "failed to set DMA mask\n");
  
        pci_set_master(pdev);
  
        }
  
        tb = nhi_select_cm(nhi);
-       if (!tb) {
-               dev_err(&nhi->pdev->dev,
+       if (!tb)
+               return dev_err_probe(dev, -ENODEV,
                        "failed to determine connection manager, aborting\n");
-               return -ENODEV;
-       }
  
-       dev_dbg(&nhi->pdev->dev, "NHI initialized, starting thunderbolt\n");
+       dev_dbg(dev, "NHI initialized, starting thunderbolt\n");
  
        res = tb_domain_add(tb);
        if (res) {
@@@ -1433,6 -1386,7 +1421,7 @@@ static struct pci_device_id nhi_ids[] 
          .driver_data = (kernel_ulong_t)&icl_nhi_ops },
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1),
          .driver_data = (kernel_ulong_t)&icl_nhi_ops },
+       /* Thunderbolt 4 */
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI0),
          .driver_data = (kernel_ulong_t)&icl_nhi_ops },
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI1),
          .driver_data = (kernel_ulong_t)&icl_nhi_ops },
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI1),
          .driver_data = (kernel_ulong_t)&icl_nhi_ops },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_M_NHI0),
+         .driver_data = (kernel_ulong_t)&icl_nhi_ops },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI0),
+         .driver_data = (kernel_ulong_t)&icl_nhi_ops },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI1),
+         .driver_data = (kernel_ulong_t)&icl_nhi_ops },
  
        /* Any USB4 compliant host */
        { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) },
@@@ -19,8 -19,6 +19,6 @@@
  
  /* Switch NVM support */
  
- #define NVM_CSS                       0x10
  struct nvm_auth_status {
        struct list_head list;
        uuid_t uuid;
@@@ -102,70 -100,30 +100,30 @@@ static void nvm_clear_auth_status(cons
  
  static int nvm_validate_and_write(struct tb_switch *sw)
  {
-       unsigned int image_size, hdr_size;
-       const u8 *buf = sw->nvm->buf;
-       u16 ds_size;
+       unsigned int image_size;
+       const u8 *buf;
        int ret;
  
-       if (!buf)
-               return -EINVAL;
-       image_size = sw->nvm->buf_data_size;
-       if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
-               return -EINVAL;
-       /*
-        * FARB pointer must point inside the image and must at least
-        * contain parts of the digital section we will be reading here.
-        */
-       hdr_size = (*(u32 *)buf) & 0xffffff;
-       if (hdr_size + NVM_DEVID + 2 >= image_size)
-               return -EINVAL;
-       /* Digital section start should be aligned to 4k page */
-       if (!IS_ALIGNED(hdr_size, SZ_4K))
-               return -EINVAL;
-       /*
-        * Read digital section size and check that it also fits inside
-        * the image.
-        */
-       ds_size = *(u16 *)(buf + hdr_size);
-       if (ds_size >= image_size)
-               return -EINVAL;
-       if (!sw->safe_mode) {
-               u16 device_id;
+       ret = tb_nvm_validate(sw->nvm);
+       if (ret)
+               return ret;
  
-               /*
-                * Make sure the device ID in the image matches the one
-                * we read from the switch config space.
-                */
-               device_id = *(u16 *)(buf + hdr_size + NVM_DEVID);
-               if (device_id != sw->config.device_id)
-                       return -EINVAL;
-               if (sw->generation < 3) {
-                       /* Write CSS headers first */
-                       ret = dma_port_flash_write(sw->dma_port,
-                               DMA_PORT_CSS_ADDRESS, buf + NVM_CSS,
-                               DMA_PORT_CSS_MAX_SIZE);
-                       if (ret)
-                               return ret;
-               }
+       ret = tb_nvm_write_headers(sw->nvm);
+       if (ret)
+               return ret;
  
-               /* Skip headers in the image */
-               buf += hdr_size;
-               image_size -= hdr_size;
-       }
+       buf = sw->nvm->buf_data_start;
+       image_size = sw->nvm->buf_data_size;
  
        if (tb_switch_is_usb4(sw))
                ret = usb4_switch_nvm_write(sw, 0, buf, image_size);
        else
                ret = dma_port_flash_write(sw->dma_port, 0, buf, image_size);
-       if (!ret)
-               sw->nvm->flushed = true;
-       return ret;
+       if (ret)
+               return ret;
+       sw->nvm->flushed = true;
+       return 0;
  }
  
  static int nvm_authenticate_host_dma_port(struct tb_switch *sw)
@@@ -300,14 -258,6 +258,6 @@@ static inline bool nvm_upgradeable(stru
        return nvm_readable(sw);
  }
  
- static inline int nvm_read(struct tb_switch *sw, unsigned int address,
-                          void *buf, size_t size)
- {
-       if (tb_switch_is_usb4(sw))
-               return usb4_switch_nvm_read(sw, address, buf, size);
-       return dma_port_flash_read(sw->dma_port, address, buf, size);
- }
  static int nvm_authenticate(struct tb_switch *sw, bool auth_only)
  {
        int ret;
        return ret;
  }
  
- static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val,
-                             size_t bytes)
+ /**
+  * tb_switch_nvm_read() - Read router NVM
+  * @sw: Router whose NVM to read
+  * @address: Start address on the NVM
+  * @buf: Buffer where the read data is copied
+  * @size: Size of the buffer in bytes
+  *
+  * Reads from router NVM and returns the requested data in @buf. Locking
+  * is up to the caller. Returns %0 in success and negative errno in case
+  * of failure.
+  */
+ int tb_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
+                      size_t size)
+ {
+       if (tb_switch_is_usb4(sw))
+               return usb4_switch_nvm_read(sw, address, buf, size);
+       return dma_port_flash_read(sw->dma_port, address, buf, size);
+ }
+ static int nvm_read(void *priv, unsigned int offset, void *val, size_t bytes)
  {
        struct tb_nvm *nvm = priv;
        struct tb_switch *sw = tb_to_switch(nvm->dev);
                goto out;
        }
  
-       ret = nvm_read(sw, offset, val, bytes);
+       ret = tb_switch_nvm_read(sw, offset, val, bytes);
        mutex_unlock(&sw->tb->lock);
  
  out:
        return ret;
  }
  
- static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val,
-                              size_t bytes)
+ static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes)
  {
        struct tb_nvm *nvm = priv;
        struct tb_switch *sw = tb_to_switch(nvm->dev);
  static int tb_switch_nvm_add(struct tb_switch *sw)
  {
        struct tb_nvm *nvm;
-       u32 val;
        int ret;
  
        if (!nvm_readable(sw))
                return 0;
  
-       /*
-        * The NVM format of non-Intel hardware is not known so
-        * currently restrict NVM upgrade for Intel hardware. We may
-        * relax this in the future when we learn other NVM formats.
-        */
-       if (sw->config.vendor_id != PCI_VENDOR_ID_INTEL &&
-           sw->config.vendor_id != 0x8087) {
-               dev_info(&sw->dev,
-                        "NVM format of vendor %#x is not known, disabling NVM upgrade\n",
-                        sw->config.vendor_id);
-               return 0;
+       nvm = tb_nvm_alloc(&sw->dev);
+       if (IS_ERR(nvm)) {
+               ret = PTR_ERR(nvm) == -EOPNOTSUPP ? 0 : PTR_ERR(nvm);
+               goto err_nvm;
        }
  
-       nvm = tb_nvm_alloc(&sw->dev);
-       if (IS_ERR(nvm))
-               return PTR_ERR(nvm);
+       ret = tb_nvm_read_version(nvm);
+       if (ret)
+               goto err_nvm;
  
        /*
         * If the switch is in safe-mode the only accessible portion of
         * write new functional NVM.
         */
        if (!sw->safe_mode) {
-               u32 nvm_size, hdr_size;
-               ret = nvm_read(sw, NVM_FLASH_SIZE, &val, sizeof(val));
-               if (ret)
-                       goto err_nvm;
-               hdr_size = sw->generation < 3 ? SZ_8K : SZ_16K;
-               nvm_size = (SZ_1M << (val & 7)) / 8;
-               nvm_size = (nvm_size - hdr_size) / 2;
-               ret = nvm_read(sw, NVM_VERSION, &val, sizeof(val));
-               if (ret)
-                       goto err_nvm;
-               nvm->major = val >> 16;
-               nvm->minor = val >> 8;
-               ret = tb_nvm_add_active(nvm, nvm_size, tb_switch_nvm_read);
+               ret = tb_nvm_add_active(nvm, nvm_read);
                if (ret)
                        goto err_nvm;
        }
  
        if (!sw->no_nvm_upgrade) {
-               ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE,
-                                           tb_switch_nvm_write);
+               ret = tb_nvm_add_non_active(nvm, nvm_write);
                if (ret)
                        goto err_nvm;
        }
        return 0;
  
  err_nvm:
-       tb_nvm_free(nvm);
+       tb_sw_dbg(sw, "NVM upgrade disabled\n");
+       sw->no_nvm_upgrade = true;
+       if (!IS_ERR(nvm))
+               tb_nvm_free(nvm);
        return ret;
  }
  
@@@ -1229,6 -1174,135 +1174,135 @@@ int tb_port_update_credits(struct tb_po
        return tb_port_do_update_credits(port->dual_link_port);
  }
  
+ static int __tb_port_pm_secondary_set(struct tb_port *port, bool secondary)
+ {
+       u32 phy;
+       int ret;
+       ret = tb_port_read(port, &phy, TB_CFG_PORT,
+                          port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+       if (secondary)
+               phy |= LANE_ADP_CS_1_PMS;
+       else
+               phy &= ~LANE_ADP_CS_1_PMS;
+       return tb_port_write(port, &phy, TB_CFG_PORT,
+                            port->cap_phy + LANE_ADP_CS_1, 1);
+ }
+ static int tb_port_pm_secondary_enable(struct tb_port *port)
+ {
+       return __tb_port_pm_secondary_set(port, true);
+ }
+ static int tb_port_pm_secondary_disable(struct tb_port *port)
+ {
+       return __tb_port_pm_secondary_set(port, false);
+ }
+ /* Called for USB4 or Titan Ridge routers only */
+ static bool tb_port_clx_supported(struct tb_port *port, unsigned int clx_mask)
+ {
+       u32 val, mask = 0;
+       bool ret;
+       /* Don't enable CLx in case of two single-lane links */
+       if (!port->bonded && port->dual_link_port)
+               return false;
+       /* Don't enable CLx in case of inter-domain link */
+       if (port->xdomain)
+               return false;
+       if (tb_switch_is_usb4(port->sw)) {
+               if (!usb4_port_clx_supported(port))
+                       return false;
+       } else if (!tb_lc_is_clx_supported(port)) {
+               return false;
+       }
+       if (clx_mask & TB_CL1) {
+               /* CL0s and CL1 are enabled and supported together */
+               mask |= LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT;
+       }
+       if (clx_mask & TB_CL2)
+               mask |= LANE_ADP_CS_0_CL2_SUPPORT;
+       ret = tb_port_read(port, &val, TB_CFG_PORT,
+                          port->cap_phy + LANE_ADP_CS_0, 1);
+       if (ret)
+               return false;
+       return !!(val & mask);
+ }
+ static int __tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable)
+ {
+       u32 phy, mask;
+       int ret;
+       /* CL0s and CL1 are enabled and supported together */
+       if (clx == TB_CL1)
+               mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE;
+       else
+               /* For now we support only CL0s and CL1. Not CL2 */
+               return -EOPNOTSUPP;
+       ret = tb_port_read(port, &phy, TB_CFG_PORT,
+                          port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+       if (enable)
+               phy |= mask;
+       else
+               phy &= ~mask;
+       return tb_port_write(port, &phy, TB_CFG_PORT,
+                            port->cap_phy + LANE_ADP_CS_1, 1);
+ }
+ static int tb_port_clx_disable(struct tb_port *port, enum tb_clx clx)
+ {
+       return __tb_port_clx_set(port, clx, false);
+ }
+ static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx)
+ {
+       return __tb_port_clx_set(port, clx, true);
+ }
+ /**
+  * tb_port_is_clx_enabled() - Is given CL state enabled
+  * @port: USB4 port to check
+  * @clx_mask: Mask of CL states to check
+  *
+  * Returns true if any of the given CL states is enabled for @port.
+  */
+ bool tb_port_is_clx_enabled(struct tb_port *port, unsigned int clx_mask)
+ {
+       u32 val, mask = 0;
+       int ret;
+       if (!tb_port_clx_supported(port, clx_mask))
+               return false;
+       if (clx_mask & TB_CL1)
+               mask |= LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE;
+       if (clx_mask & TB_CL2)
+               mask |= LANE_ADP_CS_1_CL2_ENABLE;
+       ret = tb_port_read(port, &val, TB_CFG_PORT,
+                          port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return false;
+       return !!(val & mask);
+ }
  static int tb_port_start_lane_initialization(struct tb_port *port)
  {
        int ret;
@@@ -1620,7 -1694,7 +1694,7 @@@ static ssize_t authorized_show(struct d
  {
        struct tb_switch *sw = tb_to_switch(dev);
  
-       return sprintf(buf, "%u\n", sw->authorized);
+       return sysfs_emit(buf, "%u\n", sw->authorized);
  }
  
  static int disapprove_switch(struct device *dev, void *not_used)
@@@ -1730,7 -1804,7 +1804,7 @@@ static ssize_t boot_show(struct device 
  {
        struct tb_switch *sw = tb_to_switch(dev);
  
-       return sprintf(buf, "%u\n", sw->boot);
+       return sysfs_emit(buf, "%u\n", sw->boot);
  }
  static DEVICE_ATTR_RO(boot);
  
@@@ -1739,7 -1813,7 +1813,7 @@@ static ssize_t device_show(struct devic
  {
        struct tb_switch *sw = tb_to_switch(dev);
  
-       return sprintf(buf, "%#x\n", sw->device);
+       return sysfs_emit(buf, "%#x\n", sw->device);
  }
  static DEVICE_ATTR_RO(device);
  
@@@ -1748,7 -1822,7 +1822,7 @@@ device_name_show(struct device *dev, st
  {
        struct tb_switch *sw = tb_to_switch(dev);
  
-       return sprintf(buf, "%s\n", sw->device_name ? sw->device_name : "");
+       return sysfs_emit(buf, "%s\n", sw->device_name ?: "");
  }
  static DEVICE_ATTR_RO(device_name);
  
@@@ -1757,7 -1831,7 +1831,7 @@@ generation_show(struct device *dev, str
  {
        struct tb_switch *sw = tb_to_switch(dev);
  
-       return sprintf(buf, "%u\n", sw->generation);
+       return sysfs_emit(buf, "%u\n", sw->generation);
  }
  static DEVICE_ATTR_RO(generation);
  
@@@ -1771,9 -1845,9 +1845,9 @@@ static ssize_t key_show(struct device *
                return restart_syscall();
  
        if (sw->key)
-               ret = sprintf(buf, "%*phN\n", TB_SWITCH_KEY_SIZE, sw->key);
+               ret = sysfs_emit(buf, "%*phN\n", TB_SWITCH_KEY_SIZE, sw->key);
        else
-               ret = sprintf(buf, "\n");
+               ret = sysfs_emit(buf, "\n");
  
        mutex_unlock(&sw->tb->lock);
        return ret;
@@@ -1818,7 -1892,7 +1892,7 @@@ static ssize_t speed_show(struct devic
  {
        struct tb_switch *sw = tb_to_switch(dev);
  
-       return sprintf(buf, "%u.0 Gb/s\n", sw->link_speed);
+       return sysfs_emit(buf, "%u.0 Gb/s\n", sw->link_speed);
  }
  
  /*
@@@ -1833,7 -1907,7 +1907,7 @@@ static ssize_t lanes_show(struct devic
  {
        struct tb_switch *sw = tb_to_switch(dev);
  
-       return sprintf(buf, "%u\n", sw->link_width);
+       return sysfs_emit(buf, "%u\n", sw->link_width);
  }
  
  /*
@@@ -1850,7 -1924,7 +1924,7 @@@ static ssize_t nvm_authenticate_show(st
        u32 status;
  
        nvm_get_auth_status(sw, &status);
-       return sprintf(buf, "%#x\n", status);
+       return sysfs_emit(buf, "%#x\n", status);
  }
  
  static ssize_t nvm_authenticate_sysfs(struct device *dev, const char *buf,
                goto exit_rpm;
        }
  
+       if (sw->no_nvm_upgrade) {
+               ret = -EOPNOTSUPP;
+               goto exit_unlock;
+       }
        /* If NVMem devices are not yet added */
        if (!sw->nvm) {
                ret = -EAGAIN;
@@@ -1954,7 -2033,7 +2033,7 @@@ static ssize_t nvm_version_show(struct 
        else if (!sw->nvm)
                ret = -EAGAIN;
        else
-               ret = sprintf(buf, "%x.%x\n", sw->nvm->major, sw->nvm->minor);
+               ret = sysfs_emit(buf, "%x.%x\n", sw->nvm->major, sw->nvm->minor);
  
        mutex_unlock(&sw->tb->lock);
  
@@@ -1967,7 -2046,7 +2046,7 @@@ static ssize_t vendor_show(struct devic
  {
        struct tb_switch *sw = tb_to_switch(dev);
  
-       return sprintf(buf, "%#x\n", sw->vendor);
+       return sysfs_emit(buf, "%#x\n", sw->vendor);
  }
  static DEVICE_ATTR_RO(vendor);
  
@@@ -1976,7 -2055,7 +2055,7 @@@ vendor_name_show(struct device *dev, st
  {
        struct tb_switch *sw = tb_to_switch(dev);
  
-       return sprintf(buf, "%s\n", sw->vendor_name ? sw->vendor_name : "");
+       return sysfs_emit(buf, "%s\n", sw->vendor_name ?: "");
  }
  static DEVICE_ATTR_RO(vendor_name);
  
@@@ -1985,7 -2064,7 +2064,7 @@@ static ssize_t unique_id_show(struct de
  {
        struct tb_switch *sw = tb_to_switch(dev);
  
-       return sprintf(buf, "%pUb\n", sw->uuid);
+       return sysfs_emit(buf, "%pUb\n", sw->uuid);
  }
  static DEVICE_ATTR_RO(unique_id);
  
@@@ -2413,7 -2492,6 +2492,7 @@@ int tb_switch_configure(struct tb_switc
                 * additional capabilities.
                 */
                sw->config.cmuv = USB4_VERSION_1_0;
 +              sw->config.plug_events_delay = 0xa;
  
                /* Enumerate the switch */
                ret = tb_sw_write(sw, (u32 *)&sw->config + 1, TB_CFG_SWITCH,
@@@ -2822,6 -2900,26 +2901,26 @@@ static void tb_switch_credits_init(stru
                tb_sw_info(sw, "failed to determine preferred buffer allocation, using defaults\n");
  }
  
+ static int tb_switch_port_hotplug_enable(struct tb_switch *sw)
+ {
+       struct tb_port *port;
+       if (tb_switch_is_icm(sw))
+               return 0;
+       tb_switch_for_each_port(sw, port) {
+               int res;
+               if (!port->cap_usb4)
+                       continue;
+               res = usb4_port_hotplug_enable(port);
+               if (res)
+                       return res;
+       }
+       return 0;
+ }
  /**
   * tb_switch_add() - Add a switch to the domain
   * @sw: Switch to add
@@@ -2891,6 -2989,10 +2990,10 @@@ int tb_switch_add(struct tb_switch *sw
                        return ret;
        }
  
+       ret = tb_switch_port_hotplug_enable(sw);
+       if (ret)
+               return ret;
        ret = device_add(&sw->dev);
        if (ret) {
                dev_err(&sw->dev, "failed to add device: %d\n", ret);
@@@ -3362,35 -3464,6 +3465,6 @@@ struct tb_port *tb_switch_find_port(str
        return NULL;
  }
  
- static int __tb_port_pm_secondary_set(struct tb_port *port, bool secondary)
- {
-       u32 phy;
-       int ret;
-       ret = tb_port_read(port, &phy, TB_CFG_PORT,
-                          port->cap_phy + LANE_ADP_CS_1, 1);
-       if (ret)
-               return ret;
-       if (secondary)
-               phy |= LANE_ADP_CS_1_PMS;
-       else
-               phy &= ~LANE_ADP_CS_1_PMS;
-       return tb_port_write(port, &phy, TB_CFG_PORT,
-                            port->cap_phy + LANE_ADP_CS_1, 1);
- }
- static int tb_port_pm_secondary_enable(struct tb_port *port)
- {
-       return __tb_port_pm_secondary_set(port, true);
- }
- static int tb_port_pm_secondary_disable(struct tb_port *port)
- {
-       return __tb_port_pm_secondary_set(port, false);
- }
  static int tb_switch_pm_secondary_resolve(struct tb_switch *sw)
  {
        struct tb_switch *parent = tb_switch_parent(sw);
        return tb_port_pm_secondary_disable(down);
  }
  
- /* Called for USB4 or Titan Ridge routers only */
- static bool tb_port_clx_supported(struct tb_port *port, enum tb_clx clx)
- {
-       u32 mask, val;
-       bool ret;
-       /* Don't enable CLx in case of two single-lane links */
-       if (!port->bonded && port->dual_link_port)
-               return false;
-       /* Don't enable CLx in case of inter-domain link */
-       if (port->xdomain)
-               return false;
-       if (tb_switch_is_usb4(port->sw)) {
-               if (!usb4_port_clx_supported(port))
-                       return false;
-       } else if (!tb_lc_is_clx_supported(port)) {
-               return false;
-       }
-       switch (clx) {
-       case TB_CL1:
-               /* CL0s and CL1 are enabled and supported together */
-               mask = LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT;
-               break;
-       /* For now we support only CL0s and CL1. Not CL2 */
-       case TB_CL2:
-       default:
-               return false;
-       }
-       ret = tb_port_read(port, &val, TB_CFG_PORT,
-                          port->cap_phy + LANE_ADP_CS_0, 1);
-       if (ret)
-               return false;
-       return !!(val & mask);
- }
- static int __tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable)
- {
-       u32 phy, mask;
-       int ret;
-       /* CL0s and CL1 are enabled and supported together */
-       if (clx == TB_CL1)
-               mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE;
-       else
-               /* For now we support only CL0s and CL1. Not CL2 */
-               return -EOPNOTSUPP;
-       ret = tb_port_read(port, &phy, TB_CFG_PORT,
-                          port->cap_phy + LANE_ADP_CS_1, 1);
-       if (ret)
-               return ret;
-       if (enable)
-               phy |= mask;
-       else
-               phy &= ~mask;
-       return tb_port_write(port, &phy, TB_CFG_PORT,
-                            port->cap_phy + LANE_ADP_CS_1, 1);
- }
- static int tb_port_clx_disable(struct tb_port *port, enum tb_clx clx)
- {
-       return __tb_port_clx_set(port, clx, false);
- }
- static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx)
- {
-       return __tb_port_clx_set(port, clx, true);
- }
  static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx)
  {
        struct tb_switch *parent = tb_switch_parent(sw);
diff --combined drivers/thunderbolt/tb.c
@@@ -105,6 -105,32 +105,32 @@@ static void tb_remove_dp_resources(stru
        }
  }
  
+ static void tb_discover_dp_resource(struct tb *tb, struct tb_port *port)
+ {
+       struct tb_cm *tcm = tb_priv(tb);
+       struct tb_port *p;
+       list_for_each_entry(p, &tcm->dp_resources, list) {
+               if (p == port)
+                       return;
+       }
+       tb_port_dbg(port, "DP %s resource available discovered\n",
+                   tb_port_is_dpin(port) ? "IN" : "OUT");
+       list_add_tail(&port->list, &tcm->dp_resources);
+ }
+ static void tb_discover_dp_resources(struct tb *tb)
+ {
+       struct tb_cm *tcm = tb_priv(tb);
+       struct tb_tunnel *tunnel;
+       list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
+               if (tb_tunnel_is_dp(tunnel))
+                       tb_discover_dp_resource(tb, tunnel->dst_port);
+       }
+ }
  static void tb_switch_discover_tunnels(struct tb_switch *sw,
                                       struct list_head *list,
                                       bool alloc_hopids)
@@@ -174,10 -200,10 +200,10 @@@ static void tb_discover_tunnels(struct 
        }
  }
  
 -static int tb_port_configure_xdomain(struct tb_port *port)
 +static int tb_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd)
  {
        if (tb_switch_is_usb4(port->sw))
 -              return usb4_port_configure_xdomain(port);
 +              return usb4_port_configure_xdomain(port, xd);
        return tb_lc_configure_xdomain(port);
  }
  
@@@ -212,7 -238,7 +238,7 @@@ static void tb_scan_xdomain(struct tb_p
                              NULL);
        if (xd) {
                tb_port_at(route, sw)->xdomain = xd;
 -              tb_port_configure_xdomain(port);
 +              tb_port_configure_xdomain(port, xd);
                tb_xdomain_add(xd);
        }
  }
@@@ -1416,8 -1442,11 +1442,11 @@@ static int tb_start(struct tb *tb
         * ICM firmware upgrade needs running firmware and in native
         * mode that is not available so disable firmware upgrade of the
         * root switch.
+        *
+        * However, USB4 routers support NVM firmware upgrade if they
+        * implement the necessary router operations.
         */
-       tb->root_switch->no_nvm_upgrade = true;
+       tb->root_switch->no_nvm_upgrade = !tb_switch_is_usb4(tb->root_switch);
        /* All USB4 routers support runtime PM */
        tb->root_switch->rpm = tb_switch_is_usb4(tb->root_switch);
  
        tb_scan_switch(tb->root_switch);
        /* Find out tunnels created by the boot firmware */
        tb_discover_tunnels(tb);
+       /* Add DP resources from the DP tunnels created by the boot firmware */
+       tb_discover_dp_resources(tb);
        /*
         * If the boot firmware did not create USB 3.x tunnels create them
         * now for the whole topology.
@@@ -1516,7 -1547,7 +1547,7 @@@ static void tb_restore_children(struct 
  
                        tb_restore_children(port->remote->sw);
                } else if (port->xdomain) {
 -                      tb_port_configure_xdomain(port);
 +                      tb_port_configure_xdomain(port, port->xdomain);
                }
        }
  }
diff --combined drivers/thunderbolt/tb.h
  #define NVM_MAX_SIZE          SZ_512K
  #define NVM_DATA_DWORDS               16
  
- /* Intel specific NVM offsets */
- #define NVM_DEVID             0x05
- #define NVM_VERSION           0x08
- #define NVM_FLASH_SIZE                0x45
  /**
   * struct tb_nvm - Structure holding NVM information
   * @dev: Owner of the NVM
   * @minor: Minor version number of the active NVM portion
   * @id: Identifier used with both NVM portions
   * @active: Active portion NVMem device
+  * @active_size: Size in bytes of the active NVM
   * @non_active: Non-active portion NVMem device
   * @buf: Buffer where the NVM image is stored before it is written to
   *     the actual NVM flash device
+  * @buf_data_start: Where the actual image starts after skipping
+  *                possible headers
   * @buf_data_size: Number of bytes actually consumed by the new NVM
   *               image
   * @authenticating: The device is authenticating the new NVM
   * @flushed: The image has been flushed to the storage area
+  * @vops: Router vendor specific NVM operations (optional)
   *
   * The user of this structure needs to handle serialization of possible
   * concurrent access.
   */
  struct tb_nvm {
        struct device *dev;
-       u8 major;
-       u8 minor;
+       u32 major;
+       u32 minor;
        int id;
        struct nvmem_device *active;
+       size_t active_size;
        struct nvmem_device *non_active;
        void *buf;
+       void *buf_data_start;
        size_t buf_data_size;
        bool authenticating;
        bool flushed;
+       const struct tb_nvm_vendor_ops *vops;
  };
  
  enum tb_nvm_write_ops {
@@@ -113,8 -115,8 +115,8 @@@ struct tb_switch_tmu 
  enum tb_clx {
        TB_CLX_DISABLE,
        /* CL0s and CL1 are enabled and supported together */
-       TB_CL1,
-       TB_CL2,
+       TB_CL1 = BIT(0),
+       TB_CL2 = BIT(1),
  };
  
  /**
@@@ -279,12 -281,16 +281,16 @@@ struct tb_port 
   * @can_offline: Does the port have necessary platform support to moved
   *             it into offline mode and back
   * @offline: The port is currently in offline mode
+  * @margining: Pointer to margining structure if enabled
   */
  struct usb4_port {
        struct device dev;
        struct tb_port *port;
        bool can_offline;
        bool offline;
+ #ifdef CONFIG_USB4_DEBUGFS_MARGINING
+       struct tb_margining *margining;
+ #endif
  };
  
  /**
   * @device: Device ID of the retimer
   * @port: Pointer to the lane 0 adapter
   * @nvm: Pointer to the NVM if the retimer has one (%NULL otherwise)
+  * @no_nvm_upgrade: Prevent NVM upgrade of this retimer
   * @auth_status: Status of last NVM authentication
   */
  struct tb_retimer {
        u32 device;
        struct tb_port *port;
        struct tb_nvm *nvm;
+       bool no_nvm_upgrade;
        u32 auth_status;
  };
  
@@@ -737,11 -745,13 +745,13 @@@ static inline void tb_domain_put(struc
  }
  
  struct tb_nvm *tb_nvm_alloc(struct device *dev);
- int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read);
+ int tb_nvm_read_version(struct tb_nvm *nvm);
+ int tb_nvm_validate(struct tb_nvm *nvm);
+ int tb_nvm_write_headers(struct tb_nvm *nvm);
+ int tb_nvm_add_active(struct tb_nvm *nvm, nvmem_reg_read_t reg_read);
  int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,
                     size_t bytes);
- int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
-                         nvmem_reg_write_t reg_write);
+ int tb_nvm_add_non_active(struct tb_nvm *nvm, nvmem_reg_write_t reg_write);
  void tb_nvm_free(struct tb_nvm *nvm);
  void tb_nvm_exit(void);
  
@@@ -755,6 -765,8 +765,8 @@@ int tb_nvm_write_data(unsigned int addr
                      unsigned int retries, write_block_fn write_next_block,
                      void *write_block_data);
  
+ int tb_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
+                      size_t size);
  struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
                                  u64 route);
  struct tb_switch *tb_switch_alloc_safe_mode(struct tb *tb,
@@@ -1035,6 -1047,7 +1047,7 @@@ void tb_port_lane_bonding_disable(struc
  int tb_port_wait_for_link_width(struct tb_port *port, int width,
                                int timeout_msec);
  int tb_port_update_credits(struct tb_port *port);
+ bool tb_port_is_clx_enabled(struct tb_port *port, enum tb_clx clx);
  
  int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
  int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap);
@@@ -1132,6 -1145,13 +1145,13 @@@ void tb_xdomain_remove(struct tb_xdomai
  struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
                                                 u8 depth);
  
+ static inline struct tb_switch *tb_xdomain_parent(struct tb_xdomain *xd)
+ {
+       return tb_to_switch(xd->dev.parent);
+ }
+ int tb_retimer_nvm_read(struct tb_retimer *rt, unsigned int address, void *buf,
+                       size_t size);
  int tb_retimer_scan(struct tb_port *port, bool add);
  void tb_retimer_remove_all(struct tb_port *port);
  
@@@ -1174,14 -1194,22 +1194,22 @@@ int usb4_switch_add_ports(struct tb_swi
  void usb4_switch_remove_ports(struct tb_switch *sw);
  
  int usb4_port_unlock(struct tb_port *port);
+ int usb4_port_hotplug_enable(struct tb_port *port);
  int usb4_port_configure(struct tb_port *port);
  void usb4_port_unconfigure(struct tb_port *port);
 -int usb4_port_configure_xdomain(struct tb_port *port);
 +int usb4_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd);
  void usb4_port_unconfigure_xdomain(struct tb_port *port);
  int usb4_port_router_offline(struct tb_port *port);
  int usb4_port_router_online(struct tb_port *port);
  int usb4_port_enumerate_retimers(struct tb_port *port);
  bool usb4_port_clx_supported(struct tb_port *port);
+ int usb4_port_margining_caps(struct tb_port *port, u32 *caps);
+ int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
+                       unsigned int ber_level, bool timing, bool right_high,
+                       u32 *results);
+ int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
+                       bool right_high, u32 counter);
+ int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors);
  
  int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
  int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
@@@ -1264,6 -1292,8 +1292,8 @@@ void tb_debugfs_init(void)
  void tb_debugfs_exit(void);
  void tb_switch_debugfs_init(struct tb_switch *sw);
  void tb_switch_debugfs_remove(struct tb_switch *sw);
+ void tb_xdomain_debugfs_init(struct tb_xdomain *xd);
+ void tb_xdomain_debugfs_remove(struct tb_xdomain *xd);
  void tb_service_debugfs_init(struct tb_service *svc);
  void tb_service_debugfs_remove(struct tb_service *svc);
  #else
@@@ -1271,6 -1301,8 +1301,8 @@@ static inline void tb_debugfs_init(void
  static inline void tb_debugfs_exit(void) { }
  static inline void tb_switch_debugfs_init(struct tb_switch *sw) { }
  static inline void tb_switch_debugfs_remove(struct tb_switch *sw) { }
+ static inline void tb_xdomain_debugfs_init(struct tb_xdomain *xd) { }
+ static inline void tb_xdomain_debugfs_remove(struct tb_xdomain *xd) { }
  static inline void tb_service_debugfs_init(struct tb_service *svc) { }
  static inline void tb_service_debugfs_remove(struct tb_service *svc) { }
  #endif
@@@ -1046,6 -1046,26 +1046,26 @@@ int usb4_port_unlock(struct tb_port *po
        return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_4, 1);
  }
  
+ /**
+  * usb4_port_hotplug_enable() - Enables hotplug for a port
+  * @port: USB4 port to operate on
+  *
+  * Enables hot plug events on a given port. This is only intended
+  * to be used on lane, DP-IN, and DP-OUT adapters.
+  */
+ int usb4_port_hotplug_enable(struct tb_port *port)
+ {
+       int ret;
+       u32 val;
+       ret = tb_port_read(port, &val, TB_CFG_PORT, ADP_CS_5, 1);
+       if (ret)
+               return ret;
+       val &= ~ADP_CS_5_DHP;
+       return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_5, 1);
+ }
  static int usb4_port_set_configured(struct tb_port *port, bool configured)
  {
        int ret;
@@@ -1115,14 -1135,12 +1135,14 @@@ static int usb4_set_xdomain_configured(
  /**
   * usb4_port_configure_xdomain() - Configure port for XDomain
   * @port: USB4 port connected to another host
 + * @xd: XDomain that is connected to the port
   *
 - * Marks the USB4 port as being connected to another host. Returns %0 in
 - * success and negative errno in failure.
 + * Marks the USB4 port as being connected to another host and updates
 + * the link type. Returns %0 in success and negative errno in failure.
   */
 -int usb4_port_configure_xdomain(struct tb_port *port)
 +int usb4_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd)
  {
 +      xd->link_usb4 = link_is_usb4(port);
        return usb4_set_xdomain_configured(port, true);
  }
  
@@@ -1386,6 -1404,126 +1406,126 @@@ bool usb4_port_clx_supported(struct tb_
        return !!(val & PORT_CS_18_CPS);
  }
  
+ /**
+  * usb4_port_margining_caps() - Read USB4 port marginig capabilities
+  * @port: USB4 port
+  * @caps: Array with at least two elements to hold the results
+  *
+  * Reads the USB4 port lane margining capabilities into @caps.
+  */
+ int usb4_port_margining_caps(struct tb_port *port, u32 *caps)
+ {
+       int ret;
+       ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
+                             USB4_SB_OPCODE_READ_LANE_MARGINING_CAP, 500);
+       if (ret)
+               return ret;
+       return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
+                                USB4_SB_DATA, caps, sizeof(*caps) * 2);
+ }
+ /**
+  * usb4_port_hw_margin() - Run hardware lane margining on port
+  * @port: USB4 port
+  * @lanes: Which lanes to run (must match the port capabilities). Can be
+  *       %0, %1 or %7.
+  * @ber_level: BER level contour value
+  * @timing: Perform timing margining instead of voltage
+  * @right_high: Use Right/high margin instead of left/low
+  * @results: Array with at least two elements to hold the results
+  *
+  * Runs hardware lane margining on USB4 port and returns the result in
+  * @results.
+  */
+ int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
+                       unsigned int ber_level, bool timing, bool right_high,
+                       u32 *results)
+ {
+       u32 val;
+       int ret;
+       val = lanes;
+       if (timing)
+               val |= USB4_MARGIN_HW_TIME;
+       if (right_high)
+               val |= USB4_MARGIN_HW_RH;
+       if (ber_level)
+               val |= (ber_level << USB4_MARGIN_HW_BER_SHIFT) &
+                       USB4_MARGIN_HW_BER_MASK;
+       ret = usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0,
+                                USB4_SB_METADATA, &val, sizeof(val));
+       if (ret)
+               return ret;
+       ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
+                             USB4_SB_OPCODE_RUN_HW_LANE_MARGINING, 2500);
+       if (ret)
+               return ret;
+       return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
+                                USB4_SB_DATA, results, sizeof(*results) * 2);
+ }
+ /**
+  * usb4_port_sw_margin() - Run software lane margining on port
+  * @port: USB4 port
+  * @lanes: Which lanes to run (must match the port capabilities). Can be
+  *       %0, %1 or %7.
+  * @timing: Perform timing margining instead of voltage
+  * @right_high: Use Right/high margin instead of left/low
+  * @counter: What to do with the error counter
+  *
+  * Runs software lane margining on USB4 port. Read back the error
+  * counters by calling usb4_port_sw_margin_errors(). Returns %0 in
+  * success and negative errno otherwise.
+  */
+ int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
+                       bool right_high, u32 counter)
+ {
+       u32 val;
+       int ret;
+       val = lanes;
+       if (timing)
+               val |= USB4_MARGIN_SW_TIME;
+       if (right_high)
+               val |= USB4_MARGIN_SW_RH;
+       val |= (counter << USB4_MARGIN_SW_COUNTER_SHIFT) &
+               USB4_MARGIN_SW_COUNTER_MASK;
+       ret = usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0,
+                                USB4_SB_METADATA, &val, sizeof(val));
+       if (ret)
+               return ret;
+       return usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
+                              USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500);
+ }
+ /**
+  * usb4_port_sw_margin_errors() - Read the software margining error counters
+  * @port: USB4 port
+  * @errors: Error metadata is copied here.
+  *
+  * This reads back the software margining error counters from the port.
+  * Returns %0 in success and negative errno otherwise.
+  */
+ int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors)
+ {
+       int ret;
+       ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
+                             USB4_SB_OPCODE_READ_SW_MARGIN_ERR, 150);
+       if (ret)
+               return ret;
+       return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
+                                USB4_SB_METADATA, errors, sizeof(*errors));
+ }
  static inline int usb4_port_retimer_op(struct tb_port *port, u8 index,
                                       enum usb4_sb_opcode opcode,
                                       int timeout_msec)
@@@ -160,6 -160,7 +160,7 @@@ static int usb3503_probe(struct usb350
        struct usb3503_platform_data *pdata = dev_get_platdata(dev);
        struct device_node *np = dev->of_node;
        int err;
+       bool is_clk_enabled = false;
        u32 mode = USB3503_MODE_HUB;
        const u32 *property;
        enum gpiod_flags flags;
                        return err;
                }
  
+               is_clk_enabled = true;
                property = of_get_property(np, "disabled-ports", &len);
                if (property && (len / sizeof(u32)) > 0) {
                        int i;
        else
                flags = GPIOD_OUT_HIGH;
        hub->intn = devm_gpiod_get_optional(dev, "intn", flags);
-       if (IS_ERR(hub->intn))
-               return PTR_ERR(hub->intn);
+       if (IS_ERR(hub->intn)) {
+               err = PTR_ERR(hub->intn);
+               goto err_clk;
+       }
        if (hub->intn)
                gpiod_set_consumer_name(hub->intn, "usb3503 intn");
  
        hub->connect = devm_gpiod_get_optional(dev, "connect", GPIOD_OUT_LOW);
-       if (IS_ERR(hub->connect))
-               return PTR_ERR(hub->connect);
+       if (IS_ERR(hub->connect)) {
+               err = PTR_ERR(hub->connect);
+               goto err_clk;
+       }
        if (hub->connect)
                gpiod_set_consumer_name(hub->connect, "usb3503 connect");
  
        hub->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
-       if (IS_ERR(hub->reset))
-               return PTR_ERR(hub->reset);
+       if (IS_ERR(hub->reset)) {
+               err = PTR_ERR(hub->reset);
+               goto err_clk;
+       }
        if (hub->reset) {
                /* Datasheet defines a hardware reset to be at least 100us */
                usleep_range(100, 10000);
                        (hub->mode == USB3503_MODE_HUB) ? "hub" : "standby");
  
        return 0;
+ err_clk:
+       if (is_clk_enabled)
+               clk_disable_unprepare(hub->clk);
+       return err;
  }
  
  static int usb3503_i2c_probe(struct i2c_client *i2c,
        return usb3503_probe(hub);
  }
  
 -static int usb3503_i2c_remove(struct i2c_client *i2c)
 +static void usb3503_i2c_remove(struct i2c_client *i2c)
  {
        struct usb3503 *hub;
  
        hub = i2c_get_clientdata(i2c);
        clk_disable_unprepare(hub->clk);
 -
 -      return 0;
  }
  
  static int usb3503_platform_probe(struct platform_device *pdev)
  #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>, Andreas Mohr, Johan Hovold <jhovold@gmail.com>"
  #define DRIVER_DESC "USB FTDI Serial Converters Driver"
  
+ enum ftdi_chip_type {
+       SIO,
+       FT232A,
+       FT232B,
+       FT2232C,
+       FT232R,
+       FT232H,
+       FT2232H,
+       FT4232H,
+       FT4232HA,
+       FT232HP,
+       FT233HP,
+       FT2232HP,
+       FT2233HP,
+       FT4232HP,
+       FT4233HP,
+       FTX,
+ };
  
  struct ftdi_private {
        enum ftdi_chip_type chip_type;
-                               /* type of device, either SIO or FT8U232AM */
        int baud_base;          /* baud base clock for divisor setting */
        int custom_divisor;     /* custom_divisor kludge, this is for
                                   baud_base (different from what goes to the
@@@ -62,8 -79,7 +79,7 @@@
        unsigned long last_dtr_rts;     /* saved modem control outputs */
        char prev_status;        /* Used for TIOCMIWAIT */
        char transmit_empty;    /* If transmitter is empty or not */
-       u16 interface;          /* FT2232C, FT2232H or FT4232H port interface
-                                  (0 for FT232/245) */
+       u16 channel;            /* channel index, or 0 for legacy types */
  
        speed_t force_baud;     /* if non-zero, force the baud rate to
                                   this value */
  #endif
  };
  
- /* struct ftdi_sio_quirk is used by devices requiring special attention. */
- struct ftdi_sio_quirk {
+ struct ftdi_quirk {
        int (*probe)(struct usb_serial *);
        /* Special settings for probed ports. */
        void (*port_probe)(struct ftdi_private *);
@@@ -98,27 -113,27 +113,27 @@@ static int   ftdi_8u2232c_probe(struct 
  static void  ftdi_USB_UIRT_setup(struct ftdi_private *priv);
  static void  ftdi_HE_TIRA1_setup(struct ftdi_private *priv);
  
- static const struct ftdi_sio_quirk ftdi_jtag_quirk = {
+ static const struct ftdi_quirk ftdi_jtag_quirk = {
        .probe  = ftdi_jtag_probe,
  };
  
- static const struct ftdi_sio_quirk ftdi_NDI_device_quirk = {
+ static const struct ftdi_quirk ftdi_NDI_device_quirk = {
        .probe  = ftdi_NDI_device_setup,
  };
  
- static const struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = {
+ static const struct ftdi_quirk ftdi_USB_UIRT_quirk = {
        .port_probe = ftdi_USB_UIRT_setup,
  };
  
- static const struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
+ static const struct ftdi_quirk ftdi_HE_TIRA1_quirk = {
        .port_probe = ftdi_HE_TIRA1_setup,
  };
  
- static const struct ftdi_sio_quirk ftdi_stmclite_quirk = {
+ static const struct ftdi_quirk ftdi_stmclite_quirk = {
        .probe  = ftdi_stmclite_probe,
  };
  
- static const struct ftdi_sio_quirk ftdi_8u2232c_quirk = {
+ static const struct ftdi_quirk ftdi_8u2232c_quirk = {
        .probe  = ftdi_8u2232c_probe,
  };
  
@@@ -180,6 -195,13 +195,13 @@@ static const struct usb_device_id id_ta
        { USB_DEVICE(FTDI_VID, FTDI_4232H_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_232H_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_FTX_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_FT2233HP_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_FT4233HP_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_FT2232HP_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_FT4232HP_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_FT233HP_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_FT232HP_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_FT4232HA_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) },
  MODULE_DEVICE_TABLE(usb, id_table_combined);
  
  static const char *ftdi_chip_name[] = {
-       [SIO] = "SIO",  /* the serial part of FT8U100AX */
-       [FT8U232AM] = "FT8U232AM",
-       [FT232BM] = "FT232BM",
-       [FT2232C] = "FT2232C",
-       [FT232RL] = "FT232RL",
-       [FT2232H] = "FT2232H",
-       [FT4232H] = "FT4232H",
-       [FT232H]  = "FT232H",
-       [FTX]     = "FT-X"
+       [SIO]           = "SIO",        /* the serial part of FT8U100AX */
+       [FT232A]        = "FT232A",
+       [FT232B]        = "FT232B",
+       [FT2232C]       = "FT2232C/D",
+       [FT232R]        = "FT232R",
+       [FT232H]        = "FT232H",
+       [FT2232H]       = "FT2232H",
+       [FT4232H]       = "FT4232H",
+       [FT4232HA]      = "FT4232HA",
+       [FT232HP]       = "FT232HP",
+       [FT233HP]       = "FT233HP",
+       [FT2232HP]      = "FT2232HP",
+       [FT2233HP]      = "FT2233HP",
+       [FT4232HP]      = "FT4232HP",
+       [FT4233HP]      = "FT4233HP",
+       [FTX]           = "FT-X",
  };
  
  
  #define FTDI_STATUS_B1_MASK   (FTDI_RS_BI)
  /* End TIOCMIWAIT */
  
- /* function prototypes for a FTDI serial converter */
- static int  ftdi_sio_probe(struct usb_serial *serial,
-                                       const struct usb_device_id *id);
- static int  ftdi_sio_port_probe(struct usb_serial_port *port);
- static void ftdi_sio_port_remove(struct usb_serial_port *port);
- static int  ftdi_open(struct tty_struct *tty, struct usb_serial_port *port);
- static void ftdi_dtr_rts(struct usb_serial_port *port, int on);
- static void ftdi_process_read_urb(struct urb *urb);
- static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
-                                               void *dest, size_t size);
  static void ftdi_set_termios(struct tty_struct *tty,
 -                      struct usb_serial_port *port, struct ktermios *old);
 +                           struct usb_serial_port *port,
 +                           const struct ktermios *old_termios);
- static int  ftdi_tiocmget(struct tty_struct *tty);
- static int  ftdi_tiocmset(struct tty_struct *tty,
-                       unsigned int set, unsigned int clear);
- static int  ftdi_ioctl(struct tty_struct *tty,
-                       unsigned int cmd, unsigned long arg);
- static void get_serial_info(struct tty_struct *tty, struct serial_struct *ss);
- static int set_serial_info(struct tty_struct *tty,
-                               struct serial_struct *ss);
- static void ftdi_break_ctl(struct tty_struct *tty, int break_state);
- static bool ftdi_tx_empty(struct usb_serial_port *port);
  static int ftdi_get_modem_status(struct usb_serial_port *port,
                                                unsigned char status[2]);
  
- static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base);
- static unsigned short int ftdi_232am_baud_to_divisor(int baud);
- static u32 ftdi_232bm_baud_base_to_divisor(int baud, int base);
- static u32 ftdi_232bm_baud_to_divisor(int baud);
- static u32 ftdi_2232h_baud_base_to_divisor(int baud, int base);
- static u32 ftdi_2232h_baud_to_divisor(int baud);
- static struct usb_serial_driver ftdi_sio_device = {
-       .driver = {
-               .owner =        THIS_MODULE,
-               .name =         "ftdi_sio",
-       },
-       .description =          "FTDI USB Serial Device",
-       .id_table =             id_table_combined,
-       .num_ports =            1,
-       .bulk_in_size =         512,
-       .bulk_out_size =        256,
-       .probe =                ftdi_sio_probe,
-       .port_probe =           ftdi_sio_port_probe,
-       .port_remove =          ftdi_sio_port_remove,
-       .open =                 ftdi_open,
-       .dtr_rts =              ftdi_dtr_rts,
-       .throttle =             usb_serial_generic_throttle,
-       .unthrottle =           usb_serial_generic_unthrottle,
-       .process_read_urb =     ftdi_process_read_urb,
-       .prepare_write_buffer = ftdi_prepare_write_buffer,
-       .tiocmget =             ftdi_tiocmget,
-       .tiocmset =             ftdi_tiocmset,
-       .tiocmiwait =           usb_serial_generic_tiocmiwait,
-       .get_icount =           usb_serial_generic_get_icount,
-       .ioctl =                ftdi_ioctl,
-       .get_serial =           get_serial_info,
-       .set_serial =           set_serial_info,
-       .set_termios =          ftdi_set_termios,
-       .break_ctl =            ftdi_break_ctl,
-       .tx_empty =             ftdi_tx_empty,
- };
- static struct usb_serial_driver * const serial_drivers[] = {
-       &ftdi_sio_device, NULL
- };
  #define WDR_TIMEOUT 5000 /* default urb timeout */
  #define WDR_SHORT_TIMEOUT 1000        /* shorter urb timeout */
  
@@@ -1262,7 -1227,7 +1228,7 @@@ static int update_mctrl(struct usb_seri
                               usb_sndctrlpipe(port->serial->dev, 0),
                               FTDI_SIO_SET_MODEM_CTRL_REQUEST,
                               FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
-                              value, priv->interface,
+                              value, priv->channel,
                               NULL, 0, WDR_TIMEOUT);
        if (rv < 0) {
                dev_dbg(dev, "%s Error from MODEM_CTRL urb: DTR %s, RTS %s\n",
@@@ -1308,7 -1273,7 +1274,7 @@@ static u32 get_ftdi_divisor(struct tty_
        if (!baud)
                baud = 9600;
        switch (priv->chip_type) {
-       case SIO: /* SIO chip */
+       case SIO:
                switch (baud) {
                case 300: div_value = ftdi_sio_b300; break;
                case 600: div_value = ftdi_sio_b600; break;
                case 38400: div_value = ftdi_sio_b38400; break;
                case 57600: div_value = ftdi_sio_b57600;  break;
                case 115200: div_value = ftdi_sio_b115200; break;
-               } /* baud */
-               if (div_value == 0) {
+               default:
                        dev_dbg(dev, "%s - Baudrate (%d) requested is not supported\n",
                                __func__,  baud);
                        div_value = ftdi_sio_b9600;
                        div_okay = 0;
                }
                break;
-       case FT8U232AM: /* 8U232AM chip */
+       case FT232A:
                if (baud <= 3000000) {
                        div_value = ftdi_232am_baud_to_divisor(baud);
                } else {
                        div_okay = 0;
                }
                break;
-       case FT232BM: /* FT232BM chip */
-       case FT2232C: /* FT2232C chip */
-       case FT232RL: /* FT232RL chip */
-       case FTX:     /* FT-X series */
+       case FT232B:
+       case FT2232C:
+       case FT232R:
+       case FTX:
                if (baud <= 3000000) {
                        u16 product_id = le16_to_cpu(
                                port->serial->dev->descriptor.idProduct);
                        baud = 9600;
                }
                break;
-       case FT2232H: /* FT2232H chip */
-       case FT4232H: /* FT4232H chip */
-       case FT232H:  /* FT232H chip */
+       default:
                if ((baud <= 12000000) && (baud >= 1200)) {
                        div_value = ftdi_2232h_baud_to_divisor(baud);
                } else if (baud < 1200) {
                        baud = 9600;
                }
                break;
-       } /* priv->chip_type */
+       }
  
        if (div_okay) {
                dev_dbg(dev, "%s - Baud rate set to %d (divisor 0x%lX) on chip %s\n",
@@@ -1399,13 -1361,8 +1362,8 @@@ static int change_speed(struct tty_stru
        index_value = get_ftdi_divisor(tty, port);
        value = (u16)index_value;
        index = (u16)(index_value >> 16);
-       if (priv->chip_type == FT2232C || priv->chip_type == FT2232H ||
-                       priv->chip_type == FT4232H || priv->chip_type == FT232H ||
-                       priv->chip_type == FTX) {
-               /* Probably the BM type needs the MSB of the encoded fractional
-                * divider also moved like for the chips above. Any infos? */
-               index = (u16)((index << 8) | priv->interface);
-       }
+       if (priv->channel)
+               index = (u16)((index << 8) | priv->channel);
  
        rv = usb_control_msg(port->serial->dev,
                            usb_sndctrlpipe(port->serial->dev, 0),
@@@ -1423,7 -1380,7 +1381,7 @@@ static int write_latency_timer(struct u
        int rv;
        int l = priv->latency;
  
-       if (priv->chip_type == SIO || priv->chip_type == FT8U232AM)
+       if (priv->chip_type == SIO || priv->chip_type == FT232A)
                return -EINVAL;
  
        if (priv->flags & ASYNC_LOW_LATENCY)
                             usb_sndctrlpipe(udev, 0),
                             FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
                             FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
-                            l, priv->interface,
+                            l, priv->channel,
                             NULL, 0, WDR_TIMEOUT);
        if (rv < 0)
                dev_err(&port->dev, "Unable to write latency timer: %i\n", rv);
@@@ -1451,7 -1408,7 +1409,7 @@@ static int _read_latency_timer(struct u
  
        rv = usb_control_msg_recv(udev, 0, FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
                                  FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE, 0,
-                                 priv->interface, &buf, 1, WDR_TIMEOUT,
+                                 priv->channel, &buf, 1, WDR_TIMEOUT,
                                  GFP_KERNEL);
        if (rv == 0)
                rv = buf;
@@@ -1464,7 -1421,7 +1422,7 @@@ static int read_latency_timer(struct us
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        int rv;
  
-       if (priv->chip_type == SIO || priv->chip_type == FT8U232AM)
+       if (priv->chip_type == SIO || priv->chip_type == FT232A)
                return -EINVAL;
  
        rv = _read_latency_timer(port);
@@@ -1539,90 -1496,97 +1497,97 @@@ static int get_lsr_info(struct usb_seri
        return 0;
  }
  
- /* Determine type of FTDI chip based on USB config and descriptor. */
- static void ftdi_determine_type(struct usb_serial_port *port)
+ static int ftdi_determine_type(struct usb_serial_port *port)
  {
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        struct usb_serial *serial = port->serial;
        struct usb_device *udev = serial->dev;
-       unsigned version;
-       unsigned interfaces;
-       /* Assume it is not the original SIO device for now. */
-       priv->baud_base = 48000000 / 2;
+       unsigned int version, ifnum;
  
        version = le16_to_cpu(udev->descriptor.bcdDevice);
-       interfaces = udev->actconfig->desc.bNumInterfaces;
-       dev_dbg(&port->dev, "%s: bcdDevice = 0x%x, bNumInterfaces = %u\n", __func__,
-               version, interfaces);
-       if (interfaces > 1) {
-               struct usb_interface *intf = serial->interface;
-               int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
-               /* Multiple interfaces.*/
-               if (version == 0x0800) {
-                       priv->chip_type = FT4232H;
-                       /* Hi-speed - baud clock runs at 120MHz */
-                       priv->baud_base = 120000000 / 2;
-               } else if (version == 0x0700) {
-                       priv->chip_type = FT2232H;
-                       /* Hi-speed - baud clock runs at 120MHz */
-                       priv->baud_base = 120000000 / 2;
-               } else
-                       priv->chip_type = FT2232C;
-               /* Determine interface code. */
-               if (ifnum == 0)
-                       priv->interface = INTERFACE_A;
-               else if (ifnum == 1)
-                       priv->interface = INTERFACE_B;
-               else if (ifnum == 2)
-                       priv->interface = INTERFACE_C;
-               else if (ifnum == 3)
-                       priv->interface = INTERFACE_D;
-               /* BM-type devices have a bug where bcdDevice gets set
-                * to 0x200 when iSerialNumber is 0.  */
-               if (version < 0x500) {
-                       dev_dbg(&port->dev,
-                               "%s: something fishy - bcdDevice too low for multi-interface device\n",
-                               __func__);
-               }
-       } else if (version < 0x200) {
-               /* Old device.  Assume it's the original SIO. */
-               priv->chip_type = SIO;
-               priv->baud_base = 12000000 / 16;
-       } else if (version < 0x400) {
-               /* Assume it's an FT8U232AM (or FT8U245AM) */
-               priv->chip_type = FT8U232AM;
+       ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
+       /* Assume Hi-Speed type */
+       priv->baud_base = 120000000 / 2;
+       priv->channel = CHANNEL_A + ifnum;
+       switch (version) {
+       case 0x200:
+               priv->chip_type = FT232A;
+               priv->baud_base = 48000000 / 2;
+               priv->channel = 0;
                /*
-                * It might be a BM type because of the iSerialNumber bug.
-                * If iSerialNumber==0 and the latency timer is readable,
-                * assume it is BM type.
+                * FT232B devices have a bug where bcdDevice gets set to 0x200
+                * when iSerialNumber is 0. Assume it is an FT232B in case the
+                * latency timer is readable.
                 */
                if (udev->descriptor.iSerialNumber == 0 &&
                                _read_latency_timer(port) >= 0) {
-                       dev_dbg(&port->dev,
-                               "%s: has latency timer so not an AM type\n",
-                               __func__);
-                       priv->chip_type = FT232BM;
+                       priv->chip_type = FT232B;
                }
-       } else if (version < 0x600) {
-               /* Assume it's an FT232BM (or FT245BM) */
-               priv->chip_type = FT232BM;
-       } else if (version < 0x900) {
-               /* Assume it's an FT232RL */
-               priv->chip_type = FT232RL;
-       } else if (version < 0x1000) {
-               /* Assume it's an FT232H */
+               break;
+       case 0x400:
+               priv->chip_type = FT232B;
+               priv->baud_base = 48000000 / 2;
+               priv->channel = 0;
+               break;
+       case 0x500:
+               priv->chip_type = FT2232C;
+               priv->baud_base = 48000000 / 2;
+               break;
+       case 0x600:
+               priv->chip_type = FT232R;
+               priv->baud_base = 48000000 / 2;
+               priv->channel = 0;
+               break;
+       case 0x700:
+               priv->chip_type = FT2232H;
+               break;
+       case 0x800:
+               priv->chip_type = FT4232H;
+               break;
+       case 0x900:
                priv->chip_type = FT232H;
-       } else {
-               /* Assume it's an FT-X series device */
+               break;
+       case 0x1000:
                priv->chip_type = FTX;
+               priv->baud_base = 48000000 / 2;
+               break;
+       case 0x2800:
+               priv->chip_type = FT2233HP;
+               break;
+       case 0x2900:
+               priv->chip_type = FT4233HP;
+               break;
+       case 0x3000:
+               priv->chip_type = FT2232HP;
+               break;
+       case 0x3100:
+               priv->chip_type = FT4232HP;
+               break;
+       case 0x3200:
+               priv->chip_type = FT233HP;
+               break;
+       case 0x3300:
+               priv->chip_type = FT232HP;
+               break;
+       case 0x3600:
+               priv->chip_type = FT4232HA;
+               break;
+       default:
+               if (version < 0x200) {
+                       priv->chip_type = SIO;
+                       priv->baud_base = 12000000 / 16;
+                       priv->channel = 0;
+               } else {
+                       dev_err(&port->dev, "unknown device type: 0x%02x\n", version);
+                       return -ENODEV;
+               }
        }
  
        dev_info(&udev->dev, "Detected %s\n", ftdi_chip_name[priv->chip_type]);
+       return 0;
  }
  
  
@@@ -1721,7 -1685,7 +1686,7 @@@ static ssize_t event_char_store(struct 
                             usb_sndctrlpipe(udev, 0),
                             FTDI_SIO_SET_EVENT_CHAR_REQUEST,
                             FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE,
-                            v, priv->interface,
+                            v, priv->channel,
                             NULL, 0, WDR_TIMEOUT);
        if (rv < 0) {
                dev_dbg(&port->dev, "Unable to write event character: %i\n", rv);
  }
  static DEVICE_ATTR_WO(event_char);
  
- static int create_sysfs_attrs(struct usb_serial_port *port)
- {
-       struct ftdi_private *priv = usb_get_serial_port_data(port);
-       int retval = 0;
-       /* XXX I've no idea if the original SIO supports the event_char
-        * sysfs parameter, so I'm playing it safe.  */
-       if (priv->chip_type != SIO) {
-               dev_dbg(&port->dev, "sysfs attributes for %s\n", ftdi_chip_name[priv->chip_type]);
-               retval = device_create_file(&port->dev, &dev_attr_event_char);
-               if ((!retval) &&
-                   (priv->chip_type == FT232BM ||
-                    priv->chip_type == FT2232C ||
-                    priv->chip_type == FT232RL ||
-                    priv->chip_type == FT2232H ||
-                    priv->chip_type == FT4232H ||
-                    priv->chip_type == FT232H ||
-                    priv->chip_type == FTX)) {
-                       retval = device_create_file(&port->dev,
-                                                   &dev_attr_latency_timer);
-               }
-       }
-       return retval;
- }
+ static struct attribute *ftdi_attrs[] = {
+       &dev_attr_event_char.attr,
+       &dev_attr_latency_timer.attr,
+       NULL
+ };
  
- static void remove_sysfs_attrs(struct usb_serial_port *port)
+ static umode_t ftdi_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
  {
+       struct device *dev = kobj_to_dev(kobj);
+       struct usb_serial_port *port = to_usb_serial_port(dev);
        struct ftdi_private *priv = usb_get_serial_port_data(port);
+       enum ftdi_chip_type type = priv->chip_type;
  
-       /* XXX see create_sysfs_attrs */
-       if (priv->chip_type != SIO) {
-               device_remove_file(&port->dev, &dev_attr_event_char);
-               if (priv->chip_type == FT232BM ||
-                   priv->chip_type == FT2232C ||
-                   priv->chip_type == FT232RL ||
-                   priv->chip_type == FT2232H ||
-                   priv->chip_type == FT4232H ||
-                   priv->chip_type == FT232H ||
-                   priv->chip_type == FTX) {
-                       device_remove_file(&port->dev, &dev_attr_latency_timer);
-               }
+       if (attr == &dev_attr_event_char.attr) {
+               if (type == SIO)
+                       return 0;
+       }
+       if (attr == &dev_attr_latency_timer.attr) {
+               if (type == SIO || type == FT232A)
+                       return 0;
        }
  
+       return attr->mode;
  }
  
+ static const struct attribute_group ftdi_group = {
+       .attrs          = ftdi_attrs,
+       .is_visible     = ftdi_is_visible,
+ };
+ static const struct attribute_group *ftdi_groups[] = {
+       &ftdi_group,
+       NULL
+ };
  #ifdef CONFIG_GPIOLIB
  
  static int ftdi_set_bitmode(struct usb_serial_port *port, u8 mode)
                                 usb_sndctrlpipe(serial->dev, 0),
                                 FTDI_SIO_SET_BITMODE_REQUEST,
                                 FTDI_SIO_SET_BITMODE_REQUEST_TYPE, val,
-                                priv->interface, NULL, 0, WDR_TIMEOUT);
+                                priv->channel, NULL, 0, WDR_TIMEOUT);
        if (result < 0) {
                dev_err(&serial->interface->dev,
                        "bitmode request failed for value 0x%04x: %d\n",
@@@ -1859,7 -1814,7 +1815,7 @@@ static int ftdi_read_cbus_pins(struct u
        result = usb_control_msg_recv(serial->dev, 0,
                                      FTDI_SIO_READ_PINS_REQUEST,
                                      FTDI_SIO_READ_PINS_REQUEST_TYPE, 0,
-                                     priv->interface, &buf, 1, WDR_TIMEOUT,
+                                     priv->channel, &buf, 1, WDR_TIMEOUT,
                                      GFP_KERNEL);
        if (result == 0)
                result = buf;
@@@ -2144,7 -2099,7 +2100,7 @@@ static int ftdi_gpio_init(struct usb_se
        case FT232H:
                result = ftdi_gpio_init_ft232h(port);
                break;
-       case FT232RL:
+       case FT232R:
                result = ftdi_gpio_init_ft232r(port);
                break;
        case FTX:
@@@ -2214,12 -2169,9 +2170,9 @@@ static void ftdi_gpio_remove(struct usb
   * ***************************************************************************
   */
  
- /* Probe function to check for special devices */
- static int ftdi_sio_probe(struct usb_serial *serial,
-                                       const struct usb_device_id *id)
+ static int ftdi_probe(struct usb_serial *serial, const struct usb_device_id *id)
  {
-       const struct ftdi_sio_quirk *quirk =
-                               (struct ftdi_sio_quirk *)id->driver_info;
+       const struct ftdi_quirk *quirk = (struct ftdi_quirk *)id->driver_info;
  
        if (quirk && quirk->probe) {
                int ret = quirk->probe(serial);
        return 0;
  }
  
- static int ftdi_sio_port_probe(struct usb_serial_port *port)
+ static int ftdi_port_probe(struct usb_serial_port *port)
  {
+       const struct ftdi_quirk *quirk = usb_get_serial_data(port->serial);
        struct ftdi_private *priv;
-       const struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
        int result;
  
        priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
  
        usb_set_serial_port_data(port, priv);
  
-       ftdi_determine_type(port);
+       result = ftdi_determine_type(port);
+       if (result)
+               goto err_free;
        ftdi_set_max_packet_size(port);
        if (read_latency_timer(port) < 0)
                priv->latency = 16;
        write_latency_timer(port);
-       create_sysfs_attrs(port);
  
        result = ftdi_gpio_init(port);
        if (result < 0) {
        }
  
        return 0;
+ err_free:
+       kfree(priv);
+       return result;
  }
  
  /* Setup for the USB-UIRT device, which requires hardwired
@@@ -2374,14 -2333,12 +2334,12 @@@ static int ftdi_stmclite_probe(struct u
        return 0;
  }
  
- static void ftdi_sio_port_remove(struct usb_serial_port *port)
+ static void ftdi_port_remove(struct usb_serial_port *port)
  {
        struct ftdi_private *priv = usb_get_serial_port_data(port);
  
        ftdi_gpio_remove(port);
  
-       remove_sysfs_attrs(port);
        kfree(priv);
  }
  
@@@ -2395,7 -2352,7 +2353,7 @@@ static int ftdi_open(struct tty_struct 
        usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                        FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
                        FTDI_SIO_RESET_SIO,
-                       priv->interface, NULL, 0, WDR_TIMEOUT);
+                       priv->channel, NULL, 0, WDR_TIMEOUT);
  
        /* Termios defaults are set by usb_serial_init. We don't change
           port->tty->termios - this would lose speed settings, etc.
@@@ -2418,7 -2375,7 +2376,7 @@@ static void ftdi_dtr_rts(struct usb_ser
                            usb_sndctrlpipe(port->serial->dev, 0),
                            FTDI_SIO_SET_FLOW_CTRL_REQUEST,
                            FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
-                           0, priv->interface, NULL, 0,
+                           0, priv->channel, NULL, 0,
                            WDR_TIMEOUT) < 0) {
                        dev_err(&port->dev, "error from flowcontrol urb\n");
                }
@@@ -2611,7 -2568,7 +2569,7 @@@ static void ftdi_break_ctl(struct tty_s
                        usb_sndctrlpipe(port->serial->dev, 0),
                        FTDI_SIO_SET_DATA_REQUEST,
                        FTDI_SIO_SET_DATA_REQUEST_TYPE,
-                       value , priv->interface,
+                       value, priv->channel,
                        NULL, 0, WDR_TIMEOUT) < 0) {
                dev_err(&port->dev, "%s FAILED to enable/disable break state (state was %d)\n",
                        __func__, break_state);
@@@ -2641,8 -2598,7 +2599,8 @@@ static bool ftdi_tx_empty(struct usb_se
   * WARNING: set_termios calls this with old_termios in kernel space
   */
  static void ftdi_set_termios(struct tty_struct *tty,
 -              struct usb_serial_port *port, struct ktermios *old_termios)
 +                           struct usb_serial_port *port,
 +                           const struct ktermios *old_termios)
  {
        struct usb_device *dev = port->serial->dev;
        struct device *ddev = &port->dev;
@@@ -2748,7 -2704,7 +2706,7 @@@ no_skip
        if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                            FTDI_SIO_SET_DATA_REQUEST,
                            FTDI_SIO_SET_DATA_REQUEST_TYPE,
-                           value , priv->interface,
+                           value, priv->channel,
                            NULL, 0, WDR_SHORT_TIMEOUT) < 0) {
                dev_err(ddev, "%s FAILED to set databits/stopbits/parity\n",
                        __func__);
@@@ -2761,7 -2717,7 +2719,7 @@@ no_data_parity_stop_changes
                if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                                    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
                                    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
-                                   0, priv->interface,
+                                   0, priv->channel,
                                    NULL, 0, WDR_TIMEOUT) < 0) {
                        dev_err(ddev, "%s error from disable flowcontrol urb\n",
                                __func__);
@@@ -2795,7 -2751,7 +2753,7 @@@ no_c_cflag_changes
                index = FTDI_SIO_DISABLE_FLOW_CTRL;
        }
  
-       index |= priv->interface;
+       index |= priv->channel;
  
        ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                        FTDI_SIO_SET_FLOW_CTRL_REQUEST,
@@@ -2823,33 -2779,19 +2781,19 @@@ static int ftdi_get_modem_status(struc
        if (!buf)
                return -ENOMEM;
        /*
-        * The 8U232AM returns a two byte value (the SIO a 1 byte value) in
-        * the same format as the data returned from the in point.
+        * The device returns a two byte value (the SIO a 1 byte value) in the
+        * same format as the data returned from the IN endpoint.
         */
-       switch (priv->chip_type) {
-       case SIO:
+       if (priv->chip_type == SIO)
                len = 1;
-               break;
-       case FT8U232AM:
-       case FT232BM:
-       case FT2232C:
-       case FT232RL:
-       case FT2232H:
-       case FT4232H:
-       case FT232H:
-       case FTX:
+       else
                len = 2;
-               break;
-       default:
-               ret = -EFAULT;
-               goto out;
-       }
  
        ret = usb_control_msg(port->serial->dev,
                        usb_rcvctrlpipe(port->serial->dev, 0),
                        FTDI_SIO_GET_MODEM_STATUS_REQUEST,
                        FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
-                       0, priv->interface,
+                       0, priv->channel,
                        buf, len, WDR_TIMEOUT);
  
        /* NOTE: We allow short responses and handle that below. */
@@@ -2919,6 -2861,41 +2863,41 @@@ static int ftdi_ioctl(struct tty_struc
        return -ENOIOCTLCMD;
  }
  
+ static struct usb_serial_driver ftdi_device = {
+       .driver = {
+               .owner =        THIS_MODULE,
+               .name =         "ftdi_sio",
+               .dev_groups =   ftdi_groups,
+       },
+       .description =          "FTDI USB Serial Device",
+       .id_table =             id_table_combined,
+       .num_ports =            1,
+       .bulk_in_size =         512,
+       .bulk_out_size =        256,
+       .probe =                ftdi_probe,
+       .port_probe =           ftdi_port_probe,
+       .port_remove =          ftdi_port_remove,
+       .open =                 ftdi_open,
+       .dtr_rts =              ftdi_dtr_rts,
+       .throttle =             usb_serial_generic_throttle,
+       .unthrottle =           usb_serial_generic_unthrottle,
+       .process_read_urb =     ftdi_process_read_urb,
+       .prepare_write_buffer = ftdi_prepare_write_buffer,
+       .tiocmget =             ftdi_tiocmget,
+       .tiocmset =             ftdi_tiocmset,
+       .tiocmiwait =           usb_serial_generic_tiocmiwait,
+       .get_icount =           usb_serial_generic_get_icount,
+       .ioctl =                ftdi_ioctl,
+       .get_serial =           get_serial_info,
+       .set_serial =           set_serial_info,
+       .set_termios =          ftdi_set_termios,
+       .break_ctl =            ftdi_break_ctl,
+       .tx_empty =             ftdi_tx_empty,
+ };
+ static struct usb_serial_driver * const serial_drivers[] = {
+       &ftdi_device, NULL
+ };
  module_usb_serial_driver(serial_drivers, id_table_combined);
  
  MODULE_AUTHOR(DRIVER_AUTHOR);
@@@ -1105,7 -1105,7 +1105,7 @@@ static int anx7411_typec_switch_probe(s
        int ret;
        struct device_node *node;
  
-       node = of_find_node_by_name(dev->of_node, "orientation_switch");
+       node = of_get_child_by_name(dev->of_node, "orientation_switch");
        if (!node)
                return 0;
  
                return ret;
        }
  
-       node = of_find_node_by_name(dev->of_node, "mode_switch");
+       node = of_get_child_by_name(dev->of_node, "mode_switch");
        if (!node) {
                dev_err(dev, "no typec mux exist");
                ret = -ENODEV;
@@@ -1541,7 -1541,7 +1541,7 @@@ free_i2c_dummy
        return ret;
  }
  
 -static int anx7411_i2c_remove(struct i2c_client *client)
 +static void anx7411_i2c_remove(struct i2c_client *client)
  {
        struct anx7411_data *plat = i2c_get_clientdata(client);
  
                typec_unregister_port(plat->typec.port);
  
        anx7411_port_unregister_altmodes(plat->typec.port_amode);
 -
 -      return 0;
  }
  
  static const struct i2c_device_id anx7411_id[] = {
@@@ -750,11 -750,8 +750,8 @@@ static int stusb160x_probe(struct i2c_c
        if (client->irq) {
                chip->role_sw = fwnode_usb_role_switch_get(fwnode);
                if (IS_ERR(chip->role_sw)) {
-                       ret = PTR_ERR(chip->role_sw);
-                       if (ret != -EPROBE_DEFER)
-                               dev_err(chip->dev,
-                                       "Failed to get usb role switch: %d\n",
-                                       ret);
+                       ret = dev_err_probe(chip->dev, PTR_ERR(chip->role_sw),
+                                           "Failed to get usb role switch\n");
                        goto port_unregister;
                }
  
@@@ -801,7 -798,7 +798,7 @@@ fwnode_put
        return ret;
  }
  
 -static int stusb160x_remove(struct i2c_client *client)
 +static void stusb160x_remove(struct i2c_client *client)
  {
        struct stusb160x *chip = i2c_get_clientdata(client);
  
  
        if (chip->main_supply)
                regulator_disable(chip->main_supply);
 -
 -      return 0;
  }
  
  static int __maybe_unused stusb160x_suspend(struct device *dev)
@@@ -151,7 -151,7 +151,7 @@@ static void _fusb302_log(struct fusb302
  
        if (fusb302_log_full(chip)) {
                chip->logbuffer_head = max(chip->logbuffer_head - 1, 0);
-               strlcpy(tmpbuffer, "overflow", sizeof(tmpbuffer));
+               strscpy(tmpbuffer, "overflow", sizeof(tmpbuffer));
        }
  
        if (chip->logbuffer_head < 0 ||
@@@ -1743,9 -1743,8 +1743,8 @@@ static int fusb302_probe(struct i2c_cli
        chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev);
        if (IS_ERR(chip->tcpm_port)) {
                fwnode_handle_put(chip->tcpc_dev.fwnode);
-               ret = PTR_ERR(chip->tcpm_port);
-               if (ret != -EPROBE_DEFER)
-                       dev_err(dev, "cannot register tcpm port, ret=%d", ret);
+               ret = dev_err_probe(dev, PTR_ERR(chip->tcpm_port),
+                                   "cannot register tcpm port\n");
                goto destroy_workqueue;
        }
  
@@@ -1771,7 -1770,7 +1770,7 @@@ destroy_workqueue
        return ret;
  }
  
 -static int fusb302_remove(struct i2c_client *client)
 +static void fusb302_remove(struct i2c_client *client)
  {
        struct fusb302_chip *chip = i2c_get_clientdata(client);
  
        fwnode_handle_put(chip->tcpc_dev.fwnode);
        destroy_workqueue(chip->wq);
        fusb302_debugfs_exit(chip);
 -
 -      return 0;
  }
  
  static int fusb302_pm_suspend(struct device *dev)
  #define       VPPS_VALID_MIN_MV                       100
  #define       VSINKDISCONNECT_PD_MIN_PERCENT          90
  
- #define tcpc_presenting_rd(reg, cc) \
-       (!(TCPC_ROLE_CTRL_DRP & (reg)) && \
-        (((reg) & (TCPC_ROLE_CTRL_## cc ##_MASK << TCPC_ROLE_CTRL_## cc ##_SHIFT)) == \
-         (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_## cc ##_SHIFT)))
  struct tcpci {
        struct device *dev;
  
@@@ -218,23 -213,6 +213,6 @@@ static int tcpci_start_toggling(struct 
                            TCPC_CMD_LOOK4CONNECTION);
  }
  
- static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink)
- {
-       switch (cc) {
-       case 0x1:
-               return sink ? TYPEC_CC_RP_DEF : TYPEC_CC_RA;
-       case 0x2:
-               return sink ? TYPEC_CC_RP_1_5 : TYPEC_CC_RD;
-       case 0x3:
-               if (sink)
-                       return TYPEC_CC_RP_3_0;
-               fallthrough;
-       case 0x0:
-       default:
-               return TYPEC_CC_OPEN;
-       }
- }
  static int tcpci_get_cc(struct tcpc_dev *tcpc,
                        enum typec_cc_status *cc1, enum typec_cc_status *cc2)
  {
@@@ -868,7 -846,7 +846,7 @@@ static int tcpci_probe(struct i2c_clien
        return 0;
  }
  
 -static int tcpci_remove(struct i2c_client *client)
 +static void tcpci_remove(struct i2c_client *client)
  {
        struct tcpci_chip *chip = i2c_get_clientdata(client);
        int err;
                dev_warn(&client->dev, "Failed to disable irqs (%pe)\n", ERR_PTR(err));
  
        tcpci_unregister_port(chip->tcpci);
 -
 -      return 0;
  }
  
  static const struct i2c_device_id tcpci_id[] = {
@@@ -5,6 -5,7 +5,7 @@@
   * Richtek RT1711H Type-C Chip Driver
   */
  
+ #include <linux/bits.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/i2c.h>
  #include <linux/usb/tcpci.h>
  #include <linux/usb/tcpm.h>
  #include <linux/regmap.h>
+ #include <linux/regulator/consumer.h>
  
  #define RT1711H_VID           0x29CF
  #define RT1711H_PID           0x1711
+ #define RT1711H_DID           0x2171
+ #define RT1715_DID            0x2173
  
- #define RT1711H_RTCTRL8               0x9B
+ #define RT1711H_PHYCTRL1      0x80
+ #define RT1711H_PHYCTRL2      0x81
+ #define RT1711H_RTCTRL4               0x93
+ /* rx threshold of rd/rp: 1b0 for level 0.4V/0.7V, 1b1 for 0.35V/0.75V */
+ #define RT1711H_BMCIO_RXDZSEL BIT(0)
  
+ #define RT1711H_RTCTRL8               0x9B
  /* Autoidle timeout = (tout * 2 + 1) * 6.4ms */
  #define RT1711H_RTCTRL8_SET(ck300, ship_off, auto_idle, tout) \
                            (((ck300) << 7) | ((ship_off) << 5) | \
                            ((auto_idle) << 3) | ((tout) & 0x07))
+ #define RT1711H_AUTOIDLEEN    BIT(3)
+ #define RT1711H_ENEXTMSG      BIT(4)
  
  #define RT1711H_RTCTRL11      0x9E
  
  #define RT1711H_RTCTRL15      0xA2
  #define RT1711H_RTCTRL16      0xA3
  
+ #define RT1711H_RTCTRL18      0xAF
+ /* 1b0 as fixed rx threshold of rd/rp 0.55V, 1b1 depends on RTCRTL4[0] */
+ #define BMCIO_RXDZEN  BIT(0)
  struct rt1711h_chip {
        struct tcpci_data data;
        struct tcpci *tcpci;
        struct device *dev;
+       struct regulator *vbus;
+       bool src_en;
+       u16 did;
  };
  
  static int rt1711h_read16(struct rt1711h_chip *chip, unsigned int reg, u16 *val)
@@@ -75,8 -94,9 +94,9 @@@ static struct rt1711h_chip *tdata_to_rt
  
  static int rt1711h_init(struct tcpci *tcpci, struct tcpci_data *tdata)
  {
-       int ret;
        struct rt1711h_chip *chip = tdata_to_rt1711h(tdata);
+       struct regmap *regmap = chip->data.regmap;
+       int ret;
  
        /* CK 300K from 320K, shipping off, auto_idle enable, tout = 32ms */
        ret = rt1711h_write8(chip, RT1711H_RTCTRL8,
        if (ret < 0)
                return ret;
  
+       /* Enable PD30 extended message for RT1715 */
+       if (chip->did == RT1715_DID) {
+               ret = regmap_update_bits(regmap, RT1711H_RTCTRL8,
+                                        RT1711H_ENEXTMSG, RT1711H_ENEXTMSG);
+               if (ret < 0)
+                       return ret;
+       }
        /* I2C reset : (val + 1) * 12.5ms */
        ret = rt1711h_write8(chip, RT1711H_RTCTRL11,
                             RT1711H_RTCTRL11_SET(1, 0x0F));
                return ret;
  
        /* dcSRC.DRP : 33% */
-       return rt1711h_write16(chip, RT1711H_RTCTRL16, 330);
+       ret = rt1711h_write16(chip, RT1711H_RTCTRL16, 330);
+       if (ret < 0)
+               return ret;
+       /* Enable phy discard retry, retry count 7, rx filter deglitch 100 us */
+       ret = rt1711h_write8(chip, RT1711H_PHYCTRL1, 0xF1);
+       if (ret < 0)
+               return ret;
+       /* Decrease wait time of BMC-encoded 1 bit from 2.67us to 2.55us */
+       /* wait time : (val * .4167) us */
+       return rt1711h_write8(chip, RT1711H_PHYCTRL2, 62);
+ }
+ static int rt1711h_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdata,
+                           bool src, bool snk)
+ {
+       struct rt1711h_chip *chip = tdata_to_rt1711h(tdata);
+       int ret;
+       if (chip->src_en == src)
+               return 0;
+       if (src)
+               ret = regulator_enable(chip->vbus);
+       else
+               ret = regulator_disable(chip->vbus);
+       if (!ret)
+               chip->src_en = src;
+       return ret;
  }
  
  static int rt1711h_set_vconn(struct tcpci *tcpci, struct tcpci_data *tdata,
  {
        struct rt1711h_chip *chip = tdata_to_rt1711h(tdata);
  
-       return rt1711h_write8(chip, RT1711H_RTCTRL8,
-                             RT1711H_RTCTRL8_SET(0, 1, !enable, 2));
+       return regmap_update_bits(chip->data.regmap, RT1711H_RTCTRL8,
+                                 RT1711H_AUTOIDLEEN, enable ? 0 : RT1711H_AUTOIDLEEN);
+ }
+ /*
+  * Selects the CC PHY noise filter voltage level according to the remote current
+  * CC voltage level.
+  *
+  * @status: The port's current cc status read from IC
+  * Return 0 if writes succeed; failure code otherwise
+  */
+ static inline int rt1711h_init_cc_params(struct rt1711h_chip *chip, u8 status)
+ {
+       int ret, cc1, cc2;
+       u8 role = 0;
+       u32 rxdz_en, rxdz_sel;
+       ret = rt1711h_read8(chip, TCPC_ROLE_CTRL, &role);
+       if (ret < 0)
+               return ret;
+       cc1 = tcpci_to_typec_cc((status >> TCPC_CC_STATUS_CC1_SHIFT) &
+                               TCPC_CC_STATUS_CC1_MASK,
+                               status & TCPC_CC_STATUS_TERM ||
+                               tcpc_presenting_rd(role, CC1));
+       cc2 = tcpci_to_typec_cc((status >> TCPC_CC_STATUS_CC2_SHIFT) &
+                               TCPC_CC_STATUS_CC2_MASK,
+                               status & TCPC_CC_STATUS_TERM ||
+                               tcpc_presenting_rd(role, CC2));
+       if ((cc1 >= TYPEC_CC_RP_1_5 && cc2 < TYPEC_CC_RP_DEF) ||
+           (cc2 >= TYPEC_CC_RP_1_5 && cc1 < TYPEC_CC_RP_DEF)) {
+               rxdz_en = BMCIO_RXDZEN;
+               if (chip->did == RT1715_DID)
+                       rxdz_sel = RT1711H_BMCIO_RXDZSEL;
+               else
+                       rxdz_sel = 0;
+       } else {
+               rxdz_en = 0;
+               rxdz_sel = RT1711H_BMCIO_RXDZSEL;
+       }
+       ret = regmap_update_bits(chip->data.regmap, RT1711H_RTCTRL18,
+                                BMCIO_RXDZEN, rxdz_en);
+       if (ret < 0)
+               return ret;
+       return regmap_update_bits(chip->data.regmap, RT1711H_RTCTRL4,
+                                 RT1711H_BMCIO_RXDZSEL, rxdz_sel);
  }
  
  static int rt1711h_start_drp_toggling(struct tcpci *tcpci,
@@@ -173,6 -278,8 +278,8 @@@ static irqreturn_t rt1711h_irq(int irq
                /* Clear cc change event triggered by starting toggling */
                if (status & TCPC_CC_STATUS_TOGGLING)
                        rt1711h_write8(chip, TCPC_ALERT, TCPC_ALERT_CC_STATUS);
+               else
+                       rt1711h_init_cc_params(chip, status);
        }
  
  out:
@@@ -191,7 -298,7 +298,7 @@@ static int rt1711h_sw_reset(struct rt17
        return 0;
  }
  
- static int rt1711h_check_revision(struct i2c_client *i2c)
+ static int rt1711h_check_revision(struct i2c_client *i2c, struct rt1711h_chip *chip)
  {
        int ret;
  
                dev_err(&i2c->dev, "pid is not correct, 0x%04x\n", ret);
                return -ENODEV;
        }
-       return 0;
+       ret = i2c_smbus_read_word_data(i2c, TCPC_BCD_DEV);
+       if (ret < 0)
+               return ret;
+       if (ret != chip->did) {
+               dev_err(&i2c->dev, "did is not correct, 0x%04x\n", ret);
+               return -ENODEV;
+       }
+       dev_dbg(&i2c->dev, "did is 0x%04x\n", ret);
+       return ret;
  }
  
  static int rt1711h_probe(struct i2c_client *client,
        int ret;
        struct rt1711h_chip *chip;
  
-       ret = rt1711h_check_revision(client);
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+       chip->did = (size_t)device_get_match_data(&client->dev);
+       ret = rt1711h_check_revision(client, chip);
        if (ret < 0) {
                dev_err(&client->dev, "check vid/pid fail\n");
                return ret;
        }
  
-       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
-       if (!chip)
-               return -ENOMEM;
        chip->data.regmap = devm_regmap_init_i2c(client,
                                                 &rt1711h_regmap_config);
        if (IS_ERR(chip->data.regmap))
        if (ret < 0)
                return ret;
  
+       chip->vbus = devm_regulator_get(&client->dev, "vbus");
+       if (IS_ERR(chip->vbus))
+               return PTR_ERR(chip->vbus);
        chip->data.init = rt1711h_init;
+       chip->data.set_vbus = rt1711h_set_vbus;
        chip->data.set_vconn = rt1711h_set_vconn;
        chip->data.start_drp_toggling = rt1711h_start_drp_toggling;
        chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
        return 0;
  }
  
 -static int rt1711h_remove(struct i2c_client *client)
 +static void rt1711h_remove(struct i2c_client *client)
  {
        struct rt1711h_chip *chip = i2c_get_clientdata(client);
  
        tcpci_unregister_port(chip->tcpci);
 -      return 0;
  }
  
  static const struct i2c_device_id rt1711h_id[] = {
        { "rt1711h", 0 },
+       { "rt1715", 0 },
        { }
  };
  MODULE_DEVICE_TABLE(i2c, rt1711h_id);
  
  #ifdef CONFIG_OF
  static const struct of_device_id rt1711h_of_match[] = {
-       { .compatible = "richtek,rt1711h", },
+       { .compatible = "richtek,rt1711h", .data = (void *)RT1711H_DID },
+       { .compatible = "richtek,rt1715", .data = (void *)RT1715_DID },
        {},
  };
  MODULE_DEVICE_TABLE(of, rt1711h_of_match);
@@@ -588,6 -588,8 +588,6 @@@ static int ucsi_get_pdos(struct ucsi_co
                                num_pdos * sizeof(u32));
        if (ret < 0 && ret != -ETIMEDOUT)
                dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret);
 -      if (ret == 0 && offset == 0)
 -              dev_warn(ucsi->dev, "UCSI_GET_PDOS returned 0 bytes\n");
  
        return ret;
  }
@@@ -1067,11 -1069,9 +1067,9 @@@ static int ucsi_register_port(struct uc
  
        cap->fwnode = ucsi_find_fwnode(con);
        con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode);
-       if (IS_ERR(con->usb_role_sw)) {
-               dev_err(ucsi->dev, "con%d: failed to get usb role switch\n",
-                       con->num);
-               return PTR_ERR(con->usb_role_sw);
-       }
+       if (IS_ERR(con->usb_role_sw))
+               return dev_err_probe(ucsi->dev, PTR_ERR(con->usb_role_sw),
+                       "con%d: failed to get usb role switch\n", con->num);
  
        /* Delay other interactions with the con until registration is complete */
        mutex_lock(&con->lock);
@@@ -125,6 -125,11 +125,11 @@@ struct version_format 
  #define CCG_FW_BUILD_NVIDIA   (('n' << 8) | 'v')
  #define CCG_OLD_FW_VERSION    (CCG_VERSION(0x31) | CCG_VERSION_PATCH(10))
  
+ /* Firmware for Tegra doesn't support UCSI ALT command, built
+  * for NVIDIA has known issue of reporting wrong capability info
+  */
+ #define CCG_FW_BUILD_NVIDIA_TEGRA     (('g' << 8) | 'n')
  /* Altmode offset for NVIDIA Function Test Board (FTB) */
  #define NVIDIA_FTB_DP_OFFSET  (2)
  #define NVIDIA_FTB_DBG_OFFSET (3)
@@@ -513,6 -518,7 +518,7 @@@ static int ucsi_ccg_read(struct ucsi *u
  {
        struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
        u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
+       struct ucsi_capability *cap;
        struct ucsi_altmode *alt;
        int ret;
  
                                ucsi_ccg_nvidia_altmode(uc, alt);
                }
                break;
+       case UCSI_GET_CAPABILITY:
+               if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) {
+                       cap = val;
+                       cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS;
+               }
+               break;
        default:
                break;
        }
@@@ -1403,7 -1415,7 +1415,7 @@@ out_ucsi_destroy
        return status;
  }
  
 -static int ucsi_ccg_remove(struct i2c_client *client)
 +static void ucsi_ccg_remove(struct i2c_client *client)
  {
        struct ucsi_ccg *uc = i2c_get_clientdata(client);
  
        ucsi_unregister(uc->ucsi);
        ucsi_destroy(uc->ucsi);
        free_irq(uc->irq, uc);
 -
 -      return 0;
  }
  
  static const struct i2c_device_id ucsi_ccg_device_id[] = {
@@@ -599,7 -599,7 +599,7 @@@ static int ucsi_stm32g0_probe_bootloade
                g0->i2c_bl = i2c_new_dummy_device(g0->client->adapter, STM32G0_I2C_BL_ADDR);
                if (IS_ERR(g0->i2c_bl)) {
                        ret = dev_err_probe(g0->dev, PTR_ERR(g0->i2c_bl),
-                                           "Failed to register booloader I2C address\n");
+                                           "Failed to register bootloader I2C address\n");
                        return ret;
                }
        }
@@@ -688,7 -688,7 +688,7 @@@ destroy
        return ret;
  }
  
 -static int ucsi_stm32g0_remove(struct i2c_client *client)
 +static void ucsi_stm32g0_remove(struct i2c_client *client)
  {
        struct ucsi_stm32g0 *g0 = i2c_get_clientdata(client);
  
        if (g0->fw_name)
                i2c_unregister_device(g0->i2c_bl);
        ucsi_destroy(g0->ucsi);
 -
 -      return 0;
  }
  
  static int ucsi_stm32g0_suspend(struct device *dev)