Merge tag 'usb-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 1 Sep 2023 16:23:34 +0000 (09:23 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 1 Sep 2023 16:23:34 +0000 (09:23 -0700)
Pull USB / Thunderbolt / PHY driver updates from Greg KH:
 "Here is the big set of USB, Thunderbolt, and PHY driver updates for
  6.6-rc1. Included in here are:

   - PHY driver additions and cleanups

   - Thunderbolt minor additions and fixes

   - USB MIDI 2 gadget support added

   - dwc3 driver updates and additions

   - Removal of some old USB wireless code that was missed when that
     codebase was originally removed a few years ago, cleaning up some
     core USB code paths

   - USB core potential use-after-free fixes that syzbot from different
     people/groups keeps tripping over

   - typec updates and additions

   - gadget fixes and cleanups

   - loads of smaller USB core and driver cleanups all over the place

  Full details are in the shortlog. All of these have been in linux-next
  for a while with no reported problems"

* tag 'usb-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (154 commits)
  platform/chrome: cros_ec_typec: Configure Retimer cable type
  tcpm: Avoid soft reset when partner does not support get_status
  usb: typec: tcpm: reset counter when enter into unattached state after try role
  usb: typec: tcpm: set initial svdm version based on pd revision
  USB: serial: option: add FOXCONN T99W368/T99W373 product
  USB: serial: option: add Quectel EM05G variant (0x030e)
  usb: dwc2: add pci_device_id driver_data parse support
  usb: gadget: remove max support speed info in bind operation
  usb: gadget: composite: cleanup function config_ep_by_speed_and_alt()
  usb: gadget: config: remove max speed check in usb_assign_descriptors()
  usb: gadget: unconditionally allocate hs/ss descriptor in bind operation
  usb: gadget: f_uvc: change endpoint allocation in uvc_function_bind()
  usb: gadget: add a inline function gether_bitrate()
  usb: gadget: use working speed to calcaulate network bitrate and qlen
  dt-bindings: usb: samsung,exynos-dwc3: Add Exynos850 support
  usb: dwc3: exynos: Add support for Exynos850 variant
  usb: gadget: udc-xilinx: fix incorrect type in assignment warning
  usb: gadget: udc-xilinx: fix cast from restricted __le16 warning
  usb: gadget: udc-xilinx: fix restricted __le16 degrades to integer warning
  USB: dwc2: hande irq on dead controller correctly
  ...

211 files changed:
CREDITS
Documentation/ABI/testing/configfs-usb-gadget-midi2 [new file with mode: 0644]
Documentation/ABI/testing/sysfs-bus-thunderbolt
Documentation/ABI/testing/sysfs-bus-umc [deleted file]
Documentation/ABI/testing/sysfs-bus-usb
Documentation/ABI/testing/sysfs-class-uwb_rc [deleted file]
Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc [deleted file]
Documentation/ABI/testing/sysfs-wusb_cbaf [deleted file]
Documentation/admin-guide/kernel-parameters.txt
Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
Documentation/devicetree/bindings/usb/cypress,hx3.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/generic-ehci.yaml
Documentation/devicetree/bindings/usb/genesys,gl850g.yaml
Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml
Documentation/driver-api/usb/usb.rst
Documentation/usb/authorization.rst
Documentation/usb/gadget-testing.rst
arch/mips/cavium-octeon/Makefile
arch/mips/cavium-octeon/octeon-platform.c
drivers/net/wireless/mediatek/mt76/usb.c
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/realtek/Kconfig [new file with mode: 0644]
drivers/phy/realtek/Makefile [new file with mode: 0644]
drivers/phy/realtek/phy-rtk-usb2.c [new file with mode: 0644]
drivers/phy/realtek/phy-rtk-usb3.c [new file with mode: 0644]
drivers/platform/chrome/cros_ec_typec.c
drivers/thunderbolt/acpi.c
drivers/thunderbolt/switch.c
drivers/thunderbolt/tb.c
drivers/thunderbolt/tb.h
drivers/thunderbolt/tmu.c
drivers/usb/cdns3/cdns3-gadget.c
drivers/usb/cdns3/cdns3-plat.c
drivers/usb/cdns3/cdns3-starfive.c
drivers/usb/cdns3/cdns3-ti.c
drivers/usb/cdns3/cdnsp-pci.c
drivers/usb/cdns3/core.c
drivers/usb/cdns3/core.h
drivers/usb/cdns3/drd.c
drivers/usb/chipidea/ci.h
drivers/usb/chipidea/ci_hdrc_imx.c
drivers/usb/chipidea/ci_hdrc_imx.h
drivers/usb/chipidea/ci_hdrc_tegra.c
drivers/usb/chipidea/core.c
drivers/usb/chipidea/host.c
drivers/usb/chipidea/udc.c
drivers/usb/chipidea/usbmisc_imx.c
drivers/usb/class/cdc-acm.c
drivers/usb/common/common.c
drivers/usb/core/config.c
drivers/usb/core/devices.c
drivers/usb/core/file.c
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
drivers/usb/core/ledtrig-usbport.c
drivers/usb/core/message.c
drivers/usb/core/of.c
drivers/usb/core/sysfs.c
drivers/usb/core/urb.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
drivers/usb/dwc2/core.h
drivers/usb/dwc2/gadget.c
drivers/usb/dwc2/hcd_intr.c
drivers/usb/dwc2/params.c
drivers/usb/dwc2/pci.c
drivers/usb/dwc2/platform.c
drivers/usb/dwc3/Kconfig
drivers/usb/dwc3/Makefile
drivers/usb/dwc3/dwc3-am62.c
drivers/usb/dwc3/dwc3-exynos.c
drivers/usb/dwc3/dwc3-imx8mp.c
drivers/usb/dwc3/dwc3-keystone.c
drivers/usb/dwc3/dwc3-meson-g12a.c
drivers/usb/dwc3/dwc3-octeon.c [moved from arch/mips/cavium-octeon/octeon-usb.c with 61% similarity]
drivers/usb/dwc3/dwc3-of-simple.c
drivers/usb/gadget/Kconfig
drivers/usb/gadget/composite.c
drivers/usb/gadget/config.c
drivers/usb/gadget/function/Makefile
drivers/usb/gadget/function/f_acm.c
drivers/usb/gadget/function/f_ecm.c
drivers/usb/gadget/function/f_eem.c
drivers/usb/gadget/function/f_loopback.c
drivers/usb/gadget/function/f_mass_storage.c
drivers/usb/gadget/function/f_midi.c
drivers/usb/gadget/function/f_midi2.c [new file with mode: 0644]
drivers/usb/gadget/function/f_ncm.c
drivers/usb/gadget/function/f_obex.c
drivers/usb/gadget/function/f_rndis.c
drivers/usb/gadget/function/f_serial.c
drivers/usb/gadget/function/f_sourcesink.c
drivers/usb/gadget/function/f_subset.c
drivers/usb/gadget/function/f_uvc.c
drivers/usb/gadget/function/u_ether.c
drivers/usb/gadget/function/u_ether.h
drivers/usb/gadget/function/u_midi2.h [new file with mode: 0644]
drivers/usb/gadget/function/u_phonet.h
drivers/usb/gadget/function/u_serial.h
drivers/usb/gadget/function/uvc.h
drivers/usb/gadget/udc/aspeed-vhub/core.c
drivers/usb/gadget/udc/aspeed_udc.c
drivers/usb/gadget/udc/atmel_usba_udc.c
drivers/usb/gadget/udc/core.c
drivers/usb/gadget/udc/fsl_qe_udc.c
drivers/usb/gadget/udc/fsl_udc_core.c
drivers/usb/gadget/udc/gr_udc.c
drivers/usb/gadget/udc/max3420_udc.c
drivers/usb/gadget/udc/mv_u3d_core.c
drivers/usb/gadget/udc/mv_udc_core.c
drivers/usb/gadget/udc/pxa27x_udc.c
drivers/usb/gadget/udc/renesas_usb3.c
drivers/usb/gadget/udc/renesas_usbf.c
drivers/usb/gadget/udc/snps_udc_plat.c
drivers/usb/gadget/udc/tegra-xudc.c
drivers/usb/gadget/udc/udc-xilinx.c
drivers/usb/host/ehci-atmel.c
drivers/usb/host/ehci-brcm.c
drivers/usb/host/ehci-exynos.c
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-mv.c
drivers/usb/host/ehci-npcm7xx.c
drivers/usb/host/ehci-omap.c
drivers/usb/host/ehci-orion.c
drivers/usb/host/ehci-platform.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/ehci-sh.c
drivers/usb/host/ehci-spear.c
drivers/usb/host/ehci-st.c
drivers/usb/host/ehci.h
drivers/usb/host/fhci-hcd.c
drivers/usb/host/fsl-mph-dr-of.c
drivers/usb/host/isp1362-hcd.c
drivers/usb/host/ohci-at91.c
drivers/usb/host/ohci-da8xx.c
drivers/usb/host/ohci-exynos.c
drivers/usb/host/ohci-nxp.c
drivers/usb/host/ohci-platform.c
drivers/usb/host/ohci-ppc-of.c
drivers/usb/host/ohci-pxa27x.c
drivers/usb/host/ohci-sm501.c
drivers/usb/host/ohci-spear.c
drivers/usb/host/ohci-st.c
drivers/usb/host/oxu210hp-hcd.c
drivers/usb/host/uhci-platform.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci-rcar.c
drivers/usb/host/xhci-tegra.c
drivers/usb/host/xhci.c
drivers/usb/misc/cypress_cy7c63.c
drivers/usb/misc/cytherm.c
drivers/usb/misc/onboard_usb_hub.c
drivers/usb/misc/onboard_usb_hub.h
drivers/usb/misc/usb251xb.c
drivers/usb/misc/usb_u132.h [deleted file]
drivers/usb/misc/usbsevseg.c
drivers/usb/mtu3/mtu3.h
drivers/usb/mtu3/mtu3_host.c
drivers/usb/musb/cppi_dma.h
drivers/usb/musb/jz4740.c
drivers/usb/musb/mediatek.c
drivers/usb/musb/mpfs.c
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_dma.h
drivers/usb/musb/musb_dsps.c
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/sunxi.c
drivers/usb/musb/tusb6010.c
drivers/usb/phy/phy-mxs-usb.c
drivers/usb/phy/phy-tegra-usb.c
drivers/usb/renesas_usbhs/common.c
drivers/usb/renesas_usbhs/rza.c
drivers/usb/renesas_usbhs/rza2.c
drivers/usb/serial/option.c
drivers/usb/serial/xr_serial.c
drivers/usb/typec/altmodes/displayport.c
drivers/usb/typec/bus.c
drivers/usb/typec/mux/intel_pmc_mux.c
drivers/usb/typec/mux/nb7vpq904m.c
drivers/usb/typec/tcpm/Kconfig
drivers/usb/typec/tcpm/fusb302.c
drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
drivers/usb/typec/tcpm/tcpci.c
drivers/usb/typec/tcpm/tcpci_mt6370.c
drivers/usb/typec/tcpm/tcpm.c
drivers/usb/typec/ucsi/Kconfig
drivers/usb/typec/ucsi/Makefile
drivers/usb/typec/ucsi/debugfs.c [new file with mode: 0644]
drivers/usb/typec/ucsi/ucsi.c
drivers/usb/typec/ucsi/ucsi.h
drivers/usb/typec/ucsi/ucsi_glink.c
drivers/usb/usbip/vudc_dev.c
include/linux/usb.h
include/linux/usb/ch9.h
include/linux/usb/chipidea.h
include/linux/usb/composite.h
include/linux/usb/hcd.h
include/linux/usb/phy.h
include/linux/usb/tcpci.h
include/linux/usb/typec_altmode.h
include/uapi/linux/usb/ch11.h
include/uapi/linux/usb/ch9.h

diff --git a/CREDITS b/CREDITS
index 8b4882024635dca60f05684699add61a935b58ea..f33a33fd2371707b5b22e99408dc18cae59653de 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -666,11 +666,6 @@ S: Tamsui town, Taipei county,
 S: Taiwan 251
 S: Republic of China
 
-N: Reinette Chatre
-E: reinette.chatre@intel.com
-D: WiMedia Link Protocol implementation
-D: UWB stack bits and pieces
-
 N: Michael Elizabeth Chastain
 E: mec@shout.net
 D: Configure, Menuconfig, xconfig
@@ -3023,12 +3018,6 @@ S: Demonstratsii 8-382
 S: Tula 300000
 S: Russia
 
-N: Inaky Perez-Gonzalez
-E: inaky.perez-gonzalez@intel.com
-D: UWB stack, HWA-RC driver and HWA-HC drivers
-D: Wireless USB additions to the USB stack
-D: WiMedia Link Protocol bits and pieces
-
 N: Gordon Peters
 E: GordPeters@smarttech.com
 D: Isochronous receive for IEEE 1394 driver (OHCI module).
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi2 b/Documentation/ABI/testing/configfs-usb-gadget-midi2
new file mode 100644 (file)
index 0000000..0eac3aa
--- /dev/null
@@ -0,0 +1,54 @@
+What:          /config/usb-gadget/gadget/functions/midi2.name
+Date:          Jul 2023
+KernelVersion: 6.6
+Description:
+               The attributes:
+
+               ============    ===============================================
+               process_ump     Flag to process UMP Stream messages (0 or 1)
+               static_block    Flag for static blocks (0 or 1)
+               iface_name      MIDI interface name string
+               ============    ===============================================
+
+What:          /config/usb-gadget/gadget/functions/midi2.name/ep.number
+Date:          Jul 2023
+KernelVersion: 6.6
+Description:
+               This group contains a UMP Endpoint configuration.
+               A new Endpoint starts from 0, and can be up to 3.
+
+               The attributes:
+
+               =============   ===============================================
+               protocol_caps   MIDI protocol capabilities (1, 2 or 3 for both)
+               protocol        Default MIDI protocol (1 or 2)
+               ep_name         UMP Endpoint name string
+               product_id      Product ID string
+               manufacturer    Manufacture ID (24 bit)
+               family          Device family ID (16 bit)
+               model           Device model ID (16 bit)
+               sw_revision     Software Revision (32 bit)
+               =============   ===============================================
+
+What:          /config/usb-gadget/gadget/functions/midi2.name/ep.number/block.number
+Date:          Jul 2023
+KernelVersion: 6.6
+Description:
+               This group contains a UMP Function Block configuration.
+               A new block starts from 0, and can be up to 31.
+
+               The attributes:
+
+               =================       ==============================================
+               name                    Function Block name string
+               direction               1: input, 2: output, 3: bidirectional
+               first_group             The first UMP Group number (0-15)
+               num_groups              The number of groups in this FB (1-16)
+               midi1_first_group       The first UMP Group number for MIDI 1.0 (0-15)
+               midi1_num_groups        The number of groups for MIDI 1.0 (0-16)
+               ui_hint                 0: unknown, 1: receiver, 2: sender, 3: both
+               midi_ci_verison         Supported MIDI-CI version number (8 bit)
+               is_midi1                Legacy MIDI 1.0 device (0, 1 or 2)
+               sysex8_streams          Max number of SysEx8 streams (8 bit)
+               active                  Active FB flag (0 or 1)
+               =================       ==============================================
index 76ab3e1fe374c4d0d42998daeb63f03fe0a164b0..221b6c75ed93d860e410bcd2d2bcfa78ebd925a2 100644 (file)
@@ -1,7 +1,7 @@
 What:          /sys/bus/thunderbolt/devices/.../domainX/boot_acl
 Date:          Jun 2018
 KernelVersion: 4.17
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   Holds a comma separated list of device unique_ids that
                are allowed to be connected automatically during system
                startup (e.g boot devices). The list always contains
@@ -33,7 +33,7 @@ Description:  This attribute tells whether the system supports
 What:          /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection
 Date:          Mar 2019
 KernelVersion: 4.21
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This attribute tells whether the system uses IOMMU
                for DMA protection. Value of 1 means IOMMU is used 0 means
                it is not (DMA protection is solely based on Thunderbolt
@@ -42,7 +42,7 @@ Description:  This attribute tells whether the system uses IOMMU
 What:          /sys/bus/thunderbolt/devices/.../domainX/security
 Date:          Sep 2017
 KernelVersion: 4.13
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This attribute holds current Thunderbolt security level
                set by the system BIOS. Possible values are:
 
@@ -64,7 +64,7 @@ Description:  This attribute holds current Thunderbolt security level
 What:          /sys/bus/thunderbolt/devices/.../authorized
 Date:          Sep 2017
 KernelVersion: 4.13
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This attribute is used to authorize Thunderbolt devices
                after they have been connected. If the device is not
                authorized, no PCIe devices are available to the system.
@@ -98,7 +98,7 @@ Description:  This attribute is used to authorize Thunderbolt devices
 What:          /sys/bus/thunderbolt/devices/.../boot
 Date:          Jun 2018
 KernelVersion: 4.17
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This attribute contains 1 if Thunderbolt device was already
                authorized on boot and 0 otherwise.
 
@@ -113,7 +113,7 @@ Description:        This attribute contains the generation of the Thunderbolt
 What:          /sys/bus/thunderbolt/devices/.../key
 Date:          Sep 2017
 KernelVersion: 4.13
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   When a devices supports Thunderbolt secure connect it will
                have this attribute. Writing 32 byte hex string changes
                authorization to use the secure connection method instead.
@@ -123,14 +123,14 @@ Description:      When a devices supports Thunderbolt secure connect it will
 What:          /sys/bus/thunderbolt/devices/.../device
 Date:          Sep 2017
 KernelVersion: 4.13
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This attribute contains id of this device extracted from
                the device DROM.
 
 What:          /sys/bus/thunderbolt/devices/.../device_name
 Date:          Sep 2017
 KernelVersion: 4.13
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This attribute contains name of this device extracted from
                the device DROM.
 
@@ -172,21 +172,21 @@ Description:      This attribute reports number of TX lanes the device is
 What:          /sys/bus/thunderbolt/devices/.../vendor
 Date:          Sep 2017
 KernelVersion: 4.13
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This attribute contains vendor id of this device extracted
                from the device DROM.
 
 What:          /sys/bus/thunderbolt/devices/.../vendor_name
 Date:          Sep 2017
 KernelVersion: 4.13
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This attribute contains vendor name of this device extracted
                from the device DROM.
 
 What:          /sys/bus/thunderbolt/devices/.../unique_id
 Date:          Sep 2017
 KernelVersion: 4.13
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This attribute contains unique_id string of this device.
                This is either read from hardware registers (UUID on
                newer hardware) or based on UID from the device DROM.
@@ -195,7 +195,7 @@ Description:        This attribute contains unique_id string of this device.
 What:          /sys/bus/thunderbolt/devices/.../nvm_version
 Date:          Sep 2017
 KernelVersion: 4.13
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   If the device has upgradeable firmware the version
                number is available here. Format: %x.%x, major.minor.
                If the device is in safe mode reading the file returns
@@ -204,7 +204,7 @@ Description:        If the device has upgradeable firmware the version
 What:          /sys/bus/thunderbolt/devices/.../nvm_authenticate
 Date:          Sep 2017
 KernelVersion: 4.13
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   When new NVM image is written to the non-active NVM
                area (through non_activeX NVMem device), the
                authentication procedure is started by writing to
@@ -246,7 +246,7 @@ Description:        For supported devices, automatically authenticate the new Thunderbo
 What:          /sys/bus/thunderbolt/devices/<xdomain>.<service>/key
 Date:          Jan 2018
 KernelVersion: 4.15
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This contains name of the property directory the XDomain
                service exposes. This entry describes the protocol in
                question. Following directories are already reserved by
@@ -261,35 +261,35 @@ Description:      This contains name of the property directory the XDomain
 What:          /sys/bus/thunderbolt/devices/<xdomain>.<service>/modalias
 Date:          Jan 2018
 KernelVersion: 4.15
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   Stores the same MODALIAS value emitted by uevent for
                the XDomain service. Format: tbtsvc:kSpNvNrN
 
 What:          /sys/bus/thunderbolt/devices/<xdomain>.<service>/prtcid
 Date:          Jan 2018
 KernelVersion: 4.15
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This contains XDomain protocol identifier the XDomain
                service supports.
 
 What:          /sys/bus/thunderbolt/devices/<xdomain>.<service>/prtcvers
 Date:          Jan 2018
 KernelVersion: 4.15
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This contains XDomain protocol version the XDomain
                service supports.
 
 What:          /sys/bus/thunderbolt/devices/<xdomain>.<service>/prtcrevs
 Date:          Jan 2018
 KernelVersion: 4.15
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This contains XDomain software version the XDomain
                service supports.
 
 What:          /sys/bus/thunderbolt/devices/<xdomain>.<service>/prtcstns
 Date:          Jan 2018
 KernelVersion: 4.15
-Contact:       thunderbolt-software@lists.01.org
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
 Description:   This contains XDomain service specific settings as
                bitmask. Format: %x
 
diff --git a/Documentation/ABI/testing/sysfs-bus-umc b/Documentation/ABI/testing/sysfs-bus-umc
deleted file mode 100644 (file)
index 80ef88b..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-What:           /sys/bus/umc/
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                The Wireless Host Controller Interface (WHCI)
-                specification describes a PCI-based device with
-                multiple capabilities; the UWB Multi-interface
-                Controller (UMC).
-
-                The umc bus presents each of the individual
-                capabilities as a device.
-
-What:           /sys/bus/umc/devices/.../capability_id
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                The ID of this capability, with 0 being the radio
-                controller capability.
-
-What:           /sys/bus/umc/devices/.../version
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                The specification version this capability's hardware
-                interface complies with.
index be663258b9b792a9397a2f65b74b2ed9ccdbf671..a44bfe0200616cf0c32461ef6d0963c9c936a4fe 100644 (file)
@@ -28,40 +28,6 @@ Description:
                drivers, non-authorized one are not.  By default, wired
                USB devices are authorized.
 
-               Certified Wireless USB devices are not authorized
-               initially and should be (by writing 1) after the
-               device has been authenticated.
-
-What:          /sys/bus/usb/device/.../wusb_cdid
-Date:          July 2008
-KernelVersion: 2.6.27
-Contact:       David Vrabel <david.vrabel@csr.com>
-Description:
-               For Certified Wireless USB devices only.
-
-               A devices's CDID, as 16 space-separated hex octets.
-
-What:          /sys/bus/usb/device/.../wusb_ck
-Date:          July 2008
-KernelVersion: 2.6.27
-Contact:       David Vrabel <david.vrabel@csr.com>
-Description:
-               For Certified Wireless USB devices only.
-
-               Write the device's connection key (CK) to start the
-               authentication of the device.  The CK is 16
-               space-separated hex octets.
-
-What:          /sys/bus/usb/device/.../wusb_disconnect
-Date:          July 2008
-KernelVersion: 2.6.27
-Contact:       David Vrabel <david.vrabel@csr.com>
-Description:
-               For Certified Wireless USB devices only.
-
-               Write a 1 to force the device to disconnect
-               (equivalent to unplugging a wired USB device).
-
 What:          /sys/bus/usb/drivers/.../new_id
 Date:          October 2011
 Contact:       linux-usb@vger.kernel.org
diff --git a/Documentation/ABI/testing/sysfs-class-uwb_rc b/Documentation/ABI/testing/sysfs-class-uwb_rc
deleted file mode 100644 (file)
index a7ea169..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-What:           /sys/class/uwb_rc
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                Interfaces for WiMedia Ultra Wideband Common Radio
-                Platform (UWB) radio controllers.
-
-                Familiarity with the ECMA-368 'High Rate Ultra
-                Wideband MAC and PHY Specification' is assumed.
-
-What:           /sys/class/uwb_rc/beacon_timeout_ms
-Date:           July 2008
-KernelVersion:  2.6.27
-Description:
-                If no beacons are received from a device for at least
-                this time, the device will be considered to have gone
-                and it will be removed.  The default is 3 superframes
-                (~197 ms) as required by the specification.
-
-What:           /sys/class/uwb_rc/uwb<N>/
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                An individual UWB radio controller.
-
-What:           /sys/class/uwb_rc/uwb<N>/beacon
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                Write:
-
-                <channel>
-
-                to force a specific channel to be used when beaconing,
-                or, if <channel> is -1, to prohibit beaconing.  If
-                <channel> is 0, then the default channel selection
-                algorithm will be used.  Valid channels depends on the
-                radio controller's supported band groups.
-
-                Reading returns the currently active channel, or -1 if
-                the radio controller is not beaconing.
-
-What:           /sys/class/uwb_rc/uwb<N>/ASIE
-Date:           August 2014
-KernelVersion:  3.18
-Contact:        linux-usb@vger.kernel.org
-Description:
-
-                The application-specific information element (ASIE)
-                included in this device's beacon, in space separated
-                hex octets.
-
-                Reading returns the current ASIE.  Writing replaces
-                the current ASIE with the one written.
-
-What:           /sys/class/uwb_rc/uwb<N>/scan
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                Write:
-
-                <channel> <type> [<bpst offset>]
-
-                to start (or stop) scanning on a channel.  <type> is one of:
-
-                  ==   =======================================
-                    0   scan
-                    1   scan outside BP
-                    2   scan while inactive
-                    3   scanning disabled
-                    4   scan (with start time of <bpst offset>)
-                  ==   =======================================
-
-What:           /sys/class/uwb_rc/uwb<N>/mac_address
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                The EUI-48, in colon-separated hex octets, for this
-                radio controller.  A write will change the radio
-                controller's EUI-48 but only do so while the device is
-                not beaconing or scanning.
-
-What:           /sys/class/uwb_rc/uwb<N>/wusbhc
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                A symlink to the device (if any) of the WUSB Host
-                Controller PAL using this radio controller.
-
-What:           /sys/class/uwb_rc/uwb<N>/<EUI-48>/
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                A neighbour UWB device that has either been detected
-                as part of a scan or is a member of the radio
-                controllers beacon group.
-
-What:           /sys/class/uwb_rc/uwb<N>/<EUI-48>/BPST
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                The time (using the radio controllers internal 1 ms
-                interval superframe timer) of the last beacon from
-                this device was received.
-
-What:           /sys/class/uwb_rc/uwb<N>/<EUI-48>/DevAddr
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                The current DevAddr of this device in colon separated
-                hex octets.
-
-What:           /sys/class/uwb_rc/uwb<N>/<EUI-48>/EUI_48
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-
-                The EUI-48 of this device in colon separated hex
-                octets.
-
-What:           /sys/class/uwb_rc/uwb<N>/<EUI-48>/IEs
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                The latest IEs included in this device's beacon, in
-                space separated hex octets with one IE per line.
-
-What:           /sys/class/uwb_rc/uwb<N>/<EUI-48>/LQE
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                Link Quality Estimate - the Signal to Noise Ratio
-                (SNR) of all packets received from this device in dB.
-                This gives an estimate on a suitable PHY rate. Refer
-                to [ECMA-368] section 13.3 for more details.
-
-What:           /sys/class/uwb_rc/uwb<N>/<EUI-48>/RSSI
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        linux-usb@vger.kernel.org
-Description:
-                Received Signal Strength Indication - the strength of
-                the received signal in dB.  LQE is a more useful
-                measure of the radio link quality.
diff --git a/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc b/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc
deleted file mode 100644 (file)
index 130ea51..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-What:           /sys/class/uwb_rc/uwb<N>/wusbhc/wusb_chid
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                Write the CHID (16 space-separated hex octets) for this host controller.
-                This starts the host controller, allowing it to accept connection from
-                WUSB devices.
-
-                Set an all zero CHID to stop the host controller.
-
-What:           /sys/class/uwb_rc/uwb<N>/wusbhc/wusb_trust_timeout
-Date:           July 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                Devices that haven't sent a WUSB packet to the host
-                within 'wusb_trust_timeout' ms are considered to have
-                disconnected and are removed.  The default value of
-                4000 ms is the value required by the WUSB
-                specification.
-
-                Since this relates to security (specifically, the
-                lifetime of PTKs and GTKs) it should not be changed
-                from the default.
-
-What:           /sys/class/uwb_rc/uwb<N>/wusbhc/wusb_phy_rate
-Date:           August 2009
-KernelVersion:  2.6.32
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                The maximum PHY rate to use for all connected devices.
-                This is only of limited use for testing and
-                development as the hardware's automatic rate
-                adaptation is better then this simple control.
-
-                Refer to [ECMA-368] section 10.3.1.1 for the value to
-                use.
-
-What:           /sys/class/uwb_rc/uwb<N>/wusbhc/wusb_dnts
-Date:           June 2013
-KernelVersion:  3.11
-Contact:        Thomas Pugliese <thomas.pugliese@gmail.com>
-Description:
-                The device notification time slot (DNTS) count and interval in
-                milliseconds that the WUSB host should use.  This controls how
-                often the devices will have the opportunity to send
-                notifications to the host.
-
-What:           /sys/class/uwb_rc/uwb<N>/wusbhc/wusb_retry_count
-Date:           June 2013
-KernelVersion:  3.11
-Contact:        Thomas Pugliese <thomas.pugliese@gmail.com>
-Description:
-                The number of retries that the WUSB host should attempt
-                before reporting an error for a bus transaction.  The range of
-                valid values is [0..15], where 0 indicates infinite retries.
diff --git a/Documentation/ABI/testing/sysfs-wusb_cbaf b/Documentation/ABI/testing/sysfs-wusb_cbaf
deleted file mode 100644 (file)
index 2969d36..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_*
-Date:           August 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                Various files for managing Cable Based Association of
-                (wireless) USB devices.
-
-                The sequence of operations should be:
-
-                1. Device is plugged in.
-
-                2. The connection manager (CM) sees a device with CBA capability.
-                   (the wusb_chid etc. files in /sys/devices/blah/OURDEVICE).
-
-                3. The CM writes the host name, supported band groups,
-                   and the CHID (host ID) into the wusb_host_name,
-                   wusb_host_band_groups and wusb_chid files. These
-                   get sent to the device and the CDID (if any) for
-                   this host is requested.
-
-                4. The CM can verify that the device's supported band
-                   groups (wusb_device_band_groups) are compatible
-                   with the host.
-
-                5. The CM reads the wusb_cdid file.
-
-                6. The CM looks it up its database.
-
-                   - If it has a matching CHID,CDID entry, the device
-                     has been authorized before and nothing further
-                     needs to be done.
-
-                   - If the CDID is zero (or the CM doesn't find a
-                     matching CDID in its database), the device is
-                     assumed to be not known.  The CM may associate
-                     the host with device by: writing a randomly
-                     generated CDID to wusb_cdid and then a random CK
-                     to wusb_ck (this uploads the new CC to the
-                     device).
-
-                     CMD may choose to prompt the user before
-                     associating with a new device.
-
-                7. Device is unplugged.
-
-                References:
-                  [WUSB-AM]
-                           Association Models Supplement to the
-                            Certified Wireless Universal Serial Bus
-                            Specification, version 1.0.
-
-What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_chid
-Date:           August 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                The CHID of the host formatted as 16 space-separated
-                hex octets.
-
-                Writes fetches device's supported band groups and the
-                the CDID for any existing association with this host.
-
-What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_host_name
-Date:           August 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                A friendly name for the host as a UTF-8 encoded string.
-
-What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_host_band_groups
-Date:           August 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                The band groups supported by the host, in the format
-                defined in [WUSB-AM].
-
-What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_device_band_groups
-Date:           August 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                The band groups supported by the device, in the format
-                defined in [WUSB-AM].
-
-What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_cdid
-Date:           August 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                The device's CDID formatted as 16 space-separated hex
-                octets.
-
-What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_ck
-Date:           August 2008
-KernelVersion:  2.6.27
-Contact:        David Vrabel <david.vrabel@csr.com>
-Description:
-                Write 16 space-separated random, hex octets to
-                associate with the device.
index 98efa34bf530dc5882900ff8d9b068d39680e6bb..b2b1280342250045cd01ae6bbb8e5f407668f73d 100644 (file)
 
        usbcore.authorized_default=
                        [USB] Default USB device authorization:
-                       (default -1 = authorized except for wireless USB,
+                       (default -1 = authorized (same as 1),
                        0 = not authorized, 1 = authorized, 2 = authorized
                        if device connected to internal port)
 
diff --git a/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml b/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml
new file mode 100644 (file)
index 0000000..9911ada
--- /dev/null
@@ -0,0 +1,175 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2023 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/realtek,usb2phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek DHC SoCs USB 2.0 PHY
+
+maintainers:
+  - Stanley Chang <stanley_chang@realtek.com>
+
+description: |
+  Realtek USB 2.0 PHY support the digital home center (DHC) RTD series SoCs.
+  The USB 2.0 PHY driver is designed to support the XHCI controller. The SoCs
+  support multiple XHCI controllers. One PHY device node maps to one XHCI
+  controller.
+
+  RTD1295/RTD1619 SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on some
+  controllers.
+  XHCI controller#0 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+
+  RTD1395 SoCs USB
+  The USB architecture includes two XHCI controllers.
+  The controller#0 has one USB 2.0 PHY. The controller#1 includes two USB 2.0
+  PHY.
+  XHCI controller#0 -- usb2phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+                               |- phy#1
+
+  RTD1319/RTD1619b SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#2.
+  XHCI controller#0 -- usb2phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+
+  RTD1319d SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each xhci maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#0.
+  XHCI controller#0 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+
+  RTD1312c/RTD1315e SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each XHCI maps to one USB 2.0 PHY.
+  XHCI controller#0 -- usb2phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+
+properties:
+  compatible:
+    enum:
+      - realtek,rtd1295-usb2phy
+      - realtek,rtd1312c-usb2phy
+      - realtek,rtd1315e-usb2phy
+      - realtek,rtd1319-usb2phy
+      - realtek,rtd1319d-usb2phy
+      - realtek,rtd1395-usb2phy
+      - realtek,rtd1395-usb2phy-2port
+      - realtek,rtd1619-usb2phy
+      - realtek,rtd1619b-usb2phy
+
+  reg:
+    items:
+      - description: PHY data registers
+      - description: PHY control registers
+
+  "#phy-cells":
+    const: 0
+
+  nvmem-cells:
+    maxItems: 2
+    description:
+      Phandles to nvmem cell that contains the trimming data.
+      If unspecified, default value is used.
+
+  nvmem-cell-names:
+    items:
+      - const: usb-dc-cal
+      - const: usb-dc-dis
+    description:
+      The following names, which correspond to each nvmem-cells.
+      usb-dc-cal is the driving level for each phy specified via efuse.
+      usb-dc-dis is the disconnection level for each phy specified via efuse.
+
+  realtek,inverse-hstx-sync-clock:
+    description:
+      For one of the phys of RTD1619b SoC, the synchronous clock of the
+      high-speed tx must be inverted.
+    type: boolean
+
+  realtek,driving-level:
+    description:
+      Control the magnitude of High speed Dp/Dm output swing (mV).
+      For a different board or port, the original magnitude maybe not meet
+      the specification. In this situation we can adjust the value to meet
+      the specification.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    default: 8
+    minimum: 0
+    maximum: 31
+
+  realtek,driving-level-compensate:
+    description:
+      For RTD1315e SoC, the driving level can be adjusted by reading the
+      efuse table. This property provides drive compensation.
+      If the magnitude of High speed Dp/Dm output swing still not meet the
+      specification, then we can set this value to meet the specification.
+    $ref: /schemas/types.yaml#/definitions/int32
+    default: 0
+    minimum: -8
+    maximum: 8
+
+  realtek,disconnection-compensate:
+    description:
+      This adjusts the disconnection level compensation for the different
+      boards with different disconnection level.
+    $ref: /schemas/types.yaml#/definitions/int32
+    default: 0
+    minimum: -8
+    maximum: 8
+
+required:
+  - compatible
+  - reg
+  - "#phy-cells"
+
+allOf:
+  - if:
+      not:
+        properties:
+          compatible:
+            contains:
+              enum:
+                - realtek,rtd1619b-usb2phy
+    then:
+      properties:
+        realtek,inverse-hstx-sync-clock: false
+
+  - if:
+      not:
+        properties:
+          compatible:
+            contains:
+              enum:
+                - realtek,rtd1315e-usb2phy
+    then:
+      properties:
+        realtek,driving-level-compensate: false
+
+additionalProperties: false
+
+examples:
+  - |
+    usb-phy@13214 {
+        compatible = "realtek,rtd1619b-usb2phy";
+        reg = <0x13214 0x4>, <0x28280 0x4>;
+        #phy-cells = <0>;
+        nvmem-cells = <&otp_usb_port0_dc_cal>, <&otp_usb_port0_dc_dis>;
+        nvmem-cell-names = "usb-dc-cal", "usb-dc-dis";
+
+        realtek,inverse-hstx-sync-clock;
+        realtek,driving-level = <0xa>;
+        realtek,disconnection-compensate = <(-1)>;
+    };
diff --git a/Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml b/Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml
new file mode 100644 (file)
index 0000000..dfe2bb4
--- /dev/null
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2023 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/realtek,usb3phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek DHC SoCs USB 3.0 PHY
+
+maintainers:
+  - Stanley Chang <stanley_chang@realtek.com>
+
+description: |
+  Realtek USB 3.0 PHY support the digital home center (DHC) RTD series SoCs.
+  The USB 3.0 PHY driver is designed to support the XHCI controller. The SoCs
+  support multiple XHCI controllers. One PHY device node maps to one XHCI
+  controller.
+
+  RTD1295/RTD1619 SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on some
+  controllers.
+  XHCI controller#0 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+
+  RTD1319/RTD1619b SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#2.
+  XHCI controller#0 -- usb2phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+
+  RTD1319d SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each xhci maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#0.
+  XHCI controller#0 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+
+properties:
+  compatible:
+    enum:
+      - realtek,rtd1295-usb3phy
+      - realtek,rtd1319-usb3phy
+      - realtek,rtd1319d-usb3phy
+      - realtek,rtd1619-usb3phy
+      - realtek,rtd1619b-usb3phy
+
+  reg:
+    maxItems: 1
+
+  "#phy-cells":
+    const: 0
+
+  nvmem-cells:
+    maxItems: 1
+    description: A phandle to the tx lfps swing trim data provided by
+      a nvmem device, if unspecified, default values shall be used.
+
+  nvmem-cell-names:
+    items:
+      - const: usb_u3_tx_lfps_swing_trim
+
+  realtek,amplitude-control-coarse-tuning:
+    description:
+      This adjusts the signal amplitude for normal operation and beacon LFPS.
+      This value is a parameter for coarse tuning.
+      For different boards, if the default value is inappropriate, this
+      property can be assigned to adjust.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    default: 255
+    minimum: 0
+    maximum: 255
+
+  realtek,amplitude-control-fine-tuning:
+    description:
+      This adjusts the signal amplitude for normal operation and beacon LFPS.
+      This value is used for fine-tuning parameters.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    default: 65535
+    minimum: 0
+    maximum: 65535
+
+required:
+  - compatible
+  - reg
+  - "#phy-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    usb-phy@13e10 {
+        compatible = "realtek,rtd1319d-usb3phy";
+        reg = <0x13e10 0x4>;
+        #phy-cells = <0>;
+
+        nvmem-cells = <&otp_usb_u3_tx_lfps_swing_trim>;
+        nvmem-cell-names = "usb_u3_tx_lfps_swing_trim";
+
+        realtek,amplitude-control-coarse-tuning = <0x77>;
+    };
index 91f135c3495b5cf0fba9e74670d0df1349560f61..1394557517b1822d8502b8502160f0d6bc857b1a 100644 (file)
@@ -34,6 +34,7 @@ properties:
               - fsl,imx23-usb
               - fsl,imx25-usb
               - fsl,imx28-usb
+              - fsl,imx35-usb
               - fsl,imx50-usb
               - fsl,imx51-usb
               - fsl,imx53-usb
@@ -76,11 +77,11 @@ properties:
 
   clocks:
     minItems: 1
-    maxItems: 2
+    maxItems: 3
 
   clock-names:
     minItems: 1
-    maxItems: 2
+    maxItems: 3
 
   dr_mode: true
 
@@ -292,6 +293,18 @@ properties:
     minimum: 0x0
     maximum: 0xf
 
+  fsl,picophy-rise-fall-time-adjust:
+    description:
+      HS Transmitter Rise/Fall Time Adjustment. Adjust the rise/fall times
+      of the high-speed transmitter waveform. It has no unit. The rise/fall
+      time will be increased or decreased by a certain percentage relative
+      to design default time. (0:-10%; 1:design default; 2:+15%; 3:+20%)
+      Details can refer to TXRISETUNE0 bit of USBNC_n_PHY_CFG1.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 3
+    default: 1
+
   usb-phy:
     description: phandle for the PHY device. Use "phys" instead.
     $ref: /schemas/types.yaml#/definitions/phandle
diff --git a/Documentation/devicetree/bindings/usb/cypress,hx3.yaml b/Documentation/devicetree/bindings/usb/cypress,hx3.yaml
new file mode 100644 (file)
index 0000000..47add0d
--- /dev/null
@@ -0,0 +1,77 @@
+# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/cypress,hx3.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cypress HX3 USB 3.0 hub controller family
+
+maintainers:
+  - Benjamin Bara <benjamin.bara@skidata.com>
+
+allOf:
+  - $ref: usb-device.yaml#
+
+properties:
+  compatible:
+    enum:
+      - usb4b4,6504
+      - usb4b4,6506
+
+  reg: true
+
+  reset-gpios:
+    items:
+      - description: GPIO specifier for RESETN pin.
+
+  vdd-supply:
+    description:
+      1V2 power supply (VDD_EFUSE, AVDD12, DVDD12).
+
+  vdd2-supply:
+    description:
+      3V3 power supply (AVDD33, VDD_IO).
+
+  peer-hub:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      phandle to the peer hub on the controller.
+
+required:
+  - compatible
+  - reg
+  - peer-hub
+  - vdd-supply
+  - vdd2-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    usb {
+        dr_mode = "host";
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        /* 2.0 hub on port 1 */
+        hub_2_0: hub@1 {
+          compatible = "usb4b4,6504";
+          reg = <1>;
+          peer-hub = <&hub_3_0>;
+          reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
+          vdd-supply = <&reg_1v2_usb>;
+          vdd2-supply = <&reg_3v3_usb>;
+        };
+
+        /* 3.0 hub on port 2 */
+        hub_3_0: hub@2 {
+          compatible = "usb4b4,6506";
+          reg = <2>;
+          peer-hub = <&hub_2_0>;
+          reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
+          vdd-supply = <&reg_1v2_usb>;
+          vdd2-supply = <&reg_3v3_usb>;
+        };
+    };
index f37191f21501e079a5c50593a2c50e0fcdd93074..87986c45be88efdcc1953022da688f929ddb6797 100644 (file)
@@ -68,6 +68,7 @@ properties:
           - const: generic-ehci
       - items:
           - enum:
+              - atmel,at91sam9g45-ehci
               - cavium,octeon-6335-ehci
               - ibm,usb-ehci-440epx
               - ibm,usb-ehci-460ex
index 6378356727e1ef0252377d95d05d3667d2578920..d0927f6768a4822e6ae605fb9b4adb9281681ab2 100644 (file)
@@ -17,6 +17,7 @@ properties:
     enum:
       - usb5e3,608
       - usb5e3,610
+      - usb5e3,620
 
   reg: true
 
index ae24dac78d9a35f6d4fc79a4bb0a32473ed92e53..67591057f2349b07ca10029933531e0d54030795 100644 (file)
@@ -14,6 +14,7 @@ properties:
     items:
       - enum:
           - qcom,ipq4019-dwc3
+          - qcom,ipq5332-dwc3
           - qcom,ipq6018-dwc3
           - qcom,ipq8064-dwc3
           - qcom,ipq8074-dwc3
@@ -82,15 +83,6 @@ properties:
     minItems: 1
     maxItems: 9
 
-  assigned-clocks:
-    items:
-      - description: Phandle and clock specifier of MOCK_UTMI_CLK.
-      - description: Phandle and clock specifoer of MASTER_CLK.
-
-  assigned-clock-rates:
-    items:
-      - description: Must be 19.2MHz (19200000).
-      - description: Must be >= 60 MHz in HS mode, >= 125 MHz in SS mode.
   resets:
     maxItems: 1
 
@@ -246,6 +238,7 @@ allOf:
         compatible:
           contains:
             enum:
+              - qcom,ipq5332-dwc3
               - qcom,msm8994-dwc3
               - qcom,qcs404-dwc3
     then:
@@ -290,15 +283,23 @@ allOf:
     then:
       properties:
         clocks:
-          minItems: 6
+          minItems: 5
+          maxItems: 6
         clock-names:
-          items:
-            - const: cfg_noc
-            - const: core
-            - const: iface
-            - const: sleep
-            - const: mock_utmi
-            - const: bus
+          oneOf:
+            - items:
+                - const: cfg_noc
+                - const: core
+                - const: iface
+                - const: sleep
+                - const: mock_utmi
+                - const: bus
+            - items:
+                - const: cfg_noc
+                - const: core
+                - const: sleep
+                - const: mock_utmi
+                - const: bus
 
   - if:
       properties:
@@ -410,6 +411,7 @@ allOf:
         compatible:
           contains:
             enum:
+              - qcom,ipq5332-dwc3
               - qcom,sdm660-dwc3
     then:
       properties:
index 42ceaf13cd5da5d96378d209eeaaeb92bf039062..1ade99e85ba8f5078239ccb8bf9110b4f323cf11 100644 (file)
@@ -15,6 +15,7 @@ properties:
       - samsung,exynos5250-dwusb3
       - samsung,exynos5433-dwusb3
       - samsung,exynos7-dwusb3
+      - samsung,exynos850-dwusb3
 
   '#address-cells':
     const: 1
@@ -72,7 +73,7 @@ allOf:
       properties:
         compatible:
           contains:
-            const: samsung,exynos54333-dwusb3
+            const: samsung,exynos5433-dwusb3
     then:
       properties:
         clocks:
@@ -82,8 +83,8 @@ allOf:
           items:
             - const: aclk
             - const: susp_clk
-            - const: pipe_pclk
             - const: phyclk
+            - const: pipe_pclk
 
   - if:
       properties:
@@ -101,6 +102,21 @@ allOf:
             - const: usbdrd30_susp_clk
             - const: usbdrd30_axius_clk
 
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: samsung,exynos850-dwusb3
+    then:
+      properties:
+        clocks:
+          minItems: 2
+          maxItems: 2
+        clock-names:
+          items:
+            - const: bus_early
+            - const: ref
+
 additionalProperties: false
 
 examples:
index 2c94ff2f438573625e6db043698df1edbb2bb42d..fb41768696ecbc74ed10bc97832882172d5beb49 100644 (file)
@@ -420,6 +420,12 @@ USBDEVFS_CONNECTINFO
     know the devnum value already, it's the DDD value of the device file
     name.
 
+USBDEVFS_GET_SPEED
+    Returns the speed of the device. The speed is returned as a
+    nummerical value in accordance with enum usb_device_speed
+
+    File modification time is not updated by this request.
+
 USBDEVFS_GETDRIVER
     Returns the name of the kernel driver bound to a given interface (a
     string). Parameter is a pointer to this structure, which is
@@ -771,8 +777,7 @@ Speed may be:
        ======= ======================================================
        1.5     Mbit/s for low speed USB
        12      Mbit/s for full speed USB
-       480     Mbit/s for high speed USB (added for USB 2.0);
-               also used for Wireless USB, which has no fixed speed
+       480     Mbit/s for high speed USB (added for USB 2.0)
        5000    Mbit/s for SuperSpeed USB (added for USB 3.0)
        ======= ======================================================
 
index 9e53909d04c2893602f15f57e9ade96ee1da4327..150a14970e951d5ececefccebf1b609fb324c3fd 100644 (file)
@@ -33,12 +33,9 @@ Remove the lock down::
 
        $ echo 1 > /sys/bus/usb/devices/usbX/authorized_default
 
-By default, Wired USB devices are authorized by default to
-connect. Wireless USB hosts deauthorize by default all new connected
-devices (this is so because we need to do an authentication phase
-before authorizing). Writing "2" to the authorized_default attribute
-causes kernel to only authorize by default devices connected to internal
-USB ports.
+By default, all USB devices are authorized.  Writing "2" to the
+authorized_default attribute causes the kernel to authorize by default
+only devices connected to internal USB ports.
 
 
 Example system lockdown (lame)
index 2fca40443dc9b3ed73f57f613f88d3b274107920..394cd226bfaeb489aa0f42414f9fbcb5210272ef 100644 (file)
@@ -27,6 +27,7 @@ provided by gadgets.
    18. UVC function
    19. PRINTER function
    20. UAC1 function (new API)
+   21. MIDI2 function
 
 
 1. ACM function
@@ -965,3 +966,156 @@ e.g.::
 
        $ arecord -f dat -t wav -D hw:CARD=UAC1Gadget,DEV=0 | \
          aplay -D default:CARD=OdroidU3
+
+
+21. MIDI2 function
+==================
+
+The function is provided by usb_f_midi2.ko module.
+It will create a virtual ALSA card containing a UMP rawmidi device
+where the UMP packet is looped back. In addition, a legacy rawmidi
+device is created. The UMP rawmidi is bound with ALSA sequencer
+clients, too.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "midi2".
+The midi2 function provides these attributes in its function directory
+as the card top-level information:
+
+       =============   =================================================
+       process_ump     Bool flag to process UMP Stream messages (0 or 1)
+       static_block    Bool flag for static blocks (0 or 1)
+       iface_name      Optional interface name string
+       =============   =================================================
+
+The directory contains a subdirectory "ep.0", and this provides the
+attributes for a UMP Endpoint (which is a pair of USB MIDI Endpoints):
+
+       =============   =================================================
+       protocol_caps   MIDI protocol capabilities;
+                       1: MIDI 1.0, 2: MIDI 2.0, or 3: both protocols
+       protocol        Default MIDI protocol (either 1 or 2)
+       ep_name         UMP Endpoint name string
+       product_id      Product ID string
+       manufacturer    Manufacture ID number (24 bit)
+       family          Device family ID number (16 bit)
+       model           Device model ID number (16 bit)
+       sw_revision     Software revision (32 bit)
+       =============   =================================================
+
+Each Endpoint subdirectory contains a subdirectory "block.0", which
+represents the Function Block for Block 0 information.
+Its attributes are:
+
+       =================       ===============================================
+       name                    Function Block name string
+       direction               Direction of this FB
+                               1: input, 2: output, or 3: bidirectional
+       first_group             The first UMP Group number (0-15)
+       num_groups              The number of groups in this FB (1-16)
+       midi1_first_group       The first UMP Group number for MIDI 1.0 (0-15)
+       midi1_num_groups        The number of groups for MIDI 1.0 (0-16)
+       ui_hint                 UI-hint of this FB
+                               0: unknown, 1: receiver, 2: sender, 3: both
+       midi_ci_verison         Supported MIDI-CI version number (8 bit)
+       is_midi1                Legacy MIDI 1.0 device (0-2)
+                               0: MIDI 2.0 device,
+                               1: MIDI 1.0 without restriction, or
+                               2: MIDI 1.0 with low speed
+       sysex8_streams          Max number of SysEx8 streams (8 bit)
+       active                  Bool flag for FB activity (0 or 1)
+       =================       ===============================================
+
+If multiple Function Blocks are required, you can add more Function
+Blocks by creating subdirectories "block.<num>" with the corresponding
+Function Block number (1, 2, ....). The FB subdirectories can be
+dynamically removed, too. Note that the Function Block numbers must be
+continuous.
+
+Similarly, if you multiple UMP Endpoints are required, you can add
+more Endpoints by creating subdirectories "ep.<num>". The number must
+be continuous.
+
+For emulating the old MIDI 2.0 device without UMP v1.1 support, pass 0
+to `process_ump` flag. Then the whole UMP v1.1 requests are ignored.
+
+Testing the MIDI2 function
+--------------------------
+
+On the device: run the gadget, and running::
+
+  $ cat /proc/asound/cards
+
+will show a new sound card containing a MIDI2 device.
+
+OTOH, on the host::
+
+  $ cat /proc/asound/cards
+
+will show a new sound card containing either MIDI1 or MIDI2 device,
+depending on the USB audio driver configuration.
+
+On both, when ALSA sequencer is enabled on the host, you can find the
+UMP MIDI client such as "MIDI 2.0 Gadget".
+
+As the driver simply loops back the data, there is no need for a real
+device just for testing.
+
+For testing a MIDI input from the gadget to the host (e.g. emulating a
+MIDI keyboard), you can send a MIDI stream like the following.
+
+On the gadget::
+
+  $ aconnect -o
+  ....
+  client 20: 'MIDI 2.0 Gadget' [type=kernel,card=1]
+      0 'MIDI 2.0        '
+      1 'Group 1 (MIDI 2.0 Gadget I/O)'
+  $ aplaymidi -p 20:1 to_host.mid
+
+On the host::
+
+  $ aconnect -i
+  ....
+  client 24: 'MIDI 2.0 Gadget' [type=kernel,card=2]
+      0 'MIDI 2.0        '
+      1 'Group 1 (MIDI 2.0 Gadget I/O)'
+  $ arecordmidi -p 24:1 from_gadget.mid
+
+If you have a UMP-capable application, you can use the UMP port to
+send/receive the raw UMP packets, too. For example, aseqdump program
+with UMP support can receive from UMP port. On the host::
+
+  $ aseqdump -u 2 -p 24:1
+  Waiting for data. Press Ctrl+C to end.
+  Source  Group    Event                  Ch  Data
+   24:1   Group  0, Program change          0, program 0, Bank select 0:0
+   24:1   Group  0, Channel pressure        0, value 0x80000000
+
+For testing a MIDI output to the gadget to the host (e.g. emulating a
+MIDI synth), it'll be just other way round.
+
+On the gadget::
+
+  $ arecordmidi -p 20:1 from_host.mid
+
+On the host::
+
+  $ aplaymidi -p 24:1 to_gadget.mid
+
+The access to MIDI 1.0 on altset 0 on the host is supported, and it's
+translated from/to UMP packets on the gadget. It's bound to only
+Function Block 0.
+
+The current operation mode can be observed in ALSA control element
+"Operation Mode" for SND_CTL_IFACE_RAWMIDI.  For example::
+
+  $ amixer -c1 contents
+  numid=1,iface=RAWMIDI,name='Operation Mode'
+    ; type=INTEGER,access=r--v----,values=1,min=0,max=2,step=0
+    : values=2
+
+where 0 = unused, 1 = MIDI 1.0 (altset 0), 2 = MIDI 2.0 (altset 1).
+The example above shows it's running in 2, i.e. MIDI 2.0.
index 7c02e542959a2cdd23e49283f656129e211b1266..2a59265788413e3387cb358e35a9c720d1ba4a3b 100644 (file)
@@ -18,4 +18,3 @@ obj-y += crypto/
 obj-$(CONFIG_MTD)                    += flash_setup.o
 obj-$(CONFIG_SMP)                    += smp.o
 obj-$(CONFIG_OCTEON_ILM)             += oct_ilm.o
-obj-$(CONFIG_USB)                    += octeon-usb.o
index ce05c0dd3acd780493148d17f7a2ede59f7ff482..235c77ce7b18ab3727593591879d9bea77dc3a40 100644 (file)
@@ -450,7 +450,6 @@ static const struct of_device_id octeon_ids[] __initconst = {
        { .compatible = "cavium,octeon-3860-bootbus", },
        { .compatible = "cavium,mdio-mux", },
        { .compatible = "gpio-leds", },
-       { .compatible = "cavium,octeon-7130-usb-uctl", },
        {},
 };
 
index 5e5c7bf51174ac76f60a20a71a7664e148c24c0e..1584665fe3cb68d890bd7606e3eedcd05a470a2c 100644 (file)
@@ -286,8 +286,7 @@ static bool mt76u_check_sg(struct mt76_dev *dev)
        struct usb_device *udev = interface_to_usbdev(uintf);
 
        return (!disable_usb_sg && udev->bus->sg_tablesize > 0 &&
-               (udev->bus->no_sg_constraint ||
-                udev->speed == USB_SPEED_WIRELESS));
+               udev->bus->no_sg_constraint);
 }
 
 static int
index 8dba9596408f24c0045584b682436b090355e8a7..aac670b90589bc6d07d33e7568aed042f9b3e3eb 100644 (file)
@@ -87,6 +87,7 @@ source "drivers/phy/motorola/Kconfig"
 source "drivers/phy/mscc/Kconfig"
 source "drivers/phy/qualcomm/Kconfig"
 source "drivers/phy/ralink/Kconfig"
+source "drivers/phy/realtek/Kconfig"
 source "drivers/phy/renesas/Kconfig"
 source "drivers/phy/rockchip/Kconfig"
 source "drivers/phy/samsung/Kconfig"
index 54f312c10a408aa81fc4251fe75dd90e54d0ae73..ba7c100b14fc7da4c6367e60f845b6ebb9f31fb9 100644 (file)
@@ -26,6 +26,7 @@ obj-y                                 += allwinner/   \
                                           mscc/        \
                                           qualcomm/    \
                                           ralink/      \
+                                          realtek/     \
                                           renesas/     \
                                           rockchip/    \
                                           samsung/     \
diff --git a/drivers/phy/realtek/Kconfig b/drivers/phy/realtek/Kconfig
new file mode 100644 (file)
index 0000000..650e20e
--- /dev/null
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Phy drivers for Realtek platforms
+#
+config PHY_RTK_RTD_USB2PHY
+       tristate "Realtek RTD USB2 PHY Transceiver Driver"
+       depends on USB_SUPPORT
+       select GENERIC_PHY
+       select USB_PHY
+       select USB_COMMON
+       help
+         Enable this to support Realtek SoC USB2 phy transceiver.
+         The DHC (digital home center) RTD series SoCs used the Synopsys
+         DWC3 USB IP. This driver will do the PHY initialization
+         of the parameters.
+
+config PHY_RTK_RTD_USB3PHY
+       tristate "Realtek RTD USB3 PHY Transceiver Driver"
+       depends on USB_SUPPORT
+       select GENERIC_PHY
+       select USB_PHY
+       select USB_COMMON
+       help
+         Enable this to support Realtek SoC USB3 phy transceiver.
+         The DHC (digital home center) RTD series SoCs used the Synopsys
+         DWC3 USB IP. This driver will do the PHY initialization
+         of the parameters.
diff --git a/drivers/phy/realtek/Makefile b/drivers/phy/realtek/Makefile
new file mode 100644 (file)
index 0000000..ed7b47f
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_RTK_RTD_USB2PHY)      += phy-rtk-usb2.o
+obj-$(CONFIG_PHY_RTK_RTD_USB3PHY)      += phy-rtk-usb3.o
diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c
new file mode 100644 (file)
index 0000000..5e7ee06
--- /dev/null
@@ -0,0 +1,1331 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  phy-rtk-usb2.c RTK usb2.0 PHY driver
+ *
+ * Copyright (C) 2023 Realtek Semiconductor Corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/regmap.h>
+#include <linux/sys_soc.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+#include <linux/usb.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/hcd.h>
+
+/* GUSB2PHYACCn register */
+#define PHY_NEW_REG_REQ BIT(25)
+#define PHY_VSTS_BUSY   BIT(23)
+#define PHY_VCTRL_SHIFT 8
+#define PHY_REG_DATA_MASK 0xff
+
+#define GET_LOW_NIBBLE(addr) ((addr) & 0x0f)
+#define GET_HIGH_NIBBLE(addr) (((addr) & 0xf0) >> 4)
+
+#define EFUS_USB_DC_CAL_RATE 2
+#define EFUS_USB_DC_CAL_MAX 7
+
+#define EFUS_USB_DC_DIS_RATE 1
+#define EFUS_USB_DC_DIS_MAX 7
+
+#define MAX_PHY_DATA_SIZE 20
+#define OFFEST_PHY_READ 0x20
+
+#define MAX_USB_PHY_NUM 4
+#define MAX_USB_PHY_PAGE0_DATA_SIZE 16
+#define MAX_USB_PHY_PAGE1_DATA_SIZE 16
+#define MAX_USB_PHY_PAGE2_DATA_SIZE 8
+
+#define SET_PAGE_OFFSET 0xf4
+#define SET_PAGE_0 0x9b
+#define SET_PAGE_1 0xbb
+#define SET_PAGE_2 0xdb
+
+#define PAGE_START 0xe0
+#define PAGE0_0XE4 0xe4
+#define PAGE0_0XE6 0xe6
+#define PAGE0_0XE7 0xe7
+#define PAGE1_0XE0 0xe0
+#define PAGE1_0XE2 0xe2
+
+#define SENSITIVITY_CTRL (BIT(4) | BIT(5) | BIT(6))
+#define ENABLE_AUTO_SENSITIVITY_CALIBRATION BIT(2)
+#define DEFAULT_DC_DRIVING_VALUE (0x8)
+#define DEFAULT_DC_DISCONNECTION_VALUE (0x6)
+#define HS_CLK_SELECT BIT(6)
+
+struct phy_reg {
+       void __iomem *reg_wrap_vstatus;
+       void __iomem *reg_gusb2phyacc0;
+       int vstatus_index;
+};
+
+struct phy_data {
+       u8 addr;
+       u8 data;
+};
+
+struct phy_cfg {
+       int page0_size;
+       struct phy_data page0[MAX_USB_PHY_PAGE0_DATA_SIZE];
+       int page1_size;
+       struct phy_data page1[MAX_USB_PHY_PAGE1_DATA_SIZE];
+       int page2_size;
+       struct phy_data page2[MAX_USB_PHY_PAGE2_DATA_SIZE];
+
+       int num_phy;
+
+       bool check_efuse;
+       int check_efuse_version;
+#define CHECK_EFUSE_V1 1
+#define CHECK_EFUSE_V2 2
+       int efuse_dc_driving_rate;
+       int efuse_dc_disconnect_rate;
+       int dc_driving_mask;
+       int dc_disconnect_mask;
+       bool usb_dc_disconnect_at_page0;
+       int driving_updated_for_dev_dis;
+
+       bool do_toggle;
+       bool do_toggle_driving;
+       bool use_default_parameter;
+       bool is_double_sensitivity_mode;
+};
+
+struct phy_parameter {
+       struct phy_reg phy_reg;
+
+       /* Get from efuse */
+       s8 efuse_usb_dc_cal;
+       s8 efuse_usb_dc_dis;
+
+       /* Get from dts */
+       bool inverse_hstx_sync_clock;
+       u32 driving_level;
+       s32 driving_level_compensate;
+       s32 disconnection_compensate;
+};
+
+struct rtk_phy {
+       struct usb_phy phy;
+       struct device *dev;
+
+       struct phy_cfg *phy_cfg;
+       int num_phy;
+       struct phy_parameter *phy_parameter;
+
+       struct dentry *debug_dir;
+};
+
+/* mapping 0xE0 to 0 ... 0xE7 to 7, 0xF0 to 8 ,,, 0xF7 to 15 */
+static inline int page_addr_to_array_index(u8 addr)
+{
+       return (int)((((addr) - PAGE_START) & 0x7) +
+               ((((addr) - PAGE_START) & 0x10) >> 1));
+}
+
+static inline u8 array_index_to_page_addr(int index)
+{
+       return ((((index) + PAGE_START) & 0x7) +
+               ((((index) & 0x8) << 1) + PAGE_START));
+}
+
+#define PHY_IO_TIMEOUT_USEC            (50000)
+#define PHY_IO_DELAY_US                        (100)
+
+static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
+{
+       int ret;
+       unsigned int val;
+
+       ret = read_poll_timeout(readl, val, ((val & mask) == result),
+                               PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg);
+       if (ret) {
+               pr_err("%s can't program USB phy\n", __func__);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static char rtk_phy_read(struct phy_reg *phy_reg, char addr)
+{
+       void __iomem *reg_gusb2phyacc0 = phy_reg->reg_gusb2phyacc0;
+       unsigned int val;
+       int ret = 0;
+
+       addr -= OFFEST_PHY_READ;
+
+       /* polling until VBusy == 0 */
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return (char)ret;
+
+       /* VCtrl = low nibble of addr, and set PHY_NEW_REG_REQ */
+       val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+       writel(val, reg_gusb2phyacc0);
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return (char)ret;
+
+       /* VCtrl = high nibble of addr, and set PHY_NEW_REG_REQ */
+       val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+       writel(val, reg_gusb2phyacc0);
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return (char)ret;
+
+       val = readl(reg_gusb2phyacc0);
+
+       return (char)(val & PHY_REG_DATA_MASK);
+}
+
+static int rtk_phy_write(struct phy_reg *phy_reg, char addr, char data)
+{
+       unsigned int val;
+       void __iomem *reg_wrap_vstatus = phy_reg->reg_wrap_vstatus;
+       void __iomem *reg_gusb2phyacc0 = phy_reg->reg_gusb2phyacc0;
+       int shift_bits = phy_reg->vstatus_index * 8;
+       int ret = 0;
+
+       /* write data to VStatusOut2 (data output to phy) */
+       writel((u32)data << shift_bits, reg_wrap_vstatus);
+
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return ret;
+
+       /* VCtrl = low nibble of addr, set PHY_NEW_REG_REQ */
+       val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+
+       writel(val, reg_gusb2phyacc0);
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return ret;
+
+       /* VCtrl = high nibble of addr, set PHY_NEW_REG_REQ */
+       val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+
+       writel(val, reg_gusb2phyacc0);
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtk_phy_set_page(struct phy_reg *phy_reg, int page)
+{
+       switch (page) {
+       case 0:
+               return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_0);
+       case 1:
+               return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_1);
+       case 2:
+               return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_2);
+       default:
+               pr_err("%s error page=%d\n", __func__, page);
+       }
+
+       return -EINVAL;
+}
+
+static u8 __updated_dc_disconnect_level_page0_0xe4(struct phy_cfg *phy_cfg,
+                                                  struct phy_parameter *phy_parameter, u8 data)
+{
+       u8 ret;
+       s32 val;
+       s32 dc_disconnect_mask = phy_cfg->dc_disconnect_mask;
+       int offset = 4;
+
+       val = (s32)((data >> offset) & dc_disconnect_mask)
+                    + phy_parameter->efuse_usb_dc_dis
+                    + phy_parameter->disconnection_compensate;
+
+       if (val > dc_disconnect_mask)
+               val = dc_disconnect_mask;
+       else if (val < 0)
+               val = 0;
+
+       ret = (data & (~(dc_disconnect_mask << offset))) |
+                   (val & dc_disconnect_mask) << offset;
+
+       return ret;
+}
+
+/* updated disconnect level at page0 */
+static void update_dc_disconnect_level_at_page0(struct rtk_phy *rtk_phy,
+                                               struct phy_parameter *phy_parameter, bool update)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+       struct phy_data *phy_data_page;
+       struct phy_data *phy_data;
+       u8 addr, data;
+       int offset = 4;
+       s32 dc_disconnect_mask;
+       int i;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_reg = &phy_parameter->phy_reg;
+
+       /* Set page 0 */
+       phy_data_page = phy_cfg->page0;
+       rtk_phy_set_page(phy_reg, 0);
+
+       i = page_addr_to_array_index(PAGE0_0XE4);
+       phy_data = phy_data_page + i;
+       if (!phy_data->addr) {
+               phy_data->addr = PAGE0_0XE4;
+               phy_data->data = rtk_phy_read(phy_reg, PAGE0_0XE4);
+       }
+
+       addr = phy_data->addr;
+       data = phy_data->data;
+       dc_disconnect_mask = phy_cfg->dc_disconnect_mask;
+
+       if (update)
+               data = __updated_dc_disconnect_level_page0_0xe4(phy_cfg, phy_parameter, data);
+       else
+               data = (data & ~(dc_disconnect_mask << offset)) |
+                       (DEFAULT_DC_DISCONNECTION_VALUE << offset);
+
+       if (rtk_phy_write(phy_reg, addr, data))
+               dev_err(rtk_phy->dev,
+                       "%s: Error to set page1 parameter addr=0x%x value=0x%x\n",
+                       __func__, addr, data);
+}
+
+static u8 __updated_dc_disconnect_level_page1_0xe2(struct phy_cfg *phy_cfg,
+                                                  struct phy_parameter *phy_parameter, u8 data)
+{
+       u8 ret;
+       s32 val;
+       s32 dc_disconnect_mask = phy_cfg->dc_disconnect_mask;
+
+       if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) {
+               val = (s32)(data & dc_disconnect_mask)
+                           + phy_parameter->efuse_usb_dc_dis
+                           + phy_parameter->disconnection_compensate;
+       } else { /* for CHECK_EFUSE_V2 or no efuse */
+               if (phy_parameter->efuse_usb_dc_dis)
+                       val = (s32)(phy_parameter->efuse_usb_dc_dis +
+                                   phy_parameter->disconnection_compensate);
+               else
+                       val = (s32)((data & dc_disconnect_mask) +
+                                   phy_parameter->disconnection_compensate);
+       }
+
+       if (val > dc_disconnect_mask)
+               val = dc_disconnect_mask;
+       else if (val < 0)
+               val = 0;
+
+       ret = (data & (~dc_disconnect_mask)) | (val & dc_disconnect_mask);
+
+       return ret;
+}
+
+/* updated disconnect level at page1 */
+static void update_dc_disconnect_level_at_page1(struct rtk_phy *rtk_phy,
+                                               struct phy_parameter *phy_parameter, bool update)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_data *phy_data_page;
+       struct phy_data *phy_data;
+       struct phy_reg *phy_reg;
+       u8 addr, data;
+       s32 dc_disconnect_mask;
+       int i;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_reg = &phy_parameter->phy_reg;
+
+       /* Set page 1 */
+       phy_data_page = phy_cfg->page1;
+       rtk_phy_set_page(phy_reg, 1);
+
+       i = page_addr_to_array_index(PAGE1_0XE2);
+       phy_data = phy_data_page + i;
+       if (!phy_data->addr) {
+               phy_data->addr = PAGE1_0XE2;
+               phy_data->data = rtk_phy_read(phy_reg, PAGE1_0XE2);
+       }
+
+       addr = phy_data->addr;
+       data = phy_data->data;
+       dc_disconnect_mask = phy_cfg->dc_disconnect_mask;
+
+       if (update)
+               data = __updated_dc_disconnect_level_page1_0xe2(phy_cfg, phy_parameter, data);
+       else
+               data = (data & ~dc_disconnect_mask) | DEFAULT_DC_DISCONNECTION_VALUE;
+
+       if (rtk_phy_write(phy_reg, addr, data))
+               dev_err(rtk_phy->dev,
+                       "%s: Error to set page1 parameter addr=0x%x value=0x%x\n",
+                       __func__, addr, data);
+}
+
+static void update_dc_disconnect_level(struct rtk_phy *rtk_phy,
+                                      struct phy_parameter *phy_parameter, bool update)
+{
+       struct phy_cfg *phy_cfg = rtk_phy->phy_cfg;
+
+       if (phy_cfg->usb_dc_disconnect_at_page0)
+               update_dc_disconnect_level_at_page0(rtk_phy, phy_parameter, update);
+       else
+               update_dc_disconnect_level_at_page1(rtk_phy, phy_parameter, update);
+}
+
+static u8 __update_dc_driving_page0_0xe4(struct phy_cfg *phy_cfg,
+                                        struct phy_parameter *phy_parameter, u8 data)
+{
+       s32 driving_level_compensate = phy_parameter->driving_level_compensate;
+       s32 dc_driving_mask = phy_cfg->dc_driving_mask;
+       s32 val;
+       u8 ret;
+
+       if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) {
+               val = (s32)(data & dc_driving_mask) + driving_level_compensate
+                           + phy_parameter->efuse_usb_dc_cal;
+       } else { /* for CHECK_EFUSE_V2 or no efuse */
+               if (phy_parameter->efuse_usb_dc_cal)
+                       val = (s32)((phy_parameter->efuse_usb_dc_cal & dc_driving_mask)
+                                   + driving_level_compensate);
+               else
+                       val = (s32)(data & dc_driving_mask);
+       }
+
+       if (val > dc_driving_mask)
+               val = dc_driving_mask;
+       else if (val < 0)
+               val = 0;
+
+       ret = (data & (~dc_driving_mask)) | (val & dc_driving_mask);
+
+       return ret;
+}
+
+static void update_dc_driving_level(struct rtk_phy *rtk_phy,
+                                   struct phy_parameter *phy_parameter)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+
+       phy_reg = &phy_parameter->phy_reg;
+       phy_cfg = rtk_phy->phy_cfg;
+       if (!phy_cfg->page0[4].addr) {
+               rtk_phy_set_page(phy_reg, 0);
+               phy_cfg->page0[4].addr = PAGE0_0XE4;
+               phy_cfg->page0[4].data = rtk_phy_read(phy_reg, PAGE0_0XE4);
+       }
+
+       if (phy_parameter->driving_level != DEFAULT_DC_DRIVING_VALUE) {
+               u32 dc_driving_mask;
+               u8 driving_level;
+               u8 data;
+
+               data = phy_cfg->page0[4].data;
+               dc_driving_mask = phy_cfg->dc_driving_mask;
+               driving_level = data & dc_driving_mask;
+
+               dev_dbg(rtk_phy->dev, "%s driving_level=%d => dts driving_level=%d\n",
+                       __func__, driving_level, phy_parameter->driving_level);
+
+               phy_cfg->page0[4].data = (data & (~dc_driving_mask)) |
+                           (phy_parameter->driving_level & dc_driving_mask);
+       }
+
+       phy_cfg->page0[4].data = __update_dc_driving_page0_0xe4(phy_cfg,
+                                                               phy_parameter,
+                                                               phy_cfg->page0[4].data);
+}
+
+static void update_hs_clk_select(struct rtk_phy *rtk_phy,
+                                struct phy_parameter *phy_parameter)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_reg = &phy_parameter->phy_reg;
+
+       if (phy_parameter->inverse_hstx_sync_clock) {
+               if (!phy_cfg->page0[6].addr) {
+                       rtk_phy_set_page(phy_reg, 0);
+                       phy_cfg->page0[6].addr = PAGE0_0XE6;
+                       phy_cfg->page0[6].data = rtk_phy_read(phy_reg, PAGE0_0XE6);
+               }
+
+               phy_cfg->page0[6].data = phy_cfg->page0[6].data | HS_CLK_SELECT;
+       }
+}
+
+static void do_rtk_phy_toggle(struct rtk_phy *rtk_phy,
+                             int index, bool connect)
+{
+       struct phy_parameter *phy_parameter;
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+       struct phy_data *phy_data_page;
+       u8 addr, data;
+       int i;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+       phy_reg = &phy_parameter->phy_reg;
+
+       if (!phy_cfg->do_toggle)
+               goto out;
+
+       if (phy_cfg->is_double_sensitivity_mode)
+               goto do_toggle_driving;
+
+       /* Set page 0 */
+       rtk_phy_set_page(phy_reg, 0);
+
+       addr = PAGE0_0XE7;
+       data = rtk_phy_read(phy_reg, addr);
+
+       if (connect)
+               rtk_phy_write(phy_reg, addr, data & (~SENSITIVITY_CTRL));
+       else
+               rtk_phy_write(phy_reg, addr, data | (SENSITIVITY_CTRL));
+
+do_toggle_driving:
+
+       if (!phy_cfg->do_toggle_driving)
+               goto do_toggle;
+
+       /* Page 0 addr 0xE4 driving capability */
+
+       /* Set page 0 */
+       phy_data_page = phy_cfg->page0;
+       rtk_phy_set_page(phy_reg, 0);
+
+       i = page_addr_to_array_index(PAGE0_0XE4);
+       addr = phy_data_page[i].addr;
+       data = phy_data_page[i].data;
+
+       if (connect) {
+               rtk_phy_write(phy_reg, addr, data);
+       } else {
+               u8 value;
+               s32 tmp;
+               s32 driving_updated =
+                           phy_cfg->driving_updated_for_dev_dis;
+               s32 dc_driving_mask = phy_cfg->dc_driving_mask;
+
+               tmp = (s32)(data & dc_driving_mask) + driving_updated;
+
+               if (tmp > dc_driving_mask)
+                       tmp = dc_driving_mask;
+               else if (tmp < 0)
+                       tmp = 0;
+
+               value = (data & (~dc_driving_mask)) | (tmp & dc_driving_mask);
+
+               rtk_phy_write(phy_reg, addr, value);
+       }
+
+do_toggle:
+       /* restore dc disconnect level before toggle */
+       update_dc_disconnect_level(rtk_phy, phy_parameter, false);
+
+       /* Set page 1 */
+       rtk_phy_set_page(phy_reg, 1);
+
+       addr = PAGE1_0XE0;
+       data = rtk_phy_read(phy_reg, addr);
+
+       rtk_phy_write(phy_reg, addr, data &
+                     (~ENABLE_AUTO_SENSITIVITY_CALIBRATION));
+       mdelay(1);
+       rtk_phy_write(phy_reg, addr, data |
+                     (ENABLE_AUTO_SENSITIVITY_CALIBRATION));
+
+       /* update dc disconnect level after toggle */
+       update_dc_disconnect_level(rtk_phy, phy_parameter, true);
+
+out:
+       return;
+}
+
+static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index)
+{
+       struct phy_parameter *phy_parameter;
+       struct phy_cfg *phy_cfg;
+       struct phy_data *phy_data_page;
+       struct phy_reg *phy_reg;
+       int i;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+       phy_reg = &phy_parameter->phy_reg;
+
+       if (phy_cfg->use_default_parameter) {
+               dev_dbg(rtk_phy->dev, "%s phy#%d use default parameter\n",
+                       __func__, index);
+               goto do_toggle;
+       }
+
+       /* Set page 0 */
+       phy_data_page = phy_cfg->page0;
+       rtk_phy_set_page(phy_reg, 0);
+
+       for (i = 0; i < phy_cfg->page0_size; i++) {
+               struct phy_data *phy_data = phy_data_page + i;
+               u8 addr = phy_data->addr;
+               u8 data = phy_data->data;
+
+               if (!addr)
+                       continue;
+
+               if (rtk_phy_write(phy_reg, addr, data)) {
+                       dev_err(rtk_phy->dev,
+                               "%s: Error to set page0 parameter addr=0x%x value=0x%x\n",
+                               __func__, addr, data);
+                       return -EINVAL;
+               }
+       }
+
+       /* Set page 1 */
+       phy_data_page = phy_cfg->page1;
+       rtk_phy_set_page(phy_reg, 1);
+
+       for (i = 0; i < phy_cfg->page1_size; i++) {
+               struct phy_data *phy_data = phy_data_page + i;
+               u8 addr = phy_data->addr;
+               u8 data = phy_data->data;
+
+               if (!addr)
+                       continue;
+
+               if (rtk_phy_write(phy_reg, addr, data)) {
+                       dev_err(rtk_phy->dev,
+                               "%s: Error to set page1 parameter addr=0x%x value=0x%x\n",
+                               __func__, addr, data);
+                       return -EINVAL;
+               }
+       }
+
+       if (phy_cfg->page2_size == 0)
+               goto do_toggle;
+
+       /* Set page 2 */
+       phy_data_page = phy_cfg->page2;
+       rtk_phy_set_page(phy_reg, 2);
+
+       for (i = 0; i < phy_cfg->page2_size; i++) {
+               struct phy_data *phy_data = phy_data_page + i;
+               u8 addr = phy_data->addr;
+               u8 data = phy_data->data;
+
+               if (!addr)
+                       continue;
+
+               if (rtk_phy_write(phy_reg, addr, data)) {
+                       dev_err(rtk_phy->dev,
+                               "%s: Error to set page2 parameter addr=0x%x value=0x%x\n",
+                               __func__, addr, data);
+                       return -EINVAL;
+               }
+       }
+
+do_toggle:
+       do_rtk_phy_toggle(rtk_phy, index, false);
+
+       return 0;
+}
+
+static int rtk_phy_init(struct phy *phy)
+{
+       struct rtk_phy *rtk_phy = phy_get_drvdata(phy);
+       unsigned long phy_init_time = jiffies;
+       int i, ret = 0;
+
+       if (!rtk_phy)
+               return -EINVAL;
+
+       for (i = 0; i < rtk_phy->num_phy; i++)
+               ret = do_rtk_phy_init(rtk_phy, i);
+
+       dev_dbg(rtk_phy->dev, "Initialized RTK USB 2.0 PHY (take %dms)\n",
+               jiffies_to_msecs(jiffies - phy_init_time));
+       return ret;
+}
+
+static int rtk_phy_exit(struct phy *phy)
+{
+       return 0;
+}
+
+static const struct phy_ops ops = {
+       .init           = rtk_phy_init,
+       .exit           = rtk_phy_exit,
+       .owner          = THIS_MODULE,
+};
+
+static void rtk_phy_toggle(struct usb_phy *usb2_phy, bool connect, int port)
+{
+       int index = port;
+       struct rtk_phy *rtk_phy = NULL;
+
+       rtk_phy = dev_get_drvdata(usb2_phy->dev);
+
+       if (index > rtk_phy->num_phy) {
+               dev_err(rtk_phy->dev, "%s: The port=%d is not in usb phy (num_phy=%d)\n",
+                       __func__, index, rtk_phy->num_phy);
+               return;
+       }
+
+       do_rtk_phy_toggle(rtk_phy, index, connect);
+}
+
+static int rtk_phy_notify_port_status(struct usb_phy *x, int port,
+                                     u16 portstatus, u16 portchange)
+{
+       bool connect = false;
+
+       pr_debug("%s port=%d portstatus=0x%x portchange=0x%x\n",
+                __func__, port, (int)portstatus, (int)portchange);
+       if (portstatus & USB_PORT_STAT_CONNECTION)
+               connect = true;
+
+       if (portchange & USB_PORT_STAT_C_CONNECTION)
+               rtk_phy_toggle(x, connect, port);
+
+       return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *create_phy_debug_root(void)
+{
+       struct dentry *phy_debug_root;
+
+       phy_debug_root = debugfs_lookup("phy", usb_debug_root);
+       if (!phy_debug_root)
+               phy_debug_root = debugfs_create_dir("phy", usb_debug_root);
+
+       return phy_debug_root;
+}
+
+static int rtk_usb2_parameter_show(struct seq_file *s, void *unused)
+{
+       struct rtk_phy *rtk_phy = s->private;
+       struct phy_cfg *phy_cfg;
+       int i, index;
+
+       phy_cfg = rtk_phy->phy_cfg;
+
+       seq_puts(s, "Property:\n");
+       seq_printf(s, "  check_efuse: %s\n",
+                  phy_cfg->check_efuse ? "Enable" : "Disable");
+       seq_printf(s, "  check_efuse_version: %d\n",
+                  phy_cfg->check_efuse_version);
+       seq_printf(s, "  efuse_dc_driving_rate: %d\n",
+                  phy_cfg->efuse_dc_driving_rate);
+       seq_printf(s, "  dc_driving_mask: 0x%x\n",
+                  phy_cfg->dc_driving_mask);
+       seq_printf(s, "  efuse_dc_disconnect_rate: %d\n",
+                  phy_cfg->efuse_dc_disconnect_rate);
+       seq_printf(s, "  dc_disconnect_mask: 0x%x\n",
+                  phy_cfg->dc_disconnect_mask);
+       seq_printf(s, "  usb_dc_disconnect_at_page0: %s\n",
+                  phy_cfg->usb_dc_disconnect_at_page0 ? "true" : "false");
+       seq_printf(s, "  do_toggle: %s\n",
+                  phy_cfg->do_toggle ? "Enable" : "Disable");
+       seq_printf(s, "  do_toggle_driving: %s\n",
+                  phy_cfg->do_toggle_driving ? "Enable" : "Disable");
+       seq_printf(s, "  driving_updated_for_dev_dis: 0x%x\n",
+                  phy_cfg->driving_updated_for_dev_dis);
+       seq_printf(s, "  use_default_parameter: %s\n",
+                  phy_cfg->use_default_parameter ? "Enable" : "Disable");
+       seq_printf(s, "  is_double_sensitivity_mode: %s\n",
+                  phy_cfg->is_double_sensitivity_mode ? "Enable" : "Disable");
+
+       for (index = 0; index < rtk_phy->num_phy; index++) {
+               struct phy_parameter *phy_parameter;
+               struct phy_reg *phy_reg;
+               struct phy_data *phy_data_page;
+
+               phy_parameter =  &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+               phy_reg = &phy_parameter->phy_reg;
+
+               seq_printf(s, "PHY %d:\n", index);
+
+               seq_puts(s, "Page 0:\n");
+               /* Set page 0 */
+               phy_data_page = phy_cfg->page0;
+               rtk_phy_set_page(phy_reg, 0);
+
+               for (i = 0; i < phy_cfg->page0_size; i++) {
+                       struct phy_data *phy_data = phy_data_page + i;
+                       u8 addr = array_index_to_page_addr(i);
+                       u8 data = phy_data->data;
+                       u8 value = rtk_phy_read(phy_reg, addr);
+
+                       if (phy_data->addr)
+                               seq_printf(s, "  Page 0: addr=0x%x data=0x%02x ==> read value=0x%02x\n",
+                                          addr, data, value);
+                       else
+                               seq_printf(s, "  Page 0: addr=0x%x data=none ==> read value=0x%02x\n",
+                                          addr, value);
+               }
+
+               seq_puts(s, "Page 1:\n");
+               /* Set page 1 */
+               phy_data_page = phy_cfg->page1;
+               rtk_phy_set_page(phy_reg, 1);
+
+               for (i = 0; i < phy_cfg->page1_size; i++) {
+                       struct phy_data *phy_data = phy_data_page + i;
+                       u8 addr = array_index_to_page_addr(i);
+                       u8 data = phy_data->data;
+                       u8 value = rtk_phy_read(phy_reg, addr);
+
+                       if (phy_data->addr)
+                               seq_printf(s, "  Page 1: addr=0x%x data=0x%02x ==> read value=0x%02x\n",
+                                          addr, data, value);
+                       else
+                               seq_printf(s, "  Page 1: addr=0x%x data=none ==> read value=0x%02x\n",
+                                          addr, value);
+               }
+
+               if (phy_cfg->page2_size == 0)
+                       goto out;
+
+               seq_puts(s, "Page 2:\n");
+               /* Set page 2 */
+               phy_data_page = phy_cfg->page2;
+               rtk_phy_set_page(phy_reg, 2);
+
+               for (i = 0; i < phy_cfg->page2_size; i++) {
+                       struct phy_data *phy_data = phy_data_page + i;
+                       u8 addr = array_index_to_page_addr(i);
+                       u8 data = phy_data->data;
+                       u8 value = rtk_phy_read(phy_reg, addr);
+
+                       if (phy_data->addr)
+                               seq_printf(s, "  Page 2: addr=0x%x data=0x%02x ==> read value=0x%02x\n",
+                                          addr, data, value);
+                       else
+                               seq_printf(s, "  Page 2: addr=0x%x data=none ==> read value=0x%02x\n",
+                                          addr, value);
+               }
+
+out:
+               seq_puts(s, "PHY Property:\n");
+               seq_printf(s, "  efuse_usb_dc_cal: %d\n",
+                          (int)phy_parameter->efuse_usb_dc_cal);
+               seq_printf(s, "  efuse_usb_dc_dis: %d\n",
+                          (int)phy_parameter->efuse_usb_dc_dis);
+               seq_printf(s, "  inverse_hstx_sync_clock: %s\n",
+                          phy_parameter->inverse_hstx_sync_clock ? "Enable" : "Disable");
+               seq_printf(s, "  driving_level: %d\n",
+                          phy_parameter->driving_level);
+               seq_printf(s, "  driving_level_compensate: %d\n",
+                          phy_parameter->driving_level_compensate);
+               seq_printf(s, "  disconnection_compensate: %d\n",
+                          phy_parameter->disconnection_compensate);
+       }
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rtk_usb2_parameter);
+
+static inline void create_debug_files(struct rtk_phy *rtk_phy)
+{
+       struct dentry *phy_debug_root = NULL;
+
+       phy_debug_root = create_phy_debug_root();
+       if (!phy_debug_root)
+               return;
+
+       rtk_phy->debug_dir = debugfs_create_dir(dev_name(rtk_phy->dev),
+                                               phy_debug_root);
+       if (!rtk_phy->debug_dir)
+               return;
+
+       if (!debugfs_create_file("parameter", 0444, rtk_phy->debug_dir, rtk_phy,
+                                &rtk_usb2_parameter_fops))
+               goto file_error;
+
+       return;
+
+file_error:
+       debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+
+static inline void remove_debug_files(struct rtk_phy *rtk_phy)
+{
+       debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+#else
+static inline void create_debug_files(struct rtk_phy *rtk_phy) { }
+static inline void remove_debug_files(struct rtk_phy *rtk_phy) { }
+#endif /* CONFIG_DEBUG_FS */
+
+static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy,
+                                struct phy_parameter *phy_parameter, int index)
+{
+       struct phy_cfg *phy_cfg = rtk_phy->phy_cfg;
+       u8 value = 0;
+       struct nvmem_cell *cell;
+       struct soc_device_attribute rtk_soc_groot[] = {
+                   { .family = "Realtek Groot",},
+                   { /* empty */ } };
+
+       if (!phy_cfg->check_efuse)
+               goto out;
+
+       /* Read efuse for usb dc cal */
+       cell = nvmem_cell_get(rtk_phy->dev, "usb-dc-cal");
+       if (IS_ERR(cell)) {
+               dev_dbg(rtk_phy->dev, "%s no usb-dc-cal: %ld\n",
+                       __func__, PTR_ERR(cell));
+       } else {
+               unsigned char *buf;
+               size_t buf_size;
+
+               buf = nvmem_cell_read(cell, &buf_size);
+               if (!IS_ERR(buf)) {
+                       value = buf[0] & phy_cfg->dc_driving_mask;
+                       kfree(buf);
+               }
+               nvmem_cell_put(cell);
+       }
+
+       if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) {
+               int rate = phy_cfg->efuse_dc_driving_rate;
+
+               if (value <= EFUS_USB_DC_CAL_MAX)
+                       phy_parameter->efuse_usb_dc_cal = (int8_t)(value * rate);
+               else
+                       phy_parameter->efuse_usb_dc_cal = -(int8_t)
+                                   ((EFUS_USB_DC_CAL_MAX & value) * rate);
+
+               if (soc_device_match(rtk_soc_groot)) {
+                       dev_dbg(rtk_phy->dev, "For groot IC we need a workaround to adjust efuse_usb_dc_cal\n");
+
+                       /* We don't multiple dc_cal_rate=2 for positive dc cal compensate */
+                       if (value <= EFUS_USB_DC_CAL_MAX)
+                               phy_parameter->efuse_usb_dc_cal = (int8_t)(value);
+
+                       /* We set max dc cal compensate is 0x8 if otp is 0x7 */
+                       if (value == 0x7)
+                               phy_parameter->efuse_usb_dc_cal = (int8_t)(value + 1);
+               }
+       } else { /* for CHECK_EFUSE_V2 */
+               phy_parameter->efuse_usb_dc_cal = value & phy_cfg->dc_driving_mask;
+       }
+
+       /* Read efuse for usb dc disconnect level */
+       value = 0;
+       cell = nvmem_cell_get(rtk_phy->dev, "usb-dc-dis");
+       if (IS_ERR(cell)) {
+               dev_dbg(rtk_phy->dev, "%s no usb-dc-dis: %ld\n",
+                       __func__, PTR_ERR(cell));
+       } else {
+               unsigned char *buf;
+               size_t buf_size;
+
+               buf = nvmem_cell_read(cell, &buf_size);
+               if (!IS_ERR(buf)) {
+                       value = buf[0] & phy_cfg->dc_disconnect_mask;
+                       kfree(buf);
+               }
+               nvmem_cell_put(cell);
+       }
+
+       if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) {
+               int rate = phy_cfg->efuse_dc_disconnect_rate;
+
+               if (value <= EFUS_USB_DC_DIS_MAX)
+                       phy_parameter->efuse_usb_dc_dis = (int8_t)(value * rate);
+               else
+                       phy_parameter->efuse_usb_dc_dis = -(int8_t)
+                                   ((EFUS_USB_DC_DIS_MAX & value) * rate);
+       } else { /* for CHECK_EFUSE_V2 */
+               phy_parameter->efuse_usb_dc_dis = value & phy_cfg->dc_disconnect_mask;
+       }
+
+out:
+       return 0;
+}
+
+static int parse_phy_data(struct rtk_phy *rtk_phy)
+{
+       struct device *dev = rtk_phy->dev;
+       struct device_node *np = dev->of_node;
+       struct phy_parameter *phy_parameter;
+       int ret = 0;
+       int index;
+
+       rtk_phy->phy_parameter = devm_kzalloc(dev, sizeof(struct phy_parameter) *
+                                               rtk_phy->num_phy, GFP_KERNEL);
+       if (!rtk_phy->phy_parameter)
+               return -ENOMEM;
+
+       for (index = 0; index < rtk_phy->num_phy; index++) {
+               phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+
+               phy_parameter->phy_reg.reg_wrap_vstatus = of_iomap(np, 0);
+               phy_parameter->phy_reg.reg_gusb2phyacc0 = of_iomap(np, 1) + index;
+               phy_parameter->phy_reg.vstatus_index = index;
+
+               if (of_property_read_bool(np, "realtek,inverse-hstx-sync-clock"))
+                       phy_parameter->inverse_hstx_sync_clock = true;
+               else
+                       phy_parameter->inverse_hstx_sync_clock = false;
+
+               if (of_property_read_u32_index(np, "realtek,driving-level",
+                                              index, &phy_parameter->driving_level))
+                       phy_parameter->driving_level = DEFAULT_DC_DRIVING_VALUE;
+
+               if (of_property_read_u32_index(np, "realtek,driving-level-compensate",
+                                              index, &phy_parameter->driving_level_compensate))
+                       phy_parameter->driving_level_compensate = 0;
+
+               if (of_property_read_u32_index(np, "realtek,disconnection-compensate",
+                                              index, &phy_parameter->disconnection_compensate))
+                       phy_parameter->disconnection_compensate = 0;
+
+               get_phy_data_by_efuse(rtk_phy, phy_parameter, index);
+
+               update_dc_driving_level(rtk_phy, phy_parameter);
+
+               update_hs_clk_select(rtk_phy, phy_parameter);
+       }
+
+       return ret;
+}
+
+static int rtk_usb2phy_probe(struct platform_device *pdev)
+{
+       struct rtk_phy *rtk_phy;
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy;
+       struct phy_provider *phy_provider;
+       const struct phy_cfg *phy_cfg;
+       int ret = 0;
+
+       phy_cfg = of_device_get_match_data(dev);
+       if (!phy_cfg) {
+               dev_err(dev, "phy config are not assigned!\n");
+               return -EINVAL;
+       }
+
+       rtk_phy = devm_kzalloc(dev, sizeof(*rtk_phy), GFP_KERNEL);
+       if (!rtk_phy)
+               return -ENOMEM;
+
+       rtk_phy->dev                    = &pdev->dev;
+       rtk_phy->phy.dev                = rtk_phy->dev;
+       rtk_phy->phy.label              = "rtk-usb2phy";
+       rtk_phy->phy.notify_port_status = rtk_phy_notify_port_status;
+
+       rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL);
+
+       memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg));
+
+       rtk_phy->num_phy = phy_cfg->num_phy;
+
+       ret = parse_phy_data(rtk_phy);
+       if (ret)
+               goto err;
+
+       platform_set_drvdata(pdev, rtk_phy);
+
+       generic_phy = devm_phy_create(rtk_phy->dev, NULL, &ops);
+       if (IS_ERR(generic_phy))
+               return PTR_ERR(generic_phy);
+
+       phy_set_drvdata(generic_phy, rtk_phy);
+
+       phy_provider = devm_of_phy_provider_register(rtk_phy->dev,
+                                                    of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       ret = usb_add_phy_dev(&rtk_phy->phy);
+       if (ret)
+               goto err;
+
+       create_debug_files(rtk_phy);
+
+err:
+       return ret;
+}
+
+static void rtk_usb2phy_remove(struct platform_device *pdev)
+{
+       struct rtk_phy *rtk_phy = platform_get_drvdata(pdev);
+
+       remove_debug_files(rtk_phy);
+
+       usb_remove_phy(&rtk_phy->phy);
+}
+
+static const struct phy_cfg rtd1295_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0x90},
+                  [3] = {0xe3, 0x3a},
+                  [4] = {0xe4, 0x68},
+                  [6] = {0xe6, 0x91},
+                 [13] = {0xf5, 0x81},
+                 [15] = {0xf7, 0x02}, },
+       .page1_size = 8,
+       .page1 = { /* default parameter */ },
+       .page2_size = 0,
+       .page2 = { /* no parameter */ },
+       .num_phy = 1,
+       .check_efuse = false,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = false,
+};
+
+static const struct phy_cfg rtd1395_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [4] = {0xe4, 0xac},
+                 [13] = {0xf5, 0x00},
+                 [15] = {0xf7, 0x02}, },
+       .page1_size = 8,
+       .page1 = { /* default parameter */ },
+       .page2_size = 0,
+       .page2 = { /* no parameter */ },
+       .num_phy = 1,
+       .check_efuse = false,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = false,
+};
+
+static const struct phy_cfg rtd1395_phy_cfg_2port = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [4] = {0xe4, 0xac},
+                 [13] = {0xf5, 0x00},
+                 [15] = {0xf7, 0x02}, },
+       .page1_size = 8,
+       .page1 = { /* default parameter */ },
+       .page2_size = 0,
+       .page2 = { /* no parameter */ },
+       .num_phy = 2,
+       .check_efuse = false,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = false,
+};
+
+static const struct phy_cfg rtd1619_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [4] = {0xe4, 0x68}, },
+       .page1_size = 8,
+       .page1 = { /* default parameter */ },
+       .page2_size = 0,
+       .page2 = { /* no parameter */ },
+       .num_phy = 1,
+       .check_efuse = true,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = false,
+};
+
+static const struct phy_cfg rtd1319_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0x18},
+                  [4] = {0xe4, 0x6a},
+                  [7] = {0xe7, 0x71},
+                 [13] = {0xf5, 0x15},
+                 [15] = {0xf7, 0x32}, },
+       .page1_size = 8,
+       .page1 = { [3] = {0xe3, 0x44}, },
+       .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+       .page2 = { [0] = {0xe0, 0x01}, },
+       .num_phy = 1,
+       .check_efuse = true,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = true,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = true,
+};
+
+static const struct phy_cfg rtd1312c_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0x14},
+                  [4] = {0xe4, 0x67},
+                  [5] = {0xe5, 0x55}, },
+       .page1_size = 8,
+       .page1 = { [3] = {0xe3, 0x23},
+                  [6] = {0xe6, 0x58}, },
+       .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+       .page2 = { /* default parameter */ },
+       .num_phy = 1,
+       .check_efuse = true,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = true,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = true,
+};
+
+static const struct phy_cfg rtd1619b_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0xa3},
+                  [4] = {0xe4, 0x88},
+                  [5] = {0xe5, 0x4f},
+                  [6] = {0xe6, 0x02}, },
+       .page1_size = 8,
+       .page1 = { [3] = {0xe3, 0x64}, },
+       .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+       .page2 = { [7] = {0xe7, 0x45}, },
+       .num_phy = 1,
+       .check_efuse = true,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE,
+       .dc_driving_mask = 0x1f,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = false,
+       .do_toggle = true,
+       .do_toggle_driving = true,
+       .driving_updated_for_dev_dis = 0x8,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = true,
+};
+
+static const struct phy_cfg rtd1319d_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0xa3},
+                  [4] = {0xe4, 0x8e},
+                  [5] = {0xe5, 0x4f},
+                  [6] = {0xe6, 0x02}, },
+       .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE,
+       .page1 = { [14] = {0xf5, 0x1}, },
+       .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+       .page2 = { [7] = {0xe7, 0x44}, },
+       .check_efuse = true,
+       .num_phy = 1,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE,
+       .dc_driving_mask = 0x1f,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = false,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0x8,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = true,
+};
+
+static const struct phy_cfg rtd1315e_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0xa3},
+                  [4] = {0xe4, 0x8c},
+                  [5] = {0xe5, 0x4f},
+                  [6] = {0xe6, 0x02}, },
+       .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE,
+       .page1 = { [3] = {0xe3, 0x7f},
+                 [14] = {0xf5, 0x01}, },
+       .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+       .page2 = { [7] = {0xe7, 0x44}, },
+       .num_phy = 1,
+       .check_efuse = true,
+       .check_efuse_version = CHECK_EFUSE_V2,
+       .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE,
+       .dc_driving_mask = 0x1f,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = false,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0x8,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = true,
+};
+
+static const struct of_device_id usbphy_rtk_dt_match[] = {
+       { .compatible = "realtek,rtd1295-usb2phy", .data = &rtd1295_phy_cfg },
+       { .compatible = "realtek,rtd1312c-usb2phy", .data = &rtd1312c_phy_cfg },
+       { .compatible = "realtek,rtd1315e-usb2phy", .data = &rtd1315e_phy_cfg },
+       { .compatible = "realtek,rtd1319-usb2phy", .data = &rtd1319_phy_cfg },
+       { .compatible = "realtek,rtd1319d-usb2phy", .data = &rtd1319d_phy_cfg },
+       { .compatible = "realtek,rtd1395-usb2phy", .data = &rtd1395_phy_cfg },
+       { .compatible = "realtek,rtd1395-usb2phy-2port", .data = &rtd1395_phy_cfg_2port },
+       { .compatible = "realtek,rtd1619-usb2phy", .data = &rtd1619_phy_cfg },
+       { .compatible = "realtek,rtd1619b-usb2phy", .data = &rtd1619b_phy_cfg },
+       {},
+};
+MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match);
+
+static struct platform_driver rtk_usb2phy_driver = {
+       .probe          = rtk_usb2phy_probe,
+       .remove_new     = rtk_usb2phy_remove,
+       .driver         = {
+               .name   = "rtk-usb2phy",
+               .of_match_table = usbphy_rtk_dt_match,
+       },
+};
+
+module_platform_driver(rtk_usb2phy_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform: rtk-usb2phy");
+MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>");
+MODULE_DESCRIPTION("Realtek usb 2.0 phy driver");
diff --git a/drivers/phy/realtek/phy-rtk-usb3.c b/drivers/phy/realtek/phy-rtk-usb3.c
new file mode 100644 (file)
index 0000000..7881f90
--- /dev/null
@@ -0,0 +1,767 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  phy-rtk-usb3.c RTK usb3.0 phy driver
+ *
+ * copyright (c) 2023 realtek semiconductor corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/regmap.h>
+#include <linux/sys_soc.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/phy.h>
+
+#define USB_MDIO_CTRL_PHY_BUSY BIT(7)
+#define USB_MDIO_CTRL_PHY_WRITE BIT(0)
+#define USB_MDIO_CTRL_PHY_ADDR_SHIFT 8
+#define USB_MDIO_CTRL_PHY_DATA_SHIFT 16
+
+#define MAX_USB_PHY_DATA_SIZE 0x30
+#define PHY_ADDR_0X09 0x09
+#define PHY_ADDR_0X0B 0x0b
+#define PHY_ADDR_0X0D 0x0d
+#define PHY_ADDR_0X10 0x10
+#define PHY_ADDR_0X1F 0x1f
+#define PHY_ADDR_0X20 0x20
+#define PHY_ADDR_0X21 0x21
+#define PHY_ADDR_0X30 0x30
+
+#define REG_0X09_FORCE_CALIBRATION BIT(9)
+#define REG_0X0B_RX_OFFSET_RANGE_MASK 0xc
+#define REG_0X0D_RX_DEBUG_TEST_EN BIT(6)
+#define REG_0X10_DEBUG_MODE_SETTING 0x3c0
+#define REG_0X10_DEBUG_MODE_SETTING_MASK 0x3f8
+#define REG_0X1F_RX_OFFSET_CODE_MASK 0x1e
+
+#define USB_U3_TX_LFPS_SWING_TRIM_SHIFT 4
+#define USB_U3_TX_LFPS_SWING_TRIM_MASK 0xf
+#define AMPLITUDE_CONTROL_COARSE_MASK 0xff
+#define AMPLITUDE_CONTROL_FINE_MASK 0xffff
+#define AMPLITUDE_CONTROL_COARSE_DEFAULT 0xff
+#define AMPLITUDE_CONTROL_FINE_DEFAULT 0xffff
+
+#define PHY_ADDR_MAP_ARRAY_INDEX(addr) (addr)
+#define ARRAY_INDEX_MAP_PHY_ADDR(index) (index)
+
+struct phy_reg {
+       void __iomem *reg_mdio_ctl;
+};
+
+struct phy_data {
+       u8 addr;
+       u16 data;
+};
+
+struct phy_cfg {
+       int param_size;
+       struct phy_data param[MAX_USB_PHY_DATA_SIZE];
+
+       bool check_efuse;
+       bool do_toggle;
+       bool do_toggle_once;
+       bool use_default_parameter;
+       bool check_rx_front_end_offset;
+};
+
+struct phy_parameter {
+       struct phy_reg phy_reg;
+
+       /* Get from efuse */
+       u8 efuse_usb_u3_tx_lfps_swing_trim;
+
+       /* Get from dts */
+       u32 amplitude_control_coarse;
+       u32 amplitude_control_fine;
+};
+
+struct rtk_phy {
+       struct usb_phy phy;
+       struct device *dev;
+
+       struct phy_cfg *phy_cfg;
+       int num_phy;
+       struct phy_parameter *phy_parameter;
+
+       struct dentry *debug_dir;
+};
+
+#define PHY_IO_TIMEOUT_USEC            (50000)
+#define PHY_IO_DELAY_US                        (100)
+
+static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
+{
+       int ret;
+       unsigned int val;
+
+       ret = read_poll_timeout(readl, val, ((val & mask) == result),
+                               PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg);
+       if (ret) {
+               pr_err("%s can't program USB phy\n", __func__);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int rtk_phy3_wait_vbusy(struct phy_reg *phy_reg)
+{
+       return utmi_wait_register(phy_reg->reg_mdio_ctl, USB_MDIO_CTRL_PHY_BUSY, 0);
+}
+
+static u16 rtk_phy_read(struct phy_reg *phy_reg, char addr)
+{
+       unsigned int tmp;
+       u32 value;
+
+       tmp = (addr << USB_MDIO_CTRL_PHY_ADDR_SHIFT);
+
+       writel(tmp, phy_reg->reg_mdio_ctl);
+
+       rtk_phy3_wait_vbusy(phy_reg);
+
+       value = readl(phy_reg->reg_mdio_ctl);
+       value = value >> USB_MDIO_CTRL_PHY_DATA_SHIFT;
+
+       return (u16)value;
+}
+
+static int rtk_phy_write(struct phy_reg *phy_reg, char addr, u16 data)
+{
+       unsigned int val;
+
+       val = USB_MDIO_CTRL_PHY_WRITE |
+                   (addr << USB_MDIO_CTRL_PHY_ADDR_SHIFT) |
+                   (data << USB_MDIO_CTRL_PHY_DATA_SHIFT);
+
+       writel(val, phy_reg->reg_mdio_ctl);
+
+       rtk_phy3_wait_vbusy(phy_reg);
+
+       return 0;
+}
+
+static void do_rtk_usb3_phy_toggle(struct rtk_phy *rtk_phy, int index, bool connect)
+{
+       struct phy_cfg *phy_cfg = rtk_phy->phy_cfg;
+       struct phy_reg *phy_reg;
+       struct phy_parameter *phy_parameter;
+       struct phy_data *phy_data;
+       u8 addr;
+       u16 data;
+       int i;
+
+       phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+       phy_reg = &phy_parameter->phy_reg;
+
+       if (!phy_cfg->do_toggle)
+               return;
+
+       i = PHY_ADDR_MAP_ARRAY_INDEX(PHY_ADDR_0X09);
+       phy_data = phy_cfg->param + i;
+       addr = phy_data->addr;
+       data = phy_data->data;
+
+       if (!addr && !data) {
+               addr = PHY_ADDR_0X09;
+               data = rtk_phy_read(phy_reg, addr);
+               phy_data->addr = addr;
+               phy_data->data = data;
+       }
+
+       rtk_phy_write(phy_reg, addr, data & (~REG_0X09_FORCE_CALIBRATION));
+       mdelay(1);
+       rtk_phy_write(phy_reg, addr, data | REG_0X09_FORCE_CALIBRATION);
+}
+
+static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+       struct phy_parameter *phy_parameter;
+       int i = 0;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+       phy_reg = &phy_parameter->phy_reg;
+
+       if (phy_cfg->use_default_parameter)
+               goto do_toggle;
+
+       for (i = 0; i < phy_cfg->param_size; i++) {
+               struct phy_data *phy_data = phy_cfg->param + i;
+               u8 addr = phy_data->addr;
+               u16 data = phy_data->data;
+
+               if (!addr && !data)
+                       continue;
+
+               rtk_phy_write(phy_reg, addr, data);
+       }
+
+do_toggle:
+       if (phy_cfg->do_toggle_once)
+               phy_cfg->do_toggle = true;
+
+       do_rtk_usb3_phy_toggle(rtk_phy, index, false);
+
+       if (phy_cfg->do_toggle_once) {
+               u16 check_value = 0;
+               int count = 10;
+               u16 value_0x0d, value_0x10;
+
+               /* Enable Debug mode by set 0x0D and 0x10 */
+               value_0x0d = rtk_phy_read(phy_reg, PHY_ADDR_0X0D);
+               value_0x10 = rtk_phy_read(phy_reg, PHY_ADDR_0X10);
+
+               rtk_phy_write(phy_reg, PHY_ADDR_0X0D,
+                             value_0x0d | REG_0X0D_RX_DEBUG_TEST_EN);
+               rtk_phy_write(phy_reg, PHY_ADDR_0X10,
+                             (value_0x10 & ~REG_0X10_DEBUG_MODE_SETTING_MASK) |
+                             REG_0X10_DEBUG_MODE_SETTING);
+
+               check_value = rtk_phy_read(phy_reg, PHY_ADDR_0X30);
+
+               while (!(check_value & BIT(15))) {
+                       check_value = rtk_phy_read(phy_reg, PHY_ADDR_0X30);
+                       mdelay(1);
+                       if (count-- < 0)
+                               break;
+               }
+
+               if (!(check_value & BIT(15)))
+                       dev_info(rtk_phy->dev, "toggle fail addr=0x%02x, data=0x%04x\n",
+                                PHY_ADDR_0X30, check_value);
+
+               /* Disable Debug mode by set 0x0D and 0x10 to default*/
+               rtk_phy_write(phy_reg, PHY_ADDR_0X0D, value_0x0d);
+               rtk_phy_write(phy_reg, PHY_ADDR_0X10, value_0x10);
+
+               phy_cfg->do_toggle = false;
+       }
+
+       if (phy_cfg->check_rx_front_end_offset) {
+               u16 rx_offset_code, rx_offset_range;
+               u16 code_mask = REG_0X1F_RX_OFFSET_CODE_MASK;
+               u16 range_mask = REG_0X0B_RX_OFFSET_RANGE_MASK;
+               bool do_update = false;
+
+               rx_offset_code = rtk_phy_read(phy_reg, PHY_ADDR_0X1F);
+               if (((rx_offset_code & code_mask) == 0x0) ||
+                   ((rx_offset_code & code_mask) == code_mask))
+                       do_update = true;
+
+               rx_offset_range = rtk_phy_read(phy_reg, PHY_ADDR_0X0B);
+               if (((rx_offset_range & range_mask) == range_mask) && do_update) {
+                       dev_warn(rtk_phy->dev, "Don't update rx_offset_range (rx_offset_code=0x%x, rx_offset_range=0x%x)\n",
+                                rx_offset_code, rx_offset_range);
+                       do_update = false;
+               }
+
+               if (do_update) {
+                       u16 tmp1, tmp2;
+
+                       tmp1 = rx_offset_range & (~range_mask);
+                       tmp2 = rx_offset_range & range_mask;
+                       tmp2 += (1 << 2);
+                       rx_offset_range = tmp1 | (tmp2 & range_mask);
+                       rtk_phy_write(phy_reg, PHY_ADDR_0X0B, rx_offset_range);
+                       goto do_toggle;
+               }
+       }
+
+       return 0;
+}
+
+static int rtk_phy_init(struct phy *phy)
+{
+       struct rtk_phy *rtk_phy = phy_get_drvdata(phy);
+       int ret = 0;
+       int i;
+       unsigned long phy_init_time = jiffies;
+
+       for (i = 0; i < rtk_phy->num_phy; i++)
+               ret = do_rtk_phy_init(rtk_phy, i);
+
+       dev_dbg(rtk_phy->dev, "Initialized RTK USB 3.0 PHY (take %dms)\n",
+               jiffies_to_msecs(jiffies - phy_init_time));
+
+       return ret;
+}
+
+static int rtk_phy_exit(struct phy *phy)
+{
+       return 0;
+}
+
+static const struct phy_ops ops = {
+       .init           = rtk_phy_init,
+       .exit           = rtk_phy_exit,
+       .owner          = THIS_MODULE,
+};
+
+static void rtk_phy_toggle(struct usb_phy *usb3_phy, bool connect, int port)
+{
+       int index = port;
+       struct rtk_phy *rtk_phy = NULL;
+
+       rtk_phy = dev_get_drvdata(usb3_phy->dev);
+
+       if (index > rtk_phy->num_phy) {
+               dev_err(rtk_phy->dev, "%s: The port=%d is not in usb phy (num_phy=%d)\n",
+                       __func__, index, rtk_phy->num_phy);
+               return;
+       }
+
+       do_rtk_usb3_phy_toggle(rtk_phy, index, connect);
+}
+
+static int rtk_phy_notify_port_status(struct usb_phy *x, int port,
+                                     u16 portstatus, u16 portchange)
+{
+       bool connect = false;
+
+       pr_debug("%s port=%d portstatus=0x%x portchange=0x%x\n",
+                __func__, port, (int)portstatus, (int)portchange);
+       if (portstatus & USB_PORT_STAT_CONNECTION)
+               connect = true;
+
+       if (portchange & USB_PORT_STAT_C_CONNECTION)
+               rtk_phy_toggle(x, connect, port);
+
+       return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *create_phy_debug_root(void)
+{
+       struct dentry *phy_debug_root;
+
+       phy_debug_root = debugfs_lookup("phy", usb_debug_root);
+       if (!phy_debug_root)
+               phy_debug_root = debugfs_create_dir("phy", usb_debug_root);
+
+       return phy_debug_root;
+}
+
+static int rtk_usb3_parameter_show(struct seq_file *s, void *unused)
+{
+       struct rtk_phy *rtk_phy = s->private;
+       struct phy_cfg *phy_cfg;
+       int i, index;
+
+       phy_cfg = rtk_phy->phy_cfg;
+
+       seq_puts(s, "Property:\n");
+       seq_printf(s, "  check_efuse: %s\n",
+                  phy_cfg->check_efuse ? "Enable" : "Disable");
+       seq_printf(s, "  do_toggle: %s\n",
+                  phy_cfg->do_toggle ? "Enable" : "Disable");
+       seq_printf(s, "  do_toggle_once: %s\n",
+                  phy_cfg->do_toggle_once ? "Enable" : "Disable");
+       seq_printf(s, "  use_default_parameter: %s\n",
+                  phy_cfg->use_default_parameter ? "Enable" : "Disable");
+
+       for (index = 0; index < rtk_phy->num_phy; index++) {
+               struct phy_reg *phy_reg;
+               struct phy_parameter *phy_parameter;
+
+               phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+               phy_reg = &phy_parameter->phy_reg;
+
+               seq_printf(s, "PHY %d:\n", index);
+
+               for (i = 0; i < phy_cfg->param_size; i++) {
+                       struct phy_data *phy_data = phy_cfg->param + i;
+                       u8 addr = ARRAY_INDEX_MAP_PHY_ADDR(i);
+                       u16 data = phy_data->data;
+
+                       if (!phy_data->addr && !data)
+                               seq_printf(s, "  addr = 0x%02x, data = none   ==> read value = 0x%04x\n",
+                                          addr, rtk_phy_read(phy_reg, addr));
+                       else
+                               seq_printf(s, "  addr = 0x%02x, data = 0x%04x ==> read value = 0x%04x\n",
+                                          addr, data, rtk_phy_read(phy_reg, addr));
+               }
+
+               seq_puts(s, "PHY Property:\n");
+               seq_printf(s, "  efuse_usb_u3_tx_lfps_swing_trim: 0x%x\n",
+                          (int)phy_parameter->efuse_usb_u3_tx_lfps_swing_trim);
+               seq_printf(s, "  amplitude_control_coarse: 0x%x\n",
+                          (int)phy_parameter->amplitude_control_coarse);
+               seq_printf(s, "  amplitude_control_fine: 0x%x\n",
+                          (int)phy_parameter->amplitude_control_fine);
+       }
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rtk_usb3_parameter);
+
+static inline void create_debug_files(struct rtk_phy *rtk_phy)
+{
+       struct dentry *phy_debug_root = NULL;
+
+       phy_debug_root = create_phy_debug_root();
+
+       if (!phy_debug_root)
+               return;
+
+       rtk_phy->debug_dir = debugfs_create_dir(dev_name(rtk_phy->dev), phy_debug_root);
+       if (!rtk_phy->debug_dir)
+               return;
+
+       if (!debugfs_create_file("parameter", 0444, rtk_phy->debug_dir, rtk_phy,
+                                &rtk_usb3_parameter_fops))
+               goto file_error;
+
+       return;
+
+file_error:
+       debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+
+static inline void remove_debug_files(struct rtk_phy *rtk_phy)
+{
+       debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+#else
+static inline void create_debug_files(struct rtk_phy *rtk_phy) { }
+static inline void remove_debug_files(struct rtk_phy *rtk_phy) { }
+#endif /* CONFIG_DEBUG_FS */
+
+static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy,
+                                struct phy_parameter *phy_parameter, int index)
+{
+       struct phy_cfg *phy_cfg = rtk_phy->phy_cfg;
+       u8 value = 0;
+       struct nvmem_cell *cell;
+
+       if (!phy_cfg->check_efuse)
+               goto out;
+
+       cell = nvmem_cell_get(rtk_phy->dev, "usb_u3_tx_lfps_swing_trim");
+       if (IS_ERR(cell)) {
+               dev_dbg(rtk_phy->dev, "%s no usb_u3_tx_lfps_swing_trim: %ld\n",
+                       __func__, PTR_ERR(cell));
+       } else {
+               unsigned char *buf;
+               size_t buf_size;
+
+               buf = nvmem_cell_read(cell, &buf_size);
+               if (!IS_ERR(buf)) {
+                       value = buf[0] & USB_U3_TX_LFPS_SWING_TRIM_MASK;
+                       kfree(buf);
+               }
+               nvmem_cell_put(cell);
+       }
+
+       if (value > 0 && value < 0x8)
+               phy_parameter->efuse_usb_u3_tx_lfps_swing_trim = 0x8;
+       else
+               phy_parameter->efuse_usb_u3_tx_lfps_swing_trim = (u8)value;
+
+out:
+       return 0;
+}
+
+static void update_amplitude_control_value(struct rtk_phy *rtk_phy,
+                                          struct phy_parameter *phy_parameter)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+
+       phy_reg = &phy_parameter->phy_reg;
+       phy_cfg = rtk_phy->phy_cfg;
+
+       if (phy_parameter->amplitude_control_coarse != AMPLITUDE_CONTROL_COARSE_DEFAULT) {
+               u16 val_mask = AMPLITUDE_CONTROL_COARSE_MASK;
+               u16 data;
+
+               if (!phy_cfg->param[PHY_ADDR_0X20].addr && !phy_cfg->param[PHY_ADDR_0X20].data) {
+                       phy_cfg->param[PHY_ADDR_0X20].addr = PHY_ADDR_0X20;
+                       data = rtk_phy_read(phy_reg, PHY_ADDR_0X20);
+               } else {
+                       data = phy_cfg->param[PHY_ADDR_0X20].data;
+               }
+
+               data &= (~val_mask);
+               data |= (phy_parameter->amplitude_control_coarse & val_mask);
+
+               phy_cfg->param[PHY_ADDR_0X20].data = data;
+       }
+
+       if (phy_parameter->efuse_usb_u3_tx_lfps_swing_trim) {
+               u8 efuse_val = phy_parameter->efuse_usb_u3_tx_lfps_swing_trim;
+               u16 val_mask = USB_U3_TX_LFPS_SWING_TRIM_MASK;
+               int val_shift = USB_U3_TX_LFPS_SWING_TRIM_SHIFT;
+               u16 data;
+
+               if (!phy_cfg->param[PHY_ADDR_0X20].addr && !phy_cfg->param[PHY_ADDR_0X20].data) {
+                       phy_cfg->param[PHY_ADDR_0X20].addr = PHY_ADDR_0X20;
+                       data = rtk_phy_read(phy_reg, PHY_ADDR_0X20);
+               } else {
+                       data = phy_cfg->param[PHY_ADDR_0X20].data;
+               }
+
+               data &= ~(val_mask << val_shift);
+               data |= ((efuse_val & val_mask) << val_shift);
+
+               phy_cfg->param[PHY_ADDR_0X20].data = data;
+       }
+
+       if (phy_parameter->amplitude_control_fine != AMPLITUDE_CONTROL_FINE_DEFAULT) {
+               u16 val_mask = AMPLITUDE_CONTROL_FINE_MASK;
+
+               if (!phy_cfg->param[PHY_ADDR_0X21].addr && !phy_cfg->param[PHY_ADDR_0X21].data)
+                       phy_cfg->param[PHY_ADDR_0X21].addr = PHY_ADDR_0X21;
+
+               phy_cfg->param[PHY_ADDR_0X21].data =
+                           phy_parameter->amplitude_control_fine & val_mask;
+       }
+}
+
+static int parse_phy_data(struct rtk_phy *rtk_phy)
+{
+       struct device *dev = rtk_phy->dev;
+       struct phy_parameter *phy_parameter;
+       int ret = 0;
+       int index;
+
+       rtk_phy->phy_parameter = devm_kzalloc(dev, sizeof(struct phy_parameter) *
+                                             rtk_phy->num_phy, GFP_KERNEL);
+       if (!rtk_phy->phy_parameter)
+               return -ENOMEM;
+
+       for (index = 0; index < rtk_phy->num_phy; index++) {
+               phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+
+               phy_parameter->phy_reg.reg_mdio_ctl = of_iomap(dev->of_node, 0) + index;
+
+               /* Amplitude control address 0x20 bit 0 to bit 7 */
+               if (of_property_read_u32(dev->of_node, "realtek,amplitude-control-coarse-tuning",
+                                        &phy_parameter->amplitude_control_coarse))
+                       phy_parameter->amplitude_control_coarse = AMPLITUDE_CONTROL_COARSE_DEFAULT;
+
+               /* Amplitude control address 0x21 bit 0 to bit 16 */
+               if (of_property_read_u32(dev->of_node, "realtek,amplitude-control-fine-tuning",
+                                        &phy_parameter->amplitude_control_fine))
+                       phy_parameter->amplitude_control_fine = AMPLITUDE_CONTROL_FINE_DEFAULT;
+
+               get_phy_data_by_efuse(rtk_phy, phy_parameter, index);
+
+               update_amplitude_control_value(rtk_phy, phy_parameter);
+       }
+
+       return ret;
+}
+
+static int rtk_usb3phy_probe(struct platform_device *pdev)
+{
+       struct rtk_phy *rtk_phy;
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy;
+       struct phy_provider *phy_provider;
+       const struct phy_cfg *phy_cfg;
+       int ret;
+
+       phy_cfg = of_device_get_match_data(dev);
+       if (!phy_cfg) {
+               dev_err(dev, "phy config are not assigned!\n");
+               return -EINVAL;
+       }
+
+       rtk_phy = devm_kzalloc(dev, sizeof(*rtk_phy), GFP_KERNEL);
+       if (!rtk_phy)
+               return -ENOMEM;
+
+       rtk_phy->dev                    = &pdev->dev;
+       rtk_phy->phy.dev                = rtk_phy->dev;
+       rtk_phy->phy.label              = "rtk-usb3phy";
+       rtk_phy->phy.notify_port_status = rtk_phy_notify_port_status;
+
+       rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL);
+
+       memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg));
+
+       rtk_phy->num_phy = 1;
+
+       ret = parse_phy_data(rtk_phy);
+       if (ret)
+               goto err;
+
+       platform_set_drvdata(pdev, rtk_phy);
+
+       generic_phy = devm_phy_create(rtk_phy->dev, NULL, &ops);
+       if (IS_ERR(generic_phy))
+               return PTR_ERR(generic_phy);
+
+       phy_set_drvdata(generic_phy, rtk_phy);
+
+       phy_provider = devm_of_phy_provider_register(rtk_phy->dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       ret = usb_add_phy_dev(&rtk_phy->phy);
+       if (ret)
+               goto err;
+
+       create_debug_files(rtk_phy);
+
+err:
+       return ret;
+}
+
+static void rtk_usb3phy_remove(struct platform_device *pdev)
+{
+       struct rtk_phy *rtk_phy = platform_get_drvdata(pdev);
+
+       remove_debug_files(rtk_phy);
+
+       usb_remove_phy(&rtk_phy->phy);
+}
+
+static const struct phy_cfg rtd1295_phy_cfg = {
+       .param_size = MAX_USB_PHY_DATA_SIZE,
+       .param = {  [0] = {0x01, 0x4008},  [1] = {0x01, 0xe046},
+                   [2] = {0x02, 0x6046},  [3] = {0x03, 0x2779},
+                   [4] = {0x04, 0x72f5},  [5] = {0x05, 0x2ad3},
+                   [6] = {0x06, 0x000e},  [7] = {0x07, 0x2e00},
+                   [8] = {0x08, 0x3591},  [9] = {0x09, 0x525c},
+                  [10] = {0x0a, 0xa600}, [11] = {0x0b, 0xa904},
+                  [12] = {0x0c, 0xc000}, [13] = {0x0d, 0xef1c},
+                  [14] = {0x0e, 0x2000}, [15] = {0x0f, 0x0000},
+                  [16] = {0x10, 0x000c}, [17] = {0x11, 0x4c00},
+                  [18] = {0x12, 0xfc00}, [19] = {0x13, 0x0c81},
+                  [20] = {0x14, 0xde01}, [21] = {0x15, 0x0000},
+                  [22] = {0x16, 0x0000}, [23] = {0x17, 0x0000},
+                  [24] = {0x18, 0x0000}, [25] = {0x19, 0x4004},
+                  [26] = {0x1a, 0x1260}, [27] = {0x1b, 0xff00},
+                  [28] = {0x1c, 0xcb00}, [29] = {0x1d, 0xa03f},
+                  [30] = {0x1e, 0xc2e0}, [31] = {0x1f, 0x2807},
+                  [32] = {0x20, 0x947a}, [33] = {0x21, 0x88aa},
+                  [34] = {0x22, 0x0057}, [35] = {0x23, 0xab66},
+                  [36] = {0x24, 0x0800}, [37] = {0x25, 0x0000},
+                  [38] = {0x26, 0x040a}, [39] = {0x27, 0x01d6},
+                  [40] = {0x28, 0xf8c2}, [41] = {0x29, 0x3080},
+                  [42] = {0x2a, 0x3082}, [43] = {0x2b, 0x2078},
+                  [44] = {0x2c, 0xffff}, [45] = {0x2d, 0xffff},
+                  [46] = {0x2e, 0x0000}, [47] = {0x2f, 0x0040}, },
+       .check_efuse = false,
+       .do_toggle = true,
+       .do_toggle_once = false,
+       .use_default_parameter = false,
+       .check_rx_front_end_offset = false,
+};
+
+static const struct phy_cfg rtd1619_phy_cfg = {
+       .param_size = MAX_USB_PHY_DATA_SIZE,
+       .param = {  [8] = {0x08, 0x3591},
+                  [38] = {0x26, 0x840b},
+                  [40] = {0x28, 0xf842}, },
+       .check_efuse = false,
+       .do_toggle = true,
+       .do_toggle_once = false,
+       .use_default_parameter = false,
+       .check_rx_front_end_offset = false,
+};
+
+static const struct phy_cfg rtd1319_phy_cfg = {
+       .param_size = MAX_USB_PHY_DATA_SIZE,
+       .param = {  [1] = {0x01, 0xac86},
+                   [6] = {0x06, 0x0003},
+                   [9] = {0x09, 0x924c},
+                  [10] = {0x0a, 0xa608},
+                  [11] = {0x0b, 0xb905},
+                  [14] = {0x0e, 0x2010},
+                  [32] = {0x20, 0x705a},
+                  [33] = {0x21, 0xf645},
+                  [34] = {0x22, 0x0013},
+                  [35] = {0x23, 0xcb66},
+                  [41] = {0x29, 0xff00}, },
+       .check_efuse = true,
+       .do_toggle = true,
+       .do_toggle_once = false,
+       .use_default_parameter = false,
+       .check_rx_front_end_offset = false,
+};
+
+static const struct phy_cfg rtd1619b_phy_cfg = {
+       .param_size = MAX_USB_PHY_DATA_SIZE,
+       .param = {  [1] = {0x01, 0xac8c},
+                   [6] = {0x06, 0x0017},
+                   [9] = {0x09, 0x724c},
+                  [10] = {0x0a, 0xb610},
+                  [11] = {0x0b, 0xb90d},
+                  [13] = {0x0d, 0xef2a},
+                  [15] = {0x0f, 0x9050},
+                  [16] = {0x10, 0x000c},
+                  [32] = {0x20, 0x70ff},
+                  [34] = {0x22, 0x0013},
+                  [35] = {0x23, 0xdb66},
+                  [38] = {0x26, 0x8609},
+                  [41] = {0x29, 0xff13},
+                  [42] = {0x2a, 0x3070}, },
+       .check_efuse = true,
+       .do_toggle = false,
+       .do_toggle_once = true,
+       .use_default_parameter = false,
+       .check_rx_front_end_offset = false,
+};
+
+static const  struct phy_cfg rtd1319d_phy_cfg = {
+       .param_size = MAX_USB_PHY_DATA_SIZE,
+       .param = {  [1] = {0x01, 0xac89},
+                   [4] = {0x04, 0xf2f5},
+                   [6] = {0x06, 0x0017},
+                   [9] = {0x09, 0x424c},
+                  [10] = {0x0a, 0x9610},
+                  [11] = {0x0b, 0x9901},
+                  [12] = {0x0c, 0xf000},
+                  [13] = {0x0d, 0xef2a},
+                  [14] = {0x0e, 0x1000},
+                  [15] = {0x0f, 0x9050},
+                  [32] = {0x20, 0x7077},
+                  [35] = {0x23, 0x0b62},
+                  [37] = {0x25, 0x10ec},
+                  [42] = {0x2a, 0x3070}, },
+       .check_efuse = true,
+       .do_toggle = false,
+       .do_toggle_once = true,
+       .use_default_parameter = false,
+       .check_rx_front_end_offset = true,
+};
+
+static const struct of_device_id usbphy_rtk_dt_match[] = {
+       { .compatible = "realtek,rtd1295-usb3phy", .data = &rtd1295_phy_cfg },
+       { .compatible = "realtek,rtd1319-usb3phy", .data = &rtd1319_phy_cfg },
+       { .compatible = "realtek,rtd1319d-usb3phy", .data = &rtd1319d_phy_cfg },
+       { .compatible = "realtek,rtd1619-usb3phy", .data = &rtd1619_phy_cfg },
+       { .compatible = "realtek,rtd1619b-usb3phy", .data = &rtd1619b_phy_cfg },
+       {},
+};
+MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match);
+
+static struct platform_driver rtk_usb3phy_driver = {
+       .probe          = rtk_usb3phy_probe,
+       .remove_new     = rtk_usb3phy_remove,
+       .driver         = {
+               .name   = "rtk-usb3phy",
+               .of_match_table = usbphy_rtk_dt_match,
+       },
+};
+
+module_platform_driver(rtk_usb3phy_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform: rtk-usb3phy");
+MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>");
+MODULE_DESCRIPTION("Realtek usb 3.0 phy driver");
index 25f9767c28e82ee516b34846f88dcfa8a27a6f79..d0b4d3fc40ed8fa9e85ac6b9698784bbbc4e3264 100644 (file)
@@ -406,6 +406,27 @@ static int cros_typec_usb_safe_state(struct cros_typec_port *port)
        return ret;
 }
 
+/**
+ * cros_typec_get_cable_vdo() - Get Cable VDO of the connected cable
+ * @port: Type-C port data
+ * @svid: Standard or Vendor ID to match
+ *
+ * Returns the Cable VDO if match is found and returns 0 if match is not found.
+ */
+static int cros_typec_get_cable_vdo(struct cros_typec_port *port, u16 svid)
+{
+       struct list_head *head = &port->plug_mode_list;
+       struct cros_typec_altmode_node *node;
+       u32 ret = 0;
+
+       list_for_each_entry(node, head, list) {
+               if (node->amode->svid == svid)
+                       return node->amode->vdo;
+       }
+
+       return ret;
+}
+
 /*
  * Spoof the VDOs that were likely communicated by the partner for TBT alt
  * mode.
@@ -432,6 +453,9 @@ static int cros_typec_enable_tbt(struct cros_typec_data *typec,
 
        /* Cable Discover Mode VDO */
        data.cable_mode = TBT_MODE;
+
+       data.cable_mode |= cros_typec_get_cable_vdo(port, USB_TYPEC_TBT_SID);
+
        data.cable_mode |= TBT_SET_CABLE_SPEED(pd_ctrl->cable_speed);
 
        if (pd_ctrl->control_flags & USB_PD_CTRL_OPTICAL_CABLE)
@@ -522,8 +546,10 @@ static int cros_typec_enable_usb4(struct cros_typec_data *typec,
        /* Cable Type */
        if (pd_ctrl->control_flags & USB_PD_CTRL_OPTICAL_CABLE)
                data.eudo |= EUDO_CABLE_TYPE_OPTICAL << EUDO_CABLE_TYPE_SHIFT;
-       else if (pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_CABLE)
+       else if (cros_typec_get_cable_vdo(port, USB_TYPEC_TBT_SID) & TBT_CABLE_RETIMER)
                data.eudo |= EUDO_CABLE_TYPE_RE_TIMER << EUDO_CABLE_TYPE_SHIFT;
+       else if (pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_CABLE)
+               data.eudo |= EUDO_CABLE_TYPE_RE_DRIVER << EUDO_CABLE_TYPE_SHIFT;
 
        data.active_link_training = !!(pd_ctrl->control_flags &
                                       USB_PD_CTRL_ACTIVE_LINK_UNIDIR);
index 38fefd0e5268d230b5745e6f431530df4591f791..c9b6bb46111c4b71128559b9d49dde1043aa268a 100644 (file)
@@ -12,7 +12,7 @@
 #include "tb.h"
 
 static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
-                                   void **return_value)
+                                   void **ret)
 {
        struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
        struct fwnode_handle *fwnode;
@@ -84,6 +84,7 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
                if (link) {
                        dev_dbg(&nhi->pdev->dev, "created link from %s\n",
                                dev_name(&pdev->dev));
+                       *(bool *)ret = true;
                } else {
                        dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
                                 dev_name(&pdev->dev));
@@ -104,22 +105,29 @@ out_put:
  * Goes over ACPI namespace finding tunneled ports that reference to
  * @nhi ACPI node. For each reference a device link is added. The link
  * is automatically removed by the driver core.
+ *
+ * Returns %true if at least one link was created.
  */
-void tb_acpi_add_links(struct tb_nhi *nhi)
+bool tb_acpi_add_links(struct tb_nhi *nhi)
 {
        acpi_status status;
+       bool ret = false;
 
        if (!has_acpi_companion(&nhi->pdev->dev))
-               return;
+               return false;
 
        /*
         * Find all devices that have usb4-host-controller interface
         * property that references to this NHI.
         */
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 32,
-                                    tb_acpi_add_link, NULL, nhi, NULL);
-       if (ACPI_FAILURE(status))
+                                    tb_acpi_add_link, NULL, nhi, (void **)&ret);
+       if (ACPI_FAILURE(status)) {
                dev_warn(&nhi->pdev->dev, "failed to enumerate tunneled ports\n");
+               return false;
+       }
+
+       return ret;
 }
 
 /**
index 7ea63bb3171409cf504eb7b362ac2663df46d350..43171cc1cc2d83c223bef20dad7a18fd22841c0e 100644 (file)
@@ -2188,46 +2188,47 @@ struct device_type tb_switch_type = {
 
 static int tb_switch_get_generation(struct tb_switch *sw)
 {
-       switch (sw->config.device_id) {
-       case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
-       case PCI_DEVICE_ID_INTEL_EAGLE_RIDGE:
-       case PCI_DEVICE_ID_INTEL_LIGHT_PEAK:
-       case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_2C:
-       case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
-       case PCI_DEVICE_ID_INTEL_PORT_RIDGE:
-       case PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_BRIDGE:
-               return 1;
-
-       case PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE:
-               return 2;
-
-       case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE:
-       case PCI_DEVICE_ID_INTEL_ICL_NHI0:
-       case PCI_DEVICE_ID_INTEL_ICL_NHI1:
-               return 3;
+       if (tb_switch_is_usb4(sw))
+               return 4;
 
-       default:
-               if (tb_switch_is_usb4(sw))
-                       return 4;
+       if (sw->config.vendor_id == PCI_VENDOR_ID_INTEL) {
+               switch (sw->config.device_id) {
+               case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
+               case PCI_DEVICE_ID_INTEL_EAGLE_RIDGE:
+               case PCI_DEVICE_ID_INTEL_LIGHT_PEAK:
+               case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_2C:
+               case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
+               case PCI_DEVICE_ID_INTEL_PORT_RIDGE:
+               case PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_BRIDGE:
+                       return 1;
 
-               /*
-                * For unknown switches assume generation to be 1 to be
-                * on the safe side.
-                */
-               tb_sw_warn(sw, "unsupported switch device id %#x\n",
-                          sw->config.device_id);
-               return 1;
+               case PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE:
+                       return 2;
+
+               case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE:
+               case PCI_DEVICE_ID_INTEL_ICL_NHI0:
+               case PCI_DEVICE_ID_INTEL_ICL_NHI1:
+                       return 3;
+               }
        }
+
+       /*
+        * For unknown switches assume generation to be 1 to be on the
+        * safe side.
+        */
+       tb_sw_warn(sw, "unsupported switch device id %#x\n",
+                  sw->config.device_id);
+       return 1;
 }
 
 static bool tb_switch_exceeds_max_depth(const struct tb_switch *sw, int depth)
index 3fb4553a6442b48e09afbbd3945e358d6caa0f8c..dd0a1ef8cf1217e2e21dc204a17b65fafc7ebd86 100644 (file)
@@ -2368,12 +2368,13 @@ static const struct tb_cm_ops tb_cm_ops = {
  * downstream ports and the NHI so that the device core will make sure
  * NHI is resumed first before the rest.
  */
-static void tb_apple_add_links(struct tb_nhi *nhi)
+static bool tb_apple_add_links(struct tb_nhi *nhi)
 {
        struct pci_dev *upstream, *pdev;
+       bool ret;
 
        if (!x86_apple_machine)
-               return;
+               return false;
 
        switch (nhi->pdev->device) {
        case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
@@ -2382,26 +2383,27 @@ static void tb_apple_add_links(struct tb_nhi *nhi)
        case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
                break;
        default:
-               return;
+               return false;
        }
 
        upstream = pci_upstream_bridge(nhi->pdev);
        while (upstream) {
                if (!pci_is_pcie(upstream))
-                       return;
+                       return false;
                if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
                        break;
                upstream = pci_upstream_bridge(upstream);
        }
 
        if (!upstream)
-               return;
+               return false;
 
        /*
         * For each hotplug downstream port, create add device link
         * back to NHI so that PCIe tunnels can be re-established after
         * sleep.
         */
+       ret = false;
        for_each_pci_bridge(pdev, upstream->subordinate) {
                const struct device_link *link;
 
@@ -2417,11 +2419,14 @@ static void tb_apple_add_links(struct tb_nhi *nhi)
                if (link) {
                        dev_dbg(&nhi->pdev->dev, "created link from %s\n",
                                dev_name(&pdev->dev));
+                       ret = true;
                } else {
                        dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
                                 dev_name(&pdev->dev));
                }
        }
+
+       return ret;
 }
 
 struct tb *tb_probe(struct tb_nhi *nhi)
@@ -2448,8 +2453,13 @@ struct tb *tb_probe(struct tb_nhi *nhi)
 
        tb_dbg(tb, "using software connection manager\n");
 
-       tb_apple_add_links(nhi);
-       tb_acpi_add_links(nhi);
+       /*
+        * Device links are needed to make sure we establish tunnels
+        * before the PCIe/USB stack is resumed so complain here if we
+        * found them missing.
+        */
+       if (!tb_apple_add_links(nhi) && !tb_acpi_add_links(nhi))
+               tb_warn(tb, "device links to tunneled native ports are missing!\n");
 
        return tb;
 }
index 57a9b272cb94bd0e1f646153669b04478bc552be..d2a55ad2fd3e698045db391c14ae79d8a8d3f71e 100644 (file)
@@ -1333,7 +1333,7 @@ static inline bool usb4_port_device_is_offline(const struct usb4_port *usb4)
 void tb_check_quirks(struct tb_switch *sw);
 
 #ifdef CONFIG_ACPI
-void tb_acpi_add_links(struct tb_nhi *nhi);
+bool tb_acpi_add_links(struct tb_nhi *nhi);
 
 bool tb_acpi_is_native(void);
 bool tb_acpi_may_tunnel_usb3(void);
@@ -1346,7 +1346,7 @@ void tb_acpi_exit(void);
 int tb_acpi_power_on_retimers(struct tb_port *port);
 int tb_acpi_power_off_retimers(struct tb_port *port);
 #else
-static inline void tb_acpi_add_links(struct tb_nhi *nhi) { }
+static inline bool tb_acpi_add_links(struct tb_nhi *nhi) { return false; }
 
 static inline bool tb_acpi_is_native(void) { return true; }
 static inline bool tb_acpi_may_tunnel_usb3(void) { return true; }
index 0dfd1e083994e6d222efda74f7794d7cdc22c6f3..747f88703d5ca118f07209ae9989fb4008802b8f 100644 (file)
@@ -19,7 +19,7 @@ static const unsigned int tmu_rates[] = {
        [TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI] = 16,
 };
 
-const struct {
+static const struct {
        unsigned int freq_meas_window;
        unsigned int avg_const;
        unsigned int delta_avg_const;
index aa0111b365bb28759d0ab2b355dda54156fa1d13..11a5b3437c32d2fe152743bf44c49ab1ce6142f7 100644 (file)
@@ -61,6 +61,7 @@
 #include <linux/module.h>
 #include <linux/dmapool.h>
 #include <linux/iopoll.h>
+#include <linux/property.h>
 
 #include "core.h"
 #include "gadget-export.h"
index 884e2301237f4de465b7f77546fa00182c42c76e..2c1aca84f2264d0e450ff0528d4c4fbd1af59a5b 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/irq.h>
 #include <linux/kernel.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 
@@ -255,9 +256,10 @@ static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
        cdns3_set_platform_suspend(cdns->dev, false, false);
 
        spin_lock_irqsave(&cdns->lock, flags);
-       cdns_resume(cdns, !PMSG_IS_AUTO(msg));
+       cdns_resume(cdns);
        cdns->in_lpm = false;
        spin_unlock_irqrestore(&cdns->lock, flags);
+       cdns_set_active(cdns, !PMSG_IS_AUTO(msg));
        if (cdns->wakeup_pending) {
                cdns->wakeup_pending = false;
                enable_irq(cdns->wakeup_irq);
index fc1f003b145dcdbda0ada2d8900c21e6f855a7ac..a7265b86e42775bab7e286ffa9b5cd2852454294 100644 (file)
@@ -166,7 +166,7 @@ static int cdns_starfive_remove_core(struct device *dev, void *c)
        return 0;
 }
 
-static int cdns_starfive_remove(struct platform_device *pdev)
+static void cdns_starfive_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct cdns_starfive *data = dev_get_drvdata(dev);
@@ -178,8 +178,6 @@ static int cdns_starfive_remove(struct platform_device *pdev)
        pm_runtime_put_noidle(dev);
        cdns_clk_rst_deinit(data);
        platform_set_drvdata(pdev, NULL);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -232,7 +230,7 @@ MODULE_DEVICE_TABLE(of, cdns_starfive_of_match);
 
 static struct platform_driver cdns_starfive_driver = {
        .probe          = cdns_starfive_probe,
-       .remove         = cdns_starfive_remove,
+       .remove_new     = cdns_starfive_remove,
        .driver         = {
                .name   = "cdns3-starfive",
                .of_match_table = cdns_starfive_of_match,
index 81b9132e3aaa11d6032be082085f812abb872851..5945c4b1e11f6a3e20878341e4a893d4ab7794a8 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/io.h>
 #include <linux/of_platform.h>
 #include <linux/pm_runtime.h>
+#include <linux/property.h>
 
 /* USB Wrapper register offsets */
 #define USBSS_PID              0x0
index 7b151f5af3ccb82ceb37c0a71a7c63da677d3d7b..0725668ffea4c84dd7300dab2665b5a0170876c2 100644 (file)
@@ -208,8 +208,9 @@ static int __maybe_unused cdnsp_pci_resume(struct device *dev)
        int ret;
 
        spin_lock_irqsave(&cdns->lock, flags);
-       ret = cdns_resume(cdns, 1);
+       ret = cdns_resume(cdns);
        spin_unlock_irqrestore(&cdns->lock, flags);
+       cdns_set_active(cdns, 1);
 
        return ret;
 }
index dbcdf3b24b47793caffd28ff1ed542c31c2ba133..33548771a0d3a7212781ff39814fedb7d01f0ab4 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -522,9 +523,8 @@ int cdns_suspend(struct cdns *cdns)
 }
 EXPORT_SYMBOL_GPL(cdns_suspend);
 
-int cdns_resume(struct cdns *cdns, u8 set_active)
+int cdns_resume(struct cdns *cdns)
 {
-       struct device *dev = cdns->dev;
        enum usb_role real_role;
        bool role_changed = false;
        int ret = 0;
@@ -556,15 +556,23 @@ int cdns_resume(struct cdns *cdns, u8 set_active)
        if (cdns->roles[cdns->role]->resume)
                cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns));
 
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cdns_resume);
+
+void cdns_set_active(struct cdns *cdns, u8 set_active)
+{
+       struct device *dev = cdns->dev;
+
        if (set_active) {
                pm_runtime_disable(dev);
                pm_runtime_set_active(dev);
                pm_runtime_enable(dev);
        }
 
-       return 0;
+       return;
 }
-EXPORT_SYMBOL_GPL(cdns_resume);
+EXPORT_SYMBOL_GPL(cdns_set_active);
 #endif /* CONFIG_PM_SLEEP */
 
 MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
index 2d332a788871ed50b88055d891b61f8182b343cd..4a4dbc2c156151e33871276fc12c359c53c8833b 100644 (file)
@@ -125,10 +125,13 @@ int cdns_init(struct cdns *cdns);
 int cdns_remove(struct cdns *cdns);
 
 #ifdef CONFIG_PM_SLEEP
-int cdns_resume(struct cdns *cdns, u8 set_active);
+int cdns_resume(struct cdns *cdns);
 int cdns_suspend(struct cdns *cdns);
+void cdns_set_active(struct cdns *cdns, u8 set_active);
 #else /* CONFIG_PM_SLEEP */
-static inline int cdns_resume(struct cdns *cdns, u8 set_active)
+static inline int cdns_resume(struct cdns *cdns)
+{ return 0; }
+static inline int cdns_set_active(struct cdns *cdns, u8 set_active)
 { return 0; }
 static inline int cdns_suspend(struct cdns *cdns)
 { return 0; }
index d00ff98dffabfa9cc1fdcde47212b9a0af843b7e..04b6d12f2b9a39b9bfad76fe1909b22f7c010990 100644 (file)
@@ -196,6 +196,7 @@ int cdns_drd_host_on(struct cdns *cdns)
        if (ret)
                dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
 
+       phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_HOST);
        phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST);
        return ret;
 }
@@ -216,6 +217,7 @@ void cdns_drd_host_off(struct cdns *cdns)
        readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
                                  !(val & OTGSTATE_HOST_STATE_MASK),
                                  1, 2000000);
+       phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID);
        phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
 }
 
@@ -248,6 +250,7 @@ int cdns_drd_gadget_on(struct cdns *cdns)
                return ret;
        }
 
+       phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_DEVICE);
        phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE);
        return 0;
 }
@@ -273,6 +276,7 @@ void cdns_drd_gadget_off(struct cdns *cdns)
        readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
                                  !(val & OTGSTATE_DEV_STATE_MASK),
                                  1, 2000000);
+       phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID);
        phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
 }
 EXPORT_SYMBOL_GPL(cdns_drd_gadget_off);
index f210b7489fd5b8ff825905da38c87674ce5a87a0..d9bb3d3f026e68cae40de5dee4fa9d81ed391f10 100644 (file)
@@ -257,6 +257,7 @@ struct ci_hdrc {
        bool                            id_event;
        bool                            b_sess_valid_event;
        bool                            imx28_write_fix;
+       bool                            has_portsc_pec_bug;
        bool                            supports_runtime_pm;
        bool                            in_lpm;
        bool                            wakeup_int;
@@ -281,8 +282,19 @@ static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role)
                return -ENXIO;
 
        ret = ci->roles[role]->start(ci);
-       if (!ret)
-               ci->role = role;
+       if (ret)
+               return ret;
+
+       ci->role = role;
+
+       if (ci->usb_phy) {
+               if (role == CI_ROLE_HOST)
+                       usb_phy_set_event(ci->usb_phy, USB_EVENT_ID);
+               else
+                       /* in device mode but vbus is invalid*/
+                       usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
+       }
+
        return ret;
 }
 
@@ -296,6 +308,9 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
        ci->role = CI_ROLE_END;
 
        ci->roles[role]->stop(ci);
+
+       if (ci->usb_phy)
+               usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
 }
 
 static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci)
index 336ef6dd8e7d8a0ede0a897d4de32d8d8395a937..e28bb2f2612dc60a1760a1aafe27dca9d2aeb26b 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
@@ -67,11 +68,13 @@ static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
 
 static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = {
        .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
+               CI_HDRC_HAS_PORTSC_PEC_MISSED |
                CI_HDRC_PMQOS,
 };
 
 static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = {
-       .flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
+       .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
+               CI_HDRC_HAS_PORTSC_PEC_MISSED,
 };
 
 static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
@@ -175,10 +178,15 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
        if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
                data->ulpi = 1;
 
-       of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control",
-                       &data->emp_curr_control);
-       of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust",
-                       &data->dc_vol_level_adjust);
+       if (of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control",
+                       &data->emp_curr_control))
+               data->emp_curr_control = -1;
+       if (of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust",
+                       &data->dc_vol_level_adjust))
+               data->dc_vol_level_adjust = -1;
+       if (of_property_read_u32(np, "fsl,picophy-rise-fall-time-adjust",
+                       &data->rise_fall_time_adjust))
+               data->rise_fall_time_adjust = -1;
 
        return data;
 }
index 7135b9a5d9138307d1936bf0079e65767a75e26a..88b8da79d51840622e42e6cc68b1b689ebf98c4b 100644 (file)
@@ -28,6 +28,7 @@ struct imx_usbmisc_data {
        enum usb_dr_mode available_role; /* runtime usb dr mode */
        int emp_curr_control;
        int dc_vol_level_adjust;
+       int rise_fall_time_adjust;
 };
 
 int imx_usbmisc_init(struct imx_usbmisc_data *data);
index ca36d11a69ea3f9c7e9a8ae3ed655769f4750fd1..8e78bf643e25c3c90fe853be7d3dd56867d12cee 100644 (file)
@@ -6,7 +6,8 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
index 51994d655b821b4121417b53a425fa023b3a7d40..7ac39a281b8cb58ba1c5fa085fdfb445606326f6 100644 (file)
@@ -1028,8 +1028,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       base = devm_ioremap_resource(dev, res);
+       base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(base))
                return PTR_ERR(base);
 
@@ -1045,6 +1044,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                CI_HDRC_IMX28_WRITE_FIX);
        ci->supports_runtime_pm = !!(ci->platdata->flags &
                CI_HDRC_SUPPORTS_RUNTIME_PM);
+       ci->has_portsc_pec_bug = !!(ci->platdata->flags &
+               CI_HDRC_HAS_PORTSC_PEC_MISSED);
        platform_set_drvdata(pdev, ci);
 
        ret = hw_device_init(ci, base);
index ebe7400243b127baf9c503b0060b80f9c33b6541..08af26b762a2d6c083c6a52af4dcd4e93285f5ae 100644 (file)
@@ -151,6 +151,7 @@ static int host_start(struct ci_hdrc *ci)
        ehci->has_hostpc = ci->hw_bank.lpm;
        ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
        ehci->imx28_write_fix = ci->imx28_write_fix;
+       ehci->has_ci_pec_bug = ci->has_portsc_pec_bug;
 
        priv = (struct ehci_ci_priv *)ehci->priv;
        priv->reg_vbus = NULL;
index 54c09245ad055dc8a66c8e6a83d6686b37d2ab54..0b7bd3c643c3aa4d40fcb5612ec1ea3595f025ea 100644 (file)
@@ -1463,7 +1463,7 @@ static int ep_disable(struct usb_ep *ep)
  */
 static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
 {
-       struct ci_hw_req *hwreq = NULL;
+       struct ci_hw_req *hwreq;
 
        if (ep == NULL)
                return NULL;
@@ -1718,6 +1718,13 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
                ret = ci->platdata->notify_event(ci,
                                CI_HDRC_CONTROLLER_VBUS_EVENT);
 
+       if (ci->usb_phy) {
+               if (is_active)
+                       usb_phy_set_event(ci->usb_phy, USB_EVENT_VBUS);
+               else
+                       usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
+       }
+
        if (ci->driver)
                ci_hdrc_gadget_connect(_gadget, is_active);
 
@@ -2034,6 +2041,9 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci)
                if (USBi_PCI & intr) {
                        ci->gadget.speed = hw_port_is_high_speed(ci) ?
                                USB_SPEED_HIGH : USB_SPEED_FULL;
+                       if (ci->usb_phy)
+                               usb_phy_set_event(ci->usb_phy,
+                                       USB_EVENT_ENUMERATED);
                        if (ci->suspended) {
                                if (ci->driver->resume) {
                                        spin_unlock(&ci->lock);
index 9ee9621e2ccca9766189f9d61633a8882de7efd1..173c78afd502274483a25475f3f8a9b8e2a99dd7 100644 (file)
@@ -4,10 +4,11 @@
  */
 
 #include <linux/module.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/delay.h>
+#include <linux/platform_device.h>
 #include <linux/usb/otg.h>
 
 #include "ci_hdrc_imx.h"
 #define MX7D_USB_OTG_PHY_CFG1          0x30
 #define TXPREEMPAMPTUNE0_BIT           28
 #define TXPREEMPAMPTUNE0_MASK          (3 << 28)
+#define TXRISETUNE0_BIT                        24
+#define TXRISETUNE0_MASK               (3 << 24)
 #define TXVREFTUNE0_BIT                        20
 #define TXVREFTUNE0_MASK               (0xf << 20)
 
@@ -659,18 +662,27 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
                        usbmisc->base + MX7D_USBNC_USB_CTRL2);
                /* PHY tuning for signal quality */
                reg = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
-               if (data->emp_curr_control && data->emp_curr_control <=
+               if (data->emp_curr_control >= 0 &&
+                       data->emp_curr_control <=
                        (TXPREEMPAMPTUNE0_MASK >> TXPREEMPAMPTUNE0_BIT)) {
                        reg &= ~TXPREEMPAMPTUNE0_MASK;
                        reg |= (data->emp_curr_control << TXPREEMPAMPTUNE0_BIT);
                }
 
-               if (data->dc_vol_level_adjust && data->dc_vol_level_adjust <=
+               if (data->dc_vol_level_adjust >= 0 &&
+                       data->dc_vol_level_adjust <=
                        (TXVREFTUNE0_MASK >> TXVREFTUNE0_BIT)) {
                        reg &= ~TXVREFTUNE0_MASK;
                        reg |= (data->dc_vol_level_adjust << TXVREFTUNE0_BIT);
                }
 
+               if (data->rise_fall_time_adjust >= 0 &&
+                       data->rise_fall_time_adjust <=
+                       (TXRISETUNE0_MASK >> TXRISETUNE0_BIT)) {
+                       reg &= ~TXRISETUNE0_MASK;
+                       reg |= (data->rise_fall_time_adjust << TXRISETUNE0_BIT);
+               }
+
                writel(reg, usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
        }
 
index 11da5fb284d0a10f1fa4be7e94cb9e0af688f0ff..a1933a86aee04dcbb1bed1b165054057291d26de 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/serial.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
+#include <linux/tty_ldisc.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/uaccess.h>
@@ -318,6 +319,16 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf)
                }
 
                difference = acm->ctrlin ^ newctrl;
+
+               if ((difference & USB_CDC_SERIAL_STATE_DCD) && acm->port.tty) {
+                       struct tty_ldisc *ld = tty_ldisc_ref(acm->port.tty);
+                       if (ld) {
+                               if (ld->ops->dcd_change)
+                                       ld->ops->dcd_change(acm->port.tty, newctrl & USB_CDC_SERIAL_STATE_DCD);
+                               tty_ldisc_deref(ld);
+                       }
+               }
+
                spin_lock_irqsave(&acm->read_lock, flags);
                acm->ctrlin = newctrl;
                acm->oldcount = acm->iocount;
@@ -853,6 +864,19 @@ static unsigned int acm_tty_write_room(struct tty_struct *tty)
        return acm_wb_is_avail(acm) ? acm->writesize : 0;
 }
 
+static void acm_tty_flush_buffer(struct tty_struct *tty)
+{
+       struct acm *acm = tty->driver_data;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&acm->write_lock, flags);
+       for (i = 0; i < ACM_NW; i++)
+               if (acm->wb[i].use)
+                       usb_unlink_urb(acm->wb[i].urb);
+       spin_unlock_irqrestore(&acm->write_lock, flags);
+}
+
 static unsigned int acm_tty_chars_in_buffer(struct tty_struct *tty)
 {
        struct acm *acm = tty->driver_data;
@@ -2016,6 +2040,7 @@ static const struct tty_operations acm_ops = {
        .hangup =               acm_tty_hangup,
        .write =                acm_tty_write,
        .write_room =           acm_tty_write_room,
+       .flush_buffer =         acm_tty_flush_buffer,
        .ioctl =                acm_tty_ioctl,
        .throttle =             acm_tty_throttle,
        .unthrottle =           acm_tty_unthrottle,
index c9bdeb4ddcb5741870ea2e1374e88a07077da74d..b84efae26e1543a4557ccb3be548e843b8f9928f 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/of.h>
 #include <linux/usb/otg.h>
index 725b8dbcfe5f069e19e8de28e0ffcd25c3a41ca7..b19e38d5fd10c1a4dfd84240df9e8203e4314f8f 100644 (file)
@@ -1051,9 +1051,6 @@ int usb_get_bos_descriptor(struct usb_device *dev)
                }
 
                switch (cap_type) {
-               case USB_CAP_TYPE_WIRELESS_USB:
-                       /* Wireless USB cap descriptor is handled by wusb */
-                       break;
                case USB_CAP_TYPE_EXT:
                        dev->bos->ext_cap =
                                (struct usb_ext_cap_descriptor *)buffer;
index 2c14a96360563704cdc54e3db7690b7e88eef950..a247da73f34d7d188b85c895be0ba619a2b4c37e 100644 (file)
@@ -424,7 +424,6 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
        case USB_SPEED_UNKNOWN:         /* usb 1.1 root hub code */
        case USB_SPEED_FULL:
                speed = "12"; break;
-       case USB_SPEED_WIRELESS:        /* Wireless has no real fixed speed */
        case USB_SPEED_HIGH:
                speed = "480"; break;
        case USB_SPEED_SUPER:
index c4ed3310e06965cb789e173b397143e8ec5af7c1..a88ced93b5e71b6826d53cf20c246d23f3f95359 100644 (file)
@@ -29,7 +29,6 @@
 #define MAX_USB_MINORS 256
 static const struct file_operations *usb_minors[MAX_USB_MINORS];
 static DECLARE_RWSEM(minor_rwsem);
-static DEFINE_MUTEX(init_usb_class_mutex);
 
 static int usb_open(struct inode *inode, struct file *file)
 {
@@ -57,11 +56,6 @@ static const struct file_operations usb_fops = {
        .llseek =       noop_llseek,
 };
 
-static struct usb_class {
-       struct kref kref;
-       struct class *class;
-} *usb_class;
-
 static char *usb_devnode(const struct device *dev, umode_t *mode)
 {
        struct usb_class_driver *drv;
@@ -72,50 +66,10 @@ static char *usb_devnode(const struct device *dev, umode_t *mode)
        return drv->devnode(dev, mode);
 }
 
-static int init_usb_class(void)
-{
-       int result = 0;
-
-       if (usb_class != NULL) {
-               kref_get(&usb_class->kref);
-               goto exit;
-       }
-
-       usb_class = kmalloc(sizeof(*usb_class), GFP_KERNEL);
-       if (!usb_class) {
-               result = -ENOMEM;
-               goto exit;
-       }
-
-       kref_init(&usb_class->kref);
-       usb_class->class = class_create("usbmisc");
-       if (IS_ERR(usb_class->class)) {
-               result = PTR_ERR(usb_class->class);
-               printk(KERN_ERR "class_create failed for usb devices\n");
-               kfree(usb_class);
-               usb_class = NULL;
-               goto exit;
-       }
-       usb_class->class->devnode = usb_devnode;
-
-exit:
-       return result;
-}
-
-static void release_usb_class(struct kref *kref)
-{
-       /* Ok, we cheat as we know we only have one usb_class */
-       class_destroy(usb_class->class);
-       kfree(usb_class);
-       usb_class = NULL;
-}
-
-static void destroy_usb_class(void)
-{
-       mutex_lock(&init_usb_class_mutex);
-       kref_put(&usb_class->kref, release_usb_class);
-       mutex_unlock(&init_usb_class_mutex);
-}
+const struct class usbmisc_class = {
+       .name           = "usbmisc",
+       .devnode        = usb_devnode,
+};
 
 int usb_major_init(void)
 {
@@ -156,7 +110,7 @@ void usb_major_cleanup(void)
 int usb_register_dev(struct usb_interface *intf,
                     struct usb_class_driver *class_driver)
 {
-       int retval;
+       int retval = 0;
        int minor_base = class_driver->minor_base;
        int minor;
        char name[20];
@@ -175,13 +129,6 @@ int usb_register_dev(struct usb_interface *intf,
        if (intf->minor >= 0)
                return -EADDRINUSE;
 
-       mutex_lock(&init_usb_class_mutex);
-       retval = init_usb_class();
-       mutex_unlock(&init_usb_class_mutex);
-
-       if (retval)
-               return retval;
-
        dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base);
 
        down_write(&minor_rwsem);
@@ -200,7 +147,7 @@ int usb_register_dev(struct usb_interface *intf,
 
        /* create a usb class device for this usb interface */
        snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
-       intf->usb_dev = device_create(usb_class->class, &intf->dev,
+       intf->usb_dev = device_create(&usbmisc_class, &intf->dev,
                                      MKDEV(USB_MAJOR, minor), class_driver,
                                      "%s", kbasename(name));
        if (IS_ERR(intf->usb_dev)) {
@@ -234,7 +181,7 @@ void usb_deregister_dev(struct usb_interface *intf,
                return;
 
        dev_dbg(&intf->dev, "removing %d minor\n", intf->minor);
-       device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
+       device_destroy(&usbmisc_class, MKDEV(USB_MAJOR, intf->minor));
 
        down_write(&minor_rwsem);
        usb_minors[intf->minor] = NULL;
@@ -242,6 +189,5 @@ void usb_deregister_dev(struct usb_interface *intf,
 
        intf->usb_dev = NULL;
        intf->minor = -1;
-       destroy_usb_class();
 }
 EXPORT_SYMBOL_GPL(usb_deregister_dev);
index 8300baedafd20e2d188ae0f7e537c14118b677d7..12b6dfeaf658c909aeef88699c42a40a7d15c38e 100644 (file)
@@ -156,27 +156,6 @@ static const u8 usb3_rh_dev_descriptor[18] = {
        0x01        /*  __u8  bNumConfigurations; */
 };
 
-/* usb 2.5 (wireless USB 1.0) root hub device descriptor */
-static const u8 usb25_rh_dev_descriptor[18] = {
-       0x12,       /*  __u8  bLength; */
-       USB_DT_DEVICE, /* __u8 bDescriptorType; Device */
-       0x50, 0x02, /*  __le16 bcdUSB; v2.5 */
-
-       0x09,       /*  __u8  bDeviceClass; HUB_CLASSCODE */
-       0x00,       /*  __u8  bDeviceSubClass; */
-       0x00,       /*  __u8  bDeviceProtocol; [ usb 2.0 no TT ] */
-       0xFF,       /*  __u8  bMaxPacketSize0; always 0xFF (WUSB Spec 7.4.1). */
-
-       0x6b, 0x1d, /*  __le16 idVendor; Linux Foundation 0x1d6b */
-       0x02, 0x00, /*  __le16 idProduct; device 0x0002 */
-       KERNEL_VER, KERNEL_REL, /*  __le16 bcdDevice */
-
-       0x03,       /*  __u8  iManufacturer; */
-       0x02,       /*  __u8  iProduct; */
-       0x01,       /*  __u8  iSerialNumber; */
-       0x01        /*  __u8  bNumConfigurations; */
-};
-
 /* usb 2.0 root hub device descriptor */
 static const u8 usb2_rh_dev_descriptor[18] = {
        0x12,       /*  __u8  bLength; */
@@ -368,7 +347,7 @@ static const u8 ss_rh_config_descriptor[] = {
 };
 
 /* authorized_default behaviour:
- * -1 is authorized for all devices except wireless (old behaviour)
+ * -1 is authorized for all devices (leftover from wireless USB)
  * 0 is unauthorized for all devices
  * 1 is authorized for all devices
  * 2 is authorized for internal devices
@@ -383,7 +362,7 @@ module_param(authorized_default, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(authorized_default,
                "Default USB device authorization: 0 is not authorized, 1 is "
                "authorized, 2 is authorized for internal devices, -1 is "
-               "authorized except for wireless USB (default, old behaviour)");
+               "authorized (default, same as 1)");
 /*-------------------------------------------------------------------------*/
 
 /**
@@ -578,9 +557,6 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
                        case HCD_USB3:
                                bufp = usb3_rh_dev_descriptor;
                                break;
-                       case HCD_USB25:
-                               bufp = usb25_rh_dev_descriptor;
-                               break;
                        case HCD_USB2:
                                bufp = usb2_rh_dev_descriptor;
                                break;
@@ -602,7 +578,6 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
                                bufp = ss_rh_config_descriptor;
                                len = sizeof ss_rh_config_descriptor;
                                break;
-                       case HCD_USB25:
                        case HCD_USB2:
                                bufp = hs_rh_config_descriptor;
                                len = sizeof hs_rh_config_descriptor;
@@ -983,6 +958,7 @@ static int register_root_hub(struct usb_hcd *hcd)
 {
        struct device *parent_dev = hcd->self.controller;
        struct usb_device *usb_dev = hcd->self.root_hub;
+       struct usb_device_descriptor *descr;
        const int devnum = 1;
        int retval;
 
@@ -994,13 +970,16 @@ static int register_root_hub(struct usb_hcd *hcd)
        mutex_lock(&usb_bus_idr_lock);
 
        usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
-       retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
-       if (retval != sizeof usb_dev->descriptor) {
+       descr = usb_get_device_descriptor(usb_dev);
+       if (IS_ERR(descr)) {
+               retval = PTR_ERR(descr);
                mutex_unlock(&usb_bus_idr_lock);
                dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
                                dev_name(&usb_dev->dev), retval);
-               return (retval < 0) ? retval : -EMSGSIZE;
+               return retval;
        }
+       usb_dev->descriptor = *descr;
+       kfree(descr);
 
        if (le16_to_cpu(usb_dev->descriptor.bcdUSB) >= 0x0201) {
                retval = usb_get_bos_descriptor(usb_dev);
@@ -2844,18 +2823,14 @@ int usb_add_hcd(struct usb_hcd *hcd,
                hcd->dev_policy = USB_DEVICE_AUTHORIZE_NONE;
                break;
 
-       case USB_AUTHORIZE_ALL:
-               hcd->dev_policy = USB_DEVICE_AUTHORIZE_ALL;
-               break;
-
        case USB_AUTHORIZE_INTERNAL:
                hcd->dev_policy = USB_DEVICE_AUTHORIZE_INTERNAL;
                break;
 
+       case USB_AUTHORIZE_ALL:
        case USB_AUTHORIZE_WIRED:
        default:
-               hcd->dev_policy = hcd->wireless ?
-                       USB_DEVICE_AUTHORIZE_NONE : USB_DEVICE_AUTHORIZE_ALL;
+               hcd->dev_policy = USB_DEVICE_AUTHORIZE_ALL;
                break;
        }
 
@@ -2899,9 +2874,6 @@ int usb_add_hcd(struct usb_hcd *hcd,
        case HCD_USB2:
                rhdev->speed = USB_SPEED_HIGH;
                break;
-       case HCD_USB25:
-               rhdev->speed = USB_SPEED_WIRELESS;
-               break;
        case HCD_USB3:
                rhdev->speed = USB_SPEED_SUPER;
                break;
index a739403a9e455a1f6ddf3f51fa9e96b4d9e67e5e..3c54b218301c1630b012f48f12d3c3e7f5f603aa 100644 (file)
@@ -614,6 +614,29 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
                ret = 0;
        }
        mutex_unlock(&hub->status_mutex);
+
+       /*
+        * There is no need to lock status_mutex here, because status_mutex
+        * protects hub->status, and the phy driver only checks the port
+        * status without changing the status.
+        */
+       if (!ret) {
+               struct usb_device *hdev = hub->hdev;
+
+               /*
+                * Only roothub will be notified of port state changes,
+                * since the USB PHY only cares about changes at the next
+                * level.
+                */
+               if (is_root_hub(hdev)) {
+                       struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+
+                       if (hcd->usb_phy)
+                               usb_phy_notify_port_status(hcd->usb_phy,
+                                                          port1 - 1, *status, *change);
+               }
+       }
+
        return ret;
 }
 
@@ -2117,22 +2140,6 @@ EXPORT_SYMBOL_GPL(usb_set_device_state);
  * USB-3.0 buses the address is assigned by the controller hardware
  * and it usually is not the same as the device number.
  *
- * WUSB devices are simple: they have no hubs behind, so the mapping
- * device <-> virtual port number becomes 1:1. Why? to simplify the
- * life of the device connection logic in
- * drivers/usb/wusbcore/devconnect.c. When we do the initial secret
- * handshake we need to assign a temporary address in the unauthorized
- * space. For simplicity we use the first virtual port number found to
- * be free [drivers/usb/wusbcore/devconnect.c:wusbhc_devconnect_ack()]
- * and that becomes it's address [X < 128] or its unauthorized address
- * [X | 0x80].
- *
- * We add 1 as an offset to the one-based USB-stack port number
- * (zero-based wusb virtual port index) for two reasons: (a) dev addr
- * 0 is reserved by USB for default address; (b) Linux's USB stack
- * uses always #1 for the root hub of the controller. So USB stack's
- * port #1, which is wusb virtual-port #0 has address #2.
- *
  * Devices connected under xHCI are not as simple.  The host controller
  * supports virtualization, so the hardware assigns device addresses and
  * the HCD must setup data structures before issuing a set address
@@ -2145,19 +2152,13 @@ static void choose_devnum(struct usb_device *udev)
 
        /* be safe when more hub events are proceed in parallel */
        mutex_lock(&bus->devnum_next_mutex);
-       if (udev->wusb) {
-               devnum = udev->portnum + 1;
-               BUG_ON(test_bit(devnum, bus->devmap.devicemap));
-       } else {
-               /* Try to allocate the next devnum beginning at
-                * bus->devnum_next. */
-               devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
-                                           bus->devnum_next);
-               if (devnum >= 128)
-                       devnum = find_next_zero_bit(bus->devmap.devicemap,
-                                                   128, 1);
-               bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
-       }
+
+       /* Try to allocate the next devnum beginning at bus->devnum_next. */
+       devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
+                       bus->devnum_next);
+       if (devnum >= 128)
+               devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
+       bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
        if (devnum < 128) {
                set_bit(devnum, bus->devmap.devicemap);
                udev->devnum = devnum;
@@ -2175,9 +2176,7 @@ static void release_devnum(struct usb_device *udev)
 
 static void update_devnum(struct usb_device *udev, int devnum)
 {
-       /* The address for a WUSB device is managed by wusbcore. */
-       if (!udev->wusb)
-               udev->devnum = devnum;
+       udev->devnum = devnum;
        if (!udev->devaddr)
                udev->devaddr = (u8)devnum;
 }
@@ -2670,15 +2669,6 @@ int usb_authorize_device(struct usb_device *usb_dev)
                goto error_autoresume;
        }
 
-       if (usb_dev->wusb) {
-               result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));
-               if (result < 0) {
-                       dev_err(&usb_dev->dev, "can't re-read device descriptor for "
-                               "authorization: %d\n", result);
-                       goto error_device_descriptor;
-               }
-       }
-
        usb_dev->authorized = 1;
        /* Choose and set the configuration.  This registers the interfaces
         * with the driver core and lets interface drivers bind to them.
@@ -2695,7 +2685,6 @@ int usb_authorize_device(struct usb_device *usb_dev)
        }
        dev_info(&usb_dev->dev, "authorized to connect\n");
 
-error_device_descriptor:
        usb_autosuspend_device(usb_dev);
 error_autoresume:
 out_authorized:
@@ -2778,17 +2767,6 @@ out:
        return USB_SSP_GEN_UNKNOWN;
 }
 
-/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */
-static unsigned hub_is_wusb(struct usb_hub *hub)
-{
-       struct usb_hcd *hcd;
-       if (hub->hdev->parent != NULL)  /* not a root hub? */
-               return 0;
-       hcd = bus_to_hcd(hub->hdev->bus);
-       return hcd->wireless;
-}
-
-
 #ifdef CONFIG_USB_FEW_INIT_RETRIES
 #define PORT_RESET_TRIES       2
 #define SET_ADDRESS_TRIES      1
@@ -2941,9 +2919,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                udev->tx_lanes = 1;
                udev->ssp_rate = USB_SSP_GEN_UNKNOWN;
        }
-       if (hub_is_wusb(hub))
-               udev->speed = USB_SPEED_WIRELESS;
-       else if (udev->ssp_rate != USB_SSP_GEN_UNKNOWN)
+       if (udev->ssp_rate != USB_SSP_GEN_UNKNOWN)
                udev->speed = USB_SPEED_SUPER_PLUS;
        else if (hub_is_superspeed(hub->hdev))
                udev->speed = USB_SPEED_SUPER;
@@ -4718,6 +4694,67 @@ static int hub_enable_device(struct usb_device *udev)
        return hcd->driver->enable_device(hcd, udev);
 }
 
+/*
+ * Get the bMaxPacketSize0 value during initialization by reading the
+ * device's device descriptor.  Since we don't already know this value,
+ * the transfer is unsafe and it ignores I/O errors, only testing for
+ * reasonable received values.
+ *
+ * For "old scheme" initialization, size will be 8 so we read just the
+ * start of the device descriptor, which should work okay regardless of
+ * the actual bMaxPacketSize0 value.  For "new scheme" initialization,
+ * size will be 64 (and buf will point to a sufficiently large buffer),
+ * which might not be kosher according to the USB spec but it's what
+ * Windows does and what many devices expect.
+ *
+ * Returns: bMaxPacketSize0 or a negative error code.
+ */
+static int get_bMaxPacketSize0(struct usb_device *udev,
+               struct usb_device_descriptor *buf, int size, bool first_time)
+{
+       int i, rc;
+
+       /*
+        * Retry on all errors; some devices are flakey.
+        * 255 is for WUSB devices, we actually need to use
+        * 512 (WUSB1.0[4.8.1]).
+        */
+       for (i = 0; i < GET_MAXPACKET0_TRIES; ++i) {
+               /* Start with invalid values in case the transfer fails */
+               buf->bDescriptorType = buf->bMaxPacketSize0 = 0;
+               rc = usb_control_msg(udev, usb_rcvaddr0pipe(),
+                               USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+                               USB_DT_DEVICE << 8, 0,
+                               buf, size,
+                               initial_descriptor_timeout);
+               switch (buf->bMaxPacketSize0) {
+               case 8: case 16: case 32: case 64: case 9:
+                       if (buf->bDescriptorType == USB_DT_DEVICE) {
+                               rc = buf->bMaxPacketSize0;
+                               break;
+                       }
+                       fallthrough;
+               default:
+                       if (rc >= 0)
+                               rc = -EPROTO;
+                       break;
+               }
+
+               /*
+                * Some devices time out if they are powered on
+                * when already connected. They need a second
+                * reset, so return early. But only on the first
+                * attempt, lest we get into a time-out/reset loop.
+                */
+               if (rc > 0 || (rc == -ETIMEDOUT && first_time &&
+                               udev->speed > USB_SPEED_FULL))
+                       break;
+       }
+       return rc;
+}
+
+#define GET_DESCRIPTOR_BUFSIZE 64
+
 /* Reset device, (re)assign address, get device descriptor.
  * Device connection must be stable, no more debouncing needed.
  * Returns device in USB_STATE_ADDRESS, except on error.
@@ -4727,10 +4764,17 @@ static int hub_enable_device(struct usb_device *udev)
  * the port lock.  For a newly detected device that is not accessible
  * through any global pointers, it's not necessary to lock the device,
  * but it is still necessary to lock the port.
+ *
+ * For a newly detected device, @dev_descr must be NULL.  The device
+ * descriptor retrieved from the device will then be stored in
+ * @udev->descriptor.  For an already existing device, @dev_descr
+ * must be non-NULL.  The device descriptor will be stored there,
+ * not in @udev->descriptor, because descriptors for registered
+ * devices are meant to be immutable.
  */
 static int
 hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
-               int retry_counter)
+               int retry_counter, struct usb_device_descriptor *dev_descr)
 {
        struct usb_device       *hdev = hub->hdev;
        struct usb_hcd          *hcd = bus_to_hcd(hdev->bus);
@@ -4742,6 +4786,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
        int                     devnum = udev->devnum;
        const char              *driver_name;
        bool                    do_new_scheme;
+       const bool              initial = !dev_descr;
+       int                     maxp0;
+       struct usb_device_descriptor    *buf, *descr;
+
+       buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
+       if (!buf)
+               return -ENOMEM;
 
        /* root hub ports have a slightly longer reset period
         * (from USB 2.0 spec, section 7.1.7.5)
@@ -4774,38 +4825,34 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
        }
        oldspeed = udev->speed;
 
-       /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
-        * it's fixed size except for full speed devices.
-        * For Wireless USB devices, ep0 max packet is always 512 (tho
-        * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
-        */
-       switch (udev->speed) {
-       case USB_SPEED_SUPER_PLUS:
-       case USB_SPEED_SUPER:
-       case USB_SPEED_WIRELESS:        /* fixed at 512 */
-               udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
-               break;
-       case USB_SPEED_HIGH:            /* fixed at 64 */
-               udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
-               break;
-       case USB_SPEED_FULL:            /* 8, 16, 32, or 64 */
-               /* to determine the ep0 maxpacket size, try to read
-                * the device descriptor to get bMaxPacketSize0 and
-                * then correct our initial guess.
+       if (initial) {
+               /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
+                * it's fixed size except for full speed devices.
                 */
-               udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
-               break;
-       case USB_SPEED_LOW:             /* fixed at 8 */
-               udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
-               break;
-       default:
-               goto fail;
+               switch (udev->speed) {
+               case USB_SPEED_SUPER_PLUS:
+               case USB_SPEED_SUPER:
+                       udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
+                       break;
+               case USB_SPEED_HIGH:            /* fixed at 64 */
+                       udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
+                       break;
+               case USB_SPEED_FULL:            /* 8, 16, 32, or 64 */
+                       /* to determine the ep0 maxpacket size, try to read
+                        * the device descriptor to get bMaxPacketSize0 and
+                        * then correct our initial guess.
+                        */
+                       udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
+                       break;
+               case USB_SPEED_LOW:             /* fixed at 8 */
+                       udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
+                       break;
+               default:
+                       goto fail;
+               }
        }
 
-       if (udev->speed == USB_SPEED_WIRELESS)
-               speed = "variable speed Wireless";
-       else
-               speed = usb_speed_string(udev->speed);
+       speed = usb_speed_string(udev->speed);
 
        /*
         * The controller driver may be NULL if the controller device
@@ -4822,22 +4869,24 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
        if (udev->speed < USB_SPEED_SUPER)
                dev_info(&udev->dev,
                                "%s %s USB device number %d using %s\n",
-                               (udev->config) ? "reset" : "new", speed,
+                               (initial ? "new" : "reset"), speed,
                                devnum, driver_name);
 
-       /* Set up TT records, if needed  */
-       if (hdev->tt) {
-               udev->tt = hdev->tt;
-               udev->ttport = hdev->ttport;
-       } else if (udev->speed != USB_SPEED_HIGH
-                       && hdev->speed == USB_SPEED_HIGH) {
-               if (!hub->tt.hub) {
-                       dev_err(&udev->dev, "parent hub has no TT\n");
-                       retval = -EINVAL;
-                       goto fail;
+       if (initial) {
+               /* Set up TT records, if needed  */
+               if (hdev->tt) {
+                       udev->tt = hdev->tt;
+                       udev->ttport = hdev->ttport;
+               } else if (udev->speed != USB_SPEED_HIGH
+                               && hdev->speed == USB_SPEED_HIGH) {
+                       if (!hub->tt.hub) {
+                               dev_err(&udev->dev, "parent hub has no TT\n");
+                               retval = -EINVAL;
+                               goto fail;
+                       }
+                       udev->tt = &hub->tt;
+                       udev->ttport = port1;
                }
-               udev->tt = &hub->tt;
-               udev->ttport = port1;
        }
 
        /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
@@ -4861,9 +4910,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
                }
 
                if (do_new_scheme) {
-                       struct usb_device_descriptor *buf;
-                       int r = 0;
-
                        retval = hub_enable_device(udev);
                        if (retval < 0) {
                                dev_err(&udev->dev,
@@ -4872,52 +4918,14 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
                                goto fail;
                        }
 
-#define GET_DESCRIPTOR_BUFSIZE 64
-                       buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
-                       if (!buf) {
-                               retval = -ENOMEM;
-                               continue;
-                       }
-
-                       /* Retry on all errors; some devices are flakey.
-                        * 255 is for WUSB devices, we actually need to use
-                        * 512 (WUSB1.0[4.8.1]).
-                        */
-                       for (operations = 0; operations < GET_MAXPACKET0_TRIES;
-                                       ++operations) {
-                               buf->bMaxPacketSize0 = 0;
-                               r = usb_control_msg(udev, usb_rcvaddr0pipe(),
-                                       USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
-                                       USB_DT_DEVICE << 8, 0,
-                                       buf, GET_DESCRIPTOR_BUFSIZE,
-                                       initial_descriptor_timeout);
-                               switch (buf->bMaxPacketSize0) {
-                               case 8: case 16: case 32: case 64: case 255:
-                                       if (buf->bDescriptorType ==
-                                                       USB_DT_DEVICE) {
-                                               r = 0;
-                                               break;
-                                       }
-                                       fallthrough;
-                               default:
-                                       if (r == 0)
-                                               r = -EPROTO;
-                                       break;
-                               }
-                               /*
-                                * Some devices time out if they are powered on
-                                * when already connected. They need a second
-                                * reset. But only on the first attempt,
-                                * lest we get into a time out/reset loop
-                                */
-                               if (r == 0 || (r == -ETIMEDOUT &&
-                                               retries == 0 &&
-                                               udev->speed > USB_SPEED_FULL))
-                                       break;
+                       maxp0 = get_bMaxPacketSize0(udev, buf,
+                                       GET_DESCRIPTOR_BUFSIZE, retries == 0);
+                       if (maxp0 > 0 && !initial &&
+                                       maxp0 != udev->descriptor.bMaxPacketSize0) {
+                               dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
+                               retval = -ENODEV;
+                               goto fail;
                        }
-                       udev->descriptor.bMaxPacketSize0 =
-                                       buf->bMaxPacketSize0;
-                       kfree(buf);
 
                        retval = hub_port_reset(hub, port1, udev, delay, false);
                        if (retval < 0)         /* error or disconnect */
@@ -4928,71 +4936,68 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
                                retval = -ENODEV;
                                goto fail;
                        }
-                       if (r) {
-                               if (r != -ENODEV)
+                       if (maxp0 < 0) {
+                               if (maxp0 != -ENODEV)
                                        dev_err(&udev->dev, "device descriptor read/64, error %d\n",
-                                                       r);
-                               retval = -EMSGSIZE;
+                                                       maxp0);
+                               retval = maxp0;
                                continue;
                        }
-#undef GET_DESCRIPTOR_BUFSIZE
+               }
+
+               for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
+                       retval = hub_set_address(udev, devnum);
+                       if (retval >= 0)
+                               break;
+                       msleep(200);
+               }
+               if (retval < 0) {
+                       if (retval != -ENODEV)
+                               dev_err(&udev->dev, "device not accepting address %d, error %d\n",
+                                               devnum, retval);
+                       goto fail;
+               }
+               if (udev->speed >= USB_SPEED_SUPER) {
+                       devnum = udev->devnum;
+                       dev_info(&udev->dev,
+                                       "%s SuperSpeed%s%s USB device number %d using %s\n",
+                                       (udev->config) ? "reset" : "new",
+                                (udev->speed == USB_SPEED_SUPER_PLUS) ?
+                                               " Plus" : "",
+                                (udev->ssp_rate == USB_SSP_GEN_2x2) ?
+                                               " Gen 2x2" :
+                                (udev->ssp_rate == USB_SSP_GEN_2x1) ?
+                                               " Gen 2x1" :
+                                (udev->ssp_rate == USB_SSP_GEN_1x2) ?
+                                               " Gen 1x2" : "",
+                                devnum, driver_name);
                }
 
                /*
-                * If device is WUSB, we already assigned an
-                * unauthorized address in the Connect Ack sequence;
-                * authorization will assign the final address.
+                * cope with hardware quirkiness:
+                *  - let SET_ADDRESS settle, some device hardware wants it
+                *  - read ep0 maxpacket even for high and low speed,
                 */
-               if (udev->wusb == 0) {
-                       for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
-                               retval = hub_set_address(udev, devnum);
-                               if (retval >= 0)
-                                       break;
-                               msleep(200);
-                       }
-                       if (retval < 0) {
-                               if (retval != -ENODEV)
-                                       dev_err(&udev->dev, "device not accepting address %d, error %d\n",
-                                                       devnum, retval);
-                               goto fail;
-                       }
-                       if (udev->speed >= USB_SPEED_SUPER) {
-                               devnum = udev->devnum;
-                               dev_info(&udev->dev,
-                                               "%s SuperSpeed%s%s USB device number %d using %s\n",
-                                               (udev->config) ? "reset" : "new",
-                                        (udev->speed == USB_SPEED_SUPER_PLUS) ?
-                                                       " Plus" : "",
-                                        (udev->ssp_rate == USB_SSP_GEN_2x2) ?
-                                                       " Gen 2x2" :
-                                        (udev->ssp_rate == USB_SSP_GEN_2x1) ?
-                                                       " Gen 2x1" :
-                                        (udev->ssp_rate == USB_SSP_GEN_1x2) ?
-                                                       " Gen 1x2" : "",
-                                        devnum, driver_name);
-                       }
+               msleep(10);
 
-                       /* cope with hardware quirkiness:
-                        *  - let SET_ADDRESS settle, some device hardware wants it
-                        *  - read ep0 maxpacket even for high and low speed,
-                        */
-                       msleep(10);
-                       if (do_new_scheme)
-                               break;
-               }
+               if (do_new_scheme)
+                       break;
 
-               retval = usb_get_device_descriptor(udev, 8);
-               if (retval < 8) {
+               maxp0 = get_bMaxPacketSize0(udev, buf, 8, retries == 0);
+               if (maxp0 < 0) {
+                       retval = maxp0;
                        if (retval != -ENODEV)
                                dev_err(&udev->dev,
                                        "device descriptor read/8, error %d\n",
                                        retval);
-                       if (retval >= 0)
-                               retval = -EMSGSIZE;
                } else {
                        u32 delay;
 
-                       retval = 0;
+                       if (!initial && maxp0 != udev->descriptor.bMaxPacketSize0) {
+                               dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
+                               retval = -ENODEV;
+                               goto fail;
+                       }
 
                        delay = udev->parent->hub_delay;
                        udev->hub_delay = min_t(u32, delay,
@@ -5011,54 +5016,67 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
                goto fail;
 
        /*
-        * Some superspeed devices have finished the link training process
-        * and attached to a superspeed hub port, but the device descriptor
-        * got from those devices show they aren't superspeed devices. Warm
-        * reset the port attached by the devices can fix them.
+        * Check the ep0 maxpacket guess and correct it if necessary.
+        * maxp0 is the value stored in the device descriptor;
+        * i is the value it encodes (logarithmic for SuperSpeed or greater).
         */
-       if ((udev->speed >= USB_SPEED_SUPER) &&
-                       (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
-               dev_err(&udev->dev, "got a wrong device descriptor, "
-                               "warm reset device\n");
-               hub_port_reset(hub, port1, udev,
-                               HUB_BH_RESET_TIME, true);
-               retval = -EINVAL;
-               goto fail;
-       }
-
-       if (udev->descriptor.bMaxPacketSize0 == 0xff ||
-                       udev->speed >= USB_SPEED_SUPER)
-               i = 512;
-       else
-               i = udev->descriptor.bMaxPacketSize0;
-       if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
-               if (udev->speed == USB_SPEED_LOW ||
-                               !(i == 8 || i == 16 || i == 32 || i == 64)) {
-                       dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
-                       retval = -EMSGSIZE;
-                       goto fail;
-               }
+       i = maxp0;
+       if (udev->speed >= USB_SPEED_SUPER) {
+               if (maxp0 <= 16)
+                       i = 1 << maxp0;
+               else
+                       i = 0;          /* Invalid */
+       }
+       if (usb_endpoint_maxp(&udev->ep0.desc) == i) {
+               ;       /* Initial ep0 maxpacket guess is right */
+       } else if ((udev->speed == USB_SPEED_FULL ||
+                               udev->speed == USB_SPEED_HIGH) &&
+                       (i == 8 || i == 16 || i == 32 || i == 64)) {
+               /* Initial guess is wrong; use the descriptor's value */
                if (udev->speed == USB_SPEED_FULL)
                        dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
                else
                        dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
                udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
                usb_ep0_reinit(udev);
+       } else {
+               /* Initial guess is wrong and descriptor's value is invalid */
+               dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", maxp0);
+               retval = -EMSGSIZE;
+               goto fail;
        }
 
-       retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
-       if (retval < (signed)sizeof(udev->descriptor)) {
+       descr = usb_get_device_descriptor(udev);
+       if (IS_ERR(descr)) {
+               retval = PTR_ERR(descr);
                if (retval != -ENODEV)
                        dev_err(&udev->dev, "device descriptor read/all, error %d\n",
                                        retval);
-               if (retval >= 0)
-                       retval = -ENOMSG;
+               goto fail;
+       }
+       if (initial)
+               udev->descriptor = *descr;
+       else
+               *dev_descr = *descr;
+       kfree(descr);
+
+       /*
+        * Some superspeed devices have finished the link training process
+        * and attached to a superspeed hub port, but the device descriptor
+        * got from those devices show they aren't superspeed devices. Warm
+        * reset the port attached by the devices can fix them.
+        */
+       if ((udev->speed >= USB_SPEED_SUPER) &&
+                       (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
+               dev_err(&udev->dev, "got a wrong device descriptor, warm reset device\n");
+               hub_port_reset(hub, port1, udev, HUB_BH_RESET_TIME, true);
+               retval = -EINVAL;
                goto fail;
        }
 
        usb_detect_quirks(udev);
 
-       if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
+       if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
                retval = usb_get_bos_descriptor(udev);
                if (!retval) {
                        udev->lpm_capable = usb_device_supports_lpm(udev);
@@ -5078,6 +5096,7 @@ fail:
                hub_port_disable(hub, port1, 0);
                update_devnum(udev, devnum);    /* for disconnect processing */
        }
+       kfree(buf);
        return retval;
 }
 
@@ -5158,7 +5177,7 @@ hub_power_remaining(struct usb_hub *hub)
 
 
 static int descriptors_changed(struct usb_device *udev,
-               struct usb_device_descriptor *old_device_descriptor,
+               struct usb_device_descriptor *new_device_descriptor,
                struct usb_host_bos *old_bos)
 {
        int             changed = 0;
@@ -5169,8 +5188,8 @@ static int descriptors_changed(struct usb_device *udev,
        int             length;
        char            *buf;
 
-       if (memcmp(&udev->descriptor, old_device_descriptor,
-                       sizeof(*old_device_descriptor)) != 0)
+       if (memcmp(&udev->descriptor, new_device_descriptor,
+                       sizeof(*new_device_descriptor)) != 0)
                return 1;
 
        if ((old_bos && !udev->bos) || (!old_bos && udev->bos))
@@ -5333,7 +5352,6 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
                usb_set_device_state(udev, USB_STATE_POWERED);
                udev->bus_mA = hub->mA_per_port;
                udev->level = hdev->level + 1;
-               udev->wusb = hub_is_wusb(hub);
 
                /* Devices connected to SuperSpeed hubs are USB 3.0 or later */
                if (hub_is_superspeed(hub->hdev))
@@ -5348,7 +5366,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
                }
 
                /* reset (non-USB 3.0 devices) and get descriptor */
-               status = hub_port_init(hub, udev, port1, i);
+               status = hub_port_init(hub, udev, port1, i, NULL);
                if (status < 0)
                        goto loop;
 
@@ -5495,9 +5513,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 {
        struct usb_port *port_dev = hub->ports[port1 - 1];
        struct usb_device *udev = port_dev->child;
-       struct usb_device_descriptor descriptor;
+       struct usb_device_descriptor *descr;
        int status = -ENODEV;
-       int retval;
 
        dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus,
                        portchange, portspeed(hub, portstatus));
@@ -5524,23 +5541,20 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                         * changed device descriptors before resuscitating the
                         * device.
                         */
-                       descriptor = udev->descriptor;
-                       retval = usb_get_device_descriptor(udev,
-                                       sizeof(udev->descriptor));
-                       if (retval < 0) {
+                       descr = usb_get_device_descriptor(udev);
+                       if (IS_ERR(descr)) {
                                dev_dbg(&udev->dev,
-                                               "can't read device descriptor %d\n",
-                                               retval);
+                                               "can't read device descriptor %ld\n",
+                                               PTR_ERR(descr));
                        } else {
-                               if (descriptors_changed(udev, &descriptor,
+                               if (descriptors_changed(udev, descr,
                                                udev->bos)) {
                                        dev_dbg(&udev->dev,
                                                        "device descriptor has changed\n");
-                                       /* for disconnect() calls */
-                                       udev->descriptor = descriptor;
                                } else {
                                        status = 0; /* Nothing to do */
                                }
+                               kfree(descr);
                        }
 #ifdef CONFIG_PM
                } else if (udev->state == USB_STATE_SUSPENDED &&
@@ -5982,7 +5996,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        struct usb_device               *parent_hdev = udev->parent;
        struct usb_hub                  *parent_hub;
        struct usb_hcd                  *hcd = bus_to_hcd(udev->bus);
-       struct usb_device_descriptor    descriptor = udev->descriptor;
+       struct usb_device_descriptor    descriptor;
        struct usb_host_bos             *bos;
        int                             i, j, ret = 0;
        int                             port1 = udev->portnum;
@@ -6018,7 +6032,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                /* ep0 maxpacket size may change; let the HCD know about it.
                 * Other endpoints will be handled by re-enumeration. */
                usb_ep0_reinit(udev);
-               ret = hub_port_init(parent_hub, udev, port1, i);
+               ret = hub_port_init(parent_hub, udev, port1, i, &descriptor);
                if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
                        break;
        }
@@ -6030,7 +6044,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        /* Device might have changed firmware (DFU or similar) */
        if (descriptors_changed(udev, &descriptor, bos)) {
                dev_info(&udev->dev, "device firmware changed\n");
-               udev->descriptor = descriptor;  /* for disconnect() calls */
                goto re_enumerate;
        }
 
index ba371a24ff781b45848e27f2b89b53938adde58d..85c999f71ad7c2caae981c1572b95e1d26a44545 100644 (file)
@@ -350,18 +350,7 @@ static struct led_trigger usbport_led_trigger = {
        .deactivate = usbport_trig_deactivate,
 };
 
-static int __init usbport_trig_init(void)
-{
-       return led_trigger_register(&usbport_led_trigger);
-}
-
-static void __exit usbport_trig_exit(void)
-{
-       led_trigger_unregister(&usbport_led_trigger);
-}
-
-module_init(usbport_trig_init);
-module_exit(usbport_trig_exit);
+module_led_trigger(usbport_led_trigger);
 
 MODULE_AUTHOR("RafaÅ‚ MiÅ‚ecki <rafal@milecki.pl>");
 MODULE_DESCRIPTION("USB port trigger");
index b5811620f1de174bbebcef8cd39a9f8b5f2c7243..077dfe48d01c1cb2ab807197450daae79d56c59a 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/pci.h> /* for scatterlist macros */
 #include <linux/usb.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/timer.h>
@@ -1040,40 +1041,35 @@ char *usb_cache_string(struct usb_device *udev, int index)
 EXPORT_SYMBOL_GPL(usb_cache_string);
 
 /*
- * usb_get_device_descriptor - (re)reads the device descriptor (usbcore)
- * @dev: the device whose device descriptor is being updated
- * @size: how much of the descriptor to read
+ * usb_get_device_descriptor - read the device descriptor
+ * @udev: the device whose device descriptor should be read
  *
  * Context: task context, might sleep.
  *
- * Updates the copy of the device descriptor stored in the device structure,
- * which dedicates space for this purpose.
- *
  * Not exported, only for use by the core.  If drivers really want to read
  * the device descriptor directly, they can call usb_get_descriptor() with
  * type = USB_DT_DEVICE and index = 0.
  *
- * This call is synchronous, and may not be used in an interrupt context.
- *
- * Return: The number of bytes received on success, or else the status code
- * returned by the underlying usb_control_msg() call.
+ * Returns: a pointer to a dynamically allocated usb_device_descriptor
+ * structure (which the caller must deallocate), or an ERR_PTR value.
  */
-int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
+struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev)
 {
        struct usb_device_descriptor *desc;
        int ret;
 
-       if (size > sizeof(*desc))
-               return -EINVAL;
        desc = kmalloc(sizeof(*desc), GFP_NOIO);
        if (!desc)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
+
+       ret = usb_get_descriptor(udev, USB_DT_DEVICE, 0, desc, sizeof(*desc));
+       if (ret == sizeof(*desc))
+               return desc;
 
-       ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
        if (ret >= 0)
-               memcpy(&dev->descriptor, desc, size);
+               ret = -EMSGSIZE;
        kfree(desc);
-       return ret;
+       return ERR_PTR(ret);
 }
 
 /*
index 617e92569b2c7c39d24f928e3e2e00721dc43d7b..db4ccf9ce3d9b46b518f050804831ab706adf35c 100644 (file)
@@ -8,7 +8,6 @@
  */
 
 #include <linux/of.h>
-#include <linux/of_platform.h>
 #include <linux/usb/of.h>
 
 /**
index 323dc02becbe25cdb1d3887357911488e5d11c16..5d21718afb05cff29a985dcd1678849dc706dfe6 100644 (file)
@@ -161,9 +161,6 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
        case USB_SPEED_HIGH:
                speed = "480";
                break;
-       case USB_SPEED_WIRELESS:
-               speed = "480";
-               break;
        case USB_SPEED_SUPER:
                speed = "5000";
                break;
index 9f3c54032556edadf5cda078a2c93c584cf37de9..7576920e2d5a3e6c0dfd8bee8fce9d09a55c195c 100644 (file)
@@ -480,8 +480,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                        urb->iso_frame_desc[n].status = -EXDEV;
                        urb->iso_frame_desc[n].actual_length = 0;
                }
-       } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint &&
-                       dev->speed != USB_SPEED_WIRELESS) {
+       } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint) {
                struct scatterlist *sg;
                int i;
 
@@ -540,17 +539,9 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
        case USB_ENDPOINT_XFER_ISOC:
        case USB_ENDPOINT_XFER_INT:
                /* too small? */
-               switch (dev->speed) {
-               case USB_SPEED_WIRELESS:
-                       if ((urb->interval < 6)
-                               && (xfertype == USB_ENDPOINT_XFER_INT))
-                               return -EINVAL;
-                       fallthrough;
-               default:
-                       if (urb->interval <= 0)
-                               return -EINVAL;
-                       break;
-               }
+               if (urb->interval <= 0)
+                       return -EINVAL;
+
                /* too big? */
                switch (dev->speed) {
                case USB_SPEED_SUPER_PLUS:
@@ -560,10 +551,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                                return -EINVAL;
                        max = 1 << 15;
                        break;
-               case USB_SPEED_WIRELESS:
-                       if (urb->interval > 16)
-                               return -EINVAL;
-                       break;
                case USB_SPEED_HIGH:    /* units are microframes */
                        /* NOTE usb handles 2^15 */
                        if (urb->interval > (1024 * 8))
@@ -587,10 +574,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                default:
                        return -EINVAL;
                }
-               if (dev->speed != USB_SPEED_WIRELESS) {
-                       /* Round down to a power of 2, no more than max */
-                       urb->interval = min(max, 1 << ilog2(urb->interval));
-               }
+               /* Round down to a power of 2, no more than max */
+               urb->interval = min(max, 1 << ilog2(urb->interval));
        }
 
        return usb_hcd_submit_urb(urb, mem_flags);
index 901ec732321c5eccd3133b9c5ee55579110a85e4..2a938cf47ccd6239dd0f1a9d8eed95618de75d64 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/of.h>
 #include <linux/string.h>
 #include <linux/bitops.h>
 #include <linux/slab.h>
@@ -601,14 +602,6 @@ struct device_type usb_device_type = {
 #endif
 };
 
-
-/* Returns 1 if @usb_bus is WUSB, 0 otherwise */
-static unsigned usb_bus_is_wusb(struct usb_bus *bus)
-{
-       struct usb_hcd *hcd = bus_to_hcd(bus);
-       return hcd->wireless;
-}
-
 static bool usb_dev_authorized(struct usb_device *dev, struct usb_hcd *hcd)
 {
        struct usb_hub *hub;
@@ -652,7 +645,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
 {
        struct usb_device *dev;
        struct usb_hcd *usb_hcd = bus_to_hcd(bus);
-       unsigned root_hub = 0;
        unsigned raw_port = port1;
 
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -702,7 +694,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
                dev->dev.parent = bus->controller;
                device_set_of_node_from_dev(&dev->dev, bus->sysdev);
                dev_set_name(&dev->dev, "usb%d", bus->busnum);
-               root_hub = 1;
        } else {
                /* match any labeling on the hubs; it's one-based */
                if (parent->devpath[0] == '0') {
@@ -748,9 +739,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
 #endif
 
        dev->authorized = usb_dev_authorized(dev, usb_hcd);
-       if (!root_hub)
-               dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0;
-
        return dev;
 }
 EXPORT_SYMBOL_GPL(usb_alloc_dev);
@@ -1101,6 +1089,9 @@ static int __init usb_init(void)
        retval = usb_major_init();
        if (retval)
                goto major_init_failed;
+       retval = class_register(&usbmisc_class);
+       if (retval)
+               goto class_register_failed;
        retval = usb_register(&usbfs_driver);
        if (retval)
                goto driver_register_failed;
@@ -1120,6 +1111,8 @@ hub_init_failed:
 usb_devio_init_failed:
        usb_deregister(&usbfs_driver);
 driver_register_failed:
+       class_unregister(&usbmisc_class);
+class_register_failed:
        usb_major_cleanup();
 major_init_failed:
        bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
@@ -1147,6 +1140,7 @@ static void __exit usb_exit(void)
        usb_deregister(&usbfs_driver);
        usb_devio_cleanup();
        usb_hub_cleanup();
+       class_unregister(&usbmisc_class);
        bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
        bus_unregister(&usb_bus_type);
        usb_acpi_unregister();
index ffe3f6818e9cfa3b1202d42c2074fa2d9cf564e0..60363153fc3f38aed322182bc46357ca5ca6ec74 100644 (file)
@@ -43,8 +43,8 @@ extern bool usb_endpoint_is_ignored(struct usb_device *udev,
                struct usb_endpoint_descriptor *epd);
 extern int usb_remove_device(struct usb_device *udev);
 
-extern int usb_get_device_descriptor(struct usb_device *dev,
-               unsigned int size);
+extern struct usb_device_descriptor *usb_get_device_descriptor(
+               struct usb_device *udev);
 extern int usb_set_isoch_delay(struct usb_device *dev);
 extern int usb_get_bos_descriptor(struct usb_device *dev);
 extern void usb_release_bos_descriptor(struct usb_device *dev);
@@ -141,6 +141,7 @@ static inline int usb_disable_usb2_hardware_lpm(struct usb_device *udev)
 
 #endif
 
+extern const struct class usbmisc_class;
 extern const struct bus_type usb_bus_type;
 extern struct mutex usb_port_peer_mutex;
 extern struct device_type usb_device_type;
index 0bb4c0c845bfa6d5ddde3228da328062bd4dd572..c92a1da46a01473ea2f2c9d6f94eabd1f92a8bb0 100644 (file)
@@ -1330,6 +1330,7 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev);
 /* The device ID match table */
 extern const struct of_device_id dwc2_of_match_table[];
 extern const struct acpi_device_id dwc2_acpi_match[];
+extern const struct pci_device_id dwc2_pci_ids[];
 
 int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
 int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
index 8b15742d9e8aa03301c41a7192207b33dd1a7293..b517a7216de22ae420405ae7ceae2fd04f74f2b3 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/slab.h>
-#include <linux/of_platform.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
index c9740caa5974b9b92652c232ab3616c6dee09d17..0144ca8350c31234956b5aea4bbb38bed7134a87 100644 (file)
@@ -2203,11 +2203,13 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
 irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg)
 {
        u32 gintsts, dbg_gintsts;
-       irqreturn_t retval = IRQ_NONE;
+       irqreturn_t retval = IRQ_HANDLED;
 
        if (!dwc2_is_controller_alive(hsotg)) {
                dev_warn(hsotg->dev, "Controller is dead\n");
                return retval;
+       } else {
+               retval = IRQ_NONE;
        }
 
        spin_lock(&hsotg->lock);
index 4c7c3dd15f9bec95158b8f4f10743207be010993..93f52e371cddb6ecb2fef3b068858a39df9c2578 100644 (file)
@@ -7,9 +7,14 @@
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/usb/of.h>
+#include <linux/pci_ids.h>
+#include <linux/pci.h>
 
 #include "core.h"
 
+#define PCI_PRODUCT_ID_HAPS_HSOTG      0xabc0
+#define PCI_DEVICE_ID_LOONGSON_DWC2    0x7a04
+
 static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg)
 {
        struct dwc2_core_params *p = &hsotg->params;
@@ -55,6 +60,14 @@ static void dwc2_set_jz4775_params(struct dwc2_hsotg *hsotg)
                !device_property_read_bool(hsotg->dev, "disable-over-current");
 }
 
+static void dwc2_set_loongson_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->phy_utmi_width = 8;
+       p->power_down = DWC2_POWER_DOWN_PARAM_PARTIAL;
+}
+
 static void dwc2_set_x1600_params(struct dwc2_hsotg *hsotg)
 {
        struct dwc2_core_params *p = &hsotg->params;
@@ -302,6 +315,23 @@ const struct acpi_device_id dwc2_acpi_match[] = {
 };
 MODULE_DEVICE_TABLE(acpi, dwc2_acpi_match);
 
+const struct pci_device_id dwc2_pci_ids[] = {
+       {
+               PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_PRODUCT_ID_HAPS_HSOTG),
+       },
+       {
+               PCI_DEVICE(PCI_VENDOR_ID_STMICRO,
+                          PCI_DEVICE_ID_STMICRO_USB_OTG),
+       },
+       {
+               PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DWC2),
+               .driver_data = (unsigned long)dwc2_set_loongson_params,
+       },
+       { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
+EXPORT_SYMBOL_GPL(dwc2_pci_ids);
+
 static void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg)
 {
        switch (hsotg->hw_params.op_mode) {
@@ -948,13 +978,20 @@ int dwc2_init_params(struct dwc2_hsotg *hsotg)
        if (match && match->data) {
                set_params = match->data;
                set_params(hsotg);
-       } else {
+       } else if (!match) {
                const struct acpi_device_id *amatch;
+               const struct pci_device_id *pmatch = NULL;
 
                amatch = acpi_match_device(dwc2_acpi_match, hsotg->dev);
                if (amatch && amatch->driver_data) {
                        set_params = (set_params_cb)amatch->driver_data;
                        set_params(hsotg);
+               } else if (!amatch)
+                       pmatch = pci_match_id(dwc2_pci_ids, to_pci_dev(hsotg->dev->parent));
+
+               if (pmatch && pmatch->driver_data) {
+                       set_params = (set_params_cb)pmatch->driver_data;
+                       set_params(hsotg);
                }
        }
 
index b7306ed8be4c1b38eed5f7b74db1a7ee657bf908..f3a1e4232a31dcd44345e18a7ecaca0039b92f1d 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/platform_device.h>
 #include <linux/usb/usb_phy_generic.h>
 
-#define PCI_PRODUCT_ID_HAPS_HSOTG      0xabc0
+#include "core.h"
 
 static const char dwc2_driver_name[] = "dwc2-pci";
 
@@ -122,18 +122,6 @@ err:
        return ret;
 }
 
-static const struct pci_device_id dwc2_pci_ids[] = {
-       {
-               PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_PRODUCT_ID_HAPS_HSOTG),
-       },
-       {
-               PCI_DEVICE(PCI_VENDOR_ID_STMICRO,
-                          PCI_DEVICE_ID_STMICRO_USB_OTG),
-       },
-       { /* end: all zeroes */ }
-};
-MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
-
 static struct pci_driver dwc2_pci_driver = {
        .name = dwc2_driver_name,
        .id_table = dwc2_pci_ids,
index 0a806f80217e532693ce94b028eecbacfc6d6a00..b1d48019e944f3ea4166aa748d2053a4ea4312dc 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
index be954a9abbe05e8a18bfbbb1bb2b27869b661f41..98efcbb76c882147f17981ed61dcb360f50c1739 100644 (file)
@@ -168,4 +168,14 @@ config USB_DWC3_AM62
          The Designware Core USB3 IP is programmed to operate in
          in USB 2.0 mode only.
          Say 'Y' or 'M' here if you have one such device
+
+config USB_DWC3_OCTEON
+       tristate "Cavium Octeon Platforms"
+       depends on CAVIUM_OCTEON_SOC || COMPILE_TEST
+       default USB_DWC3
+       help
+         Support Cavium Octeon platforms with DesignWare Core USB3 IP.
+         Only the host mode is currently supported.
+         Say 'Y' or 'M' here if you have one such device.
+
 endif
index 9f66bd82b639e417df07a9a933f92cb16b1a3c59..fe1493d4bbe58e5c4de0adf477e023ac386cd236 100644 (file)
@@ -54,3 +54,4 @@ obj-$(CONFIG_USB_DWC3_ST)             += dwc3-st.o
 obj-$(CONFIG_USB_DWC3_QCOM)            += dwc3-qcom.o
 obj-$(CONFIG_USB_DWC3_IMX8MP)          += dwc3-imx8mp.o
 obj-$(CONFIG_USB_DWC3_XILINX)          += dwc3-xilinx.o
+obj-$(CONFIG_USB_DWC3_OCTEON)          += dwc3-octeon.o
index 1755f2f848c5b05d343c217156f1d32aaba7bfda..90a587bc29b74e533212f41e1a25fcda9655d68f 100644 (file)
 
 #define DWC3_AM62_AUTOSUSPEND_DELAY    100
 
-struct dwc3_data {
+struct dwc3_am62 {
        struct device *dev;
        void __iomem *usbss;
        struct clk *usb2_refclk;
@@ -129,19 +129,19 @@ static const int dwc3_ti_rate_table[] = { /* in KHZ */
        52000,
 };
 
-static inline u32 dwc3_ti_readl(struct dwc3_data *data, u32 offset)
+static inline u32 dwc3_ti_readl(struct dwc3_am62 *am62, u32 offset)
 {
-       return readl((data->usbss) + offset);
+       return readl((am62->usbss) + offset);
 }
 
-static inline void dwc3_ti_writel(struct dwc3_data *data, u32 offset, u32 value)
+static inline void dwc3_ti_writel(struct dwc3_am62 *am62, u32 offset, u32 value)
 {
-       writel(value, (data->usbss) + offset);
+       writel(value, (am62->usbss) + offset);
 }
 
-static int phy_syscon_pll_refclk(struct dwc3_data *data)
+static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
 {
-       struct device *dev = data->dev;
+       struct device *dev = am62->dev;
        struct device_node *node = dev->of_node;
        struct of_phandle_args args;
        struct regmap *syscon;
@@ -153,16 +153,16 @@ static int phy_syscon_pll_refclk(struct dwc3_data *data)
                return PTR_ERR(syscon);
        }
 
-       data->syscon = syscon;
+       am62->syscon = syscon;
 
        ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1,
                                               0, &args);
        if (ret)
                return ret;
 
-       data->offset = args.args[0];
+       am62->offset = args.args[0];
 
-       ret = regmap_update_bits(data->syscon, data->offset, PHY_PLL_REFCLK_MASK, data->rate_code);
+       ret = regmap_update_bits(am62->syscon, am62->offset, PHY_PLL_REFCLK_MASK, am62->rate_code);
        if (ret) {
                dev_err(dev, "failed to set phy pll reference clock rate\n");
                return ret;
@@ -175,32 +175,32 @@ static int dwc3_ti_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct device_node *node = pdev->dev.of_node;
-       struct dwc3_data *data;
+       struct dwc3_am62 *am62;
        int i, ret;
        unsigned long rate;
        u32 reg;
 
-       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
-       if (!data)
+       am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL);
+       if (!am62)
                return -ENOMEM;
 
-       data->dev = dev;
-       platform_set_drvdata(pdev, data);
+       am62->dev = dev;
+       platform_set_drvdata(pdev, am62);
 
-       data->usbss = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(data->usbss)) {
+       am62->usbss = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(am62->usbss)) {
                dev_err(dev, "can't map IOMEM resource\n");
-               return PTR_ERR(data->usbss);
+               return PTR_ERR(am62->usbss);
        }
 
-       data->usb2_refclk = devm_clk_get(dev, "ref");
-       if (IS_ERR(data->usb2_refclk)) {
+       am62->usb2_refclk = devm_clk_get(dev, "ref");
+       if (IS_ERR(am62->usb2_refclk)) {
                dev_err(dev, "can't get usb2_refclk\n");
-               return PTR_ERR(data->usb2_refclk);
+               return PTR_ERR(am62->usb2_refclk);
        }
 
        /* Calculate the rate code */
-       rate = clk_get_rate(data->usb2_refclk);
+       rate = clk_get_rate(am62->usb2_refclk);
        rate /= 1000;   // To KHz
        for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) {
                if (dwc3_ti_rate_table[i] == rate)
@@ -212,20 +212,20 @@ static int dwc3_ti_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       data->rate_code = i;
+       am62->rate_code = i;
 
        /* Read the syscon property and set the rate code */
-       ret = phy_syscon_pll_refclk(data);
+       ret = phy_syscon_pll_refclk(am62);
        if (ret)
                return ret;
 
        /* VBUS divider select */
-       data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
-       reg = dwc3_ti_readl(data, USBSS_PHY_CONFIG);
-       if (data->vbus_divider)
+       am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
+       reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
+       if (am62->vbus_divider)
                reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
 
-       dwc3_ti_writel(data, USBSS_PHY_CONFIG, reg);
+       dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
 
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
@@ -233,7 +233,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
         * Don't ignore its dependencies with its children
         */
        pm_suspend_ignore_children(dev, false);
-       clk_prepare_enable(data->usb2_refclk);
+       clk_prepare_enable(am62->usb2_refclk);
        pm_runtime_get_noresume(dev);
 
        ret = of_platform_populate(node, NULL, NULL, dev);
@@ -243,9 +243,9 @@ static int dwc3_ti_probe(struct platform_device *pdev)
        }
 
        /* Set mode valid bit to indicate role is valid */
-       reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+       reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
        reg |= USBSS_MODE_VALID;
-       dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+       dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
 
        /* Device has capability to wakeup system from sleep */
        device_set_wakeup_capable(dev, true);
@@ -261,7 +261,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
        return 0;
 
 err_pm_disable:
-       clk_disable_unprepare(data->usb2_refclk);
+       clk_disable_unprepare(am62->usb2_refclk);
        pm_runtime_disable(dev);
        pm_runtime_set_suspended(dev);
        return ret;
@@ -278,36 +278,34 @@ static int dwc3_ti_remove_core(struct device *dev, void *c)
 static void dwc3_ti_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct dwc3_data *data = platform_get_drvdata(pdev);
+       struct dwc3_am62 *am62 = platform_get_drvdata(pdev);
        u32 reg;
 
        device_for_each_child(dev, NULL, dwc3_ti_remove_core);
 
        /* Clear mode valid bit */
-       reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+       reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
        reg &= ~USBSS_MODE_VALID;
-       dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+       dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
 
        pm_runtime_put_sync(dev);
-       clk_disable_unprepare(data->usb2_refclk);
+       clk_disable_unprepare(am62->usb2_refclk);
        pm_runtime_disable(dev);
        pm_runtime_set_suspended(dev);
-
-       platform_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM
 static int dwc3_ti_suspend_common(struct device *dev)
 {
-       struct dwc3_data *data = dev_get_drvdata(dev);
+       struct dwc3_am62 *am62 = dev_get_drvdata(dev);
        u32 reg, current_prtcap_dir;
 
        if (device_may_wakeup(dev)) {
-               reg = dwc3_ti_readl(data, USBSS_CORE_STAT);
+               reg = dwc3_ti_readl(am62, USBSS_CORE_STAT);
                current_prtcap_dir = (reg & USBSS_CORE_OPERATIONAL_MODE_MASK)
                                     >> USBSS_CORE_OPERATIONAL_MODE_SHIFT;
                /* Set wakeup config enable bits */
-               reg = dwc3_ti_readl(data, USBSS_WAKEUP_CONFIG);
+               reg = dwc3_ti_readl(am62, USBSS_WAKEUP_CONFIG);
                if (current_prtcap_dir == DWC3_GCTL_PRTCAP_HOST) {
                        reg = USBSS_WAKEUP_CFG_LINESTATE_EN | USBSS_WAKEUP_CFG_OVERCURRENT_EN;
                } else {
@@ -317,30 +315,30 @@ static int dwc3_ti_suspend_common(struct device *dev)
                         * and in U2/L3 state else it causes spurious wake-up.
                         */
                }
-               dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, reg);
+               dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, reg);
                /* clear wakeup status so we know what caused the wake up */
-               dwc3_ti_writel(data, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
+               dwc3_ti_writel(am62, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
        }
 
-       clk_disable_unprepare(data->usb2_refclk);
+       clk_disable_unprepare(am62->usb2_refclk);
 
        return 0;
 }
 
 static int dwc3_ti_resume_common(struct device *dev)
 {
-       struct dwc3_data *data = dev_get_drvdata(dev);
+       struct dwc3_am62 *am62 = dev_get_drvdata(dev);
        u32 reg;
 
-       clk_prepare_enable(data->usb2_refclk);
+       clk_prepare_enable(am62->usb2_refclk);
 
        if (device_may_wakeup(dev)) {
                /* Clear wakeup config enable bits */
-               dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE);
+               dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE);
        }
 
-       reg = dwc3_ti_readl(data, USBSS_WAKEUP_STAT);
-       data->wakeup_stat = reg;
+       reg = dwc3_ti_readl(am62, USBSS_WAKEUP_STAT);
+       am62->wakeup_stat = reg;
 
        return 0;
 }
index f882dd6473403e4c2f1bc93e1409d07e88e90a25..5d365ca51771dead5045d32ae9351cc2253a8a3a 100644 (file)
@@ -163,6 +163,12 @@ static const struct dwc3_exynos_driverdata exynos7_drvdata = {
        .suspend_clk_idx = 1,
 };
 
+static const struct dwc3_exynos_driverdata exynos850_drvdata = {
+       .clk_names = { "bus_early", "ref" },
+       .num_clks = 2,
+       .suspend_clk_idx = -1,
+};
+
 static const struct of_device_id exynos_dwc3_match[] = {
        {
                .compatible = "samsung,exynos5250-dwusb3",
@@ -173,6 +179,9 @@ static const struct of_device_id exynos_dwc3_match[] = {
        }, {
                .compatible = "samsung,exynos7-dwusb3",
                .data = &exynos7_drvdata,
+       }, {
+               .compatible = "samsung,exynos850-dwusb3",
+               .data = &exynos850_drvdata,
        }, {
        }
 };
index 8b9a3bb587bf3451bc3b71554b456cb8f9a595db..a1e15f2fffdbff6811ff09fbadd681a8967c2f30 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
@@ -279,7 +280,6 @@ static void dwc3_imx8mp_remove(struct platform_device *pdev)
 
        pm_runtime_disable(dev);
        pm_runtime_put_noidle(dev);
-       platform_set_drvdata(pdev, NULL);
 }
 
 static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx,
index 0a09aedc25736b76d7764ab051f285d7adb6c4e6..8899348b627632e647c1762c50106a124ac6eac8 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/phy/phy.h>
 #include <linux/pm_runtime.h>
@@ -196,8 +197,6 @@ static void kdwc3_remove(struct platform_device *pdev)
        phy_power_off(kdwc->usb3_phy);
        phy_exit(kdwc->usb3_phy);
        phy_pm_runtime_put_sync(kdwc->usb3_phy);
-
-       platform_set_drvdata(pdev, NULL);
 }
 
 static const struct of_device_id kdwc3_of_match[] = {
index e99c7489dba02dca796c543a5a1cc261f747153e..2c07c038b584dc9b4487a76d2a27ec62ec383bf9 100644 (file)
@@ -926,6 +926,12 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
                        return ret;
        }
 
+       if (priv->drvdata->usb_post_init) {
+               ret = priv->drvdata->usb_post_init(priv);
+               if (ret)
+                       return ret;
+       }
+
        return 0;
 }
 
similarity index 61%
rename from arch/mips/cavium-octeon/octeon-usb.c
rename to drivers/usb/dwc3/dwc3-octeon.c
index 2add435ad03872f0b1465fcc9e88d13711b5c4e9..ff01f2c17452d80c8192b7a7c6bf63470f742870 100644 (file)
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * XHCI HCD glue for Cavium Octeon III SOCs.
+ * DWC3 glue for Cavium Octeon III SOCs.
  *
  * Copyright (C) 2010-2017 Cavium Networks
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
+ * Copyright (C) 2023 RACOM s.r.o.
  */
 
 #include <linux/bitfield.h>
@@ -24,9 +22,9 @@
 /* BIST fast-clear mode select. A BIST run with this bit set
  * clears all entries in USBH RAMs to 0x0.
  */
-# define USBDRD_UCTL_CTL_CLEAR_BIST            BIT(63)
+# define USBDRD_UCTL_CTL_CLEAR_BIST            BIT_ULL(63)
 /* 1 = Start BIST and cleared by hardware */
-# define USBDRD_UCTL_CTL_START_BIST            BIT(62)
+# define USBDRD_UCTL_CTL_START_BIST            BIT_ULL(62)
 /* Reference clock select for SuperSpeed and HighSpeed PLLs:
  *     0x0 = Both PLLs use DLMC_REF_CLK0 for reference clock
  *     0x1 = Both PLLs use DLMC_REF_CLK1 for reference clock
  *     0x3 = SuperSpeed PLL uses DLMC_REF_CLK1 for reference clock &
  *           HighSpeed PLL uses PLL_REF_CLK for reference clck
  */
-# define USBDRD_UCTL_CTL_REF_CLK_SEL           GENMASK(61, 60)
+# define USBDRD_UCTL_CTL_REF_CLK_SEL           GENMASK_ULL(61, 60)
 /* 1 = Spread-spectrum clock enable, 0 = SS clock disable */
-# define USBDRD_UCTL_CTL_SSC_EN                        BIT(59)
+# define USBDRD_UCTL_CTL_SSC_EN                        BIT_ULL(59)
 /* Spread-spectrum clock modulation range:
  *     0x0 = -4980 ppm downspread
  *     0x1 = -4492 ppm downspread
  *     0x2 = -4003 ppm downspread
  *     0x3 - 0x7 = Reserved
  */
-# define USBDRD_UCTL_CTL_SSC_RANGE             GENMASK(58, 56)
+# define USBDRD_UCTL_CTL_SSC_RANGE             GENMASK_ULL(58, 56)
 /* Enable non-standard oscillator frequencies:
  *     [55:53] = modules -1
  *     [52:47] = 2's complement push amount, 0 = Feature disabled
  */
-# define USBDRD_UCTL_CTL_SSC_REF_CLK_SEL       GENMASK(55, 47)
+# define USBDRD_UCTL_CTL_SSC_REF_CLK_SEL       GENMASK_ULL(55, 47)
 /* Reference clock multiplier for non-standard frequencies:
  *     0x19 = 100MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1
  *     0x28 = 125MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1
  *     0x32 =  50MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1
  *     Other Values = Reserved
  */
-# define USBDRD_UCTL_CTL_MPLL_MULTIPLIER       GENMASK(46, 40)
+# define USBDRD_UCTL_CTL_MPLL_MULTIPLIER       GENMASK_ULL(46, 40)
 /* Enable reference clock to prescaler for SuperSpeed functionality.
  * Should always be set to "1"
  */
-# define USBDRD_UCTL_CTL_REF_SSP_EN            BIT(39)
+# define USBDRD_UCTL_CTL_REF_SSP_EN            BIT_ULL(39)
 /* Divide the reference clock by 2 before entering the
  * REF_CLK_FSEL divider:
  *     If REF_CLK_SEL = 0x0 or 0x1, then only 0x0 is legal
  *             0x1 = DLMC_REF_CLK* is 125MHz
  *             0x0 = DLMC_REF_CLK* is another supported frequency
  */
-# define USBDRD_UCTL_CTL_REF_CLK_DIV2          BIT(38)
+# define USBDRD_UCTL_CTL_REF_CLK_DIV2          BIT_ULL(38)
 /* Select reference clock freqnuency for both PLL blocks:
  *     0x27 = REF_CLK_SEL is 0x0 or 0x1
  *     0x07 = REF_CLK_SEL is 0x2 or 0x3
  */
-# define USBDRD_UCTL_CTL_REF_CLK_FSEL          GENMASK(37, 32)
+# define USBDRD_UCTL_CTL_REF_CLK_FSEL          GENMASK_ULL(37, 32)
 /* Controller clock enable. */
-# define USBDRD_UCTL_CTL_H_CLK_EN              BIT(30)
+# define USBDRD_UCTL_CTL_H_CLK_EN              BIT_ULL(30)
 /* Select bypass input to controller clock divider:
  *     0x0 = Use divided coprocessor clock from H_CLKDIV
  *     0x1 = Use clock from GPIO pins
  */
-# define USBDRD_UCTL_CTL_H_CLK_BYP_SEL         BIT(29)
+# define USBDRD_UCTL_CTL_H_CLK_BYP_SEL         BIT_ULL(29)
 /* Reset controller clock divider. */
-# define USBDRD_UCTL_CTL_H_CLKDIV_RST          BIT(28)
+# define USBDRD_UCTL_CTL_H_CLKDIV_RST          BIT_ULL(28)
 /* Clock divider select:
  *     0x0 = divide by 1
  *     0x1 = divide by 2
  *     0x6 = divide by 24
  *     0x7 = divide by 32
  */
-# define USBDRD_UCTL_CTL_H_CLKDIV_SEL          GENMASK(26, 24)
+# define USBDRD_UCTL_CTL_H_CLKDIV_SEL          GENMASK_ULL(26, 24)
 /* USB3 port permanently attached: 0x0 = No, 0x1 = Yes */
-# define USBDRD_UCTL_CTL_USB3_PORT_PERM_ATTACH BIT(21)
+# define USBDRD_UCTL_CTL_USB3_PORT_PERM_ATTACH BIT_ULL(21)
 /* USB2 port permanently attached: 0x0 = No, 0x1 = Yes */
-# define USBDRD_UCTL_CTL_USB2_PORT_PERM_ATTACH BIT(20)
+# define USBDRD_UCTL_CTL_USB2_PORT_PERM_ATTACH BIT_ULL(20)
 /* Disable SuperSpeed PHY: 0x0 = No, 0x1 = Yes */
-# define USBDRD_UCTL_CTL_USB3_PORT_DISABLE     BIT(18)
+# define USBDRD_UCTL_CTL_USB3_PORT_DISABLE     BIT_ULL(18)
 /* Disable HighSpeed PHY: 0x0 = No, 0x1 = Yes */
-# define USBDRD_UCTL_CTL_USB2_PORT_DISABLE     BIT(16)
+# define USBDRD_UCTL_CTL_USB2_PORT_DISABLE     BIT_ULL(16)
 /* Enable PHY SuperSpeed block power: 0x0 = No, 0x1 = Yes */
-# define USBDRD_UCTL_CTL_SS_POWER_EN           BIT(14)
+# define USBDRD_UCTL_CTL_SS_POWER_EN           BIT_ULL(14)
 /* Enable PHY HighSpeed block power: 0x0 = No, 0x1 = Yes */
-# define USBDRD_UCTL_CTL_HS_POWER_EN           BIT(12)
+# define USBDRD_UCTL_CTL_HS_POWER_EN           BIT_ULL(12)
 /* Enable USB UCTL interface clock: 0xx = No, 0x1 = Yes */
-# define USBDRD_UCTL_CTL_CSCLK_EN              BIT(4)
+# define USBDRD_UCTL_CTL_CSCLK_EN              BIT_ULL(4)
 /* Controller mode: 0x0 = Host, 0x1 = Device */
-# define USBDRD_UCTL_CTL_DRD_MODE              BIT(3)
+# define USBDRD_UCTL_CTL_DRD_MODE              BIT_ULL(3)
 /* PHY reset */
-# define USBDRD_UCTL_CTL_UPHY_RST              BIT(2)
+# define USBDRD_UCTL_CTL_UPHY_RST              BIT_ULL(2)
 /* Software reset UAHC */
-# define USBDRD_UCTL_CTL_UAHC_RST              BIT(1)
+# define USBDRD_UCTL_CTL_UAHC_RST              BIT_ULL(1)
 /* Software resets UCTL */
-# define USBDRD_UCTL_CTL_UCTL_RST              BIT(0)
+# define USBDRD_UCTL_CTL_UCTL_RST              BIT_ULL(0)
 
 #define USBDRD_UCTL_BIST_STATUS                        0x08
 #define USBDRD_UCTL_SPARE0                     0x10
  */
 #define USBDRD_UCTL_HOST_CFG                   0xe0
 /* Indicates minimum value of all received BELT values */
-# define USBDRD_UCTL_HOST_CFG_HOST_CURRENT_BELT        GENMASK(59, 48)
+# define USBDRD_UCTL_HOST_CFG_HOST_CURRENT_BELT        GENMASK_ULL(59, 48)
 /* HS jitter adjustment */
-# define USBDRD_UCTL_HOST_CFG_FLA              GENMASK(37, 32)
+# define USBDRD_UCTL_HOST_CFG_FLA              GENMASK_ULL(37, 32)
 /* Bus-master enable: 0x0 = Disabled (stall DMAs), 0x1 = enabled */
-# define USBDRD_UCTL_HOST_CFG_BME              BIT(28)
+# define USBDRD_UCTL_HOST_CFG_BME              BIT_ULL(28)
 /* Overcurrent protection enable: 0x0 = unavailable, 0x1 = available */
-# define USBDRD_UCTL_HOST_OCI_EN               BIT(27)
+# define USBDRD_UCTL_HOST_OCI_EN               BIT_ULL(27)
 /* Overcurrent sene selection:
  *     0x0 = Overcurrent indication from off-chip is active-low
  *     0x1 = Overcurrent indication from off-chip is active-high
  */
-# define USBDRD_UCTL_HOST_OCI_ACTIVE_HIGH_EN   BIT(26)
+# define USBDRD_UCTL_HOST_OCI_ACTIVE_HIGH_EN   BIT_ULL(26)
 /* Port power control enable: 0x0 = unavailable, 0x1 = available */
-# define USBDRD_UCTL_HOST_PPC_EN               BIT(25)
+# define USBDRD_UCTL_HOST_PPC_EN               BIT_ULL(25)
 /* Port power control sense selection:
  *     0x0 = Port power to off-chip is active-low
  *     0x1 = Port power to off-chip is active-high
  */
-# define USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN   BIT(24)
+# define USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN   BIT_ULL(24)
 
 /*
  * UCTL Shim Features Register
  */
 #define USBDRD_UCTL_SHIM_CFG                   0xe8
 /* Out-of-bound UAHC register access: 0 = read, 1 = write */
-# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_WRN   BIT(63)
+# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_WRN   BIT_ULL(63)
 /* SRCID error log for out-of-bound UAHC register access:
  *     [59:58] = chipID
  *     [57] = Request source: 0 = core, 1 = NCB-device
  *     [56:51] = Core/NCB-device number, [56] always 0 for NCB devices
  *     [50:48] = SubID
  */
-# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_OSRC  GENMASK(59, 48)
+# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_OSRC  GENMASK_ULL(59, 48)
 /* Error log for bad UAHC DMA access: 0 = Read log, 1 = Write log */
-# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_WRN   BIT(47)
+# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_WRN   BIT_ULL(47)
 /* Encoded error type for bad UAHC DMA */
-# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_TYPE  GENMASK(43, 40)
+# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_TYPE  GENMASK_ULL(43, 40)
 /* Select the IOI read command used by DMA accesses */
-# define USBDRD_UCTL_SHIM_CFG_DMA_READ_CMD     BIT(12)
+# define USBDRD_UCTL_SHIM_CFG_DMA_READ_CMD     BIT_ULL(12)
 /* Select endian format for DMA accesses to the L2C:
  *     0x0 = Little endian
  *     0x1 = Big endian
  *     0x2 = Reserved
  *     0x3 = Reserved
  */
-# define USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE  GENMASK(9, 8)
+# define USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE  GENMASK_ULL(9, 8)
 /* Select endian format for IOI CSR access to UAHC:
  *     0x0 = Little endian
  *     0x1 = Big endian
  *     0x2 = Reserved
  *     0x3 = Reserved
  */
-# define USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE  GENMASK(1, 0)
+# define USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE  GENMASK_ULL(1, 0)
 
 #define USBDRD_UCTL_ECC                                0xf0
 #define USBDRD_UCTL_SPARE1                     0xf8
 
-static DEFINE_MUTEX(dwc3_octeon_clocks_mutex);
+struct dwc3_octeon {
+       struct device *dev;
+       void __iomem *base;
+};
+
+#define DWC3_GPIO_POWER_NONE   (-1)
 
 #ifdef CONFIG_CAVIUM_OCTEON_SOC
 #include <asm/octeon/octeon.h>
@@ -233,6 +236,11 @@ static inline uint64_t dwc3_octeon_readq(void __iomem *addr)
 static inline void dwc3_octeon_writeq(void __iomem *base, uint64_t val) { }
 
 static inline void dwc3_octeon_config_gpio(int index, int gpio) { }
+
+static uint64_t octeon_get_io_clock_rate(void)
+{
+       return 150000000;
+}
 #endif
 
 static int dwc3_octeon_get_divider(void)
@@ -243,115 +251,22 @@ static int dwc3_octeon_get_divider(void)
        while (div < ARRAY_SIZE(clk_div)) {
                uint64_t rate = octeon_get_io_clock_rate() / clk_div[div];
                if (rate <= 300000000 && rate >= 150000000)
-                       break;
+                       return div;
                div++;
        }
 
-       return div;
+       return -EINVAL;
 }
 
-static int dwc3_octeon_config_power(struct device *dev, void __iomem *base)
+static int dwc3_octeon_setup(struct dwc3_octeon *octeon,
+                            int ref_clk_sel, int ref_clk_fsel, int mpll_mul,
+                            int power_gpio, int power_active_low)
 {
-       uint32_t gpio_pwr[3];
-       int gpio, len, power_active_low;
-       struct device_node *node = dev->of_node;
        u64 val;
-       void __iomem *uctl_host_cfg_reg = base + USBDRD_UCTL_HOST_CFG;
-
-       if (of_find_property(node, "power", &len) != NULL) {
-               if (len == 12) {
-                       of_property_read_u32_array(node, "power", gpio_pwr, 3);
-                       power_active_low = gpio_pwr[2] & 0x01;
-                       gpio = gpio_pwr[1];
-               } else if (len == 8) {
-                       of_property_read_u32_array(node, "power", gpio_pwr, 2);
-                       power_active_low = 0;
-                       gpio = gpio_pwr[1];
-               } else {
-                       dev_err(dev, "invalid power configuration\n");
-                       return -EINVAL;
-               }
-               dwc3_octeon_config_gpio(((u64)base >> 24) & 1, gpio);
-
-               /* Enable XHCI power control and set if active high or low. */
-               val = dwc3_octeon_readq(uctl_host_cfg_reg);
-               val |= USBDRD_UCTL_HOST_PPC_EN;
-               if (power_active_low)
-                       val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN;
-               else
-                       val |= USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN;
-               dwc3_octeon_writeq(uctl_host_cfg_reg, val);
-       } else {
-               /* Disable XHCI power control and set if active high. */
-               val = dwc3_octeon_readq(uctl_host_cfg_reg);
-               val &= ~USBDRD_UCTL_HOST_PPC_EN;
-               val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN;
-               dwc3_octeon_writeq(uctl_host_cfg_reg, val);
-               dev_info(dev, "power control disabled\n");
-       }
-       return 0;
-}
-
-static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base)
-{
-       int i, div, mpll_mul, ref_clk_fsel, ref_clk_sel = 2;
-       u32 clock_rate;
-       u64 val;
-       void __iomem *uctl_ctl_reg = base + USBDRD_UCTL_CTL;
-
-       if (dev->of_node) {
-               const char *ss_clock_type;
-               const char *hs_clock_type;
-
-               i = of_property_read_u32(dev->of_node,
-                                        "refclk-frequency", &clock_rate);
-               if (i) {
-                       dev_err(dev, "No UCTL \"refclk-frequency\"\n");
-                       return -EINVAL;
-               }
-               i = of_property_read_string(dev->of_node,
-                                           "refclk-type-ss", &ss_clock_type);
-               if (i) {
-                       dev_err(dev, "No UCTL \"refclk-type-ss\"\n");
-                       return -EINVAL;
-               }
-               i = of_property_read_string(dev->of_node,
-                                           "refclk-type-hs", &hs_clock_type);
-               if (i) {
-                       dev_err(dev, "No UCTL \"refclk-type-hs\"\n");
-                       return -EINVAL;
-               }
-               if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) {
-                       if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0)
-                               ref_clk_sel = 0;
-                       else if (strcmp(hs_clock_type, "pll_ref_clk") == 0)
-                               ref_clk_sel = 2;
-                       else
-                               dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n",
-                                        hs_clock_type);
-               } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) {
-                       if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0)
-                               ref_clk_sel = 1;
-                       else if (strcmp(hs_clock_type, "pll_ref_clk") == 0)
-                               ref_clk_sel = 3;
-                       else {
-                               dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n",
-                                        hs_clock_type);
-                               ref_clk_sel = 3;
-                       }
-               } else
-                       dev_warn(dev, "Invalid SS clock type %s, using dlmc_ref_clk0 instead\n",
-                                ss_clock_type);
-
-               if ((ref_clk_sel == 0 || ref_clk_sel == 1) &&
-                   (clock_rate != 100000000))
-                       dev_warn(dev, "Invalid UCTL clock rate of %u, using 100000000 instead\n",
-                                clock_rate);
-
-       } else {
-               dev_err(dev, "No USB UCTL device node\n");
-               return -EINVAL;
-       }
+       int div;
+       struct device *dev = octeon->dev;
+       void __iomem *uctl_ctl_reg = octeon->base + USBDRD_UCTL_CTL;
+       void __iomem *uctl_host_cfg_reg = octeon->base + USBDRD_UCTL_HOST_CFG;
 
        /*
         * Step 1: Wait for all voltages to be stable...that surely
@@ -374,6 +289,10 @@ static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base)
 
        /* Step 4b: Select controller clock frequency. */
        div = dwc3_octeon_get_divider();
+       if (div < 0) {
+               dev_err(dev, "clock divider invalid\n");
+               return div;
+       }
        val = dwc3_octeon_readq(uctl_ctl_reg);
        val &= ~USBDRD_UCTL_CTL_H_CLKDIV_SEL;
        val |= FIELD_PREP(USBDRD_UCTL_CTL_H_CLKDIV_SEL, div);
@@ -382,8 +301,8 @@ static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base)
        val = dwc3_octeon_readq(uctl_ctl_reg);
        if ((div != FIELD_GET(USBDRD_UCTL_CTL_H_CLKDIV_SEL, val)) ||
            (!(FIELD_GET(USBDRD_UCTL_CTL_H_CLK_EN, val)))) {
-               dev_err(dev, "dwc3 controller clock init failure.\n");
-                       return -EINVAL;
+               dev_err(dev, "clock init failure (UCTL_CTL=%016llx)\n", val);
+               return -EINVAL;
        }
 
        /* Step 4c: Deassert the controller clock divider reset. */
@@ -396,24 +315,6 @@ static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base)
        val &= ~USBDRD_UCTL_CTL_REF_CLK_SEL;
        val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_SEL, ref_clk_sel);
 
-       ref_clk_fsel = 0x07;
-       switch (clock_rate) {
-       default:
-               dev_warn(dev, "Invalid ref_clk %u, using 100000000 instead\n",
-                        clock_rate);
-               fallthrough;
-       case 100000000:
-               mpll_mul = 0x19;
-               if (ref_clk_sel < 2)
-                       ref_clk_fsel = 0x27;
-               break;
-       case 50000000:
-               mpll_mul = 0x32;
-               break;
-       case 125000000:
-               mpll_mul = 0x28;
-               break;
-       }
        val &= ~USBDRD_UCTL_CTL_REF_CLK_FSEL;
        val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_FSEL, ref_clk_fsel);
 
@@ -444,9 +345,22 @@ static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base)
        /* Step 8b: Wait 10 controller-clock cycles. */
        udelay(10);
 
-       /* Steo 8c: Setup power-power control. */
-       if (dwc3_octeon_config_power(dev, base))
-               return -EINVAL;
+       /* Step 8c: Setup power control. */
+       val = dwc3_octeon_readq(uctl_host_cfg_reg);
+       val |= USBDRD_UCTL_HOST_PPC_EN;
+       if (power_gpio == DWC3_GPIO_POWER_NONE) {
+               val &= ~USBDRD_UCTL_HOST_PPC_EN;
+       } else {
+               val |= USBDRD_UCTL_HOST_PPC_EN;
+               dwc3_octeon_config_gpio(((__force uintptr_t)octeon->base >> 24) & 1,
+                                       power_gpio);
+               dev_dbg(dev, "power control is using gpio%d\n", power_gpio);
+       }
+       if (power_active_low)
+               val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN;
+       else
+               val |= USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN;
+       dwc3_octeon_writeq(uctl_host_cfg_reg, val);
 
        /* Step 8d: Deassert UAHC reset signal. */
        val = dwc3_octeon_readq(uctl_ctl_reg);
@@ -469,10 +383,10 @@ static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base)
        return 0;
 }
 
-static void __init dwc3_octeon_set_endian_mode(void __iomem *base)
+static void dwc3_octeon_set_endian_mode(struct dwc3_octeon *octeon)
 {
        u64 val;
-       void __iomem *uctl_shim_cfg_reg = base + USBDRD_UCTL_SHIM_CFG;
+       void __iomem *uctl_shim_cfg_reg = octeon->base + USBDRD_UCTL_SHIM_CFG;
 
        val = dwc3_octeon_readq(uctl_shim_cfg_reg);
        val &= ~USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE;
@@ -484,68 +398,146 @@ static void __init dwc3_octeon_set_endian_mode(void __iomem *base)
        dwc3_octeon_writeq(uctl_shim_cfg_reg, val);
 }
 
-static void __init dwc3_octeon_phy_reset(void __iomem *base)
+static void dwc3_octeon_phy_reset(struct dwc3_octeon *octeon)
 {
        u64 val;
-       void __iomem *uctl_ctl_reg = base + USBDRD_UCTL_CTL;
+       void __iomem *uctl_ctl_reg = octeon->base + USBDRD_UCTL_CTL;
 
        val = dwc3_octeon_readq(uctl_ctl_reg);
        val &= ~USBDRD_UCTL_CTL_UPHY_RST;
        dwc3_octeon_writeq(uctl_ctl_reg, val);
 }
 
-static int __init dwc3_octeon_device_init(void)
+static int dwc3_octeon_probe(struct platform_device *pdev)
 {
-       const char compat_node_name[] = "cavium,octeon-7130-usb-uctl";
-       struct platform_device *pdev;
-       struct device_node *node;
-       struct resource *res;
-       void __iomem *base;
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct dwc3_octeon *octeon;
+       const char *hs_clock_type, *ss_clock_type;
+       int ref_clk_sel, ref_clk_fsel, mpll_mul;
+       int power_active_low, power_gpio;
+       int err, len;
+       u32 clock_rate;
 
-       /*
-        * There should only be three universal controllers, "uctl"
-        * in the device tree. Two USB and a SATA, which we ignore.
-        */
-       node = NULL;
-       do {
-               node = of_find_node_by_name(node, "uctl");
-               if (!node)
-                       return -ENODEV;
-
-               if (of_device_is_compatible(node, compat_node_name)) {
-                       pdev = of_find_device_by_node(node);
-                       if (!pdev)
-                               return -ENODEV;
-
-                       /*
-                        * The code below maps in the registers necessary for
-                        * setting up the clocks and reseting PHYs. We must
-                        * release the resources so the dwc3 subsystem doesn't
-                        * know the difference.
-                        */
-                       base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
-                       if (IS_ERR(base)) {
-                               put_device(&pdev->dev);
-                               return PTR_ERR(base);
-                       }
-
-                       mutex_lock(&dwc3_octeon_clocks_mutex);
-                       if (dwc3_octeon_clocks_start(&pdev->dev, base) == 0)
-                               dev_info(&pdev->dev, "clocks initialized.\n");
-                       dwc3_octeon_set_endian_mode(base);
-                       dwc3_octeon_phy_reset(base);
-                       mutex_unlock(&dwc3_octeon_clocks_mutex);
-                       devm_iounmap(&pdev->dev, base);
-                       devm_release_mem_region(&pdev->dev, res->start,
-                                               resource_size(res));
-                       put_device(&pdev->dev);
+       if (of_property_read_u32(node, "refclk-frequency", &clock_rate)) {
+               dev_err(dev, "No UCTL \"refclk-frequency\"\n");
+               return -EINVAL;
+       }
+       if (of_property_read_string(node, "refclk-type-ss", &ss_clock_type)) {
+               dev_err(dev, "No UCTL \"refclk-type-ss\"\n");
+               return -EINVAL;
+       }
+       if (of_property_read_string(node, "refclk-type-hs", &hs_clock_type)) {
+               dev_err(dev, "No UCTL \"refclk-type-hs\"\n");
+               return -EINVAL;
+       }
+
+       ref_clk_sel = 2;
+       if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) {
+               if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0)
+                       ref_clk_sel = 0;
+               else if (strcmp(hs_clock_type, "pll_ref_clk"))
+                       dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n",
+                                hs_clock_type);
+       } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) {
+               if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) {
+                       ref_clk_sel = 1;
+               } else {
+                       ref_clk_sel = 3;
+                       if (strcmp(hs_clock_type, "pll_ref_clk"))
+                               dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n",
+                                        hs_clock_type);
                }
-       } while (node != NULL);
+       } else {
+               dev_warn(dev, "Invalid SS clock type %s, using dlmc_ref_clk0 instead\n",
+                        ss_clock_type);
+       }
 
-       return 0;
+       ref_clk_fsel = 0x07;
+       switch (clock_rate) {
+       default:
+               dev_warn(dev, "Invalid ref_clk %u, using 100000000 instead\n",
+                        clock_rate);
+               fallthrough;
+       case 100000000:
+               mpll_mul = 0x19;
+               if (ref_clk_sel < 2)
+                       ref_clk_fsel = 0x27;
+               break;
+       case 50000000:
+               mpll_mul = 0x32;
+               break;
+       case 125000000:
+               mpll_mul = 0x28;
+               break;
+       }
+
+       power_gpio = DWC3_GPIO_POWER_NONE;
+       power_active_low = 0;
+       if (of_find_property(node, "power", &len)) {
+               u32 gpio_pwr[3];
+
+               switch (len) {
+               case 8:
+                       of_property_read_u32_array(node, "power", gpio_pwr, 2);
+                       break;
+               case 12:
+                       of_property_read_u32_array(node, "power", gpio_pwr, 3);
+                       power_active_low = gpio_pwr[2] & 0x01;
+                       break;
+               default:
+                       dev_err(dev, "invalid power configuration\n");
+                       return -EINVAL;
+               }
+               power_gpio = gpio_pwr[1];
+       }
+
+       octeon = devm_kzalloc(dev, sizeof(*octeon), GFP_KERNEL);
+       if (!octeon)
+               return -ENOMEM;
+
+       octeon->dev = dev;
+       octeon->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(octeon->base))
+               return PTR_ERR(octeon->base);
+
+       err = dwc3_octeon_setup(octeon, ref_clk_sel, ref_clk_fsel, mpll_mul,
+                               power_gpio, power_active_low);
+       if (err)
+               return err;
+
+       dwc3_octeon_set_endian_mode(octeon);
+       dwc3_octeon_phy_reset(octeon);
+
+       platform_set_drvdata(pdev, octeon);
+
+       return of_platform_populate(node, NULL, NULL, dev);
+}
+
+static void dwc3_octeon_remove(struct platform_device *pdev)
+{
+       struct dwc3_octeon *octeon = platform_get_drvdata(pdev);
+
+       of_platform_depopulate(octeon->dev);
 }
-device_initcall(dwc3_octeon_device_init);
 
-MODULE_AUTHOR("David Daney <david.daney@cavium.com>");
+static const struct of_device_id dwc3_octeon_of_match[] = {
+       { .compatible = "cavium,octeon-7130-usb-uctl" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, dwc3_octeon_of_match);
+
+static struct platform_driver dwc3_octeon_driver = {
+       .probe          = dwc3_octeon_probe,
+       .remove_new     = dwc3_octeon_remove,
+       .driver         = {
+               .name   = "dwc3-octeon",
+               .of_match_table = dwc3_octeon_of_match,
+       },
+};
+module_platform_driver(dwc3_octeon_driver);
+
+MODULE_ALIAS("platform:dwc3-octeon");
+MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>");
 MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("USB driver for OCTEON III SoC");
+MODULE_DESCRIPTION("DesignWare USB3 OCTEON III Glue Layer");
index 7e6ad8fe61a5a2ba1f7a6768fefd9337fba94fc0..d1539fc9eabdae5d572ab58986a457f877c0b065 100644 (file)
@@ -170,7 +170,6 @@ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
 
 static const struct of_device_id of_dwc3_simple_match[] = {
        { .compatible = "rockchip,rk3399-dwc3" },
-       { .compatible = "cavium,octeon-7130-usb-uctl" },
        { .compatible = "sprd,sc9860-dwc3" },
        { .compatible = "allwinner,sun50i-h6-dwc3" },
        { .compatible = "hisilicon,hi3670-dwc3" },
index 336db8f92afa6ae747352c32395cf69a75fc6642..b3592bcb0f96684ef41edec3d73997aba3b92fe8 100644 (file)
@@ -208,6 +208,9 @@ config USB_F_UVC
 config USB_F_MIDI
        tristate
 
+config USB_F_MIDI2
+       tristate
+
 config USB_F_HID
        tristate
 
@@ -436,6 +439,21 @@ config USB_CONFIGFS_F_MIDI
          connections can then be made on the gadget system, using
          ALSA's aconnect utility etc.
 
+config USB_CONFIGFS_F_MIDI2
+       bool "MIDI 2.0 function"
+       depends on USB_CONFIGFS
+       depends on SND
+       select USB_LIBCOMPOSITE
+       select SND_UMP
+       select SND_UMP_LEGACY_RAWMIDI
+       select USB_F_MIDI2
+       help
+         The MIDI 2.0 function driver provides the generic emulated
+         USB MIDI 2.0 interface, looped back to ALSA UMP rawmidi
+         device on the gadget host. It supports UMP 1.1 spec and
+         responds UMP Stream messages for UMP Endpoint and Function
+         Block information / configuration.
+
 config USB_CONFIGFS_F_HID
        bool "HID function"
        depends on USB_CONFIGFS
index dd9b90481b4c21083fe98652b7034102fea9fae2..0ace45b66a31c417f7e6da9f811c3d8b5c736246 100644 (file)
@@ -170,33 +170,27 @@ int config_ep_by_speed_and_alt(struct usb_gadget *g,
        /* select desired speed */
        switch (g->speed) {
        case USB_SPEED_SUPER_PLUS:
-               if (gadget_is_superspeed_plus(g)) {
-                       if (f->ssp_descriptors) {
-                               speed_desc = f->ssp_descriptors;
-                               want_comp_desc = 1;
-                               break;
-                       }
-                       incomplete_desc = true;
+               if (f->ssp_descriptors) {
+                       speed_desc = f->ssp_descriptors;
+                       want_comp_desc = 1;
+                       break;
                }
+               incomplete_desc = true;
                fallthrough;
        case USB_SPEED_SUPER:
-               if (gadget_is_superspeed(g)) {
-                       if (f->ss_descriptors) {
-                               speed_desc = f->ss_descriptors;
-                               want_comp_desc = 1;
-                               break;
-                       }
-                       incomplete_desc = true;
+               if (f->ss_descriptors) {
+                       speed_desc = f->ss_descriptors;
+                       want_comp_desc = 1;
+                       break;
                }
+               incomplete_desc = true;
                fallthrough;
        case USB_SPEED_HIGH:
-               if (gadget_is_dualspeed(g)) {
-                       if (f->hs_descriptors) {
-                               speed_desc = f->hs_descriptors;
-                               break;
-                       }
-                       incomplete_desc = true;
+               if (f->hs_descriptors) {
+                       speed_desc = f->hs_descriptors;
+                       break;
                }
+               incomplete_desc = true;
                fallthrough;
        default:
                speed_desc = f->fs_descriptors;
index 05507606b2b42ae5e65a992e3421821861d4e641..b1f625245713a47060094e341e076a80fd3225a8 100644 (file)
@@ -162,8 +162,6 @@ int usb_assign_descriptors(struct usb_function *f,
                struct usb_descriptor_header **ss,
                struct usb_descriptor_header **ssp)
 {
-       struct usb_gadget *g = f->config->cdev->gadget;
-
        /* super-speed-plus descriptor falls back to super-speed one,
         * if such a descriptor was provided, thus avoiding a NULL
         * pointer dereference if a 5gbps capable gadget is used with
@@ -177,17 +175,17 @@ int usb_assign_descriptors(struct usb_function *f,
                if (!f->fs_descriptors)
                        goto err;
        }
-       if (hs && gadget_is_dualspeed(g)) {
+       if (hs) {
                f->hs_descriptors = usb_copy_descriptors(hs);
                if (!f->hs_descriptors)
                        goto err;
        }
-       if (ss && gadget_is_superspeed(g)) {
+       if (ss) {
                f->ss_descriptors = usb_copy_descriptors(ss);
                if (!f->ss_descriptors)
                        goto err;
        }
-       if (ssp && gadget_is_superspeed_plus(g)) {
+       if (ssp) {
                f->ssp_descriptors = usb_copy_descriptors(ssp);
                if (!f->ssp_descriptors)
                        goto err;
index 5d3a6cf022185f5155e6b25c897df1198a2ff20a..87917a7d4a9be89f30fc34878978f08446ea6aae 100644 (file)
@@ -44,6 +44,8 @@ usb_f_uvc-y                   := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
 obj-$(CONFIG_USB_F_UVC)                += usb_f_uvc.o
 usb_f_midi-y                   := f_midi.o
 obj-$(CONFIG_USB_F_MIDI)       += usb_f_midi.o
+usb_f_midi2-y                  := f_midi2.o
+obj-$(CONFIG_USB_F_MIDI2)      += usb_f_midi2.o
 usb_f_hid-y                    := f_hid.o
 obj-$(CONFIG_USB_F_HID)                += usb_f_hid.o
 usb_f_printer-y                        := f_printer.o
index cb523f118f041ca76cfa0e98e7c106ef2af3c41c..f616059c5e1e4cce15df371b14bb5963e131f776 100644 (file)
@@ -691,10 +691,8 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
                goto fail;
 
        dev_dbg(&cdev->gadget->dev,
-               "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+               "acm ttyGS%d: IN/%s OUT/%s NOTIFY/%s\n",
                acm->port_num,
-               gadget_is_superspeed(c->cdev->gadget) ? "super" :
-               gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
                acm->port.in->name, acm->port.out->name,
                acm->notify->name);
        return 0;
index c6e63ad77a404067644a118b05eb824ea1dad71f..f55f60639e4251d6ee9ccd28faaeeac8104f353b 100644 (file)
@@ -65,17 +65,6 @@ static inline struct f_ecm *func_to_ecm(struct usb_function *f)
        return container_of(f, struct f_ecm, port.func);
 }
 
-/* peak (theoretical) bulk transfer rate in bits-per-second */
-static inline unsigned ecm_bitrate(struct usb_gadget *g)
-{
-       if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
-               return 13 * 1024 * 8 * 1000 * 8;
-       else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
-               return 13 * 512 * 8 * 1000 * 8;
-       else
-               return 19 * 64 * 1 * 1000 * 8;
-}
-
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -411,10 +400,10 @@ static void ecm_do_notify(struct f_ecm *ecm)
 
                /* SPEED_CHANGE data is up/down speeds in bits/sec */
                data = req->buf + sizeof *event;
-               data[0] = cpu_to_le32(ecm_bitrate(cdev->gadget));
+               data[0] = cpu_to_le32(gether_bitrate(cdev->gadget));
                data[1] = data[0];
 
-               DBG(cdev, "notify speed %d\n", ecm_bitrate(cdev->gadget));
+               DBG(cdev, "notify speed %d\n", gether_bitrate(cdev->gadget));
                ecm->notify_state = ECM_NOTIFY_NONE;
                break;
        }
@@ -799,9 +788,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
        ecm->port.open = ecm_open;
        ecm->port.close = ecm_close;
 
-       DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n",
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+       DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n",
                        ecm->port.in_ep->name, ecm->port.out_ep->name,
                        ecm->notify->name);
        return 0;
index 5d38f29bda720ed40ef9881251f568ad10352604..3b445bd884988ca92d54a50ee0991abd0949fc34 100644 (file)
@@ -311,9 +311,7 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
        if (status)
                goto fail;
 
-       DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n",
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+       DBG(cdev, "CDC Ethernet (EEM): IN/%s OUT/%s\n",
                        eem->port.in_ep->name, eem->port.out_ep->name);
        return 0;
 
index ae41f556eb752b8d4c1628a54cf4f96b1815bb8e..17ac6ace0cffa140e36681aec90d97745b6fc798 100644 (file)
@@ -211,9 +211,7 @@ autoconf_fail:
        if (ret)
                return ret;
 
-       DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
-           (gadget_is_superspeed(c->cdev->gadget) ? "super" :
-            (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
+       DBG(cdev, "%s: IN/%s, OUT/%s\n",
                        f->name, loop->in_ep->name, loop->out_ep->name);
        return 0;
 }
index da07e45ae6df5c16280652152d68d6ee8b4bec28..722a3ab2b337935e546806e21d1eab357f8f54e7 100644 (file)
@@ -927,7 +927,7 @@ static void invalidate_sub(struct fsg_lun *curlun)
 {
        struct file     *filp = curlun->filp;
        struct inode    *inode = file_inode(filp);
-       unsigned long   rc;
+       unsigned long __maybe_unused    rc;
 
        rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
        VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
index fddf539008a99a8f5c26a6f1ee237d5b46ace32b..2d02f25f9597917c7a6628b55a1deec5c70be0d9 100644 (file)
@@ -1023,40 +1023,30 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
        if (!f->fs_descriptors)
                goto fail_f_midi;
 
-       if (gadget_is_dualspeed(c->cdev->gadget)) {
-               bulk_in_desc.wMaxPacketSize = cpu_to_le16(512);
-               bulk_out_desc.wMaxPacketSize = cpu_to_le16(512);
-               f->hs_descriptors = usb_copy_descriptors(midi_function);
-               if (!f->hs_descriptors)
-                       goto fail_f_midi;
-       }
+       bulk_in_desc.wMaxPacketSize = cpu_to_le16(512);
+       bulk_out_desc.wMaxPacketSize = cpu_to_le16(512);
+       f->hs_descriptors = usb_copy_descriptors(midi_function);
+       if (!f->hs_descriptors)
+               goto fail_f_midi;
 
-       if (gadget_is_superspeed(c->cdev->gadget)) {
-               bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024);
-               bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024);
-               i = endpoint_descriptor_index;
-               midi_function[i++] = (struct usb_descriptor_header *)
-                                    &bulk_out_desc;
-               midi_function[i++] = (struct usb_descriptor_header *)
-                                    &bulk_out_ss_comp_desc;
-               midi_function[i++] = (struct usb_descriptor_header *)
-                                    &ms_out_desc;
-               midi_function[i++] = (struct usb_descriptor_header *)
-                                    &bulk_in_desc;
-               midi_function[i++] = (struct usb_descriptor_header *)
-                                    &bulk_in_ss_comp_desc;
-               midi_function[i++] = (struct usb_descriptor_header *)
-                                    &ms_in_desc;
-               f->ss_descriptors = usb_copy_descriptors(midi_function);
-               if (!f->ss_descriptors)
-                       goto fail_f_midi;
-
-               if (gadget_is_superspeed_plus(c->cdev->gadget)) {
-                       f->ssp_descriptors = usb_copy_descriptors(midi_function);
-                       if (!f->ssp_descriptors)
-                               goto fail_f_midi;
-               }
-       }
+       bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024);
+       bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024);
+       i = endpoint_descriptor_index;
+       midi_function[i++] = (struct usb_descriptor_header *)
+                            &bulk_out_desc;
+       midi_function[i++] = (struct usb_descriptor_header *)
+                            &bulk_out_ss_comp_desc;
+       midi_function[i++] = (struct usb_descriptor_header *)
+                            &ms_out_desc;
+       midi_function[i++] = (struct usb_descriptor_header *)
+                            &bulk_in_desc;
+       midi_function[i++] = (struct usb_descriptor_header *)
+                            &bulk_in_ss_comp_desc;
+       midi_function[i++] = (struct usb_descriptor_header *)
+                            &ms_in_desc;
+       f->ss_descriptors = usb_copy_descriptors(midi_function);
+       if (!f->ss_descriptors)
+               goto fail_f_midi;
 
        kfree(midi_function);
 
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
new file mode 100644 (file)
index 0000000..ec8cd7c
--- /dev/null
@@ -0,0 +1,2871 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_midi2.c -- USB MIDI 2.0 class function driver
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/ump.h>
+#include <sound/ump_msg.h>
+#include <sound/ump_convert.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi-v2.h>
+
+#include "u_f.h"
+#include "u_midi2.h"
+
+struct f_midi2;
+struct f_midi2_ep;
+struct f_midi2_usb_ep;
+
+/* Context for each USB request */
+struct f_midi2_req_ctx {
+       struct f_midi2_usb_ep *usb_ep;  /* belonging USB EP */
+       unsigned int index;             /* array index: 0-31 */
+       struct usb_request *req;        /* assigned request */
+};
+
+/* Resources for a USB Endpoint */
+struct f_midi2_usb_ep {
+       struct f_midi2 *card;           /* belonging card */
+       struct f_midi2_ep *ep;          /* belonging UMP EP (optional) */
+       struct usb_ep *usb_ep;          /* assigned USB EP */
+       void (*complete)(struct usb_ep *usb_ep, struct usb_request *req);
+       unsigned long free_reqs;        /* bitmap for unused requests */
+       unsigned int num_reqs;          /* number of allocated requests */
+       struct f_midi2_req_ctx *reqs;   /* request context array */
+};
+
+/* Resources for UMP Function Block (and USB Group Terminal Block) */
+struct f_midi2_block {
+       struct f_midi2_block_info info; /* FB info, copied from configfs */
+       struct snd_ump_block *fb;       /* assigned FB */
+       unsigned int gtb_id;            /* assigned GTB id */
+       unsigned int string_id;         /* assigned string id */
+};
+
+/* Temporary buffer for altset 0 MIDI 1.0 handling */
+struct f_midi2_midi1_port {
+       unsigned int pending; /* pending bytes on the input buffer */
+       u8 buf[32];     /* raw MIDI 1.0 byte input */
+       u8 state;       /* running status */
+       u8 data[2];     /* rendered USB MIDI 1.0 packet data */
+};
+
+/* MIDI 1.0 message states */
+enum {
+       STATE_INITIAL = 0,      /* pseudo state */
+       STATE_1PARAM,
+       STATE_2PARAM_1,
+       STATE_2PARAM_2,
+       STATE_SYSEX_0,
+       STATE_SYSEX_1,
+       STATE_SYSEX_2,
+       STATE_REAL_TIME,
+       STATE_FINISHED,         /* pseudo state */
+};
+
+/* Resources for UMP Endpoint */
+struct f_midi2_ep {
+       struct snd_ump_endpoint *ump;   /* assigned UMP EP */
+       struct f_midi2 *card;           /* belonging MIDI 2.0 device */
+
+       struct f_midi2_ep_info info;    /* UMP EP info, copied from configfs */
+       unsigned int num_blks;          /* number of FBs */
+       struct f_midi2_block blks[SNDRV_UMP_MAX_BLOCKS];        /* UMP FBs */
+
+       struct f_midi2_usb_ep ep_in;    /* USB MIDI EP-in */
+       struct f_midi2_usb_ep ep_out;   /* USB MIDI EP-out */
+
+       u8 in_group_to_cable[SNDRV_UMP_MAX_GROUPS]; /* map to cable; 1-based! */
+};
+
+/* indices for USB strings */
+enum {
+       STR_IFACE = 0,
+       STR_GTB1 = 1,
+};
+
+/* 1-based GTB id to string id */
+#define gtb_to_str_id(id)      (STR_GTB1 + (id) - 1)
+
+/* mapping from MIDI 1.0 cable to UMP group */
+struct midi1_cable_mapping {
+       struct f_midi2_ep *ep;
+       unsigned char block;
+       unsigned char group;
+};
+
+/* operation mode */
+enum {
+       MIDI_OP_MODE_UNSET,     /* no altset set yet */
+       MIDI_OP_MODE_MIDI1,     /* MIDI 1.0 (altset 0) is used */
+       MIDI_OP_MODE_MIDI2,     /* MIDI 2.0 (altset 1) is used */
+};
+
+/* Resources for MIDI 2.0 Device */
+struct f_midi2 {
+       struct usb_function func;
+       struct usb_gadget *gadget;
+       struct snd_card *card;
+
+       /* MIDI 1.0 in/out USB EPs */
+       struct f_midi2_usb_ep midi1_ep_in;
+       struct f_midi2_usb_ep midi1_ep_out;
+
+       /* number of MIDI 1.0 I/O cables */
+       unsigned int num_midi1_in;
+       unsigned int num_midi1_out;
+
+       /* conversion for MIDI 1.0 EP-in */
+       struct f_midi2_midi1_port midi1_port[MAX_CABLES];
+       /* conversion for MIDI 1.0 EP-out */
+       struct ump_cvt_to_ump midi1_ump_cvt;
+       /* mapping between cables and UMP groups */
+       struct midi1_cable_mapping in_cable_mapping[MAX_CABLES];
+       struct midi1_cable_mapping out_cable_mapping[MAX_CABLES];
+
+       int midi_if;                    /* USB MIDI interface number */
+       int operation_mode;             /* current operation mode */
+
+       spinlock_t queue_lock;
+
+       struct f_midi2_card_info info;  /* card info, copied from configfs */
+
+       unsigned int num_eps;
+       struct f_midi2_ep midi2_eps[MAX_UMP_EPS];
+
+       unsigned int total_blocks;      /* total number of blocks of all EPs */
+       struct usb_string *string_defs;
+       struct usb_string *strings;
+};
+
+#define func_to_midi2(f)       container_of(f, struct f_midi2, func)
+
+/* get EP name string */
+static const char *ump_ep_name(const struct f_midi2_ep *ep)
+{
+       return ep->info.ep_name ? ep->info.ep_name : "MIDI 2.0 Gadget";
+}
+
+/* get EP product ID string */
+static const char *ump_product_id(const struct f_midi2_ep *ep)
+{
+       return ep->info.product_id ? ep->info.product_id : "Unique Product ID";
+}
+
+/* get FB name string */
+static const char *ump_fb_name(const struct f_midi2_block_info *info)
+{
+       return info->name ? info->name : "MIDI 2.0 Gadget I/O";
+}
+
+/*
+ * USB Descriptor Definitions
+ */
+/* GTB header descriptor */
+static struct usb_ms20_gr_trm_block_header_descriptor gtb_header_desc = {
+       .bLength =              sizeof(gtb_header_desc),
+       .bDescriptorType =      USB_DT_CS_GR_TRM_BLOCK,
+       .bDescriptorSubtype =   USB_MS_GR_TRM_BLOCK_HEADER,
+       .wTotalLength =         __cpu_to_le16(0x12), // to be filled
+};
+
+/* GTB descriptor template: most items are replaced dynamically */
+static struct usb_ms20_gr_trm_block_descriptor gtb_desc = {
+       .bLength =              sizeof(gtb_desc),
+       .bDescriptorType =      USB_DT_CS_GR_TRM_BLOCK,
+       .bDescriptorSubtype =   USB_MS_GR_TRM_BLOCK,
+       .bGrpTrmBlkID =         0x01,
+       .bGrpTrmBlkType =       USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL,
+       .nGroupTrm =            0x00,
+       .nNumGroupTrm =         1,
+       .iBlockItem =           0,
+       .bMIDIProtocol =        USB_MS_MIDI_PROTO_1_0_64,
+       .wMaxInputBandwidth =   0,
+       .wMaxOutputBandwidth =  0,
+};
+
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(32);
+
+#define EP_MAX_PACKET_INT      8
+
+/* Audio Control Interface */
+static struct usb_interface_descriptor midi2_audio_if_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bInterfaceNumber =     0, // to be filled
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOCONTROL,
+       .bInterfaceProtocol =   0,
+       .iInterface =           0,
+};
+
+static struct uac1_ac_header_descriptor_1 midi2_audio_class_desc = {
+       .bLength =              0x09,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   0x01,
+       .bcdADC =               __cpu_to_le16(0x0100),
+       .wTotalLength =         __cpu_to_le16(0x0009),
+       .bInCollection =        0x01,
+       .baInterfaceNr =        { 0x01 }, // to be filled
+};
+
+/* MIDI 1.0 Streaming Interface (altset 0) */
+static struct usb_interface_descriptor midi2_midi1_if_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bInterfaceNumber =     0, // to be filled
+       .bAlternateSetting =    0,
+       .bNumEndpoints =        2, // to be filled
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_MIDISTREAMING,
+       .bInterfaceProtocol =   0,
+       .iInterface =           0, // to be filled
+};
+
+static struct usb_ms_header_descriptor midi2_midi1_class_desc = {
+       .bLength =              0x07,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_HEADER,
+       .bcdMSC =               __cpu_to_le16(0x0100),
+       .wTotalLength =         __cpu_to_le16(0x41), // to be calculated
+};
+
+/* MIDI 1.0 EP OUT */
+static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_OUT | 0, // set up dynamically
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc = {
+       .bLength                = sizeof(midi2_midi1_ep_out_ss_comp_desc),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_out_class_desc = {
+       .bLength =              0x05, // to be filled
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   USB_MS_GENERAL,
+       .bNumEmbMIDIJack =      1,
+       .baAssocJackID =        { 0x01 },
+};
+
+/* MIDI 1.0 EP IN */
+static struct usb_endpoint_descriptor midi2_midi1_ep_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_IN | 0, // set up dynamically
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc = {
+       .bLength                = sizeof(midi2_midi1_ep_in_ss_comp_desc),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_in_class_desc = {
+       .bLength =              0x05, // to be filled
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   USB_MS_GENERAL,
+       .bNumEmbMIDIJack =      1,
+       .baAssocJackID =        { 0x03 },
+};
+
+/* MIDI 2.0 Streaming Interface (altset 1) */
+static struct usb_interface_descriptor midi2_midi2_if_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bInterfaceNumber =     0, // to be filled
+       .bAlternateSetting =    1,
+       .bNumEndpoints =        2, // to be filled
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_MIDISTREAMING,
+       .bInterfaceProtocol =   0,
+       .iInterface =           0, // to be filled
+};
+
+static struct usb_ms_header_descriptor midi2_midi2_class_desc = {
+       .bLength =              0x07,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_HEADER,
+       .bcdMSC =               __cpu_to_le16(0x0200),
+       .wTotalLength =         __cpu_to_le16(0x07),
+};
+
+/* MIDI 2.0 EP OUT */
+static struct usb_endpoint_descriptor midi2_midi2_ep_out_desc[MAX_UMP_EPS];
+
+static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_out_ss_comp_desc = {
+       .bLength                = sizeof(midi2_midi1_ep_out_ss_comp_desc),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_out_class_desc[MAX_UMP_EPS];
+
+/* MIDI 2.0 EP IN */
+static struct usb_endpoint_descriptor midi2_midi2_ep_in_desc[MAX_UMP_EPS];
+
+static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_in_ss_comp_desc = {
+       .bLength                = sizeof(midi2_midi2_ep_in_ss_comp_desc),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_in_class_desc[MAX_UMP_EPS];
+
+/* Arrays of descriptors to be created */
+static void *midi2_audio_descs[] = {
+       &midi2_audio_if_desc,
+       &midi2_audio_class_desc,
+       NULL
+};
+
+static void *midi2_midi1_descs[] = {
+       &midi2_midi1_if_desc,
+       &midi2_midi1_class_desc,
+       NULL
+};
+
+static void *midi2_midi1_ep_out_descs[] = {
+       &midi2_midi1_ep_out_desc,
+       &midi2_midi1_ep_out_class_desc,
+       NULL
+};
+
+static void *midi2_midi1_ep_in_descs[] = {
+       &midi2_midi1_ep_in_desc,
+       &midi2_midi1_ep_in_class_desc,
+       NULL
+};
+
+static void *midi2_midi1_ep_out_ss_descs[] = {
+       &midi2_midi1_ep_out_desc,
+       &midi2_midi1_ep_out_ss_comp_desc,
+       &midi2_midi1_ep_out_class_desc,
+       NULL
+};
+
+static void *midi2_midi1_ep_in_ss_descs[] = {
+       &midi2_midi1_ep_in_desc,
+       &midi2_midi1_ep_in_ss_comp_desc,
+       &midi2_midi1_ep_in_class_desc,
+       NULL
+};
+
+static void *midi2_midi2_descs[] = {
+       &midi2_midi2_if_desc,
+       &midi2_midi2_class_desc,
+       NULL
+};
+
+/*
+ * USB request handling
+ */
+
+/* get an empty request for the given EP */
+static struct usb_request *get_empty_request(struct f_midi2_usb_ep *usb_ep)
+{
+       struct usb_request *req = NULL;
+       unsigned long flags;
+       int index;
+
+       spin_lock_irqsave(&usb_ep->card->queue_lock, flags);
+       if (!usb_ep->free_reqs)
+               goto unlock;
+       index = find_first_bit(&usb_ep->free_reqs, usb_ep->num_reqs);
+       if (index >= usb_ep->num_reqs)
+               goto unlock;
+       req = usb_ep->reqs[index].req;
+       if (!req)
+               goto unlock;
+       clear_bit(index, &usb_ep->free_reqs);
+       req->length = 0;
+ unlock:
+       spin_unlock_irqrestore(&usb_ep->card->queue_lock, flags);
+       return req;
+}
+
+/* put the empty request back */
+static void put_empty_request(struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctx->usb_ep->card->queue_lock, flags);
+       set_bit(ctx->index, &ctx->usb_ep->free_reqs);
+       spin_unlock_irqrestore(&ctx->usb_ep->card->queue_lock, flags);
+}
+
+/*
+ * UMP v1.1 Stream message handling
+ */
+
+/* queue a request to UMP EP; request is either queued or freed after this */
+static int queue_request_ep_raw(struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       int err;
+
+       req->complete = ctx->usb_ep->complete;
+       err = usb_ep_queue(ctx->usb_ep->usb_ep, req, GFP_ATOMIC);
+       if (err) {
+               put_empty_request(req);
+               return err;
+       }
+       return 0;
+}
+
+/* queue a request with endianness conversion */
+static int queue_request_ep_in(struct usb_request *req)
+{
+       /* UMP packets have to be converted to little-endian */
+       cpu_to_le32_array((u32 *)req->buf, req->length >> 2);
+       return queue_request_ep_raw(req);
+}
+
+/* reply a UMP packet via EP-in */
+static int reply_ep_in(struct f_midi2_ep *ep, const void *buf, int len)
+{
+       struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+       struct usb_request *req;
+
+       req = get_empty_request(usb_ep);
+       if (!req)
+               return -ENOSPC;
+
+       req->length = len;
+       memcpy(req->buf, buf, len);
+       return queue_request_ep_in(req);
+}
+
+/* reply a UMP stream EP info */
+static void reply_ump_stream_ep_info(struct f_midi2_ep *ep)
+{
+       struct snd_ump_stream_msg_ep_info rep = {
+               .type = UMP_MSG_TYPE_STREAM,
+               .status = UMP_STREAM_MSG_STATUS_EP_INFO,
+               .ump_version_major = 0x01,
+               .ump_version_minor = 0x01,
+               .num_function_blocks = ep->num_blks,
+               .static_function_block = !!ep->card->info.static_block,
+               .protocol = (UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 |
+                            UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) >> 8,
+       };
+
+       reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* reply a UMP EP device info */
+static void reply_ump_stream_ep_device(struct f_midi2_ep *ep)
+{
+       struct snd_ump_stream_msg_devince_info rep = {
+               .type = UMP_MSG_TYPE_STREAM,
+               .status = UMP_STREAM_MSG_STATUS_DEVICE_INFO,
+               .manufacture_id = ep->info.manufacturer,
+               .family_lsb = ep->info.family & 0xff,
+               .family_msb = (ep->info.family >> 8) & 0xff,
+               .model_lsb = ep->info.model & 0xff,
+               .model_msb = (ep->info.model >> 8) & 0xff,
+               .sw_revision = ep->info.sw_revision,
+       };
+
+       reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+#define UMP_STREAM_PKT_BYTES   16      /* UMP stream packet size = 16 bytes*/
+#define UMP_STREAM_EP_STR_OFF  2       /* offset of name string for EP info */
+#define UMP_STREAM_FB_STR_OFF  3       /* offset of name string for FB info */
+
+/* Helper to replay a string */
+static void reply_ump_stream_string(struct f_midi2_ep *ep, const u8 *name,
+                                   unsigned int type, unsigned int extra,
+                                   unsigned int start_ofs)
+{
+       struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+       struct f_midi2 *midi2 = ep->card;
+       struct usb_request *req;
+       unsigned int pos;
+       u32 *buf;
+
+       if (!*name)
+               return;
+       req = get_empty_request(usb_ep);
+       if (!req)
+               return;
+
+       buf = (u32 *)req->buf;
+       pos = start_ofs;
+       for (;;) {
+               if (pos == start_ofs) {
+                       memset(buf, 0, UMP_STREAM_PKT_BYTES);
+                       buf[0] = ump_stream_compose(type, 0) | extra;
+               }
+               buf[pos / 4] |= *name++ << ((3 - (pos % 4)) * 8);
+               if (!*name) {
+                       if (req->length)
+                               buf[0] |= UMP_STREAM_MSG_FORMAT_END << 26;
+                       req->length += UMP_STREAM_PKT_BYTES;
+                       break;
+               }
+               if (++pos == UMP_STREAM_PKT_BYTES) {
+                       if (!req->length)
+                               buf[0] |= UMP_STREAM_MSG_FORMAT_START << 26;
+                       else
+                               buf[0] |= UMP_STREAM_MSG_FORMAT_CONTINUE << 26;
+                       req->length += UMP_STREAM_PKT_BYTES;
+                       if (midi2->info.req_buf_size - req->length < UMP_STREAM_PKT_BYTES)
+                               break;
+                       buf += 4;
+                       pos = start_ofs;
+               }
+       }
+
+       if (req->length)
+               queue_request_ep_in(req);
+       else
+               put_empty_request(req);
+}
+
+/* Reply a UMP EP name string */
+static void reply_ump_stream_ep_name(struct f_midi2_ep *ep)
+{
+       reply_ump_stream_string(ep, ump_ep_name(ep),
+                               UMP_STREAM_MSG_STATUS_EP_NAME, 0,
+                               UMP_STREAM_EP_STR_OFF);
+}
+
+/* Reply a UMP EP product ID string */
+static void reply_ump_stream_ep_pid(struct f_midi2_ep *ep)
+{
+       reply_ump_stream_string(ep, ump_product_id(ep),
+                               UMP_STREAM_MSG_STATUS_PRODUCT_ID, 0,
+                               UMP_STREAM_EP_STR_OFF);
+}
+
+/* Reply a UMP EP stream config */
+static void reply_ump_stream_ep_config(struct f_midi2_ep *ep)
+{
+       struct snd_ump_stream_msg_stream_cfg rep = {
+               .type = UMP_MSG_TYPE_STREAM,
+               .status = UMP_STREAM_MSG_STATUS_STREAM_CFG,
+       };
+
+       if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) ==
+           SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+               rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8;
+       else
+               rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8;
+
+       reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* Reply a UMP FB info */
+static void reply_ump_stream_fb_info(struct f_midi2_ep *ep, int blk)
+{
+       struct f_midi2_block_info *b = &ep->blks[blk].info;
+       struct snd_ump_stream_msg_fb_info rep = {
+               .type = UMP_MSG_TYPE_STREAM,
+               .status = UMP_STREAM_MSG_STATUS_FB_INFO,
+               .active = !!b->active,
+               .function_block_id = blk,
+               .ui_hint = b->ui_hint,
+               .midi_10 = b->is_midi1,
+               .direction = b->direction,
+               .first_group = b->first_group,
+               .num_groups = b->num_groups,
+               .midi_ci_version = b->midi_ci_version,
+               .sysex8_streams = b->sysex8_streams,
+       };
+
+       reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* Reply a FB name string */
+static void reply_ump_stream_fb_name(struct f_midi2_ep *ep, unsigned int blk)
+{
+       reply_ump_stream_string(ep, ump_fb_name(&ep->blks[blk].info),
+                               UMP_STREAM_MSG_STATUS_FB_NAME, blk << 8,
+                               UMP_STREAM_FB_STR_OFF);
+}
+
+/* Process a UMP Stream message */
+static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
+{
+       struct f_midi2 *midi2 = ep->card;
+       unsigned int format, status, blk;
+
+       format = ump_stream_message_format(*data);
+       status = ump_stream_message_status(*data);
+       switch (status) {
+       case UMP_STREAM_MSG_STATUS_EP_DISCOVERY:
+               if (format)
+                       return; // invalid
+               if (data[1] & UMP_STREAM_MSG_REQUEST_EP_INFO)
+                       reply_ump_stream_ep_info(ep);
+               if (data[1] & UMP_STREAM_MSG_REQUEST_DEVICE_INFO)
+                       reply_ump_stream_ep_device(ep);
+               if (data[1] & UMP_STREAM_MSG_REQUEST_EP_NAME)
+                       reply_ump_stream_ep_name(ep);
+               if (data[1] & UMP_STREAM_MSG_REQUEST_PRODUCT_ID)
+                       reply_ump_stream_ep_pid(ep);
+               if (data[1] & UMP_STREAM_MSG_REQUEST_STREAM_CFG)
+                       reply_ump_stream_ep_config(ep);
+               return;
+       case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
+               if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) {
+                       ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2;
+                       DBG(midi2, "Switching Protocol to MIDI2\n");
+               } else {
+                       ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+                       DBG(midi2, "Switching Protocol to MIDI1\n");
+               }
+               snd_ump_switch_protocol(ep->ump, ep->info.protocol);
+               reply_ump_stream_ep_config(ep);
+               return;
+       case UMP_STREAM_MSG_STATUS_FB_DISCOVERY:
+               if (format)
+                       return; // invalid
+               blk = (*data >> 8) & 0xff;
+               if (blk >= ep->num_blks)
+                       return;
+               if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO)
+                       reply_ump_stream_fb_info(ep, blk);
+               if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME)
+                       reply_ump_stream_fb_name(ep, blk);
+               return;
+       }
+}
+
+/* Process UMP messages included in a USB request */
+static void process_ump(struct f_midi2_ep *ep, const struct usb_request *req)
+{
+       const u32 *data = (u32 *)req->buf;
+       int len = req->actual >> 2;
+       const u32 *in_buf = ep->ump->input_buf;
+
+       for (; len > 0; len--, data++) {
+               if (snd_ump_receive_ump_val(ep->ump, *data) <= 0)
+                       continue;
+               if (ump_message_type(*in_buf) == UMP_MSG_TYPE_STREAM)
+                       process_ump_stream_msg(ep, in_buf);
+       }
+}
+
+/*
+ * MIDI 2.0 UMP USB request handling
+ */
+
+/* complete handler for UMP EP-out requests */
+static void f_midi2_ep_out_complete(struct usb_ep *usb_ep,
+                                   struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       struct f_midi2_ep *ep = ctx->usb_ep->ep;
+       struct f_midi2 *midi2 = ep->card;
+       int status = req->status;
+
+       if (status) {
+               DBG(midi2, "%s complete error %d: %d/%d\n",
+                   usb_ep->name, status, req->actual, req->length);
+               goto error;
+       }
+
+       /* convert to UMP packet in native endianness */
+       le32_to_cpu_array((u32 *)req->buf, req->actual >> 2);
+
+       if (midi2->info.process_ump)
+               process_ump(ep, req);
+
+       snd_ump_receive(ep->ump, req->buf, req->actual & ~3);
+
+       if (midi2->operation_mode != MIDI_OP_MODE_MIDI2)
+               goto error;
+
+       if (queue_request_ep_raw(req))
+               goto error;
+       return;
+
+ error:
+       put_empty_request(req);
+}
+
+/* Transmit UMP packets received from user-space to the gadget */
+static void process_ump_transmit(struct f_midi2_ep *ep)
+{
+       struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+       struct f_midi2 *midi2 = ep->card;
+       struct usb_request *req;
+       int len;
+
+       if (!usb_ep->usb_ep->enabled)
+               return;
+
+       for (;;) {
+               req = get_empty_request(usb_ep);
+               if (!req)
+                       break;
+               len = snd_ump_transmit(ep->ump, (u32 *)req->buf,
+                                      midi2->info.req_buf_size);
+               if (len <= 0) {
+                       put_empty_request(req);
+                       break;
+               }
+
+               req->length = len;
+               if (queue_request_ep_in(req) < 0)
+                       break;
+       }
+}
+
+/* Complete handler for UMP EP-in requests */
+static void f_midi2_ep_in_complete(struct usb_ep *usb_ep,
+                                  struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       struct f_midi2_ep *ep = ctx->usb_ep->ep;
+       struct f_midi2 *midi2 = ep->card;
+       int status = req->status;
+
+       put_empty_request(req);
+
+       if (status) {
+               DBG(midi2, "%s complete error %d: %d/%d\n",
+                   usb_ep->name, status, req->actual, req->length);
+               return;
+       }
+
+       process_ump_transmit(ep);
+}
+
+/*
+ * MIDI1 (altset 0) USB request handling
+ */
+
+/* process one MIDI byte -- copied from f_midi.c
+ *
+ * fill the packet or request if needed
+ * returns true if the request became empty (queued)
+ */
+static bool process_midi1_byte(struct f_midi2 *midi2, u8 cable, u8 b,
+                              struct usb_request **req_p)
+{
+       struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
+       u8 p[4] = { cable << 4, 0, 0, 0 };
+       int next_state = STATE_INITIAL;
+       struct usb_request *req = *req_p;
+
+       switch (b) {
+       case 0xf8 ... 0xff:
+               /* System Real-Time Messages */
+               p[0] |= 0x0f;
+               p[1] = b;
+               next_state = port->state;
+               port->state = STATE_REAL_TIME;
+               break;
+
+       case 0xf7:
+               /* End of SysEx */
+               switch (port->state) {
+               case STATE_SYSEX_0:
+                       p[0] |= 0x05;
+                       p[1] = 0xf7;
+                       next_state = STATE_FINISHED;
+                       break;
+               case STATE_SYSEX_1:
+                       p[0] |= 0x06;
+                       p[1] = port->data[0];
+                       p[2] = 0xf7;
+                       next_state = STATE_FINISHED;
+                       break;
+               case STATE_SYSEX_2:
+                       p[0] |= 0x07;
+                       p[1] = port->data[0];
+                       p[2] = port->data[1];
+                       p[3] = 0xf7;
+                       next_state = STATE_FINISHED;
+                       break;
+               default:
+                       /* Ignore byte */
+                       next_state = port->state;
+                       port->state = STATE_INITIAL;
+               }
+               break;
+
+       case 0xf0 ... 0xf6:
+               /* System Common Messages */
+               port->data[0] = port->data[1] = 0;
+               port->state = STATE_INITIAL;
+               switch (b) {
+               case 0xf0:
+                       port->data[0] = b;
+                       port->data[1] = 0;
+                       next_state = STATE_SYSEX_1;
+                       break;
+               case 0xf1:
+               case 0xf3:
+                       port->data[0] = b;
+                       next_state = STATE_1PARAM;
+                       break;
+               case 0xf2:
+                       port->data[0] = b;
+                       next_state = STATE_2PARAM_1;
+                       break;
+               case 0xf4:
+               case 0xf5:
+                       next_state = STATE_INITIAL;
+                       break;
+               case 0xf6:
+                       p[0] |= 0x05;
+                       p[1] = 0xf6;
+                       next_state = STATE_FINISHED;
+                       break;
+               }
+               break;
+
+       case 0x80 ... 0xef:
+               /*
+                * Channel Voice Messages, Channel Mode Messages
+                * and Control Change Messages.
+                */
+               port->data[0] = b;
+               port->data[1] = 0;
+               port->state = STATE_INITIAL;
+               if (b >= 0xc0 && b <= 0xdf)
+                       next_state = STATE_1PARAM;
+               else
+                       next_state = STATE_2PARAM_1;
+               break;
+
+       case 0x00 ... 0x7f:
+               /* Message parameters */
+               switch (port->state) {
+               case STATE_1PARAM:
+                       if (port->data[0] < 0xf0)
+                               p[0] |= port->data[0] >> 4;
+                       else
+                               p[0] |= 0x02;
+
+                       p[1] = port->data[0];
+                       p[2] = b;
+                       /* This is to allow Running State Messages */
+                       next_state = STATE_1PARAM;
+                       break;
+               case STATE_2PARAM_1:
+                       port->data[1] = b;
+                       next_state = STATE_2PARAM_2;
+                       break;
+               case STATE_2PARAM_2:
+                       if (port->data[0] < 0xf0)
+                               p[0] |= port->data[0] >> 4;
+                       else
+                               p[0] |= 0x03;
+
+                       p[1] = port->data[0];
+                       p[2] = port->data[1];
+                       p[3] = b;
+                       /* This is to allow Running State Messages */
+                       next_state = STATE_2PARAM_1;
+                       break;
+               case STATE_SYSEX_0:
+                       port->data[0] = b;
+                       next_state = STATE_SYSEX_1;
+                       break;
+               case STATE_SYSEX_1:
+                       port->data[1] = b;
+                       next_state = STATE_SYSEX_2;
+                       break;
+               case STATE_SYSEX_2:
+                       p[0] |= 0x04;
+                       p[1] = port->data[0];
+                       p[2] = port->data[1];
+                       p[3] = b;
+                       next_state = STATE_SYSEX_0;
+                       break;
+               }
+               break;
+       }
+
+       /* States where we have to write into the USB request */
+       if (next_state == STATE_FINISHED ||
+           port->state == STATE_SYSEX_2 ||
+           port->state == STATE_1PARAM ||
+           port->state == STATE_2PARAM_2 ||
+           port->state == STATE_REAL_TIME) {
+               memcpy(req->buf + req->length, p, sizeof(p));
+               req->length += sizeof(p);
+
+               if (next_state == STATE_FINISHED) {
+                       next_state = STATE_INITIAL;
+                       port->data[0] = port->data[1] = 0;
+               }
+
+               if (midi2->info.req_buf_size - req->length <= 4) {
+                       queue_request_ep_raw(req);
+                       *req_p = NULL;
+                       return true;
+               }
+       }
+
+       port->state = next_state;
+       return false;
+}
+
+/* process all pending MIDI bytes in the internal buffer;
+ * returns true if the request gets empty
+ * returns false if all have been processed
+ */
+static bool process_midi1_pending_buf(struct f_midi2 *midi2,
+                                     struct usb_request **req_p)
+{
+       unsigned int cable, c;
+
+       for (cable = 0; cable < midi2->num_midi1_in; cable++) {
+               struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
+
+               if (!port->pending)
+                       continue;
+               for (c = 0; c < port->pending; c++) {
+                       if (process_midi1_byte(midi2, cable, port->buf[c],
+                                              req_p)) {
+                               port->pending -= c;
+                               if (port->pending)
+                                       memmove(port->buf, port->buf + c,
+                                               port->pending);
+                               return true;
+                       }
+               }
+               port->pending = 0;
+       }
+
+       return false;
+}
+
+/* fill the MIDI bytes onto the temporary buffer
+ */
+static void fill_midi1_pending_buf(struct f_midi2 *midi2, u8 cable, u8 *buf,
+                                  unsigned int size)
+{
+       struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
+
+       if (port->pending + size > sizeof(port->buf))
+               return;
+       memcpy(port->buf + port->pending, buf, size);
+       port->pending += size;
+}
+
+/* try to process data given from the associated UMP stream */
+static void process_midi1_transmit(struct f_midi2 *midi2)
+{
+       struct f_midi2_usb_ep *usb_ep = &midi2->midi1_ep_in;
+       struct f_midi2_ep *ep = &midi2->midi2_eps[0];
+       struct usb_request *req = NULL;
+       /* 12 is the largest outcome (4 MIDI1 cmds) for a single UMP packet */
+       unsigned char outbuf[12];
+       unsigned char group, cable;
+       int len, size;
+       u32 ump;
+
+       if (!usb_ep->usb_ep || !usb_ep->usb_ep->enabled)
+               return;
+
+       for (;;) {
+               if (!req) {
+                       req = get_empty_request(usb_ep);
+                       if (!req)
+                               break;
+               }
+
+               if (process_midi1_pending_buf(midi2, &req))
+                       continue;
+
+               len = snd_ump_transmit(ep->ump, &ump, 4);
+               if (len <= 0)
+                       break;
+               if (snd_ump_receive_ump_val(ep->ump, ump) <= 0)
+                       continue;
+               size = snd_ump_convert_from_ump(ep->ump->input_buf, outbuf,
+                                               &group);
+               if (size <= 0)
+                       continue;
+               cable = ep->in_group_to_cable[group];
+               if (!cable)
+                       continue;
+               cable--; /* to 0-base */
+               fill_midi1_pending_buf(midi2, cable, outbuf, size);
+       }
+
+       if (req) {
+               if (req->length)
+                       queue_request_ep_raw(req);
+               else
+                       put_empty_request(req);
+       }
+}
+
+/* complete handler for MIDI1 EP-in requests */
+static void f_midi2_midi1_ep_in_complete(struct usb_ep *usb_ep,
+                                        struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       struct f_midi2 *midi2 = ctx->usb_ep->card;
+       int status = req->status;
+
+       put_empty_request(req);
+
+       if (status) {
+               DBG(midi2, "%s complete error %d: %d/%d\n",
+                   usb_ep->name, status, req->actual, req->length);
+               return;
+       }
+
+       process_midi1_transmit(midi2);
+}
+
+/* complete handler for MIDI1 EP-out requests */
+static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep,
+                                         struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       struct f_midi2 *midi2 = ctx->usb_ep->card;
+       struct f_midi2_ep *ep;
+       struct ump_cvt_to_ump *cvt = &midi2->midi1_ump_cvt;
+       static const u8 midi1_packet_bytes[16] = {
+               0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+       };
+       unsigned int group, cable, bytes, c, len;
+       int status = req->status;
+       const u8 *buf = req->buf;
+
+       if (status) {
+               DBG(midi2, "%s complete error %d: %d/%d\n",
+                   usb_ep->name, status, req->actual, req->length);
+               goto error;
+       }
+
+       len = req->actual >> 2;
+       for (; len; len--, buf += 4) {
+               cable = *buf >> 4;
+               ep = midi2->out_cable_mapping[cable].ep;
+               if (!ep)
+                       continue;
+               group = midi2->out_cable_mapping[cable].group;
+               bytes = midi1_packet_bytes[*buf & 0x0f];
+               for (c = 0; c < bytes; c++) {
+                       snd_ump_convert_to_ump(cvt, group, ep->info.protocol,
+                                              buf[c + 1]);
+                       if (cvt->ump_bytes) {
+                               snd_ump_receive(ep->ump, cvt->ump,
+                                               cvt->ump_bytes);
+                               cvt->ump_bytes = 0;
+                       }
+               }
+       }
+
+       if (midi2->operation_mode != MIDI_OP_MODE_MIDI1)
+               goto error;
+
+       if (queue_request_ep_raw(req))
+               goto error;
+       return;
+
+ error:
+       put_empty_request(req);
+}
+
+/*
+ * Common EP handling helpers
+ */
+
+/* Start MIDI EP */
+static int f_midi2_start_ep(struct f_midi2_usb_ep *usb_ep,
+                           struct usb_function *fn)
+{
+       int err;
+
+       if (!usb_ep->usb_ep)
+               return 0;
+
+       usb_ep_disable(usb_ep->usb_ep);
+       err = config_ep_by_speed(usb_ep->card->gadget, fn, usb_ep->usb_ep);
+       if (err)
+               return err;
+       return usb_ep_enable(usb_ep->usb_ep);
+}
+
+/* Drop pending requests */
+static void f_midi2_drop_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+       int i;
+
+       if (!usb_ep->usb_ep || !usb_ep->num_reqs)
+               return;
+
+       for (i = 0; i < usb_ep->num_reqs; i++) {
+               if (!test_bit(i, &usb_ep->free_reqs) && usb_ep->reqs[i].req) {
+                       usb_ep_dequeue(usb_ep->usb_ep, usb_ep->reqs[i].req);
+                       set_bit(i, &usb_ep->free_reqs);
+               }
+       }
+}
+
+/* Allocate requests for the given EP */
+static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+       struct f_midi2 *midi2 = usb_ep->card;
+       int i;
+
+       if (!usb_ep->usb_ep)
+               return 0;
+       if (!usb_ep->reqs)
+               return -EINVAL;
+
+       for (i = 0; i < midi2->info.num_reqs; i++) {
+               if (usb_ep->reqs[i].req)
+                       continue;
+               usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep,
+                                                  midi2->info.req_buf_size);
+               if (!usb_ep->reqs[i].req)
+                       return -ENOMEM;
+               usb_ep->reqs[i].req->context = &usb_ep->reqs[i];
+       }
+       return 0;
+}
+
+/* Free allocated requests */
+static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+       struct f_midi2 *midi2 = usb_ep->card;
+       int i;
+
+       for (i = 0; i < midi2->info.num_reqs; i++) {
+               if (!usb_ep->reqs[i].req)
+                       continue;
+               free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req);
+               usb_ep->reqs[i].req = NULL;
+       }
+}
+
+/* Initialize EP */
+static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep,
+                          struct f_midi2_usb_ep *usb_ep,
+                          void *desc,
+                          void (*complete)(struct usb_ep *usb_ep,
+                                           struct usb_request *req))
+{
+       int i;
+
+       usb_ep->card = midi2;
+       usb_ep->ep = ep;
+       usb_ep->usb_ep = usb_ep_autoconfig(midi2->gadget, desc);
+       if (!usb_ep->usb_ep)
+               return -ENODEV;
+       usb_ep->complete = complete;
+
+       usb_ep->reqs = kcalloc(midi2->info.num_reqs, sizeof(*usb_ep->reqs),
+                              GFP_KERNEL);
+       if (!usb_ep->reqs)
+               return -ENOMEM;
+       for (i = 0; i < midi2->info.num_reqs; i++) {
+               usb_ep->reqs[i].index = i;
+               usb_ep->reqs[i].usb_ep = usb_ep;
+               set_bit(i, &usb_ep->free_reqs);
+               usb_ep->num_reqs++;
+       }
+
+       return 0;
+}
+
+/* Free EP */
+static void f_midi2_free_ep(struct f_midi2_usb_ep *usb_ep)
+{
+       f_midi2_drop_reqs(usb_ep);
+
+       f_midi2_free_ep_reqs(usb_ep);
+
+       kfree(usb_ep->reqs);
+       usb_ep->num_reqs = 0;
+       usb_ep->free_reqs = 0;
+       usb_ep->reqs = NULL;
+}
+
+/* Queue requests for EP-out at start */
+static void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+       int i, err;
+
+       if (!usb_ep->usb_ep)
+               return;
+
+       for (i = 0; i < usb_ep->num_reqs; i++) {
+               if (!test_bit(i, &usb_ep->free_reqs) || !usb_ep->reqs[i].req)
+                       continue;
+               usb_ep->reqs[i].req->complete = usb_ep->complete;
+               err = usb_ep_queue(usb_ep->usb_ep, usb_ep->reqs[i].req,
+                                  GFP_ATOMIC);
+               if (!err)
+                       clear_bit(i, &usb_ep->free_reqs);
+       }
+}
+
+/*
+ * Gadget Function callbacks
+ */
+
+/* stop both IN and OUT EPs */
+static void f_midi2_stop_eps(struct f_midi2_usb_ep *ep_in,
+                            struct f_midi2_usb_ep *ep_out)
+{
+       f_midi2_drop_reqs(ep_in);
+       f_midi2_drop_reqs(ep_out);
+       f_midi2_free_ep_reqs(ep_in);
+       f_midi2_free_ep_reqs(ep_out);
+}
+
+/* start/queue both IN and OUT EPs */
+static int f_midi2_start_eps(struct f_midi2_usb_ep *ep_in,
+                            struct f_midi2_usb_ep *ep_out,
+                            struct usb_function *fn)
+{
+       int err;
+
+       err = f_midi2_start_ep(ep_in, fn);
+       if (err)
+               return err;
+       err = f_midi2_start_ep(ep_out, fn);
+       if (err)
+               return err;
+
+       err = f_midi2_alloc_ep_reqs(ep_in);
+       if (err)
+               return err;
+       err = f_midi2_alloc_ep_reqs(ep_out);
+       if (err)
+               return err;
+
+       f_midi2_queue_out_reqs(ep_out);
+       return 0;
+}
+
+/* gadget function set_alt callback */
+static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf,
+                          unsigned int alt)
+{
+       struct f_midi2 *midi2 = func_to_midi2(fn);
+       struct f_midi2_ep *ep;
+       int i, op_mode, err;
+
+       if (intf != midi2->midi_if || alt > 1)
+               return 0;
+
+       if (alt == 0)
+               op_mode = MIDI_OP_MODE_MIDI1;
+       else if (alt == 1)
+               op_mode = MIDI_OP_MODE_MIDI2;
+       else
+               op_mode = MIDI_OP_MODE_UNSET;
+
+       if (midi2->operation_mode == op_mode)
+               return 0;
+
+       midi2->operation_mode = op_mode;
+
+       if (op_mode != MIDI_OP_MODE_MIDI1)
+               f_midi2_stop_eps(&midi2->midi1_ep_in, &midi2->midi1_ep_out);
+
+       if (op_mode != MIDI_OP_MODE_MIDI2) {
+               for (i = 0; i < midi2->num_eps; i++) {
+                       ep = &midi2->midi2_eps[i];
+                       f_midi2_stop_eps(&ep->ep_in, &ep->ep_out);
+               }
+       }
+
+       if (op_mode == MIDI_OP_MODE_MIDI1)
+               return f_midi2_start_eps(&midi2->midi1_ep_in,
+                                        &midi2->midi1_ep_out, fn);
+
+       if (op_mode == MIDI_OP_MODE_MIDI2) {
+               for (i = 0; i < midi2->num_eps; i++) {
+                       ep = &midi2->midi2_eps[i];
+
+                       err = f_midi2_start_eps(&ep->ep_in, &ep->ep_out, fn);
+                       if (err)
+                               return err;
+               }
+       }
+
+       return 0;
+}
+
+/* gadget function get_alt callback */
+static int f_midi2_get_alt(struct usb_function *fn, unsigned int intf)
+{
+       struct f_midi2 *midi2 = func_to_midi2(fn);
+
+       if (intf == midi2->midi_if &&
+           midi2->operation_mode == MIDI_OP_MODE_MIDI2)
+               return 1;
+       return 0;
+}
+
+/* convert UMP direction to USB MIDI 2.0 direction */
+static unsigned int ump_to_usb_dir(unsigned int ump_dir)
+{
+       switch (ump_dir) {
+       case SNDRV_UMP_DIR_INPUT:
+               return USB_MS_GR_TRM_BLOCK_TYPE_INPUT_ONLY;
+       case SNDRV_UMP_DIR_OUTPUT:
+               return USB_MS_GR_TRM_BLOCK_TYPE_OUTPUT_ONLY;
+       default:
+               return USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL;
+       }
+}
+
+/* assign GTB descriptors (for the given request) */
+static void assign_block_descriptors(struct f_midi2 *midi2,
+                                    struct usb_request *req,
+                                    int max_len)
+{
+       struct usb_ms20_gr_trm_block_header_descriptor header;
+       struct usb_ms20_gr_trm_block_descriptor *desc;
+       struct f_midi2_block_info *b;
+       struct f_midi2_ep *ep;
+       int i, blk, len;
+       char *data;
+
+       len = sizeof(gtb_header_desc) + sizeof(gtb_desc) * midi2->total_blocks;
+       if (WARN_ON(len > midi2->info.req_buf_size))
+               return;
+
+       header = gtb_header_desc;
+       header.wTotalLength = cpu_to_le16(len);
+       if (max_len < len) {
+               len = min_t(int, len, sizeof(header));
+               memcpy(req->buf, &header, len);
+               req->length = len;
+               req->zero = len < max_len;
+               return;
+       }
+
+       memcpy(req->buf, &header, sizeof(header));
+       data = req->buf + sizeof(header);
+       for (i = 0; i < midi2->num_eps; i++) {
+               ep = &midi2->midi2_eps[i];
+               for (blk = 0; blk < ep->num_blks; blk++) {
+                       b = &ep->blks[blk].info;
+                       desc = (struct usb_ms20_gr_trm_block_descriptor *)data;
+
+                       *desc = gtb_desc;
+                       desc->bGrpTrmBlkID = ep->blks[blk].gtb_id;
+                       desc->bGrpTrmBlkType = ump_to_usb_dir(b->direction);
+                       desc->nGroupTrm = b->first_group;
+                       desc->nNumGroupTrm = b->num_groups;
+                       desc->iBlockItem = ep->blks[blk].string_id;
+
+                       if (ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+                               desc->bMIDIProtocol = USB_MS_MIDI_PROTO_2_0;
+                       else
+                               desc->bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_128;
+
+                       if (b->is_midi1 == 2) {
+                               desc->wMaxInputBandwidth = cpu_to_le16(1);
+                               desc->wMaxOutputBandwidth = cpu_to_le16(1);
+                       }
+
+                       data += sizeof(*desc);
+               }
+       }
+
+       req->length = len;
+       req->zero = len < max_len;
+}
+
+/* gadget function setup callback: handle GTB requests */
+static int f_midi2_setup(struct usb_function *fn,
+                        const struct usb_ctrlrequest *ctrl)
+{
+       struct f_midi2 *midi2 = func_to_midi2(fn);
+       struct usb_composite_dev *cdev = fn->config->cdev;
+       struct usb_request *req = cdev->req;
+       u16 value, length;
+
+       if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD ||
+           ctrl->bRequest != USB_REQ_GET_DESCRIPTOR)
+               return -EOPNOTSUPP;
+
+       value = le16_to_cpu(ctrl->wValue);
+       length = le16_to_cpu(ctrl->wLength);
+
+       if ((value >> 8) != USB_DT_CS_GR_TRM_BLOCK)
+               return -EOPNOTSUPP;
+
+       /* handle only altset 1 */
+       if ((value & 0xff) != 1)
+               return -EOPNOTSUPP;
+
+       assign_block_descriptors(midi2, req, length);
+       return usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+}
+
+/* gadget function disable callback */
+static void f_midi2_disable(struct usb_function *fn)
+{
+       struct f_midi2 *midi2 = func_to_midi2(fn);
+
+       midi2->operation_mode = MIDI_OP_MODE_UNSET;
+}
+
+/*
+ * ALSA UMP ops: most of them are NOPs, only trigger for write is needed
+ */
+static int f_midi2_ump_open(struct snd_ump_endpoint *ump, int dir)
+{
+       return 0;
+}
+
+static void f_midi2_ump_close(struct snd_ump_endpoint *ump, int dir)
+{
+}
+
+static void f_midi2_ump_trigger(struct snd_ump_endpoint *ump, int dir, int up)
+{
+       struct f_midi2_ep *ep = ump->private_data;
+       struct f_midi2 *midi2 = ep->card;
+
+       if (up && dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
+               switch (midi2->operation_mode) {
+               case MIDI_OP_MODE_MIDI1:
+                       process_midi1_transmit(midi2);
+                       break;
+               case MIDI_OP_MODE_MIDI2:
+                       process_ump_transmit(ep);
+                       break;
+               }
+       }
+}
+
+static void f_midi2_ump_drain(struct snd_ump_endpoint *ump, int dir)
+{
+}
+
+static const struct snd_ump_ops f_midi2_ump_ops = {
+       .open = f_midi2_ump_open,
+       .close = f_midi2_ump_close,
+       .trigger = f_midi2_ump_trigger,
+       .drain = f_midi2_ump_drain,
+};
+
+/*
+ * "Operation Mode" control element
+ */
+static int f_midi2_operation_mode_info(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = MIDI_OP_MODE_UNSET;
+       uinfo->value.integer.max = MIDI_OP_MODE_MIDI2;
+       return 0;
+}
+
+static int f_midi2_operation_mode_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct f_midi2 *midi2 = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = midi2->operation_mode;
+       return 0;
+}
+
+static const struct snd_kcontrol_new operation_mode_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+       .name = "Operation Mode",
+       .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info = f_midi2_operation_mode_info,
+       .get = f_midi2_operation_mode_get,
+};
+
+/*
+ * ALSA UMP instance creation / deletion
+ */
+static void f_midi2_free_card(struct f_midi2 *midi2)
+{
+       if (midi2->card) {
+               snd_card_free_when_closed(midi2->card);
+               midi2->card = NULL;
+       }
+}
+
+/* use a reverse direction for the gadget host */
+static int reverse_dir(int dir)
+{
+       if (!dir || dir == SNDRV_UMP_DIR_BIDIRECTION)
+               return dir;
+       return (dir == SNDRV_UMP_DIR_OUTPUT) ?
+               SNDRV_UMP_DIR_INPUT : SNDRV_UMP_DIR_OUTPUT;
+}
+
+static int f_midi2_create_card(struct f_midi2 *midi2)
+{
+       struct snd_card *card;
+       struct snd_ump_endpoint *ump;
+       struct f_midi2_ep *ep;
+       int i, id, blk, err;
+       __be32 sw;
+
+       err = snd_card_new(&midi2->gadget->dev, -1, NULL, THIS_MODULE, 0,
+                          &card);
+       if (err < 0)
+               return err;
+       midi2->card = card;
+
+       strcpy(card->driver, "f_midi2");
+       strcpy(card->shortname, "MIDI 2.0 Gadget");
+       strcpy(card->longname, "MIDI 2.0 Gadget");
+
+       id = 0;
+       for (i = 0; i < midi2->num_eps; i++) {
+               ep = &midi2->midi2_eps[i];
+               err = snd_ump_endpoint_new(card, "MIDI 2.0 Gadget", id,
+                                          1, 1, &ump);
+               if (err < 0)
+                       goto error;
+               id++;
+
+               ep->ump = ump;
+               ump->no_process_stream = true;
+               ump->private_data = ep;
+               ump->ops = &f_midi2_ump_ops;
+               if (midi2->info.static_block)
+                       ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
+               ump->info.protocol_caps = (ep->info.protocol_caps & 3) << 8;
+               ump->info.protocol = (ep->info.protocol & 3) << 8;
+               ump->info.version = 0x0101;
+               ump->info.family_id = ep->info.family;
+               ump->info.model_id = ep->info.model;
+               ump->info.manufacturer_id = ep->info.manufacturer & 0xffffff;
+               sw = cpu_to_be32(ep->info.sw_revision);
+               memcpy(ump->info.sw_revision, &sw, 4);
+
+               strscpy(ump->info.name, ump_ep_name(ep),
+                       sizeof(ump->info.name));
+               strscpy(ump->info.product_id, ump_product_id(ep),
+                       sizeof(ump->info.product_id));
+               strscpy(ump->core.name, ump->info.name, sizeof(ump->core.name));
+
+               for (blk = 0; blk < ep->num_blks; blk++) {
+                       const struct f_midi2_block_info *b = &ep->blks[blk].info;
+                       struct snd_ump_block *fb;
+
+                       err = snd_ump_block_new(ump, blk,
+                                               reverse_dir(b->direction),
+                                               b->first_group, b->num_groups,
+                                               &ep->blks[blk].fb);
+                       if (err < 0)
+                               goto error;
+                       fb = ep->blks[blk].fb;
+                       fb->info.active = !!b->active;
+                       fb->info.midi_ci_version = b->midi_ci_version;
+                       fb->info.ui_hint = reverse_dir(b->ui_hint);
+                       fb->info.sysex8_streams = b->sysex8_streams;
+                       fb->info.flags |= b->is_midi1;
+                       strscpy(fb->info.name, ump_fb_name(b),
+                               sizeof(fb->info.name));
+               }
+       }
+
+       for (i = 0; i < midi2->num_eps; i++) {
+               err = snd_ump_attach_legacy_rawmidi(midi2->midi2_eps[i].ump,
+                                                   "Legacy MIDI", id);
+               if (err < 0)
+                       goto error;
+               id++;
+       }
+
+       err = snd_ctl_add(card, snd_ctl_new1(&operation_mode_ctl, midi2));
+       if (err < 0)
+               goto error;
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       return 0;
+
+ error:
+       f_midi2_free_card(midi2);
+       return err;
+}
+
+/*
+ * Creation of USB descriptors
+ */
+struct f_midi2_usb_config {
+       struct usb_descriptor_header **list;
+       unsigned int size;
+       unsigned int alloc;
+
+       /* MIDI 1.0 jacks */
+       unsigned char jack_in, jack_out, jack_id;
+       struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES];
+       struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES];
+};
+
+static int append_config(struct f_midi2_usb_config *config, void *d)
+{
+       unsigned int size;
+       void *buf;
+
+       if (config->size + 2 >= config->alloc) {
+               size = config->size + 16;
+               buf = krealloc(config->list, size * sizeof(void *), GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
+               config->list = buf;
+               config->alloc = size;
+       }
+
+       config->list[config->size] = d;
+       config->size++;
+       config->list[config->size] = NULL;
+       return 0;
+}
+
+static int append_configs(struct f_midi2_usb_config *config, void **d)
+{
+       int err;
+
+       for (; *d; d++) {
+               err = append_config(config, *d);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static int append_midi1_in_jack(struct f_midi2 *midi2,
+                               struct f_midi2_usb_config *config,
+                               struct midi1_cable_mapping *map,
+                               unsigned int type)
+{
+       struct usb_midi_in_jack_descriptor *jack =
+               &config->jack_ins[config->jack_in++];
+       int id = ++config->jack_id;
+       int err;
+
+       jack->bLength = 0x06;
+       jack->bDescriptorType = USB_DT_CS_INTERFACE;
+       jack->bDescriptorSubtype = USB_MS_MIDI_IN_JACK;
+       jack->bJackType = type;
+       jack->bJackID = id;
+       /* use the corresponding block name as jack name */
+       if (map->ep)
+               jack->iJack = map->ep->blks[map->block].string_id;
+
+       err = append_config(config, jack);
+       if (err < 0)
+               return err;
+       return id;
+}
+
+static int append_midi1_out_jack(struct f_midi2 *midi2,
+                                struct f_midi2_usb_config *config,
+                                struct midi1_cable_mapping *map,
+                                unsigned int type, unsigned int source)
+{
+       struct usb_midi_out_jack_descriptor_1 *jack =
+               &config->jack_outs[config->jack_out++];
+       int id = ++config->jack_id;
+       int err;
+
+       jack->bLength = 0x09;
+       jack->bDescriptorType = USB_DT_CS_INTERFACE;
+       jack->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK;
+       jack->bJackType = type;
+       jack->bJackID = id;
+       jack->bNrInputPins = 1;
+       jack->pins[0].baSourceID = source;
+       jack->pins[0].baSourcePin = 0x01;
+       /* use the corresponding block name as jack name */
+       if (map->ep)
+               jack->iJack = map->ep->blks[map->block].string_id;
+
+       err = append_config(config, jack);
+       if (err < 0)
+               return err;
+       return id;
+}
+
+static int f_midi2_create_usb_configs(struct f_midi2 *midi2,
+                                     struct f_midi2_usb_config *config,
+                                     int speed)
+{
+       void **midi1_in_eps, **midi1_out_eps;
+       int i, jack, total;
+       int err;
+
+       switch (speed) {
+       default:
+       case USB_SPEED_HIGH:
+               midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(512);
+               midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(512);
+               for (i = 0; i < midi2->num_eps; i++)
+                       midi2_midi2_ep_out_desc[i].wMaxPacketSize =
+                               cpu_to_le16(512);
+               fallthrough;
+       case USB_SPEED_FULL:
+               midi1_in_eps = midi2_midi1_ep_in_descs;
+               midi1_out_eps = midi2_midi1_ep_out_descs;
+               break;
+       case USB_SPEED_SUPER:
+               midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(1024);
+               midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(1024);
+               for (i = 0; i < midi2->num_eps; i++)
+                       midi2_midi2_ep_out_desc[i].wMaxPacketSize =
+                               cpu_to_le16(1024);
+               midi1_in_eps = midi2_midi1_ep_in_ss_descs;
+               midi1_out_eps = midi2_midi1_ep_out_ss_descs;
+               break;
+       }
+
+       err = append_configs(config, midi2_audio_descs);
+       if (err < 0)
+               return err;
+
+       if (midi2->num_midi1_in && midi2->num_midi1_out)
+               midi2_midi1_if_desc.bNumEndpoints = 2;
+       else
+               midi2_midi1_if_desc.bNumEndpoints = 1;
+
+       err = append_configs(config, midi2_midi1_descs);
+       if (err < 0)
+               return err;
+
+       total = USB_DT_MS_HEADER_SIZE;
+       if (midi2->num_midi1_out) {
+               midi2_midi1_ep_out_class_desc.bLength =
+                       USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_out);
+               total += midi2_midi1_ep_out_class_desc.bLength;
+               midi2_midi1_ep_out_class_desc.bNumEmbMIDIJack =
+                       midi2->num_midi1_out;
+               total += midi2->num_midi1_out *
+                       (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
+               for (i = 0; i < midi2->num_midi1_out; i++) {
+                       jack = append_midi1_in_jack(midi2, config,
+                                                   &midi2->in_cable_mapping[i],
+                                                   USB_MS_EMBEDDED);
+                       if (jack < 0)
+                               return jack;
+                       midi2_midi1_ep_out_class_desc.baAssocJackID[i] = jack;
+                       jack = append_midi1_out_jack(midi2, config,
+                                                    &midi2->in_cable_mapping[i],
+                                                    USB_MS_EXTERNAL, jack);
+                       if (jack < 0)
+                               return jack;
+               }
+       }
+
+       if (midi2->num_midi1_in) {
+               midi2_midi1_ep_in_class_desc.bLength =
+                       USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_in);
+               total += midi2_midi1_ep_in_class_desc.bLength;
+               midi2_midi1_ep_in_class_desc.bNumEmbMIDIJack =
+                       midi2->num_midi1_in;
+               total += midi2->num_midi1_in *
+                       (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
+               for (i = 0; i < midi2->num_midi1_in; i++) {
+                       jack = append_midi1_in_jack(midi2, config,
+                                                   &midi2->out_cable_mapping[i],
+                                                   USB_MS_EXTERNAL);
+                       if (jack < 0)
+                               return jack;
+                       jack = append_midi1_out_jack(midi2, config,
+                                                    &midi2->out_cable_mapping[i],
+                                                    USB_MS_EMBEDDED, jack);
+                       if (jack < 0)
+                               return jack;
+                       midi2_midi1_ep_in_class_desc.baAssocJackID[i] = jack;
+               }
+       }
+
+       midi2_midi1_class_desc.wTotalLength = cpu_to_le16(total);
+
+       if (midi2->num_midi1_out) {
+               err = append_configs(config, midi1_out_eps);
+               if (err < 0)
+                       return err;
+       }
+       if (midi2->num_midi1_in) {
+               err = append_configs(config, midi1_in_eps);
+               if (err < 0)
+                       return err;
+       }
+
+       err = append_configs(config, midi2_midi2_descs);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < midi2->num_eps; i++) {
+               err = append_config(config, &midi2_midi2_ep_out_desc[i]);
+               if (err < 0)
+                       return err;
+               if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) {
+                       err = append_config(config, &midi2_midi2_ep_out_ss_comp_desc);
+                       if (err < 0)
+                               return err;
+               }
+               err = append_config(config, &midi2_midi2_ep_out_class_desc[i]);
+               if (err < 0)
+                       return err;
+               err = append_config(config, &midi2_midi2_ep_in_desc[i]);
+               if (err < 0)
+                       return err;
+               if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) {
+                       err = append_config(config, &midi2_midi2_ep_in_ss_comp_desc);
+                       if (err < 0)
+                               return err;
+               }
+               err = append_config(config, &midi2_midi2_ep_in_class_desc[i]);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void f_midi2_free_usb_configs(struct f_midi2_usb_config *config)
+{
+       kfree(config->list);
+       memset(config, 0, sizeof(*config));
+}
+
+/* as we use the static descriptors for simplicity, serialize bind call */
+static DEFINE_MUTEX(f_midi2_desc_mutex);
+
+/* fill MIDI2 EP class-specific descriptor */
+static void fill_midi2_class_desc(struct f_midi2_ep *ep,
+                                 struct usb_ms20_endpoint_descriptor_32 *cdesc)
+{
+       int blk;
+
+       cdesc->bLength = USB_DT_MS20_ENDPOINT_SIZE(ep->num_blks);
+       cdesc->bDescriptorType = USB_DT_CS_ENDPOINT;
+       cdesc->bDescriptorSubtype = USB_MS_GENERAL_2_0;
+       cdesc->bNumGrpTrmBlock = ep->num_blks;
+       for (blk = 0; blk < ep->num_blks; blk++)
+               cdesc->baAssoGrpTrmBlkID[blk] = ep->blks[blk].gtb_id;
+}
+
+/* initialize MIDI2 EP-in */
+static int f_midi2_init_midi2_ep_in(struct f_midi2 *midi2, int index)
+{
+       struct f_midi2_ep *ep = &midi2->midi2_eps[index];
+       struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_in_desc[index];
+
+       desc->bLength = USB_DT_ENDPOINT_SIZE;
+       desc->bDescriptorType = USB_DT_ENDPOINT;
+       desc->bEndpointAddress = USB_DIR_IN;
+       desc->bmAttributes = USB_ENDPOINT_XFER_INT;
+       desc->wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET_INT);
+       desc->bInterval = 1;
+
+       fill_midi2_class_desc(ep, &midi2_midi2_ep_in_class_desc[index]);
+
+       return f_midi2_init_ep(midi2, ep, &ep->ep_in, desc,
+                              f_midi2_ep_in_complete);
+}
+
+/* initialize MIDI2 EP-out */
+static int f_midi2_init_midi2_ep_out(struct f_midi2 *midi2, int index)
+{
+       struct f_midi2_ep *ep = &midi2->midi2_eps[index];
+       struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_out_desc[index];
+
+       desc->bLength = USB_DT_ENDPOINT_SIZE;
+       desc->bDescriptorType = USB_DT_ENDPOINT;
+       desc->bEndpointAddress = USB_DIR_OUT;
+       desc->bmAttributes = USB_ENDPOINT_XFER_BULK;
+
+       fill_midi2_class_desc(ep, &midi2_midi2_ep_out_class_desc[index]);
+
+       return f_midi2_init_ep(midi2, ep, &ep->ep_out, desc,
+                              f_midi2_ep_out_complete);
+}
+
+/* gadget function bind callback */
+static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_midi2 *midi2 = func_to_midi2(f);
+       struct f_midi2_ep *ep;
+       struct f_midi2_usb_config config = {};
+       struct usb_gadget_strings string_fn = {
+               .language = 0x0409,     /* en-us */
+               .strings = midi2->string_defs,
+       };
+       struct usb_gadget_strings *strings[] = {
+               &string_fn,
+               NULL,
+       };
+       int i, blk, status;
+
+       midi2->gadget = cdev->gadget;
+       midi2->operation_mode = MIDI_OP_MODE_UNSET;
+
+       status = f_midi2_create_card(midi2);
+       if (status < 0)
+               goto fail_register;
+
+       /* maybe allocate device-global string ID */
+       midi2->strings = usb_gstrings_attach(c->cdev, strings,
+                                            midi2->total_blocks + 1);
+       if (IS_ERR(midi2->strings)) {
+               status = PTR_ERR(midi2->strings);
+               goto fail_string;
+       }
+
+       mutex_lock(&f_midi2_desc_mutex);
+       midi2_midi1_if_desc.iInterface = midi2->strings[STR_IFACE].id;
+       midi2_midi2_if_desc.iInterface = midi2->strings[STR_IFACE].id;
+       for (i = 0; i < midi2->num_eps; i++) {
+               ep = &midi2->midi2_eps[i];
+               for (blk = 0; blk < ep->num_blks; blk++)
+                       ep->blks[blk].string_id =
+                               midi2->strings[gtb_to_str_id(ep->blks[blk].gtb_id)].id;
+       }
+
+       midi2_midi2_if_desc.bNumEndpoints = midi2->num_eps * 2;
+
+       /* audio interface */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       midi2_audio_if_desc.bInterfaceNumber = status;
+
+       /* MIDI streaming */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       midi2->midi_if = status;
+       midi2_midi1_if_desc.bInterfaceNumber = status;
+       midi2_midi2_if_desc.bInterfaceNumber = status;
+       midi2_audio_class_desc.baInterfaceNr[0] = status;
+
+       /* allocate instance-specific endpoints */
+       if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_OUTPUT) {
+               status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_in,
+                                        &midi2_midi1_ep_in_desc,
+                                        f_midi2_midi1_ep_in_complete);
+               if (status)
+                       goto fail;
+       }
+
+       if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_INPUT) {
+               status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_out,
+                                        &midi2_midi1_ep_out_desc,
+                                        f_midi2_midi1_ep_out_complete);
+               if (status)
+                       goto fail;
+       }
+
+       for (i = 0; i < midi2->num_eps; i++) {
+               status = f_midi2_init_midi2_ep_in(midi2, i);
+               if (status)
+                       goto fail;
+               status = f_midi2_init_midi2_ep_out(midi2, i);
+               if (status)
+                       goto fail;
+       }
+
+       status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_FULL);
+       if (status < 0)
+               goto fail;
+       f->fs_descriptors = usb_copy_descriptors(config.list);
+       if (!f->fs_descriptors) {
+               status = -ENOMEM;
+               goto fail;
+       }
+       f_midi2_free_usb_configs(&config);
+
+       status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_HIGH);
+       if (status < 0)
+               goto fail;
+       f->hs_descriptors = usb_copy_descriptors(config.list);
+       if (!f->hs_descriptors) {
+               status = -ENOMEM;
+               goto fail;
+       }
+       f_midi2_free_usb_configs(&config);
+
+       status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_SUPER);
+       if (status < 0)
+               goto fail;
+       f->ss_descriptors = usb_copy_descriptors(config.list);
+       if (!f->ss_descriptors) {
+               status = -ENOMEM;
+               goto fail;
+       }
+       f_midi2_free_usb_configs(&config);
+
+       mutex_unlock(&f_midi2_desc_mutex);
+       return 0;
+
+fail:
+       f_midi2_free_usb_configs(&config);
+       mutex_unlock(&f_midi2_desc_mutex);
+       usb_free_all_descriptors(f);
+fail_string:
+       f_midi2_free_card(midi2);
+fail_register:
+       ERROR(midi2, "%s: can't bind, err %d\n", f->name, status);
+       return status;
+}
+
+/* gadget function unbind callback */
+static void f_midi2_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_midi2 *midi2 = func_to_midi2(f);
+       int i;
+
+       f_midi2_free_card(midi2);
+
+       f_midi2_free_ep(&midi2->midi1_ep_in);
+       f_midi2_free_ep(&midi2->midi1_ep_out);
+       for (i = 0; i < midi2->num_eps; i++) {
+               f_midi2_free_ep(&midi2->midi2_eps[i].ep_in);
+               f_midi2_free_ep(&midi2->midi2_eps[i].ep_out);
+       }
+
+       usb_free_all_descriptors(f);
+}
+
+/*
+ * ConfigFS interface
+ */
+
+/* type conversion helpers */
+static inline struct f_midi2_opts *to_f_midi2_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_midi2_opts,
+                           func_inst.group);
+}
+
+static inline struct f_midi2_ep_opts *
+to_f_midi2_ep_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_midi2_ep_opts,
+                           group);
+}
+
+static inline struct f_midi2_block_opts *
+to_f_midi2_block_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_midi2_block_opts,
+                           group);
+}
+
+/* trim the string to be usable for EP and FB name strings */
+static void make_name_string(char *s)
+{
+       char *p;
+
+       p = strchr(s, '\n');
+       if (p)
+               *p = 0;
+
+       p = s + strlen(s);
+       for (; p > s && isspace(*p); p--)
+               *p = 0;
+}
+
+/* configfs helpers: generic show/store for unisnged int */
+static ssize_t f_midi2_opts_uint_show(struct f_midi2_opts *opts,
+                                     u32 val, const char *format, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, format, val);
+       mutex_unlock(&opts->lock);
+       return result;
+}
+
+static ssize_t f_midi2_opts_uint_store(struct f_midi2_opts *opts,
+                                      u32 *valp, u32 minval, u32 maxval,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u32 val;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou32(page, 0, &val);
+       if (ret)
+               goto end;
+       if (val < minval || val > maxval) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       *valp = val;
+       ret = len;
+
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+/* generic store for bool */
+static ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts,
+                                      bool *valp, const char *page, size_t len)
+{
+       int ret;
+       bool val;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtobool(page, &val);
+       if (ret)
+               goto end;
+       *valp = val;
+       ret = len;
+
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+/* generic show/store for string */
+static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts,
+                                    const char *str, char *page)
+{
+       int result = 0;
+
+       mutex_lock(&opts->lock);
+       if (str)
+               result = scnprintf(page, PAGE_SIZE, "%s\n", str);
+       mutex_unlock(&opts->lock);
+       return result;
+}
+
+static ssize_t f_midi2_opts_str_store(struct f_midi2_opts *opts,
+                                     const char **strp, size_t maxlen,
+                                     const char *page, size_t len)
+{
+       char *c;
+       int ret;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       c = kstrndup(page, min(len, maxlen), GFP_KERNEL);
+       if (!c) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       kfree(*strp);
+       make_name_string(c);
+       *strp = c;
+       ret = len;
+
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+/*
+ * Definitions for UMP Block config
+ */
+
+/* define an uint option for block */
+#define F_MIDI2_BLOCK_OPT(name, format, minval, maxval)                        \
+static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
+                                         char *page)                   \
+{                                                                      \
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);  \
+       return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name,  \
+                                     format "\n", page);               \
+}                                                                      \
+                                                                       \
+static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
+                                        const char *page, size_t len)  \
+{                                                                      \
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);  \
+       return f_midi2_opts_uint_store(opts->ep->opts, &opts->info.name,\
+                                      minval, maxval, page, len);      \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_midi2_block_opts_, name)
+
+/* define a boolean option for block */
+#define F_MIDI2_BLOCK_BOOL_OPT(name)                                   \
+static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
+                                         char *page)                   \
+{                                                                      \
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);  \
+       return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name,  \
+                                     "%u\n", page);                    \
+}                                                                      \
+                                                                       \
+static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
+                                        const char *page, size_t len)  \
+{                                                                      \
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);  \
+       return f_midi2_opts_bool_store(opts->ep->opts, &opts->info.name,\
+                                      page, len);                      \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_midi2_block_opts_, name)
+
+F_MIDI2_BLOCK_OPT(direction, "0x%x", 1, 3);
+F_MIDI2_BLOCK_OPT(first_group, "0x%x", 0, 15);
+F_MIDI2_BLOCK_OPT(num_groups, "0x%x", 1, 16);
+F_MIDI2_BLOCK_OPT(midi1_first_group, "0x%x", 0, 15);
+F_MIDI2_BLOCK_OPT(midi1_num_groups, "0x%x", 0, 16);
+F_MIDI2_BLOCK_OPT(ui_hint, "0x%x", 0, 3);
+F_MIDI2_BLOCK_OPT(midi_ci_version, "%u", 0, 1);
+F_MIDI2_BLOCK_OPT(sysex8_streams, "%u", 0, 255);
+F_MIDI2_BLOCK_OPT(is_midi1, "%u", 0, 2);
+F_MIDI2_BLOCK_BOOL_OPT(active);
+
+static ssize_t f_midi2_block_opts_name_show(struct config_item *item,
+                                           char *page)
+{
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+       return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page);
+}
+
+static ssize_t f_midi2_block_opts_name_store(struct config_item *item,
+                                            const char *page, size_t len)
+{
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+       return f_midi2_opts_str_store(opts->ep->opts, &opts->info.name, 128,
+                                     page, len);
+}
+
+CONFIGFS_ATTR(f_midi2_block_opts_, name);
+
+static struct configfs_attribute *f_midi2_block_attrs[] = {
+       &f_midi2_block_opts_attr_direction,
+       &f_midi2_block_opts_attr_first_group,
+       &f_midi2_block_opts_attr_num_groups,
+       &f_midi2_block_opts_attr_midi1_first_group,
+       &f_midi2_block_opts_attr_midi1_num_groups,
+       &f_midi2_block_opts_attr_ui_hint,
+       &f_midi2_block_opts_attr_midi_ci_version,
+       &f_midi2_block_opts_attr_sysex8_streams,
+       &f_midi2_block_opts_attr_is_midi1,
+       &f_midi2_block_opts_attr_active,
+       &f_midi2_block_opts_attr_name,
+       NULL,
+};
+
+static void f_midi2_block_opts_release(struct config_item *item)
+{
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+       kfree(opts->info.name);
+       kfree(opts);
+}
+
+static struct configfs_item_operations f_midi2_block_item_ops = {
+       .release        = f_midi2_block_opts_release,
+};
+
+static const struct config_item_type f_midi2_block_type = {
+       .ct_item_ops    = &f_midi2_block_item_ops,
+       .ct_attrs       = f_midi2_block_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* create a f_midi2_block_opts instance for the given block number */
+static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
+                                    unsigned int blk,
+                                    struct f_midi2_block_opts **block_p)
+{
+       struct f_midi2_block_opts *block_opts;
+       int ret = 0;
+
+       mutex_lock(&ep_opts->opts->lock);
+       if (ep_opts->opts->refcnt || ep_opts->blks[blk]) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL);
+       if (!block_opts) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       block_opts->ep = ep_opts;
+       block_opts->id = blk;
+
+       /* set up the default values */
+       block_opts->info.direction = SNDRV_UMP_DIR_BIDIRECTION;
+       block_opts->info.first_group = 0;
+       block_opts->info.num_groups = 1;
+       block_opts->info.ui_hint = SNDRV_UMP_BLOCK_UI_HINT_BOTH;
+       block_opts->info.active = 1;
+
+       ep_opts->blks[blk] = block_opts;
+       *block_p = block_opts;
+
+ out:
+       mutex_unlock(&ep_opts->opts->lock);
+       return ret;
+}
+
+/* make_group callback for a block */
+static struct config_group *
+f_midi2_opts_block_make(struct config_group *group, const char *name)
+{
+       struct f_midi2_ep_opts *ep_opts;
+       struct f_midi2_block_opts *block_opts;
+       unsigned int blk;
+       int ret;
+
+       if (strncmp(name, "block.", 6))
+               return ERR_PTR(-EINVAL);
+       ret = kstrtouint(name + 6, 10, &blk);
+       if (ret)
+               return ERR_PTR(ret);
+
+       ep_opts = to_f_midi2_ep_opts(&group->cg_item);
+
+       if (blk >= SNDRV_UMP_MAX_BLOCKS)
+               return ERR_PTR(-EINVAL);
+       if (ep_opts->blks[blk])
+               return ERR_PTR(-EBUSY);
+       ret = f_midi2_block_opts_create(ep_opts, blk, &block_opts);
+       if (ret)
+               return ERR_PTR(ret);
+
+       config_group_init_type_name(&block_opts->group, name,
+                                   &f_midi2_block_type);
+       return &block_opts->group;
+}
+
+/* drop_item callback for a block */
+static void
+f_midi2_opts_block_drop(struct config_group *group, struct config_item *item)
+{
+       struct f_midi2_block_opts *block_opts = to_f_midi2_block_opts(item);
+
+       mutex_lock(&block_opts->ep->opts->lock);
+       block_opts->ep->blks[block_opts->id] = NULL;
+       mutex_unlock(&block_opts->ep->opts->lock);
+       config_item_put(item);
+}
+
+/*
+ * Definitions for UMP Endpoint config
+ */
+
+/* define an uint option for EP */
+#define F_MIDI2_EP_OPT(name, format, minval, maxval)                   \
+static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
+                                            char *page)                \
+{                                                                      \
+       struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);        \
+       return f_midi2_opts_uint_show(opts->opts, opts->info.name,      \
+                                     format "\n", page);               \
+}                                                                      \
+                                                                       \
+static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item,        \
+                                          const char *page, size_t len)\
+{                                                                      \
+       struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);        \
+       return f_midi2_opts_uint_store(opts->opts, &opts->info.name,    \
+                                      minval, maxval, page, len);      \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_midi2_ep_opts_, name)
+
+/* define a string option for EP */
+#define F_MIDI2_EP_STR_OPT(name, maxlen)                               \
+static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
+                                            char *page)                \
+{                                                                      \
+       struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);        \
+       return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\
+}                                                                      \
+                                                                       \
+static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item,        \
+                                        const char *page, size_t len)  \
+{                                                                      \
+       struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);        \
+       return f_midi2_opts_str_store(opts->opts, &opts->info.name, maxlen,\
+                                     page, len);                       \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_midi2_ep_opts_, name)
+
+F_MIDI2_EP_OPT(protocol, "0x%x", 1, 2);
+F_MIDI2_EP_OPT(protocol_caps, "0x%x", 1, 3);
+F_MIDI2_EP_OPT(manufacturer, "0x%x", 0, 0xffffff);
+F_MIDI2_EP_OPT(family, "0x%x", 0, 0xffff);
+F_MIDI2_EP_OPT(model, "0x%x", 0, 0xffff);
+F_MIDI2_EP_OPT(sw_revision, "0x%x", 0, 0xffffffff);
+F_MIDI2_EP_STR_OPT(ep_name, 128);
+F_MIDI2_EP_STR_OPT(product_id, 128);
+
+static struct configfs_attribute *f_midi2_ep_attrs[] = {
+       &f_midi2_ep_opts_attr_protocol,
+       &f_midi2_ep_opts_attr_protocol_caps,
+       &f_midi2_ep_opts_attr_ep_name,
+       &f_midi2_ep_opts_attr_product_id,
+       &f_midi2_ep_opts_attr_manufacturer,
+       &f_midi2_ep_opts_attr_family,
+       &f_midi2_ep_opts_attr_model,
+       &f_midi2_ep_opts_attr_sw_revision,
+       NULL,
+};
+
+static void f_midi2_ep_opts_release(struct config_item *item)
+{
+       struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+
+       kfree(opts->info.ep_name);
+       kfree(opts->info.product_id);
+       kfree(opts);
+}
+
+static struct configfs_item_operations f_midi2_ep_item_ops = {
+       .release        = f_midi2_ep_opts_release,
+};
+
+static struct configfs_group_operations f_midi2_ep_group_ops = {
+       .make_group     = f_midi2_opts_block_make,
+       .drop_item      = f_midi2_opts_block_drop,
+};
+
+static const struct config_item_type f_midi2_ep_type = {
+       .ct_item_ops    = &f_midi2_ep_item_ops,
+       .ct_group_ops   = &f_midi2_ep_group_ops,
+       .ct_attrs       = f_midi2_ep_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* create a f_midi2_ep_opts instance */
+static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
+                                 unsigned int index,
+                                 struct f_midi2_ep_opts **ep_p)
+{
+       struct f_midi2_ep_opts *ep_opts;
+
+       ep_opts = kzalloc(sizeof(*ep_opts), GFP_KERNEL);
+       if (!ep_opts)
+               return -ENOMEM;
+
+       ep_opts->opts = opts;
+       ep_opts->index = index;
+
+       /* set up the default values */
+       ep_opts->info.protocol = 2;
+       ep_opts->info.protocol_caps = 3;
+
+       opts->eps[index] = ep_opts;
+       *ep_p = ep_opts;
+       return 0;
+}
+
+/* make_group callback for an EP */
+static struct config_group *
+f_midi2_opts_ep_make(struct config_group *group, const char *name)
+{
+       struct f_midi2_opts *opts;
+       struct f_midi2_ep_opts *ep_opts;
+       unsigned int index;
+       int ret;
+
+       if (strncmp(name, "ep.", 3))
+               return ERR_PTR(-EINVAL);
+       ret = kstrtouint(name + 3, 10, &index);
+       if (ret)
+               return ERR_PTR(ret);
+
+       opts = to_f_midi2_opts(&group->cg_item);
+       if (index >= MAX_UMP_EPS)
+               return ERR_PTR(-EINVAL);
+       if (opts->eps[index])
+               return ERR_PTR(-EBUSY);
+       ret = f_midi2_ep_opts_create(opts, index, &ep_opts);
+       if (ret)
+               return ERR_PTR(ret);
+
+       config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type);
+       return &ep_opts->group;
+}
+
+/* drop_item callback for an EP */
+static void
+f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item)
+{
+       struct f_midi2_ep_opts *ep_opts = to_f_midi2_ep_opts(item);
+
+       mutex_lock(&ep_opts->opts->lock);
+       ep_opts->opts->eps[ep_opts->index] = NULL;
+       mutex_unlock(&ep_opts->opts->lock);
+       config_item_put(item);
+}
+
+/*
+ * Definitions for card config
+ */
+
+/* define a bool option for card */
+#define F_MIDI2_BOOL_OPT(name)                                         \
+static ssize_t f_midi2_opts_##name##_show(struct config_item *item,    \
+                                         char *page)                   \
+{                                                                      \
+       struct f_midi2_opts *opts = to_f_midi2_opts(item);              \
+       return f_midi2_opts_uint_show(opts, opts->info.name,            \
+                                     "%u\n", page);                    \
+}                                                                      \
+                                                                       \
+static ssize_t f_midi2_opts_##name##_store(struct config_item *item,   \
+                                        const char *page, size_t len)  \
+{                                                                      \
+       struct f_midi2_opts *opts = to_f_midi2_opts(item);              \
+       return f_midi2_opts_bool_store(opts, &opts->info.name,          \
+                                      page, len);                      \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_midi2_opts_, name)
+
+F_MIDI2_BOOL_OPT(process_ump);
+F_MIDI2_BOOL_OPT(static_block);
+
+static ssize_t f_midi2_opts_iface_name_show(struct config_item *item,
+                                           char *page)
+{
+       struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+       return f_midi2_opts_str_show(opts, opts->info.iface_name, page);
+}
+
+static ssize_t f_midi2_opts_iface_name_store(struct config_item *item,
+                                            const char *page, size_t len)
+{
+       struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+       return f_midi2_opts_str_store(opts, &opts->info.iface_name, 128,
+                                     page, len);
+}
+
+CONFIGFS_ATTR(f_midi2_opts_, iface_name);
+
+static struct configfs_attribute *f_midi2_attrs[] = {
+       &f_midi2_opts_attr_process_ump,
+       &f_midi2_opts_attr_static_block,
+       &f_midi2_opts_attr_iface_name,
+       NULL
+};
+
+static void f_midi2_opts_release(struct config_item *item)
+{
+       struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_midi2_item_ops = {
+       .release        = f_midi2_opts_release,
+};
+
+static struct configfs_group_operations f_midi2_group_ops = {
+       .make_group     = f_midi2_opts_ep_make,
+       .drop_item      = f_midi2_opts_ep_drop,
+};
+
+static const struct config_item_type f_midi2_func_type = {
+       .ct_item_ops    = &f_midi2_item_ops,
+       .ct_group_ops   = &f_midi2_group_ops,
+       .ct_attrs       = f_midi2_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void f_midi2_free_inst(struct usb_function_instance *f)
+{
+       struct f_midi2_opts *opts;
+
+       opts = container_of(f, struct f_midi2_opts, func_inst);
+
+       kfree(opts->info.iface_name);
+       kfree(opts);
+}
+
+/* gadget alloc_inst */
+static struct usb_function_instance *f_midi2_alloc_inst(void)
+{
+       struct f_midi2_opts *opts;
+       struct f_midi2_ep_opts *ep_opts;
+       struct f_midi2_block_opts *block_opts;
+       int ret;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+
+       mutex_init(&opts->lock);
+       opts->func_inst.free_func_inst = f_midi2_free_inst;
+       opts->info.process_ump = true;
+       opts->info.static_block = true;
+       opts->info.num_reqs = 32;
+       opts->info.req_buf_size = 512;
+
+       /* create the default ep */
+       ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
+       if (ret) {
+               kfree(opts);
+               return ERR_PTR(ret);
+       }
+
+       /* create the default block */
+       ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
+       if (ret) {
+               kfree(ep_opts);
+               kfree(opts);
+               return ERR_PTR(ret);
+       }
+
+       /* set up the default MIDI1 (that is mandatory) */
+       block_opts->info.midi1_num_groups = 1;
+
+       config_group_init_type_name(&opts->func_inst.group, "",
+                                   &f_midi2_func_type);
+
+       config_group_init_type_name(&ep_opts->group, "ep.0",
+                                   &f_midi2_ep_type);
+       configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
+
+       config_group_init_type_name(&block_opts->group, "block.0",
+                                   &f_midi2_block_type);
+       configfs_add_default_group(&block_opts->group, &ep_opts->group);
+
+       return &opts->func_inst;
+}
+
+static void do_f_midi2_free(struct f_midi2 *midi2, struct f_midi2_opts *opts)
+{
+       mutex_lock(&opts->lock);
+       --opts->refcnt;
+       mutex_unlock(&opts->lock);
+       kfree(midi2->string_defs);
+       kfree(midi2);
+}
+
+static void f_midi2_free(struct usb_function *f)
+{
+       do_f_midi2_free(func_to_midi2(f),
+                       container_of(f->fi, struct f_midi2_opts, func_inst));
+}
+
+/* verify the parameters set up via configfs;
+ * return the number of EPs or a negative error
+ */
+static int verify_parameters(struct f_midi2_opts *opts)
+{
+       int i, j, num_eps, num_blks;
+       struct f_midi2_ep_info *ep;
+       struct f_midi2_block_info *bp;
+
+       for (num_eps = 0; num_eps < MAX_UMP_EPS && opts->eps[num_eps];
+            num_eps++)
+               ;
+       if (!num_eps) {
+               pr_err("f_midi2: No EP is defined\n");
+               return -EINVAL;
+       }
+
+       num_blks = 0;
+       for (i = 0; i < num_eps; i++) {
+               ep = &opts->eps[i]->info;
+               if (!(ep->protocol_caps & ep->protocol)) {
+                       pr_err("f_midi2: Invalid protocol 0x%x (caps 0x%x) for EP %d\n",
+                              ep->protocol, ep->protocol_caps, i);
+                       return -EINVAL;
+               }
+
+               for (j = 0; j < SNDRV_UMP_MAX_BLOCKS && opts->eps[i]->blks[j];
+                    j++, num_blks++) {
+                       bp = &opts->eps[i]->blks[j]->info;
+                       if (bp->first_group + bp->num_groups > SNDRV_UMP_MAX_GROUPS) {
+                               pr_err("f_midi2: Invalid group definitions for block %d:%d\n",
+                                      i, j);
+                               return -EINVAL;
+                       }
+
+                       if (bp->midi1_num_groups) {
+                               if (bp->midi1_first_group < bp->first_group ||
+                                   bp->midi1_first_group + bp->midi1_num_groups >
+                                   bp->first_group + bp->num_groups) {
+                                       pr_err("f_midi2: Invalid MIDI1 group definitions for block %d:%d\n",
+                                              i, j);
+                                       return -EINVAL;
+                               }
+                       }
+               }
+       }
+       if (!num_blks) {
+               pr_err("f_midi2: No block is defined\n");
+               return -EINVAL;
+       }
+
+       return num_eps;
+}
+
+/* fill mapping between MIDI 1.0 cable and UMP EP/group */
+static void fill_midi1_cable_mapping(struct f_midi2 *midi2,
+                                    struct f_midi2_ep *ep,
+                                    int blk)
+{
+       const struct f_midi2_block_info *binfo = &ep->blks[blk].info;
+       struct midi1_cable_mapping *map;
+       int i, group;
+
+       if (!binfo->midi1_num_groups)
+               return;
+       if (binfo->direction != SNDRV_UMP_DIR_OUTPUT) {
+               group = binfo->midi1_first_group;
+               map = midi2->in_cable_mapping + midi2->num_midi1_in;
+               for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) {
+                       if (midi2->num_midi1_in >= MAX_CABLES)
+                               break;
+                       map->ep = ep;
+                       map->block = blk;
+                       map->group = group;
+                       midi2->num_midi1_in++;
+                       /* store 1-based cable number */
+                       ep->in_group_to_cable[group] = midi2->num_midi1_in;
+               }
+       }
+
+       if (binfo->direction != SNDRV_UMP_DIR_INPUT) {
+               group = binfo->midi1_first_group;
+               map = midi2->out_cable_mapping + midi2->num_midi1_out;
+               for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) {
+                       if (midi2->num_midi1_out >= MAX_CABLES)
+                               break;
+                       map->ep = ep;
+                       map->block = blk;
+                       map->group = group;
+                       midi2->num_midi1_out++;
+               }
+       }
+}
+
+/* gadget alloc callback */
+static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
+{
+       struct f_midi2 *midi2;
+       struct f_midi2_opts *opts;
+       struct f_midi2_ep *ep;
+       struct f_midi2_block *bp;
+       int i, num_eps, blk;
+
+       midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL);
+       if (!midi2)
+               return ERR_PTR(-ENOMEM);
+
+       opts = container_of(fi, struct f_midi2_opts, func_inst);
+       mutex_lock(&opts->lock);
+       num_eps = verify_parameters(opts);
+       if (num_eps < 0) {
+               mutex_unlock(&opts->lock);
+               kfree(midi2);
+               return ERR_PTR(num_eps);
+       }
+       ++opts->refcnt;
+       mutex_unlock(&opts->lock);
+
+       spin_lock_init(&midi2->queue_lock);
+
+       midi2->func.name = "midi2_func";
+       midi2->func.bind = f_midi2_bind;
+       midi2->func.unbind = f_midi2_unbind;
+       midi2->func.get_alt = f_midi2_get_alt;
+       midi2->func.set_alt = f_midi2_set_alt;
+       midi2->func.setup = f_midi2_setup;
+       midi2->func.disable = f_midi2_disable;
+       midi2->func.free_func = f_midi2_free;
+
+       midi2->info = opts->info;
+       midi2->num_eps = num_eps;
+
+       for (i = 0; i < num_eps; i++) {
+               ep = &midi2->midi2_eps[i];
+               ep->info = opts->eps[i]->info;
+               ep->card = midi2;
+               for (blk = 0; blk < SNDRV_UMP_MAX_BLOCKS &&
+                            opts->eps[i]->blks[blk]; blk++) {
+                       bp = &ep->blks[blk];
+                       ep->num_blks++;
+                       bp->info = opts->eps[i]->blks[blk]->info;
+                       bp->gtb_id = ++midi2->total_blocks;
+               }
+       }
+
+       midi2->string_defs = kcalloc(midi2->total_blocks + 1,
+                                    sizeof(*midi2->string_defs), GFP_KERNEL);
+       if (!midi2->string_defs) {
+               do_f_midi2_free(midi2, opts);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       if (opts->info.iface_name && *opts->info.iface_name)
+               midi2->string_defs[STR_IFACE].s = opts->info.iface_name;
+       else
+               midi2->string_defs[STR_IFACE].s = ump_ep_name(&midi2->midi2_eps[0]);
+
+       for (i = 0; i < midi2->num_eps; i++) {
+               ep = &midi2->midi2_eps[i];
+               for (blk = 0; blk < ep->num_blks; blk++) {
+                       bp = &ep->blks[blk];
+                       midi2->string_defs[gtb_to_str_id(bp->gtb_id)].s =
+                               ump_fb_name(&bp->info);
+
+                       fill_midi1_cable_mapping(midi2, ep, blk);
+               }
+       }
+
+       if (!midi2->num_midi1_in && !midi2->num_midi1_out) {
+               pr_err("f_midi2: MIDI1 definition is missing\n");
+               do_f_midi2_free(midi2, opts);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return &midi2->func;
+}
+
+DECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc);
+
+MODULE_LICENSE("GPL");
index 424bb3b666dbd7d545a9d9abfb835c1152b51624..feccf4c8cc4fb3d51640d74196347c643df9d0b6 100644 (file)
@@ -80,21 +80,6 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
        return container_of(f, struct f_ncm, port.func);
 }
 
-/* peak (theoretical) bulk transfer rate in bits-per-second */
-static inline unsigned ncm_bitrate(struct usb_gadget *g)
-{
-       if (!g)
-               return 0;
-       else if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS)
-               return 4250000000U;
-       else if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
-               return 3750000000U;
-       else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
-               return 13 * 512 * 8 * 1000 * 8;
-       else
-               return 19 *  64 * 1 * 1000 * 8;
-}
-
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -576,10 +561,10 @@ static void ncm_do_notify(struct f_ncm *ncm)
 
                /* SPEED_CHANGE data is up/down speeds in bits/sec */
                data = req->buf + sizeof *event;
-               data[0] = cpu_to_le32(ncm_bitrate(cdev->gadget));
+               data[0] = cpu_to_le32(gether_bitrate(cdev->gadget));
                data[1] = data[0];
 
-               DBG(cdev, "notify speed %u\n", ncm_bitrate(cdev->gadget));
+               DBG(cdev, "notify speed %u\n", gether_bitrate(cdev->gadget));
                ncm->notify_state = NCM_NOTIFY_CONNECT;
                break;
        }
@@ -1544,9 +1529,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
        hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
        ncm->task_timer.function = ncm_tx_timeout;
 
-       DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+       DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
                        ncm->port.in_ep->name, ncm->port.out_ep->name,
                        ncm->notify->name);
        return 0;
index ab26d84ed95e6c5ec4317def3f6fbe2788324f0a..dcb093210305d8a942c1d1a5479d67554e9e4112 100644 (file)
@@ -365,9 +365,8 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f)
        if (status)
                goto fail;
 
-       dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
+       dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: IN/%s OUT/%s\n",
                obex->port_num,
-               gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
                obex->port.in->name, obex->port.out->name);
 
        return 0;
index ee95e8f5f9d489a58735d2394eca222509da8a17..b47f99d17ee9a5682dfecfc7cb200d2a0e04c968 100644 (file)
@@ -84,19 +84,6 @@ static inline struct f_rndis *func_to_rndis(struct usb_function *f)
        return container_of(f, struct f_rndis, port.func);
 }
 
-/* peak (theoretical) bulk transfer rate in bits-per-second */
-static unsigned int bitrate(struct usb_gadget *g)
-{
-       if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS)
-               return 4250000000U;
-       if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
-               return 3750000000U;
-       else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
-               return 13 * 512 * 8 * 1000 * 8;
-       else
-               return 19 * 64 * 1 * 1000 * 8;
-}
-
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -640,7 +627,7 @@ static void rndis_open(struct gether *geth)
        DBG(cdev, "%s\n", __func__);
 
        rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3,
-                               bitrate(cdev->gadget) / 100);
+                               gether_bitrate(cdev->gadget) / 100);
        rndis_signal_connect(rndis->params);
 }
 
@@ -811,9 +798,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
         * until we're activated via set_alt().
         */
 
-       DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+       DBG(cdev, "RNDIS: IN/%s OUT/%s NOTIFY/%s\n",
                        rndis->port.in_ep->name, rndis->port.out_ep->name,
                        rndis->notify->name);
        return 0;
index a9480b9e312e32ab9c8325bd90e2b686a02c134e..65c50092aea2d3314a138af89dacc3aa13098f0b 100644 (file)
@@ -236,10 +236,8 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
                        gser_ss_function, gser_ss_function);
        if (status)
                goto fail;
-       dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
+       dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: IN/%s OUT/%s\n",
                gser->port_num,
-               gadget_is_superspeed(c->cdev->gadget) ? "super" :
-               gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
                gser->port.in->name, gser->port.out->name);
        return 0;
 
index 6803cd60cc6dc3147b1901c56cf28f9dda2da607..2edbd9b510d60293d56dd2fa680cd2eac0795092 100644 (file)
@@ -436,9 +436,7 @@ no_iso:
        if (ret)
                return ret;
 
-       DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
-           (gadget_is_superspeed(c->cdev->gadget) ? "super" :
-            (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
+       DBG(cdev, "%s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
                        f->name, ss->in_ep->name, ss->out_ep->name,
                        ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
                        ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
index 51c1cae162d9b0a4dc2e80c80a36db6dcc500e69..8ae9689ef2a0819f7e3805143fc73befb34d86be 100644 (file)
@@ -367,9 +367,7 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
         * until we're activated via set_alt().
         */
 
-       DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n",
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+       DBG(cdev, "CDC Subset: IN/%s OUT/%s\n",
                        geth->port.in_ep->name, geth->port.out_ep->name);
        return 0;
 
index 5e919fb6583301f285174711f03cc5d087a06f13..faa398109431fca37fbd5e5309fe4e58aa75878e 100644 (file)
@@ -719,21 +719,13 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
        }
        uvc->enable_interrupt_ep = opts->enable_interrupt_ep;
 
-       if (gadget_is_superspeed(c->cdev->gadget))
-               ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
-                                         &uvc_ss_streaming_comp);
-       else if (gadget_is_dualspeed(cdev->gadget))
-               ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
-       else
-               ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
-
+       ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
        if (!ep) {
                uvcg_info(f, "Unable to allocate streaming EP\n");
                goto error;
        }
        uvc->video.ep = ep;
 
-       uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
        uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
        uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
 
@@ -788,21 +780,19 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
                f->fs_descriptors = NULL;
                goto error;
        }
-       if (gadget_is_dualspeed(cdev->gadget)) {
-               f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
-               if (IS_ERR(f->hs_descriptors)) {
-                       ret = PTR_ERR(f->hs_descriptors);
-                       f->hs_descriptors = NULL;
-                       goto error;
-               }
+
+       f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
+       if (IS_ERR(f->hs_descriptors)) {
+               ret = PTR_ERR(f->hs_descriptors);
+               f->hs_descriptors = NULL;
+               goto error;
        }
-       if (gadget_is_superspeed(c->cdev->gadget)) {
-               f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
-               if (IS_ERR(f->ss_descriptors)) {
-                       ret = PTR_ERR(f->ss_descriptors);
-                       f->ss_descriptors = NULL;
-                       goto error;
-               }
+
+       f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
+       if (IS_ERR(f->ss_descriptors)) {
+               ret = PTR_ERR(f->ss_descriptors);
+               f->ss_descriptors = NULL;
+               goto error;
        }
 
        /* Preallocate control endpoint request. */
index a366abb456236c7033b6fbd1c7cfb9b4dcc1a7cf..4bb0553da6585761110c9fb8107241a0aeb8522a 100644 (file)
@@ -93,11 +93,10 @@ struct eth_dev {
 
 #define DEFAULT_QLEN   2       /* double buffering by default */
 
-/* for dual-speed hardware, use deeper queues at high/super speed */
+/* use deeper queues at high/super speed */
 static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
 {
-       if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
-                                           gadget->speed >= USB_SPEED_SUPER))
+       if (gadget->speed == USB_SPEED_HIGH || gadget->speed >= USB_SPEED_SUPER)
                return qmult * DEFAULT_QLEN;
        else
                return DEFAULT_QLEN;
index 851ee10d6e6383ed3f134c1193028c30501bbf43..34be220cef77c49262b2098771c211326d038407 100644 (file)
@@ -279,4 +279,17 @@ static inline bool can_support_ecm(struct usb_gadget *gadget)
        return true;
 }
 
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static inline unsigned int gether_bitrate(struct usb_gadget *g)
+{
+       if (g->speed >= USB_SPEED_SUPER_PLUS)
+               return 4250000000U;
+       if (g->speed == USB_SPEED_SUPER)
+               return 3750000000U;
+       else if (g->speed == USB_SPEED_HIGH)
+               return 13 * 512 * 8 * 1000 * 8;
+       else
+               return 19 * 64 * 1 * 1000 * 8;
+}
+
 #endif /* __U_ETHER_H */
diff --git a/drivers/usb/gadget/function/u_midi2.h b/drivers/usb/gadget/function/u_midi2.h
new file mode 100644 (file)
index 0000000..4e7adb4
--- /dev/null
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Utility definitions for MIDI 2.0 function
+ */
+
+#ifndef U_MIDI2_H
+#define U_MIDI2_H
+
+#include <linux/usb/composite.h>
+#include <sound/asound.h>
+
+struct f_midi2_opts;
+struct f_midi2_ep_opts;
+struct f_midi2_block_opts;
+
+/* UMP Function Block info */
+struct f_midi2_block_info {
+       unsigned int direction;         /* FB direction: 1-3 */
+       unsigned int first_group;       /* first UMP group: 0-15 */
+       unsigned int num_groups;        /* number of UMP groups: 1-16 */
+       unsigned int midi1_first_group; /* first UMP group for MIDI 1.0 */
+       unsigned int midi1_num_groups;  /* number of UMP groups for MIDI 1.0 */
+       unsigned int ui_hint;           /* UI-hint: 0-3 */
+       unsigned int midi_ci_version;   /* MIDI-CI version: 0-255 */
+       unsigned int sysex8_streams;    /* number of sysex8 streams: 0-255 */
+       unsigned int is_midi1;          /* MIDI 1.0 port: 0-2 */
+       bool active;                    /* FB active flag: bool */
+       const char *name;               /* FB name */
+};
+
+/* UMP Endpoint info */
+struct f_midi2_ep_info {
+       unsigned int protocol_caps;     /* protocol capabilities: 1-3 */
+       unsigned int protocol;          /* default protocol: 1-2 */
+       unsigned int manufacturer;      /* manufacturer id: 0-0xffffff */
+       unsigned int family;            /* device family id: 0-0xffff */
+       unsigned int model;             /* device model id: 0x-0xffff */
+       unsigned int sw_revision;       /* software revision: 32bit */
+
+       const char *ep_name;            /* Endpoint name */
+       const char *product_id;         /* Product ID */
+};
+
+struct f_midi2_card_info {
+       bool process_ump;               /* process UMP stream: bool */
+       bool static_block;              /* static FBs: bool */
+       unsigned int req_buf_size;      /* request buffer size */
+       unsigned int num_reqs;          /* number of requests */
+       const char *iface_name;         /* interface name */
+};
+
+struct f_midi2_block_opts {
+       struct config_group group;
+       unsigned int id;
+       struct f_midi2_block_info info;
+       struct f_midi2_ep_opts *ep;
+};
+
+struct f_midi2_ep_opts {
+       struct config_group group;
+       unsigned int index;
+       struct f_midi2_ep_info info;
+       struct f_midi2_block_opts *blks[SNDRV_UMP_MAX_BLOCKS];
+       struct f_midi2_opts *opts;
+};
+
+#define MAX_UMP_EPS            4
+#define MAX_CABLES             16
+
+struct f_midi2_opts {
+       struct usb_function_instance func_inst;
+       struct mutex lock;
+       int refcnt;
+
+       struct f_midi2_card_info info;
+
+       unsigned int num_eps;
+       struct f_midi2_ep_opts *eps[MAX_UMP_EPS];
+};
+
+#endif /* U_MIDI2_H */
index c53233b37192e00d45c283ad71e1d1ed3a43c5bf..ff62ca22c40d5718fbd11de0d87ee0b02cc637a5 100644 (file)
@@ -20,7 +20,6 @@ struct f_phonet_opts {
 struct net_device *gphonet_setup_default(void);
 void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g);
 int gphonet_register_netdev(struct net_device *net);
-int phonet_bind_config(struct usb_configuration *c, struct net_device *dev);
 void gphonet_cleanup(struct net_device *dev);
 
 #endif /* __U_PHONET_H */
index 102a7323a1fd344e490acdc4440a9fd94c96cdc9..901d99310bc48d083b2c129abc3bc99a3dc06399 100644 (file)
@@ -71,8 +71,4 @@ void gserial_disconnect(struct gserial *);
 void gserial_suspend(struct gserial *p);
 void gserial_resume(struct gserial *p);
 
-/* functions are bound to configurations by a config or gadget driver */
-int gser_bind_config(struct usb_configuration *c, u8 port_num);
-int obex_bind_config(struct usb_configuration *c, u8 port_num);
-
 #endif /* __U_SERIAL_H */
index 100475b1363e4c26af4dba791af87f6b684ef9ce..6751de8b63ad9437bbaa08ad47412048e8e79b29 100644 (file)
@@ -178,8 +178,6 @@ struct uvc_file_handle {
  */
 
 extern void uvc_function_setup_continue(struct uvc_device *uvc);
-extern void uvc_endpoint_stream(struct uvc_device *dev);
-
 extern void uvc_function_connect(struct uvc_device *uvc);
 extern void uvc_function_disconnect(struct uvc_device *uvc);
 
index 16f2db8c4a2b37013647fbaad0b40c0a8e02d28a..f60a019bb173015d843834db31557363b1e8bbcc 100644 (file)
@@ -328,8 +328,7 @@ static int ast_vhub_probe(struct platform_device *pdev)
        vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
                                      VHUB_IRQ_DEV1_BIT);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       vhub->regs = devm_ioremap_resource(&pdev->dev, res);
+       vhub->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(vhub->regs)) {
                dev_err(&pdev->dev, "Failed to map resources\n");
                return PTR_ERR(vhub->regs);
index 01968e2167f91b61d8c5c0ec1efe9094cee70d53..2ef89a442f50f129fc0acb55fca1334f3d16ed13 100644 (file)
@@ -1468,7 +1468,6 @@ static int ast_udc_probe(struct platform_device *pdev)
        enum usb_device_speed max_speed;
        struct device *dev = &pdev->dev;
        struct ast_udc_dev *udc;
-       struct resource *res;
        int rc;
 
        udc = devm_kzalloc(&pdev->dev, sizeof(struct ast_udc_dev), GFP_KERNEL);
@@ -1484,8 +1483,7 @@ static int ast_udc_probe(struct platform_device *pdev)
        udc->gadget.name = "aspeed-udc";
        udc->gadget.dev.init_name = "gadget";
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       udc->reg = devm_ioremap_resource(&pdev->dev, res);
+       udc->reg = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(udc->reg)) {
                dev_err(&pdev->dev, "Failed to map resources\n");
                return PTR_ERR(udc->reg);
index 6c0ed3fa5eb1be249dda56b319f78ee3d291dabe..02b1bef5e22e2e5c22c03630277c0193ff58aad1 100644 (file)
@@ -2285,15 +2285,13 @@ static int usba_udc_probe(struct platform_device *pdev)
        udc->gadget = usba_gadget_template;
        INIT_LIST_HEAD(&udc->gadget.ep_list);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID);
-       udc->regs = devm_ioremap_resource(&pdev->dev, res);
+       udc->regs = devm_platform_get_and_ioremap_resource(pdev, CTRL_IOMEM_ID, &res);
        if (IS_ERR(udc->regs))
                return PTR_ERR(udc->regs);
        dev_info(&pdev->dev, "MMIO registers at %pR mapped at %p\n",
                 res, udc->regs);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID);
-       udc->fifo = devm_ioremap_resource(&pdev->dev, res);
+       udc->fifo = devm_platform_get_and_ioremap_resource(pdev, FIFO_IOMEM_ID, &res);
        if (IS_ERR(udc->fifo))
                return PTR_ERR(udc->fifo);
        dev_info(&pdev->dev, "FIFO at %pR mapped at %p\n", res, udc->fifo);
index 7d49d8a0b00c2c2c85d717fd104f37752dc4584e..7166d1117742a1d267c423556d3acd32bf7405a7 100644 (file)
@@ -40,6 +40,7 @@ static const struct bus_type gadget_bus_type;
  * @allow_connect: Indicates whether UDC is allowed to be pulled up.
  * Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or
  * unbound.
+ * @vbus_work: work routine to handle VBUS status change notifications.
  * @connect_lock: protects udc->started, gadget->connect,
  * gadget->allow_connect and gadget->deactivate. The routines
  * usb_gadget_connect_locked(), usb_gadget_disconnect_locked(),
index 9c5dc1c1a68ea889a8b5f0f2c40e8b69650ecbbc..4aae86b47edfc4519d4c29b5e54b5843c08beba4 100644 (file)
@@ -1959,6 +1959,8 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value,
        } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
                /* Get endpoint status */
                int pipe = index & USB_ENDPOINT_NUMBER_MASK;
+               if (pipe >= USB_MAX_ENDPOINTS)
+                       goto stall;
                struct qe_ep *target_ep = &udc->eps[pipe];
                u16 usep;
 
index a67873a074b7bc020b30d3c54d39650b9a902c91..ee5705d336e3d62ea1277f1d75af688fe7721122 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/platform_device.h>
 #include <linux/fsl_devices.h>
 #include <linux/dmapool.h>
-#include <linux/of_device.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
@@ -672,7 +671,7 @@ static int fsl_ep_disable(struct usb_ep *_ep)
 static struct usb_request *
 fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
 {
-       struct fsl_req *req = NULL;
+       struct fsl_req *req;
 
        req = kzalloc(sizeof *req, gfp_flags);
        if (!req)
index 09762559912d3ef2fcbe4e7017ef55deae5f6c51..c6dfa7cccc11da1025302dab5ded2043a5dc806b 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/errno.h>
@@ -36,9 +37,7 @@
 #include <linux/dmapool.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
-#include <linux/of_platform.h>
-#include <linux/of_irq.h>
-#include <linux/of_address.h>
+#include <linux/of.h>
 
 #include <asm/byteorder.h>
 
@@ -2137,15 +2136,15 @@ static int gr_probe(struct platform_device *pdev)
                return PTR_ERR(regs);
 
        dev->irq = platform_get_irq(pdev, 0);
-       if (dev->irq <= 0)
-               return -ENODEV;
+       if (dev->irq < 0)
+               return dev->irq;
 
        /* Some core configurations has separate irqs for IN and OUT events */
        dev->irqi = platform_get_irq(pdev, 1);
        if (dev->irqi > 0) {
                dev->irqo = platform_get_irq(pdev, 2);
-               if (dev->irqo <= 0)
-                       return -ENODEV;
+               if (dev->irqo < 0)
+                       return dev->irqo;
        } else {
                dev->irqi = 0;
        }
index 12c519f32bf7f77eb48b7dd6bb00f2dc44418461..2d57786d3db79ab472513e47355ec81b5d2b9d0c 100644 (file)
@@ -19,9 +19,7 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/bitfield.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/prefetch.h>
 #include <linux/usb/ch9.h>
index 3473048a85f5777664f6037cd32a523cec6d7b1a..2a421f0ff9314ba398b106a2224779d5339f6d6c 100644 (file)
@@ -665,7 +665,7 @@ static int  mv_u3d_ep_disable(struct usb_ep *_ep)
 static struct usb_request *
 mv_u3d_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
 {
-       struct mv_u3d_req *req = NULL;
+       struct mv_u3d_req *req;
 
        req = kzalloc(sizeof *req, gfp_flags);
        if (!req)
@@ -1779,7 +1779,7 @@ static void mv_u3d_remove(struct platform_device *dev)
 
 static int mv_u3d_probe(struct platform_device *dev)
 {
-       struct mv_u3d *u3d = NULL;
+       struct mv_u3d *u3d;
        struct mv_usb_platform_data *pdata = dev_get_platdata(&dev->dev);
        int retval = 0;
        struct resource *r;
index 79db74e2040b8ab67273943e45d2cd7fe4c5c2d0..d888dcda2bc86c6c39ef919f07e70d0554bad1a0 100644 (file)
@@ -595,7 +595,7 @@ static int  mv_ep_disable(struct usb_ep *_ep)
 static struct usb_request *
 mv_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
 {
-       struct mv_req *req = NULL;
+       struct mv_req *req;
 
        req = kzalloc(sizeof *req, gfp_flags);
        if (!req)
index c4e1d957f91347fbc5ce3c86176d16d44e2d9152..61424cfd2e1cb4e6783b4a39c851c9527ca40ae0 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/prefetch.h>
 #include <linux/byteorder/generic.h>
 #include <linux/platform_data/pxa2xx_udc.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/of_gpio.h>
 
 #include <linux/usb.h>
index 59bb25de2015ac4b985015e2c0ca10a92ccf8a30..3b01734ce1b7e5d834f788a036b6a60605d35dff 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
index 6cd0af83e91efc162556804aedd792548b4b163b..657f265ac7cc57d2ad225aba3cc0550b6136a4c9 100644 (file)
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
 #include <linux/kfifo.h>
+#include <linux/mod_devicetable.h>
 #include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/types.h>
 #include <linux/usb/composite.h>
@@ -3379,7 +3378,6 @@ MODULE_DEVICE_TABLE(of, usbf_match);
 static struct platform_driver udc_driver = {
        .driver = {
                .name = "usbf_renesas",
-               .owner = THIS_MODULE,
                .of_match_table = usbf_match,
        },
        .probe          = usbf_probe,
index 0ed685db149d0a2d86a54e9b2b0292fa813387c6..547af2ed9e5e02c2f1326df9688667d749c00bbe 100644 (file)
@@ -112,8 +112,7 @@ static int udc_plat_probe(struct platform_device *pdev)
        spin_lock_init(&udc->lock);
        udc->dev = dev;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       udc->virt_addr = devm_ioremap_resource(dev, res);
+       udc->virt_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(udc->virt_addr))
                return PTR_ERR(udc->virt_addr);
 
@@ -301,7 +300,6 @@ static const struct dev_pm_ops udc_plat_pm_ops = {
 };
 #endif
 
-#if defined(CONFIG_OF)
 static const struct of_device_id of_udc_match[] = {
        { .compatible = "brcm,ns2-udc", },
        { .compatible = "brcm,cygnus-udc", },
@@ -309,14 +307,13 @@ static const struct of_device_id of_udc_match[] = {
        { }
 };
 MODULE_DEVICE_TABLE(of, of_udc_match);
-#endif
 
 static struct platform_driver udc_plat_driver = {
        .probe          = udc_plat_probe,
        .remove_new     = udc_plat_remove,
        .driver         = {
                .name   = "snps-udc-plat",
-               .of_match_table = of_match_ptr(of_udc_match),
+               .of_match_table = of_udc_match,
 #ifdef CONFIG_PM_SLEEP
                .pm     = &udc_plat_pm_ops,
 #endif
index df6028f7b2737984480d8abab651e3a46e5d2139..cb85168fd00c2895a70066d998169320524ccbe0 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/phy/phy.h>
 #include <linux/phy/tegra/xusb.h>
 #include <linux/pm_domain.h>
index a4a7b90a97e70c0f199142d2647127240400a49c..56b8286a8009197e9057202d6b0e13b1c5e3125d 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/prefetch.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
@@ -1617,13 +1615,13 @@ static void xudc_getstatus(struct xusb_udc *udc)
        case USB_RECIP_INTERFACE:
                break;
        case USB_RECIP_ENDPOINT:
-               epnum = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+               epnum = le16_to_cpu(udc->setup.wIndex) & USB_ENDPOINT_NUMBER_MASK;
                if (epnum >= XUSB_MAX_ENDPOINTS)
                        goto stall;
                target_ep = &udc->ep[epnum];
                epcfgreg = udc->read_fn(udc->addr + target_ep->offset);
                halt = epcfgreg & XUSB_EP_CFG_STALL_MASK;
-               if (udc->setup.wIndex & USB_DIR_IN) {
+               if (le16_to_cpu(udc->setup.wIndex) & USB_DIR_IN) {
                        if (!target_ep->is_in)
                                goto stall;
                } else {
@@ -1638,7 +1636,7 @@ static void xudc_getstatus(struct xusb_udc *udc)
        }
 
        req->usb_req.length = 2;
-       *(u16 *)req->usb_req.buf = cpu_to_le16(status);
+       *(__le16 *)req->usb_req.buf = cpu_to_le16(status);
        ret = __xudc_ep0_queue(ep0, req);
        if (ret == 0)
                return;
@@ -1666,7 +1664,7 @@ static void xudc_set_clear_feature(struct xusb_udc *udc)
 
        switch (udc->setup.bRequestType) {
        case USB_RECIP_DEVICE:
-               switch (udc->setup.wValue) {
+               switch (le16_to_cpu(udc->setup.wValue)) {
                case USB_DEVICE_TEST_MODE:
                        /*
                         * The Test Mode will be executed
@@ -1686,13 +1684,15 @@ static void xudc_set_clear_feature(struct xusb_udc *udc)
                break;
        case USB_RECIP_ENDPOINT:
                if (!udc->setup.wValue) {
-                       endpoint = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+                       endpoint = le16_to_cpu(udc->setup.wIndex) &
+                                              USB_ENDPOINT_NUMBER_MASK;
                        if (endpoint >= XUSB_MAX_ENDPOINTS) {
                                xudc_ep0_stall(udc);
                                return;
                        }
                        target_ep = &udc->ep[endpoint];
-                       outinbit = udc->setup.wIndex & USB_ENDPOINT_DIR_MASK;
+                       outinbit = le16_to_cpu(udc->setup.wIndex) &
+                                              USB_ENDPOINT_DIR_MASK;
                        outinbit = outinbit >> 7;
 
                        /* Make sure direction matches.*/
@@ -1755,9 +1755,9 @@ static void xudc_handle_setup(struct xusb_udc *udc)
        memcpy(&setup, ep0rambase, 8);
 
        udc->setup = setup;
-       udc->setup.wValue = cpu_to_le16(setup.wValue);
-       udc->setup.wIndex = cpu_to_le16(setup.wIndex);
-       udc->setup.wLength = cpu_to_le16(setup.wLength);
+       udc->setup.wValue = cpu_to_le16((u16 __force)setup.wValue);
+       udc->setup.wIndex = cpu_to_le16((u16 __force)setup.wIndex);
+       udc->setup.wLength = cpu_to_le16((u16 __force)setup.wLength);
 
        /* Clear previous requests */
        xudc_nuke(ep0, -ECONNRESET);
@@ -1869,7 +1869,7 @@ static void xudc_ep0_in(struct xusb_udc *udc)
        u16 count = 0;
        u16 length;
        u8 *ep0rambase;
-       u8 test_mode = udc->setup.wIndex >> 8;
+       u8 test_mode = le16_to_cpu(udc->setup.wIndex) >> 8;
 
        req = list_first_entry(&ep0->queue, struct xusb_req, queue);
        bytes_to_tx = req->usb_req.length - req->usb_req.actual;
@@ -1880,12 +1880,12 @@ static void xudc_ep0_in(struct xusb_udc *udc)
                case USB_REQ_SET_ADDRESS:
                        /* Set the address of the device.*/
                        udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET,
-                                     udc->setup.wValue);
+                                     le16_to_cpu(udc->setup.wValue));
                        break;
                case USB_REQ_SET_FEATURE:
                        if (udc->setup.bRequestType ==
                                        USB_RECIP_DEVICE) {
-                               if (udc->setup.wValue ==
+                               if (le16_to_cpu(udc->setup.wValue) ==
                                                USB_DEVICE_TEST_MODE)
                                        udc->write_fn(udc->addr,
                                                      XUSB_TESTMODE_OFFSET,
@@ -2080,8 +2080,7 @@ static int xudc_probe(struct platform_device *pdev)
        udc->req->usb_req.buf = buff;
 
        /* Map the registers */
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       udc->addr = devm_ioremap_resource(&pdev->dev, res);
+       udc->addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(udc->addr))
                return PTR_ERR(udc->addr);
 
index 61808c51e702cb24efd6ef789c170990c7468122..6a6e1c510b2838dc59889cca11f7d217a9b077dc 100644 (file)
@@ -102,8 +102,8 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
        pr_debug("Initializing Atmel-SoC USB Host Controller\n");
 
        irq = platform_get_irq(pdev, 0);
-       if (irq <= 0) {
-               retval = -ENODEV;
+       if (irq < 0) {
+               retval = irq;
                goto fail_create_hcd;
        }
 
@@ -122,8 +122,7 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
        }
        atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto fail_request_resource;
index 0362a082abb459a38cc451eb9953c259d0456f92..77e42c739c5806c1da2ca052ce2a0c0888d4e0f4 100644 (file)
@@ -140,8 +140,8 @@ static int ehci_brcm_probe(struct platform_device *pdev)
                return err;
 
        irq = platform_get_irq(pdev, 0);
-       if (irq <= 0)
-               return irq ? irq : -EINVAL;
+       if (irq < 0)
+               return irq;
 
        /* Hook the hub control routine to work around a bug */
        ehci_brcm_hc_driver.hub_control = ehci_brcm_hub_control;
index 20f8c0ec68101e86c5414046d5fef325bc5b3ec9..f644b131cc0b5a478b57142463e24fe2df739dfd 100644 (file)
@@ -173,8 +173,7 @@ static int exynos_ehci_probe(struct platform_device *pdev)
        if (err)
                goto fail_clk;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto fail_io;
index 81d60a69551051393f3c7a433ccded85b6658145..5b1ce394a4174d89eabffcd3b35dd48c11683462 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/usb/otg.h>
 #include <linux/platform_device.h>
 #include <linux/fsl_devices.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
 #include <linux/io.h>
 
 #include "ehci.h"
@@ -87,8 +87,7 @@ static int fsl_ehci_drv_probe(struct platform_device *pdev)
                goto err1;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err2;
index a1930db0da1c3c82af1e6becd92ac8f34f5efe48..802bfafb1012bbb5fb14aff0d685abc12515ccb0 100644 (file)
@@ -755,10 +755,14 @@ restart:
 
        /* normal [4.15.1.2] or error [4.15.1.1] completion */
        if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
-               if (likely ((status & STS_ERR) == 0))
+               if (likely ((status & STS_ERR) == 0)) {
                        INCR(ehci->stats.normal);
-               else
+               } else {
+                       /* Force to check port status */
+                       if (ehci->has_ci_pec_bug)
+                               status |= STS_PCD;
                        INCR(ehci->stats.error);
+               }
                bh = 1;
        }
 
index efe30e3be22f7284164c6210a61367d629acccb7..1aee392e84927cba6403096ce4dff91d26470cc7 100644 (file)
@@ -674,7 +674,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
 
                if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend)
                                || (ehci->reset_done[i] && time_after_eq(
-                                       jiffies, ehci->reset_done[i]))) {
+                                       jiffies, ehci->reset_done[i]))
+                               || ehci_has_ci_pec_bug(ehci, temp)) {
                        if (i < 7)
                            buf [0] |= 1 << (i + 1);
                        else
@@ -875,6 +876,13 @@ int ehci_hub_control(
                if (temp & PORT_PEC)
                        status |= USB_PORT_STAT_C_ENABLE << 16;
 
+               if (ehci_has_ci_pec_bug(ehci, temp)) {
+                       status |= USB_PORT_STAT_C_ENABLE << 16;
+                       ehci_info(ehci,
+                               "PE is cleared by HW port:%d PORTSC:%08x\n",
+                               wIndex + 1, temp);
+               }
+
                if ((temp & PORT_OCC) && (!ignore_oc && !ehci->spurious_oc)){
                        status |= USB_PORT_STAT_C_OVERCURRENT << 16;
 
index 9320cf0e5bc7020776c135de3a4e1e07f0d98fc5..2f1fc7eb8b727ffb8daa8043a49bffd5489585f7 100644 (file)
@@ -142,8 +142,7 @@ static int mv_ehci_probe(struct platform_device *pdev)
                goto err_put_hcd;
        }
 
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       ehci_mv->base = devm_ioremap_resource(&pdev->dev, r);
+       ehci_mv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
        if (IS_ERR(ehci_mv->base)) {
                retval = PTR_ERR(ehci_mv->base);
                goto err_put_hcd;
index ad79ce63afcfe7ff9364a839cb4d5ab069eae566..3d3317a1a0b3fd6c12ecd3479c4aab3b3ad829a3 100644 (file)
@@ -53,7 +53,7 @@ static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev)
        int irq;
        int retval;
 
-       dev_dbg(&pdev->dev,     "initializing npcm7xx ehci USB Controller\n");
+       dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n");
 
        if (usb_disabled())
                return -ENODEV;
@@ -79,8 +79,7 @@ static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err_put_hcd;
index cb6509a735ac7a01c73e40b35976386937228596..b24f371a46f3ecdef56f54ecc7d68a49bf970e01 100644 (file)
@@ -113,8 +113,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
        if (irq < 0)
                return irq;
 
-       res =  platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       regs = devm_ioremap_resource(dev, res);
+       regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(regs))
                return PTR_ERR(regs);
 
index 2cfb27dc943a6b4111c688736d63c84858ef9d38..6c47ab0a491d5133c59539c93b4a33e39c9262ca 100644 (file)
@@ -13,8 +13,6 @@
 #include <linux/platform_data/usb-ehci-orion.h>
 #include <linux/of.h>
 #include <linux/phy/phy.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
 #include <linux/io.h>
@@ -220,8 +218,8 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
        pr_debug("Initializing Orion-SoC USB Host Controller\n");
 
        irq = platform_get_irq(pdev, 0);
-       if (irq <= 0) {
-               err = -ENODEV;
+       if (irq < 0) {
+               err = irq;
                goto err;
        }
 
@@ -234,8 +232,7 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
        if (err)
                goto err;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       regs = devm_ioremap_resource(&pdev->dev, res);
+       regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(regs)) {
                err = PTR_ERR(regs);
                goto err;
index 83bf56c9424fbd10f5ec9887f0ec06cfafc89299..98b073185e1c733b438586d25ff4a719a9a42ace 100644 (file)
@@ -359,8 +359,7 @@ static int ehci_platform_probe(struct platform_device *dev)
                        goto err_reset;
        }
 
-       res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+       hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto err_power;
index bd542b6fc46bdab1f42721b682ed49a269f090f3..7e834587e7de1c5812e96bd3be88139d5e105eb2 100644 (file)
@@ -490,13 +490,14 @@ static int tt_no_collision(
 static void enable_periodic(struct ehci_hcd *ehci)
 {
        if (ehci->periodic_count++)
-               return;
+               goto out;
 
        /* Stop waiting to turn off the periodic schedule */
        ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC);
 
        /* Don't start the schedule until PSS is 0 */
        ehci_poll_PSS(ehci);
+out:
        turn_on_io_watchdog(ehci);
 }
 
index 0520e762801d5a6dc90ff15d2ea7e4c718b5acf9..d31d9506e41ab0844cf04f5bbe1485972f8f62a4 100644 (file)
@@ -82,8 +82,8 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev)
                return -ENODEV;
 
        irq = platform_get_irq(pdev, 0);
-       if (irq <= 0) {
-               ret = -ENODEV;
+       if (irq < 0) {
+               ret = irq;
                goto fail_create_hcd;
        }
 
@@ -95,8 +95,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev)
                goto fail_create_hcd;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                ret = PTR_ERR(hcd->regs);
                goto fail_request_resource;
index 1407703649bef7ffee1b8b1eee4c22c1c13146f3..d0e94e4c9fe2742eddc7475e33e849e51f03ed76 100644 (file)
@@ -91,8 +91,7 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err_put_hcd;
index ee0976b815b4f217c8a3d24af79db8bdde774275..2dbb0d86daaaa93543bc4458d66a8919f2da70a5 100644 (file)
@@ -158,11 +158,6 @@ static int st_ehci_platform_probe(struct platform_device *dev)
        irq = platform_get_irq(dev, 0);
        if (irq < 0)
                return irq;
-       res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       if (!res_mem) {
-               dev_err(&dev->dev, "no memory resource provided");
-               return -ENXIO;
-       }
 
        hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
                             dev_name(&dev->dev));
@@ -222,14 +217,13 @@ static int st_ehci_platform_probe(struct platform_device *dev)
                        goto err_put_clks;
        }
 
-       hcd->rsrc_start = res_mem->start;
-       hcd->rsrc_len = resource_size(res_mem);
-
-       hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+       hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto err_put_clks;
        }
+       hcd->rsrc_start = res_mem->start;
+       hcd->rsrc_len = resource_size(res_mem);
 
        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (err)
index c5c7f8782549352164035454f725c434bfff5efb..1441e340079617b1f11fd6db07d033053bee220c 100644 (file)
@@ -207,6 +207,7 @@ struct ehci_hcd {                   /* one per controller */
        unsigned                has_fsl_port_bug:1; /* FreeScale */
        unsigned                has_fsl_hs_errata:1;    /* Freescale HS quirk */
        unsigned                has_fsl_susp_errata:1;  /* NXP SUSP quirk */
+       unsigned                has_ci_pec_bug:1;       /* ChipIdea PEC bug */
        unsigned                big_endian_mmio:1;
        unsigned                big_endian_desc:1;
        unsigned                big_endian_capbase:1;
@@ -707,6 +708,15 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
  */
 #define ehci_has_fsl_susp_errata(e)    ((e)->has_fsl_susp_errata)
 
+/*
+ * Some Freescale/NXP processors using ChipIdea IP have a bug in which
+ * disabling the port (PE is cleared) does not cause PEC to be asserted
+ * when frame babble is detected.
+ */
+#define ehci_has_ci_pec_bug(e, portsc) \
+       ((e)->has_ci_pec_bug && ((e)->command & CMD_PSE) \
+        && !(portsc & PORT_PEC) && !(portsc & PORT_PE))
+
 /*
  * While most USB host controllers implement their registers in
  * little-endian format, a minority (celleb companion chip) implement
index 66a045e01dad8e6ba19fcf6ae85b3b6758330f7f..9a1b5224f2396eb441e6b7d57bb466e783831dfe 100644 (file)
 #include <linux/io.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
+#include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/gpio/consumer.h>
 #include <soc/fsl/qe/qe.h>
index a9877f2569f4e84c9b7c57511fab7732858be42d..8508d37a2aff7871bf29d5e61eaadecc80a2bb1a 100644 (file)
@@ -10,7 +10,8 @@
 #include <linux/fsl_devices.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
index 606f0a64f3b7cd6d2d626dc16cf010475e5f43fa..a52c3d858f3ee50aee29873d6663869a194c31a8 100644 (file)
@@ -2651,8 +2651,7 @@ static int isp1362_probe(struct platform_device *pdev)
        if (IS_ERR(addr_reg))
                return PTR_ERR(addr_reg);
 
-       data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       data_reg = devm_ioremap_resource(&pdev->dev, data);
+       data_reg = devm_platform_get_and_ioremap_resource(pdev, 0, &data);
        if (IS_ERR(data_reg))
                return PTR_ERR(data_reg);
 
index b805c4b52ac3170067d92cf5ea34e9331d9a4348..f691cd98a57466ecf82216843e04dc4c8ed201f4 100644 (file)
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/gpio/consumer.h>
-#include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/atmel.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mfd/syscon.h>
+#include <linux/of.h>
 #include <linux/regmap.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
@@ -190,18 +190,15 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
        int irq;
 
        irq = platform_get_irq(pdev, 0);
-       if (irq < 0) {
-               dev_dbg(dev, "hcd probe: missing irq resource\n");
+       if (irq < 0)
                return irq;
-       }
 
        hcd = usb_create_hcd(driver, dev, "at91");
        if (!hcd)
                return -ENOMEM;
        ohci_at91 = hcd_to_ohci_at91_priv(hcd);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err;
index e4191a868944e8af8b9f1b89cfa8b7a5a9d6c4c6..d9adae53466b7a36caac69e23acdd667eef62e91 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_data/usb-davinci.h>
@@ -435,8 +436,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
                        goto err;
        }
 
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(dev, mem);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
        if (IS_ERR(hcd->regs)) {
                error = PTR_ERR(hcd->regs);
                goto err;
index ab31c459b32d4a5017099244511dc984209b1de5..20e26a47459178e4aa8c90041d6c6610c98b510a 100644 (file)
@@ -149,8 +149,7 @@ static int exynos_ohci_probe(struct platform_device *pdev)
        if (err)
                goto fail_clk;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto fail_io;
index c04b2af5c766cf127df343bfee63c1000e6fbbae..8264c454f6bddfb448d3d7e8d0031865b93fa50f 100644 (file)
@@ -202,8 +202,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
                goto fail_hcd;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                ret = PTR_ERR(hcd->regs);
                goto fail_resource;
index 45a2c981319e965be50b3d675c817c683243b6a1..4a75507325ddcd7b3bed5662429280fc7189609a 100644 (file)
@@ -200,8 +200,7 @@ static int ohci_platform_probe(struct platform_device *dev)
                        goto err_reset;
        }
 
-       res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+       hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto err_power;
index 35a7ad7e256960f2280f4ec9451b0b55790af6b5..f64bfe5f4d4d9d9d869c16df50c5a156d342724f 100644 (file)
  */
 
 #include <linux/signal.h>
+#include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
 
 static int
 ohci_ppc_of_start(struct usb_hcd *hcd)
index 7410442f720f4501db1899a953db0e668917e6b6..357d9aee38a3799abef70942971c42f265f70287 100644 (file)
@@ -435,8 +435,7 @@ static int ohci_hcd_pxa27x_probe(struct platform_device *pdev)
        if (!hcd)
                return -ENOMEM;
 
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, r);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err;
index 0468eeb4fcfd91b8985676ed41fc7a90fa353d0e..4b39e9d6f33a98f10fbf74061244bf640953ad09 100644 (file)
@@ -195,8 +195,7 @@ static void ohci_hcd_sm501_drv_remove(struct platform_device *pdev)
        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
        usb_put_hcd(hcd);
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (mem)
-               release_mem_region(mem->start, resource_size(mem));
+       release_mem_region(mem->start, resource_size(mem));
 
        /* mask interrupts and disable power */
 
index f4b2656407ddd6efb4b6181bb8b554ffff49fded..993f347c5c2884b3e3e2a3410aef8d9954a4bcdf 100644 (file)
@@ -68,8 +68,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err_put_hcd;
index 884e447a8098dbf8e03207012347844972fa83d9..214342013f7ec4126634d09e2f266546e34d5fce 100644 (file)
@@ -139,12 +139,6 @@ static int st_ohci_platform_probe(struct platform_device *dev)
        if (irq < 0)
                return irq;
 
-       res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       if (!res_mem) {
-               dev_err(&dev->dev, "no memory resource provided");
-               return -ENXIO;
-       }
-
        hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
                        dev_name(&dev->dev));
        if (!hcd)
@@ -199,14 +193,14 @@ static int st_ohci_platform_probe(struct platform_device *dev)
                        goto err_power;
        }
 
-       hcd->rsrc_start = res_mem->start;
-       hcd->rsrc_len = resource_size(res_mem);
-
-       hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+       hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto err_power;
        }
+       hcd->rsrc_start = res_mem->start;
+       hcd->rsrc_len = resource_size(res_mem);
+
        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (err)
                goto err_power;
index 50c1ccabb0f501b7a8ec73acb374c5255368954a..d467472f9d3c4353cd887cc3719bb3f831ff8ca1 100644 (file)
@@ -4230,8 +4230,7 @@ static int oxu_drv_probe(struct platform_device *pdev)
                return irq;
        dev_dbg(&pdev->dev, "IRQ resource %d\n", irq);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       base = devm_ioremap_resource(&pdev->dev, res);
+       base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(base)) {
                ret = PTR_ERR(base);
                goto error;
index 71ca532fc0865bc18e558af0e8ef9c1fb0f7b88b..3dec5dd3a0d5cab9f3d66dd603a3035cf491167a 100644 (file)
@@ -91,8 +91,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
 
        uhci = hcd_to_uhci(hcd);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                ret = PTR_ERR(hcd->regs);
                goto err_rmr;
index 19a402123de02f9589a526af60ffcbbf0a2297dc..8714ab5bf04d6b7ebf4c69d625786475d7fc27d3 100644 (file)
@@ -1108,9 +1108,6 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
                slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS);
                max_packets = MAX_PACKET(8);
                break;
-       case USB_SPEED_WIRELESS:
-               xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n");
-               return -EINVAL;
        default:
                /* Speed was set earlier, this shouldn't happen. */
                return -EINVAL;
index b26ea7cb4357b503a30ae66591492bcc0850cb0f..28218c8f183768efac576b035c6cce15380bc447 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/usb/phy.h>
 #include <linux/slab.h>
index bf5261fed32c4e168e36593a3e8b9868eaa4e087..ab9c5969e4624eed70e759c938e697a636bf9fa6 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/usb/phy.h>
 
 #include "xhci.h"
index 4693d83351c6055fc8411c69b65f17935531df7b..6246d5ad146848271527c7a893c2a7c3daecc927 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/phy/phy.h>
 #include <linux/phy/tegra/xusb.h>
@@ -1912,6 +1912,15 @@ put_padctl:
        return err;
 }
 
+static void tegra_xusb_disable(struct tegra_xusb *tegra)
+{
+       tegra_xusb_powergate_partitions(tegra);
+       tegra_xusb_powerdomain_remove(tegra->dev, tegra);
+       tegra_xusb_phy_disable(tegra);
+       tegra_xusb_clk_disable(tegra);
+       regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
+}
+
 static void tegra_xusb_remove(struct platform_device *pdev)
 {
        struct tegra_xusb *tegra = platform_get_drvdata(pdev);
@@ -1934,14 +1943,18 @@ static void tegra_xusb_remove(struct platform_device *pdev)
 
        pm_runtime_put(&pdev->dev);
 
-       tegra_xusb_powergate_partitions(tegra);
+       tegra_xusb_disable(tegra);
+       tegra_xusb_padctl_put(tegra->padctl);
+}
 
-       tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
+static void tegra_xusb_shutdown(struct platform_device *pdev)
+{
+       struct tegra_xusb *tegra = platform_get_drvdata(pdev);
 
-       tegra_xusb_phy_disable(tegra);
-       tegra_xusb_clk_disable(tegra);
-       regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
-       tegra_xusb_padctl_put(tegra->padctl);
+       pm_runtime_get_sync(&pdev->dev);
+       disable_irq(tegra->xhci_irq);
+       xhci_shutdown(tegra->hcd);
+       tegra_xusb_disable(tegra);
 }
 
 static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
@@ -2652,6 +2665,7 @@ MODULE_DEVICE_TABLE(of, tegra_xusb_of_match);
 static struct platform_driver tegra_xusb_driver = {
        .probe = tegra_xusb_probe,
        .remove_new = tegra_xusb_remove,
+       .shutdown = tegra_xusb_shutdown,
        .driver = {
                .name = "tegra-xusb",
                .pm = &tegra_xusb_pm_ops,
index fae994f679d45b589c6a1e45bb0af2c2047f346a..e1b1b64a07232996d7e91b5fab1ba06fb48304b6 100644 (file)
@@ -2194,7 +2194,6 @@ static unsigned int xhci_get_block_size(struct usb_device *udev)
        case USB_SPEED_SUPER_PLUS:
                return SS_BLOCK;
        case USB_SPEED_UNKNOWN:
-       case USB_SPEED_WIRELESS:
        default:
                /* Should never happen */
                return 1;
@@ -2555,10 +2554,7 @@ static void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci,
        case USB_SPEED_HIGH:
                interval_bw->overhead[HS_OVERHEAD_TYPE] -= 1;
                break;
-       case USB_SPEED_SUPER:
-       case USB_SPEED_SUPER_PLUS:
-       case USB_SPEED_UNKNOWN:
-       case USB_SPEED_WIRELESS:
+       default:
                /* Should never happen because only LS/FS/HS endpoints will get
                 * added to the endpoint list.
                 */
@@ -2615,10 +2611,7 @@ static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci,
        case USB_SPEED_HIGH:
                interval_bw->overhead[HS_OVERHEAD_TYPE] += 1;
                break;
-       case USB_SPEED_SUPER:
-       case USB_SPEED_SUPER_PLUS:
-       case USB_SPEED_UNKNOWN:
-       case USB_SPEED_WIRELESS:
+       default:
                /* Should never happen because only LS/FS/HS endpoints will get
                 * added to the endpoint list.
                 */
index 14faec51d7a5d7041b4d72b9d5da91b0c8d10f51..cecd7693b7413c10814b8bf2432ebb3db984bcbe 100644 (file)
@@ -203,7 +203,7 @@ ATTRIBUTE_GROUPS(cypress);
 static int cypress_probe(struct usb_interface *interface,
                         const struct usb_device_id *id)
 {
-       struct cypress *dev = NULL;
+       struct cypress *dev;
        int retval = -ENOMEM;
 
        /* allocate memory for our device state and initialize it */
index 3e3802aaefa3bbe2dd1ee1e06e0b53caad61d268..875016dd073c2f67a35bcd5d1f1ec8350e1935ca 100644 (file)
@@ -304,20 +304,20 @@ static int cytherm_probe(struct usb_interface *interface,
                         const struct usb_device_id *id)
 {
        struct usb_device *udev = interface_to_usbdev(interface);
-       struct usb_cytherm *dev = NULL;
+       struct usb_cytherm *dev;
        int retval = -ENOMEM;
 
-       dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
+       dev = kzalloc(sizeof(struct usb_cytherm), GFP_KERNEL);
        if (!dev)
                goto error_mem;
 
        dev->udev = usb_get_dev(udev);
 
-       usb_set_intfdata (interface, dev);
+       usb_set_intfdata(interface, dev);
 
        dev->brightness = 0xFF;
 
-       dev_info (&interface->dev,
+       dev_info(&interface->dev,
                  "Cypress thermometer device now attached\n");
        return 0;
 
@@ -329,10 +329,10 @@ static void cytherm_disconnect(struct usb_interface *interface)
 {
        struct usb_cytherm *dev;
 
-       dev = usb_get_intfdata (interface);
+       dev = usb_get_intfdata(interface);
 
        /* first remove the files, then NULL the pointer */
-       usb_set_intfdata (interface, NULL);
+       usb_set_intfdata(interface, NULL);
 
        usb_put_dev(dev->udev);
 
index 83f14ca1d38e728d052f600255e703f935e98788..3da1a4659c5ff705f0f9fc6ee6d3e35c7b648d9e 100644 (file)
 
 #include "onboard_usb_hub.h"
 
+/*
+ * Use generic names, as the actual names might differ between hubs. If a new
+ * hub requires more than the currently supported supplies, add a new one here.
+ */
+static const char * const supply_names[] = {
+       "vdd",
+       "vdd2",
+};
+
+#define MAX_SUPPLIES ARRAY_SIZE(supply_names)
+
 static void onboard_hub_attach_usb_driver(struct work_struct *work);
 
 static struct usb_device_driver onboard_hub_usbdev_driver;
@@ -40,7 +51,7 @@ struct usbdev_node {
 };
 
 struct onboard_hub {
-       struct regulator *vdd;
+       struct regulator_bulk_data supplies[MAX_SUPPLIES];
        struct device *dev;
        const struct onboard_hub_pdata *pdata;
        struct gpio_desc *reset_gpio;
@@ -55,9 +66,9 @@ static int onboard_hub_power_on(struct onboard_hub *hub)
 {
        int err;
 
-       err = regulator_enable(hub->vdd);
+       err = regulator_bulk_enable(hub->pdata->num_supplies, hub->supplies);
        if (err) {
-               dev_err(hub->dev, "failed to enable regulator: %d\n", err);
+               dev_err(hub->dev, "failed to enable supplies: %d\n", err);
                return err;
        }
 
@@ -75,9 +86,9 @@ static int onboard_hub_power_off(struct onboard_hub *hub)
 
        gpiod_set_value_cansleep(hub->reset_gpio, 1);
 
-       err = regulator_disable(hub->vdd);
+       err = regulator_bulk_disable(hub->pdata->num_supplies, hub->supplies);
        if (err) {
-               dev_err(hub->dev, "failed to disable regulator: %d\n", err);
+               dev_err(hub->dev, "failed to disable supplies: %d\n", err);
                return err;
        }
 
@@ -232,6 +243,7 @@ static int onboard_hub_probe(struct platform_device *pdev)
        const struct of_device_id *of_id;
        struct device *dev = &pdev->dev;
        struct onboard_hub *hub;
+       unsigned int i;
        int err;
 
        hub = devm_kzalloc(dev, sizeof(*hub), GFP_KERNEL);
@@ -246,9 +258,18 @@ static int onboard_hub_probe(struct platform_device *pdev)
        if (!hub->pdata)
                return -EINVAL;
 
-       hub->vdd = devm_regulator_get(dev, "vdd");
-       if (IS_ERR(hub->vdd))
-               return PTR_ERR(hub->vdd);
+       if (hub->pdata->num_supplies > MAX_SUPPLIES)
+               return dev_err_probe(dev, -EINVAL, "max %zu supplies supported!\n",
+                                    MAX_SUPPLIES);
+
+       for (i = 0; i < hub->pdata->num_supplies; i++)
+               hub->supplies[i].supply = supply_names[i];
+
+       err = devm_regulator_bulk_get(dev, hub->pdata->num_supplies, hub->supplies);
+       if (err) {
+               dev_err(dev, "Failed to get regulator supplies: %d\n", err);
+               return err;
+       }
 
        hub->reset_gpio = devm_gpiod_get_optional(dev, "reset",
                                                  GPIOD_OUT_HIGH);
@@ -329,6 +350,7 @@ static struct platform_driver onboard_hub_driver = {
 
 /************************** USB driver **************************/
 
+#define VENDOR_ID_CYPRESS      0x04b4
 #define VENDOR_ID_GENESYS      0x05e3
 #define VENDOR_ID_MICROCHIP    0x0424
 #define VENDOR_ID_REALTEK      0x0bda
@@ -407,8 +429,11 @@ static void onboard_hub_usbdev_disconnect(struct usb_device *udev)
 }
 
 static const struct usb_device_id onboard_hub_id_table[] = {
+       { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 */
+       { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 */
        { USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 */
        { USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 */
+       { USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 */
        { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 */
        { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 */
        { USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 */
index aca5f50eb0da77f471816219d1e0d37ad57589cc..4026ba64c59213c1f9fe5ad4ec9f7b94f008cfb7 100644 (file)
@@ -8,30 +8,42 @@
 
 struct onboard_hub_pdata {
        unsigned long reset_us;         /* reset pulse width in us */
+       unsigned int num_supplies;      /* number of supplies */
 };
 
 static const struct onboard_hub_pdata microchip_usb424_data = {
        .reset_us = 1,
+       .num_supplies = 1,
 };
 
 static const struct onboard_hub_pdata realtek_rts5411_data = {
        .reset_us = 0,
+       .num_supplies = 1,
 };
 
 static const struct onboard_hub_pdata ti_tusb8041_data = {
        .reset_us = 3000,
+       .num_supplies = 1,
+};
+
+static const struct onboard_hub_pdata cypress_hx3_data = {
+       .reset_us = 10000,
+       .num_supplies = 2,
 };
 
 static const struct onboard_hub_pdata genesys_gl850g_data = {
        .reset_us = 3,
+       .num_supplies = 1,
 };
 
 static const struct onboard_hub_pdata genesys_gl852g_data = {
        .reset_us = 50,
+       .num_supplies = 1,
 };
 
 static const struct onboard_hub_pdata vialab_vl817_data = {
        .reset_us = 10,
+       .num_supplies = 1,
 };
 
 static const struct of_device_id onboard_hub_match[] = {
@@ -39,8 +51,11 @@ static const struct of_device_id onboard_hub_match[] = {
        { .compatible = "usb424,2517", .data = &microchip_usb424_data, },
        { .compatible = "usb451,8140", .data = &ti_tusb8041_data, },
        { .compatible = "usb451,8142", .data = &ti_tusb8041_data, },
+       { .compatible = "usb4b4,6504", .data = &cypress_hx3_data, },
+       { .compatible = "usb4b4,6506", .data = &cypress_hx3_data, },
        { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, },
        { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, },
+       { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, },
        { .compatible = "usbbda,411", .data = &realtek_rts5411_data, },
        { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, },
        { .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
index e4edb486b69e05bbf7a58427c20dc17d8dce0961..7da404f55a6d90682a6691b0f3af6718a8a43de5 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/nls.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
diff --git a/drivers/usb/misc/usb_u132.h b/drivers/usb/misc/usb_u132.h
deleted file mode 100644 (file)
index 1584efb..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
-* Common Header File for the Elan Digital Systems U132 adapter
-* this file should be included by both the "ftdi-u132" and
-* the "u132-hcd" modules.
-*
-* Copyright(C) 2006 Elan Digital Systems Limited
-*(http://www.elandigitalsystems.com)
-*
-* Author and Maintainer - Tony Olech - Elan Digital Systems
-*(tony.olech@elandigitalsystems.com)
-*
-* The driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
-* based on various USB client drivers in the 2.6.15 linux kernel
-* with constant reference to the 3rd Edition of Linux Device Drivers
-* published by O'Reilly
-*
-* The U132 adapter is a USB to CardBus adapter specifically designed
-* for PC cards that contain an OHCI host controller. Typical PC cards
-* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
-*
-* The U132 adapter will *NOT *work with PC cards that do not contain
-* an OHCI controller. A simple way to test whether a PC card has an
-* OHCI controller as an interface is to insert the PC card directly
-* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
-* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
-* then there is a good chance that the U132 adapter will support the
-* PC card.(you also need the specific client driver for the PC card)
-*
-* Please inform the Author and Maintainer about any PC cards that
-* contain OHCI Host Controller and work when directly connected to
-* an embedded CardBus slot but do not work when they are connected
-* via an ELAN U132 adapter.
-*
-* The driver consists of two modules, the "ftdi-u132" module is
-* a USB client driver that interfaces to the FTDI chip within
-* the U132 adapter manufactured by Elan Digital Systems, and the
-* "u132-hcd" module is a USB host controller driver that talks
-* to the OHCI controller within CardBus card that are inserted
-* in the U132 adapter.
-*
-* The "ftdi-u132" module should be loaded automatically by the
-* hot plug system when the U132 adapter is plugged in. The module
-* initialises the adapter which mostly consists of synchronising
-* the FTDI chip, before continuously polling the adapter to detect
-* PC card insertions. As soon as a PC card containing a recognised
-* OHCI controller is seen the "ftdi-u132" module explicitly requests
-* the kernel to load the "u132-hcd" module.
-*
-* The "ftdi-u132" module provides the interface to the inserted
-* PC card and the "u132-hcd" module uses the API to send and receive
-* data. The API features call-backs, so that part of the "u132-hcd"
-* module code will run in the context of one of the kernel threads
-* of the "ftdi-u132" module.
-*
-*/
-int ftdi_elan_switch_on_diagnostics(int number);
-void ftdi_elan_gone_away(struct platform_device *pdev);
-void start_usb_lock_device_tracing(void);
-struct u132_platform_data {
-        u16 vendor;
-        u16 device;
-        u8 potpg;
-        void (*port_power) (struct device *dev, int is_on);
-        void (*reset) (struct device *dev);
-};
-int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
-        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
-        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
-        int toggle_bits, int error_count, int condition_code, int repeat_number,
-         int halted, int skipped, int actual, int non_null));
-int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
-        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
-        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
-        int toggle_bits, int error_count, int condition_code, int repeat_number,
-         int halted, int skipped, int actual, int non_null));
-int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
-        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
-        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
-        int toggle_bits, int error_count, int condition_code, int repeat_number,
-         int halted, int skipped, int actual, int non_null));
-int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
-        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
-        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
-        int toggle_bits, int error_count, int condition_code, int repeat_number,
-         int halted, int skipped, int actual, int non_null));
-int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
-        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
-        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
-        int toggle_bits, int error_count, int condition_code, int repeat_number,
-         int halted, int skipped, int actual, int non_null));
-int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
-        void *endp);
-int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, int mem_offset,
-                             u8 width, u32 *data);
-int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, int mem_offset,
-                              u8 width, u32 data);
index c3114d9bd1287a29edc222501aecb331e57aacdd..546deff754ba2adcfd5c513d08f8deb48a03875e 100644 (file)
@@ -305,7 +305,7 @@ static int sevseg_probe(struct usb_interface *interface,
        const struct usb_device_id *id)
 {
        struct usb_device *udev = interface_to_usbdev(interface);
-       struct usb_sevsegdev *mydev = NULL;
+       struct usb_sevsegdev *mydev;
        int rc = -ENOMEM;
 
        mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
index b4a7662dded585178689eec269d3917ac2fa37d8..c11840b9a6f16582a3fbb14d7f6fc1a4104ef63b 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/extcon.h>
 #include <linux/interrupt.h>
 #include <linux/list.h>
+#include <linux/of.h>
 #include <linux/phy/phy.h>
 #include <linux/regulator/consumer.h>
 #include <linux/usb.h>
index 177d2caf887c87f0d47d8f7fd28984d8b10c085d..9f2be22af8440557e6b056b8e1664ad3d807b511 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/mfd/syscon.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/regmap.h>
 
index 16dd1ed44bb52f4f976ea056f2c306193ce64355..3606be897cb24736e1fd972cadd6771996c6709f 100644 (file)
@@ -121,9 +121,6 @@ struct cppi {
        struct list_head                tx_complete;
 };
 
-/* CPPI IRQ handler */
-extern irqreturn_t cppi_interrupt(int, void *);
-
 struct cppi41_dma_channel {
        struct dma_channel channel;
        struct cppi41_dma_controller *controller;
index 5aabdd7e2511d7d953e9143877de92e9a0138a01..b38df9226278b04c936cbb7c574e63577fd3565a 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/usb/role.h>
index 598ee5c0bf3471db333943b329e6075a7913f23d..0a35aab3ab815f06db02ac06c6d9493daf2852b6 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/usb/role.h>
index 24b98716f7fce29a98c7ead73ca81600ece0be72..f0f56df3883553713a32a8883571e712937c25b1 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/usb/usb_phy_generic.h>
 #include "musb_core.h"
index ecbd3784bec36b59d4a58d0b3f1f2abc47c3730c..b24adb5b399fc7dc908e42e718cf54be89b077d4 100644 (file)
@@ -2610,8 +2610,8 @@ static int musb_probe(struct platform_device *pdev)
        int             irq = platform_get_irq_byname(pdev, "mc");
        void __iomem    *base;
 
-       if (irq <= 0)
-               return -ENODEV;
+       if (irq < 0)
+               return irq;
 
        base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(base))
index e2445ca3356d09be62f9f0f3c37f1f11ca045f08..0cd7fc468de852f2cca1b4b99e2bcf5e8d89d6cc 100644 (file)
@@ -198,10 +198,6 @@ extern struct dma_controller *
 tusb_dma_controller_create(struct musb *musb, void __iomem *base);
 extern void tusb_dma_controller_destroy(struct dma_controller *c);
 
-extern struct dma_controller *
-cppi_dma_controller_create(struct musb *musb, void __iomem *base);
-extern void cppi_dma_controller_destroy(struct dma_controller *c);
-
 extern struct dma_controller *
 cppi41_dma_controller_create(struct musb *musb, void __iomem *base);
 extern void cppi41_dma_controller_destroy(struct dma_controller *c);
index 9119b1d51370ff5fd718c950a98da27a02b01400..98b42dc04dee8c2716db379e0b84e260d21b9920 100644 (file)
@@ -26,9 +26,7 @@
 #include <linux/sizes.h>
 
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/of_address.h>
-#include <linux/of_irq.h>
 #include <linux/usb/of.h>
 
 #include <linux/debugfs.h>
index 31c44325e82872ac8558ff8465250e8f8e159f2d..051c6da7cf6d74de81ca35ed186963c2f6c9b01e 100644 (file)
@@ -1130,7 +1130,7 @@ static int musb_gadget_disable(struct usb_ep *ep)
 struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
 {
        struct musb_ep          *musb_ep = to_musb_ep(ep);
-       struct musb_request     *request = NULL;
+       struct musb_request     *request;
 
        request = kzalloc(sizeof *request, gfp_flags);
        if (!request)
index c5c6c4e09300cf09981ecbac8d4624bb341aa237..d54283fd026b22324c38c09aff69069600ab7b3f 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/phy/phy-sun4i-usb.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
index cbc707fe570fa061def0e7e1e910b1f870a591e5..461587629bf26145775fa6a922e7ed0e7012c3dc 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/usb.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/device.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
@@ -1029,7 +1030,7 @@ static int tusb_musb_start(struct musb *musb)
        void __iomem    *tbase = musb->ctrl_base;
        unsigned long   flags;
        u32             reg;
-       int             i;
+       int             ret;
 
        /*
         * Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and
@@ -1037,17 +1038,13 @@ static int tusb_musb_start(struct musb *musb)
         * provide then PGOOD signal to TUSB6010 which will release it from reset.
         */
        gpiod_set_value(glue->enable, 1);
-       msleep(1);
 
        /* Wait for 100ms until TUSB6010 pulls INT pin down */
-       i = 100;
-       while (i && gpiod_get_value(glue->intpin)) {
-               msleep(1);
-               i--;
-       }
-       if (!i) {
-               pr_err("tusb: Powerup respones failed\n");
-               return -ENODEV;
+       ret = read_poll_timeout(gpiod_get_value, reg, !reg, 5000, 100000, true,
+                               glue->intpin);
+       if (ret) {
+               pr_err("tusb: Powerup response failed\n");
+               return ret;
        }
 
        spin_lock_irqsave(&musb->lock, flags);
index e1a2b2ea098b563af8b1bc35516c368a98b840ed..acd46b72899e900d126dde72922423b55300e401 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 #include <linux/iopoll.h>
@@ -388,19 +388,14 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
 
 static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy)
 {
-       void __iomem *base = mxs_phy->phy.io_priv;
-       u32 phyctrl = readl(base + HW_USBPHY_CTRL);
-
-       if (IS_ENABLED(CONFIG_USB_OTG) &&
-                       !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE))
-               return true;
-
-       return false;
+       return IS_ENABLED(CONFIG_USB_OTG) &&
+               mxs_phy->phy.last_event == USB_EVENT_ID;
 }
 
 static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
 {
        bool vbus_is_on = false;
+       enum usb_phy_events last_event = mxs_phy->phy.last_event;
 
        /* If the SoCs don't need to disconnect line without vbus, quit */
        if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS))
@@ -412,7 +407,8 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
 
        vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
 
-       if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
+       if (on && ((!vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
+               || (last_event == USB_EVENT_VBUS)))
                __mxs_phy_disconnect_line(mxs_phy, true);
        else
                __mxs_phy_disconnect_line(mxs_phy, false);
index 8b2ff3a8882d624953b3dbfffab80608d52ed8ad..4ea47e6f835bfb8517acc87e4a4513b1127bd847 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/resource.h>
 #include <linux/slab.h>
index 111b7ee152c432f3fbee5cf6aa5b4d03f0420c19..dd1c17542439424150f5dffa62089602fbabe0b6 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/gpio/consumer.h>
 #include <linux/io.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/pm_runtime.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
index 2d77edefb4b300e49f40419a7491fefc3441be9b..97b5217c5a903110516e0dc8641bfa5c49dbf5e2 100644 (file)
@@ -8,7 +8,7 @@
 
 #include <linux/delay.h>
 #include <linux/io.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include "common.h"
 #include "rza.h"
 
index 3eed3334a17f126f0f2dc20303eb6b01a386f08e..f079817250bbfaf7470fff9ebe8dad281bfd0ab3 100644 (file)
@@ -8,7 +8,6 @@
 
 #include <linux/delay.h>
 #include <linux/io.h>
-#include <linux/of_device.h>
 #include <linux/phy/phy.h>
 #include "common.h"
 #include "rza.h"
index 8ac98e60fff56bd2a51df1b5b807ba01c0cdeca1..7994a4549a6c80244eba7ce0cf63a700d2be27d7 100644 (file)
@@ -259,6 +259,7 @@ static void option_instat_callback(struct urb *urb);
 #define QUECTEL_PRODUCT_EM05G                  0x030a
 #define QUECTEL_PRODUCT_EM060K                 0x030b
 #define QUECTEL_PRODUCT_EM05G_CS               0x030c
+#define QUECTEL_PRODUCT_EM05GV2                        0x030e
 #define QUECTEL_PRODUCT_EM05CN_SG              0x0310
 #define QUECTEL_PRODUCT_EM05G_SG               0x0311
 #define QUECTEL_PRODUCT_EM05CN                 0x0312
@@ -1188,6 +1189,8 @@ static const struct usb_device_id option_ids[] = {
          .driver_info = RSVD(6) | ZLP },
        { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G, 0xff),
          .driver_info = RSVD(6) | ZLP },
+       { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05GV2, 0xff),
+         .driver_info = RSVD(4) | ZLP },
        { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_CS, 0xff),
          .driver_info = RSVD(6) | ZLP },
        { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_GR, 0xff),
@@ -2232,6 +2235,10 @@ static const struct usb_device_id option_ids[] = {
          .driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
        { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0db, 0xff),                     /* Foxconn T99W265 MBIM */
          .driver_info = RSVD(3) },
+       { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0ee, 0xff),                     /* Foxconn T99W368 MBIM */
+         .driver_info = RSVD(3) },
+       { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0f0, 0xff),                     /* Foxconn T99W373 MBIM */
+         .driver_info = RSVD(3) },
        { USB_DEVICE(0x1508, 0x1001),                                           /* Fibocom NL668 (IOT version) */
          .driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
        { USB_DEVICE(0x1782, 0x4d10) },                                         /* Fibocom L610 (AT mode) */
index 4ec7c5892b847bc8b48c82b0417c0c53b5868e36..1d9a12628f8112d19455781dc23169e0d129a650 100644 (file)
@@ -93,6 +93,7 @@ struct xr_txrx_clk_mask {
 #define XR_GPIO_MODE_SEL_DTR_DSR       0x2
 #define XR_GPIO_MODE_SEL_RS485         0x3
 #define XR_GPIO_MODE_SEL_RS485_ADDR    0x4
+#define XR_GPIO_MODE_RS485_TX_H                0x8
 #define XR_GPIO_MODE_TX_TOGGLE         0x100
 #define XR_GPIO_MODE_RX_TOGGLE         0x200
 
@@ -237,6 +238,7 @@ static const struct xr_type xr_types[] = {
 struct xr_data {
        const struct xr_type *type;
        u8 channel;                     /* zero-based index or interface number */
+       struct serial_rs485 rs485;
 };
 
 static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val)
@@ -629,6 +631,7 @@ static void xr_set_flow_mode(struct tty_struct *tty,
        struct xr_data *data = usb_get_serial_port_data(port);
        const struct xr_type *type = data->type;
        u16 flow, gpio_mode;
+       bool rs485_enabled;
        int ret;
 
        ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode);
@@ -645,7 +648,17 @@ static void xr_set_flow_mode(struct tty_struct *tty,
        /* Set GPIO mode for controlling the pins manually by default. */
        gpio_mode &= ~XR_GPIO_MODE_SEL_MASK;
 
-       if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) {
+       rs485_enabled = !!(data->rs485.flags & SER_RS485_ENABLED);
+       if (rs485_enabled) {
+               dev_dbg(&port->dev, "Enabling RS-485\n");
+               gpio_mode |= XR_GPIO_MODE_SEL_RS485;
+               if (data->rs485.flags & SER_RS485_RTS_ON_SEND)
+                       gpio_mode &= ~XR_GPIO_MODE_RS485_TX_H;
+               else
+                       gpio_mode |= XR_GPIO_MODE_RS485_TX_H;
+       }
+
+       if (C_CRTSCTS(tty) && C_BAUD(tty) != B0 && !rs485_enabled) {
                dev_dbg(&port->dev, "Enabling hardware flow ctrl\n");
                gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS;
                flow = XR_UART_FLOW_MODE_HW;
@@ -809,6 +822,79 @@ static void xr_cdc_set_line_coding(struct tty_struct *tty,
        kfree(lc);
 }
 
+static void xr_sanitize_serial_rs485(struct serial_rs485 *rs485)
+{
+       if (!(rs485->flags & SER_RS485_ENABLED)) {
+               memset(rs485, 0, sizeof(*rs485));
+               return;
+       }
+
+       /* RTS always toggles after TX */
+       if (rs485->flags & SER_RS485_RTS_ON_SEND)
+               rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+       else
+               rs485->flags |= SER_RS485_RTS_AFTER_SEND;
+
+       /* Only the flags are implemented at the moment */
+       rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
+                       SER_RS485_RTS_AFTER_SEND;
+       rs485->delay_rts_before_send = 0;
+       rs485->delay_rts_after_send = 0;
+       memset(rs485->padding, 0, sizeof(rs485->padding));
+}
+
+static int xr_get_rs485_config(struct tty_struct *tty,
+                              struct serial_rs485 __user *argp)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       struct xr_data *data = usb_get_serial_port_data(port);
+
+       down_read(&tty->termios_rwsem);
+       if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) {
+               up_read(&tty->termios_rwsem);
+               return -EFAULT;
+       }
+       up_read(&tty->termios_rwsem);
+
+       return 0;
+}
+
+static int xr_set_rs485_config(struct tty_struct *tty,
+                              struct serial_rs485 __user *argp)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       struct xr_data *data = usb_get_serial_port_data(port);
+       struct serial_rs485 rs485;
+
+       if (copy_from_user(&rs485, argp, sizeof(rs485)))
+               return -EFAULT;
+       xr_sanitize_serial_rs485(&rs485);
+
+       down_write(&tty->termios_rwsem);
+       data->rs485 = rs485;
+       xr_set_flow_mode(tty, port, NULL);
+       up_write(&tty->termios_rwsem);
+
+       if (copy_to_user(argp, &rs485, sizeof(rs485)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int xr_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+
+       switch (cmd) {
+       case TIOCGRS485:
+               return xr_get_rs485_config(tty, argp);
+       case TIOCSRS485:
+               return xr_set_rs485_config(tty, argp);
+       }
+
+       return -ENOIOCTLCMD;
+}
+
 static void xr_set_termios(struct tty_struct *tty,
                           struct usb_serial_port *port,
                           const struct ktermios *old_termios)
@@ -1010,6 +1096,7 @@ static struct usb_serial_driver xr_device = {
        .set_termios            = xr_set_termios,
        .tiocmget               = xr_tiocmget,
        .tiocmset               = xr_tiocmset,
+       .ioctl                  = xr_ioctl,
        .dtr_rts                = xr_dtr_rts
 };
 
index cdf8261e22dbd3ff45c9ff758bd00e493e737f36..426c88a516e5112b64d4aa7ebf03bd0757993333 100644 (file)
@@ -594,7 +594,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
        alt->ops = &dp_altmode_ops;
 
        fwnode = dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */
-       dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0);
+       if (fwnode_property_present(fwnode, "displayport"))
+               dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0);
+       else
+               dp->connector_fwnode = fwnode_handle_get(fwnode); /* embedded DP */
        if (IS_ERR(dp->connector_fwnode))
                dp->connector_fwnode = NULL;
 
index fe5b9a2e61f58aba7b1c1218e880d57023e7f2a9..e95ec7e382bb79031b276105b2a94f0509f9c33b 100644 (file)
@@ -183,12 +183,20 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit);
  *
  * Notifies the partner of @adev about Attention command.
  */
-void typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
+int typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
 {
-       struct typec_altmode *pdev = &to_altmode(adev)->partner->adev;
+       struct altmode *partner = to_altmode(adev)->partner;
+       struct typec_altmode *pdev;
+
+       if (!partner)
+               return -ENODEV;
+
+       pdev = &partner->adev;
 
        if (pdev->ops && pdev->ops->attention)
                pdev->ops->attention(pdev, vdo);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(typec_altmode_attention);
 
index 5e8edf3881c0dd0a252622bf4dea02985a0e20e7..60ed1f809130d8b35a527581677b2229e42b5112 100644 (file)
@@ -59,7 +59,7 @@ enum {
 };
 
 /* Common Mode Data bits */
-#define PMC_USB_ALTMODE_ACTIVE_CABLE   BIT(2)
+#define PMC_USB_ALTMODE_RETIMER_CABLE  BIT(2)
 
 #define PMC_USB_ALTMODE_ORI_SHIFT      1
 #define PMC_USB_ALTMODE_UFP_SHIFT      3
@@ -71,6 +71,7 @@ enum {
 #define PMC_USB_ALTMODE_TBT_TYPE       BIT(17)
 #define PMC_USB_ALTMODE_CABLE_TYPE     BIT(18)
 #define PMC_USB_ALTMODE_ACTIVE_LINK    BIT(20)
+#define PMC_USB_ALTMODE_ACTIVE_CABLE   BIT(22)
 #define PMC_USB_ALTMODE_FORCE_LSR      BIT(23)
 #define PMC_USB_ALTMODE_CABLE_SPD(_s_) (((_s_) & GENMASK(2, 0)) << 25)
 #define   PMC_USB_ALTMODE_CABLE_USB31  1
@@ -117,6 +118,16 @@ enum {
          IOM_PORT_STATUS_DHPD_HPD_STATUS_SHIFT) &                      \
         IOM_PORT_STATUS_DHPD_HPD_STATUS_ASSERT)
 
+/* IOM port status register */
+#define IOM_PORT_STATUS_REGS(_offset_, _size_) ((_offset_) | (_size_))
+#define IOM_PORT_STATUS_REGS_SZ_MASK           BIT(0)
+#define IOM_PORT_STATUS_REGS_SZ_4              0
+#define IOM_PORT_STATUS_REGS_SZ_8              1
+#define IOM_PORT_STATUS_REGS_OFFSET(_d_)                               \
+       ((_d_) & ~IOM_PORT_STATUS_REGS_SZ_MASK)
+#define IOM_PORT_STATUS_REGS_SIZE(_d_)                                 \
+       (4 << ((_d_) & IOM_PORT_STATUS_REGS_SZ_MASK))
+
 struct pmc_usb;
 
 struct pmc_usb_port {
@@ -145,6 +156,7 @@ struct pmc_usb {
        struct acpi_device *iom_adev;
        void __iomem *iom_base;
        u32 iom_port_status_offset;
+       u8 iom_port_status_size;
 
        struct dentry *dentry;
 };
@@ -160,7 +172,7 @@ static void update_port_status(struct pmc_usb_port *port)
 
        port->iom_status = readl(port->pmc->iom_base +
                                 port->pmc->iom_port_status_offset +
-                                port_num * sizeof(u32));
+                                port_num * port->pmc->iom_port_status_size);
 }
 
 static int sbu_orientation(struct pmc_usb_port *port)
@@ -319,8 +331,18 @@ pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state)
        if (data->cable_mode & TBT_CABLE_LINK_TRAINING)
                req.mode_data |= PMC_USB_ALTMODE_ACTIVE_LINK;
 
-       if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE)
-               req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+       if (acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) ||
+           acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL)) {
+               if ((data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE) ||
+                   (data->cable_mode & TBT_CABLE_RETIMER))
+                       req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+       } else {
+               if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE)
+                       req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+
+               if (data->cable_mode & TBT_CABLE_RETIMER)
+                       req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+       }
 
        req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed);
 
@@ -359,8 +381,17 @@ pmc_usb_mux_usb4(struct pmc_usb_port *port, struct typec_mux_state *state)
        case EUDO_CABLE_TYPE_OPTICAL:
                req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE;
                fallthrough;
+       case EUDO_CABLE_TYPE_RE_TIMER:
+               if (!acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) ||
+                   !acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL))
+                       req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+               fallthrough;
        default:
-               req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+               if (acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) ||
+                   acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL))
+                       req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+               else
+                       req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
 
                /* Configure data rate to rounded in the case of Active TBT3
                 * and USB4 cables.
@@ -589,13 +620,16 @@ err_unregister_switch:
 /* IOM ACPI IDs and IOM_PORT_STATUS_OFFSET */
 static const struct acpi_device_id iom_acpi_ids[] = {
        /* TigerLake */
-       { "INTC1072", 0x560, },
+       { "INTC1072", IOM_PORT_STATUS_REGS(0x560, IOM_PORT_STATUS_REGS_SZ_4) },
 
        /* AlderLake */
-       { "INTC1079", 0x160, },
+       { "INTC1079", IOM_PORT_STATUS_REGS(0x160, IOM_PORT_STATUS_REGS_SZ_4) },
 
        /* Meteor Lake */
-       { "INTC107A", 0x160, },
+       { "INTC107A", IOM_PORT_STATUS_REGS(0x160, IOM_PORT_STATUS_REGS_SZ_4) },
+
+       /* Lunar Lake */
+       { "INTC10EA", IOM_PORT_STATUS_REGS(0x150, IOM_PORT_STATUS_REGS_SZ_8) },
        {}
 };
 
@@ -615,7 +649,8 @@ static int pmc_usb_probe_iom(struct pmc_usb *pmc)
        if (!adev)
                return -ENODEV;
 
-       pmc->iom_port_status_offset = (u32)dev_id->driver_data;
+       pmc->iom_port_status_offset = IOM_PORT_STATUS_REGS_OFFSET(dev_id->driver_data);
+       pmc->iom_port_status_size = IOM_PORT_STATUS_REGS_SIZE(dev_id->driver_data);
 
        INIT_LIST_HEAD(&resource_list);
        ret = acpi_dev_get_memory_resources(adev, &resource_list);
index 4d1122d9501326ddca933c919197ac02f464420e..cda206cf0c38760fd28286b2062a6d324c462a67 100644 (file)
@@ -528,7 +528,7 @@ static struct i2c_driver nb7vpq904m_driver = {
                .name = "nb7vpq904m",
                .of_match_table = nb7vpq904m_of_table,
        },
-       .probe_new      = nb7vpq904m_probe,
+       .probe          = nb7vpq904m_probe,
        .remove         = nb7vpq904m_remove,
        .id_table       = nb7vpq904m_table,
 };
index 5d393f520fc2fa365340e1ef5e35c74fe1e8bd00..0b2993fef564bc255fe44806fcc1a01f3f4b982c 100644 (file)
@@ -79,6 +79,7 @@ config TYPEC_WCOVE
 config TYPEC_QCOM_PMIC
        tristate "Qualcomm PMIC USB Type-C Port Controller Manager driver"
        depends on ARCH_QCOM || COMPILE_TEST
+       depends on DRM || DRM=n
        help
          A Type-C port and Power Delivery driver which aggregates two
          discrete pieces of silicon in the PM8150b PMIC block: the
index 7fc1ffa14f76eb3504ef93de6fa4c95e1c12e128..bc21006e979c66d16303868c928787b817eda681 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/proc_fs.h>
 #include <linux/regulator/consumer.h>
index 9b467a346114e2df7dbf634c0649d66fca2873fc..581199d37b49daaceb130b378a9e690ab8233c5d 100644 (file)
@@ -8,7 +8,7 @@
 #include <linux/kernel.h>
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/of_graph.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
@@ -17,6 +17,9 @@
 #include <linux/usb/role.h>
 #include <linux/usb/tcpm.h>
 #include <linux/usb/typec_mux.h>
+
+#include <drm/drm_bridge.h>
+
 #include "qcom_pmic_typec_pdphy.h"
 #include "qcom_pmic_typec_port.h"
 
@@ -33,6 +36,7 @@ struct pmic_typec {
        struct pmic_typec_port  *pmic_typec_port;
        bool                    vbus_enabled;
        struct mutex            lock;           /* VBUS state serialization */
+       struct drm_bridge       bridge;
 };
 
 #define tcpc_to_tcpm(_tcpc_) container_of(_tcpc_, struct pmic_typec, tcpc)
@@ -146,6 +150,35 @@ static int qcom_pmic_typec_init(struct tcpc_dev *tcpc)
        return 0;
 }
 
+#if IS_ENABLED(CONFIG_DRM)
+static int qcom_pmic_typec_attach(struct drm_bridge *bridge,
+                                    enum drm_bridge_attach_flags flags)
+{
+       return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
+}
+
+static const struct drm_bridge_funcs qcom_pmic_typec_bridge_funcs = {
+       .attach = qcom_pmic_typec_attach,
+};
+
+static int qcom_pmic_typec_init_drm(struct pmic_typec *tcpm)
+{
+       tcpm->bridge.funcs = &qcom_pmic_typec_bridge_funcs;
+#ifdef CONFIG_OF
+       tcpm->bridge.of_node = of_get_child_by_name(tcpm->dev->of_node, "connector");
+#endif
+       tcpm->bridge.ops = DRM_BRIDGE_OP_HPD;
+       tcpm->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
+
+       return devm_drm_bridge_add(tcpm->dev, &tcpm->bridge);
+}
+#else
+static int qcom_pmic_typec_init_drm(struct pmic_typec *tcpm)
+{
+       return 0;
+}
+#endif
+
 static int qcom_pmic_typec_probe(struct platform_device *pdev)
 {
        struct pmic_typec *tcpm;
@@ -208,6 +241,10 @@ static int qcom_pmic_typec_probe(struct platform_device *pdev)
        mutex_init(&tcpm->lock);
        platform_set_drvdata(pdev, tcpm);
 
+       ret = qcom_pmic_typec_init_drm(tcpm);
+       if (ret)
+               return ret;
+
        tcpm->tcpc.fwnode = device_get_named_child_node(tcpm->dev, "connector");
        if (!tcpm->tcpc.fwnode)
                return -EINVAL;
index 4e1b846627d20e900e1b1a38d6e2a899ef9e47a6..bb0b8479d80f193f709c0136a5fb1db9e2b9ae87 100644 (file)
@@ -8,8 +8,6 @@
 #include <linux/kernel.h>
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
index 94285f64b67d7237a7c74cf90cc8235c22ddc5f0..a8f3f4d3a4509d3f3f7332a02e50da8d2a7c2dab 100644 (file)
@@ -9,7 +9,6 @@
 #include <linux/kernel.h>
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
@@ -214,6 +213,11 @@ int qcom_pmic_typec_port_get_cc(struct pmic_typec_port *pmic_typec_port,
                if (ret)
                        goto done;
                switch (val & DETECTED_SRC_TYPE_MASK) {
+               case AUDIO_ACCESS_RA_RA:
+                       val = TYPEC_CC_RA;
+                       *cc1 = TYPEC_CC_RA;
+                       *cc2 = TYPEC_CC_RA;
+                       break;
                case SRC_RD_OPEN:
                        val = TYPEC_CC_RD;
                        break;
index fc708c289a73a5eca20f61c36d023cc0b5ea976d..0ee3e6e29bb178418dbcfb60c3d5eaeb542245da 100644 (file)
@@ -602,6 +602,10 @@ static int tcpci_init(struct tcpc_dev *tcpc)
        if (time_after(jiffies, timeout))
                return -ETIMEDOUT;
 
+       ret = tcpci_write16(tcpci, TCPC_FAULT_STATUS, TCPC_FAULT_STATUS_ALL_REG_RST_TO_DEFAULT);
+       if (ret < 0)
+               return ret;
+
        /* Handle vendor init */
        if (tcpci->data->init) {
                ret = tcpci->data->init(tcpci, tcpci->data);
index 2a079464b398d833be70479ed1e8f6677c7d9950..9cda1005ef01a5a74a5d82e2218983b7ab482102 100644 (file)
@@ -147,7 +147,7 @@ static int mt6370_tcpc_probe(struct platform_device *pdev)
 
        irq = platform_get_irq(pdev, 0);
        if (irq < 0)
-               return dev_err_probe(dev, irq, "Failed to get irq\n");
+               return irq;
 
        /* Assign TCPCI feature and ops */
        priv->tcpci_data.auto_discharge_disconnect = 1;
index cc1d83926497723d82ca1f35cc31ad8f1143574a..d962f67c95ae66a25254f0b8fea1b4e725313112 100644 (file)
@@ -1877,7 +1877,8 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
                        }
                        break;
                case ADEV_ATTENTION:
-                       typec_altmode_attention(adev, p[1]);
+                       if (typec_altmode_attention(adev, p[1]))
+                               tcpm_log(port, "typec_altmode_attention no port partner altmode");
                        break;
                }
        }
@@ -2753,6 +2754,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
                        port->sink_cap_done = true;
                        tcpm_set_state(port, ready_state(port), 0);
                        break;
+               /*
+                * Some port partners do not support GET_STATUS, avoid soft reset the link to
+                * prevent redundant power re-negotiation
+                */
+               case GET_STATUS_SEND:
+                       tcpm_set_state(port, ready_state(port), 0);
+                       break;
                case SRC_READY:
                case SNK_READY:
                        if (port->vdm_state > VDM_STATE_READY) {
@@ -3253,23 +3261,12 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
        return ret;
 }
 
-#define min_pps_apdo_current(x, y)     \
-       min(pdo_pps_apdo_max_current(x), pdo_pps_apdo_max_current(y))
-
 static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
 {
-       unsigned int i, j, max_mw = 0, max_mv = 0;
-       unsigned int min_src_mv, max_src_mv, src_ma, src_mw;
-       unsigned int min_snk_mv, max_snk_mv;
-       unsigned int max_op_mv;
-       u32 pdo, src, snk;
-       unsigned int src_pdo = 0, snk_pdo = 0;
+       unsigned int i, src_ma, max_temp_mw = 0, max_op_ma, op_mw;
+       unsigned int src_pdo = 0;
+       u32 pdo, src;
 
-       /*
-        * Select the source PPS APDO providing the most power while staying
-        * within the board's limits. We skip the first PDO as this is always
-        * 5V 3A.
-        */
        for (i = 1; i < port->nr_source_caps; ++i) {
                pdo = port->source_caps[i];
 
@@ -3280,54 +3277,17 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
                                continue;
                        }
 
-                       min_src_mv = pdo_pps_apdo_min_voltage(pdo);
-                       max_src_mv = pdo_pps_apdo_max_voltage(pdo);
-                       src_ma = pdo_pps_apdo_max_current(pdo);
-                       src_mw = (src_ma * max_src_mv) / 1000;
-
-                       /*
-                        * Now search through the sink PDOs to find a matching
-                        * PPS APDO. Again skip the first sink PDO as this will
-                        * always be 5V 3A.
-                        */
-                       for (j = 1; j < port->nr_snk_pdo; j++) {
-                               pdo = port->snk_pdo[j];
-
-                               switch (pdo_type(pdo)) {
-                               case PDO_TYPE_APDO:
-                                       if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) {
-                                               tcpm_log(port,
-                                                        "Not PPS APDO (sink), ignoring");
-                                               continue;
-                                       }
-
-                                       min_snk_mv =
-                                               pdo_pps_apdo_min_voltage(pdo);
-                                       max_snk_mv =
-                                               pdo_pps_apdo_max_voltage(pdo);
-                                       break;
-                               default:
-                                       tcpm_log(port,
-                                                "Not APDO type (sink), ignoring");
-                                       continue;
-                               }
+                       if (port->pps_data.req_out_volt > pdo_pps_apdo_max_voltage(pdo) ||
+                           port->pps_data.req_out_volt < pdo_pps_apdo_min_voltage(pdo))
+                               continue;
 
-                               if (min_src_mv <= max_snk_mv &&
-                                   max_src_mv >= min_snk_mv) {
-                                       max_op_mv = min(max_src_mv, max_snk_mv);
-                                       src_mw = (max_op_mv * src_ma) / 1000;
-                                       /* Prefer higher voltages if available */
-                                       if ((src_mw == max_mw &&
-                                            max_op_mv > max_mv) ||
-                                           src_mw > max_mw) {
-                                               src_pdo = i;
-                                               snk_pdo = j;
-                                               max_mw = src_mw;
-                                               max_mv = max_op_mv;
-                                       }
-                               }
+                       src_ma = pdo_pps_apdo_max_current(pdo);
+                       max_op_ma = min(src_ma, port->pps_data.req_op_curr);
+                       op_mw = max_op_ma * port->pps_data.req_out_volt / 1000;
+                       if (op_mw > max_temp_mw) {
+                               src_pdo = i;
+                               max_temp_mw = op_mw;
                        }
-
                        break;
                default:
                        tcpm_log(port, "Not APDO type (source), ignoring");
@@ -3337,16 +3297,10 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
 
        if (src_pdo) {
                src = port->source_caps[src_pdo];
-               snk = port->snk_pdo[snk_pdo];
-
-               port->pps_data.req_min_volt = max(pdo_pps_apdo_min_voltage(src),
-                                                 pdo_pps_apdo_min_voltage(snk));
-               port->pps_data.req_max_volt = min(pdo_pps_apdo_max_voltage(src),
-                                                 pdo_pps_apdo_max_voltage(snk));
-               port->pps_data.req_max_curr = min_pps_apdo_current(src, snk);
-               port->pps_data.req_out_volt = min(port->pps_data.req_max_volt,
-                                                 max(port->pps_data.req_min_volt,
-                                                     port->pps_data.req_out_volt));
+
+               port->pps_data.req_min_volt = pdo_pps_apdo_min_voltage(src);
+               port->pps_data.req_max_volt = pdo_pps_apdo_max_voltage(src);
+               port->pps_data.req_max_curr = pdo_pps_apdo_max_current(src);
                port->pps_data.req_op_curr = min(port->pps_data.req_max_curr,
                                                 port->pps_data.req_op_curr);
        }
@@ -3464,32 +3418,16 @@ static int tcpm_pd_send_request(struct tcpm_port *port)
 static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo)
 {
        unsigned int out_mv, op_ma, op_mw, max_mv, max_ma, flags;
-       enum pd_pdo_type type;
        unsigned int src_pdo_index;
-       u32 pdo;
 
        src_pdo_index = tcpm_pd_select_pps_apdo(port);
        if (!src_pdo_index)
                return -EOPNOTSUPP;
 
-       pdo = port->source_caps[src_pdo_index];
-       type = pdo_type(pdo);
-
-       switch (type) {
-       case PDO_TYPE_APDO:
-               if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) {
-                       tcpm_log(port, "Invalid APDO selected!");
-                       return -EINVAL;
-               }
-               max_mv = port->pps_data.req_max_volt;
-               max_ma = port->pps_data.req_max_curr;
-               out_mv = port->pps_data.req_out_volt;
-               op_ma = port->pps_data.req_op_curr;
-               break;
-       default:
-               tcpm_log(port, "Invalid PDO selected!");
-               return -EINVAL;
-       }
+       max_mv = port->pps_data.req_max_volt;
+       max_ma = port->pps_data.req_max_curr;
+       out_mv = port->pps_data.req_out_volt;
+       op_ma = port->pps_data.req_op_curr;
 
        flags = RDO_USB_COMM | RDO_NO_SUSPEND;
 
@@ -3789,6 +3727,9 @@ static void tcpm_detach(struct tcpm_port *port)
        if (tcpm_port_is_disconnected(port))
                port->hard_reset_count = 0;
 
+       port->try_src_count = 0;
+       port->try_snk_count = 0;
+
        if (!port->attached)
                return;
 
@@ -3928,6 +3869,29 @@ static enum typec_cc_status tcpm_pwr_opmode_to_rp(enum typec_pwr_opmode opmode)
        }
 }
 
+static void tcpm_set_initial_svdm_version(struct tcpm_port *port)
+{
+       switch (port->negotiated_rev) {
+       case PD_REV30:
+               break;
+       /*
+        * 6.4.4.2.3 Structured VDM Version
+        * 2.0 states "At this time, there is only one version (1.0) defined.
+        * This field Shall be set to zero to indicate Version 1.0."
+        * 3.0 states "This field Shall be set to 01b to indicate Version 2.0."
+        * To ensure that we follow the Power Delivery revision we are currently
+        * operating on, downgrade the SVDM version to the highest one supported
+        * by the Power Delivery revision.
+        */
+       case PD_REV20:
+               typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0);
+               break;
+       default:
+               typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0);
+               break;
+       }
+}
+
 static void run_state_machine(struct tcpm_port *port)
 {
        int ret;
@@ -4165,10 +4129,12 @@ static void run_state_machine(struct tcpm_port *port)
                 * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using
                 * port->explicit_contract to decide whether to send the command.
                 */
-               if (port->explicit_contract)
+               if (port->explicit_contract) {
+                       tcpm_set_initial_svdm_version(port);
                        mod_send_discover_delayed_work(port, 0);
-               else
+               } else {
                        port->send_discover = false;
+               }
 
                /*
                 * 6.3.5
@@ -4301,7 +4267,9 @@ static void run_state_machine(struct tcpm_port *port)
                        if (port->slow_charger_loop && (current_lim > PD_P_SNK_STDBY_MW / 5))
                                current_lim = PD_P_SNK_STDBY_MW / 5;
                        tcpm_set_current_limit(port, current_lim, 5000);
-                       tcpm_set_charge(port, true);
+                       /* Not sink vbus if operational current is 0mA */
+                       tcpm_set_charge(port, !!pdo_max_current(port->snk_pdo[0]));
+
                        if (!port->pd_supported)
                                tcpm_set_state(port, SNK_READY, 0);
                        else
@@ -4455,10 +4423,12 @@ static void run_state_machine(struct tcpm_port *port)
                 * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using
                 * port->explicit_contract.
                 */
-               if (port->explicit_contract)
+               if (port->explicit_contract) {
+                       tcpm_set_initial_svdm_version(port);
                        mod_send_discover_delayed_work(port, 0);
-               else
+               } else {
                        port->send_discover = false;
+               }
 
                power_supply_changed(port->psy);
                break;
@@ -4582,7 +4552,8 @@ static void run_state_machine(struct tcpm_port *port)
                        tcpm_set_current_limit(port,
                                               tcpm_get_current_limit(port),
                                               5000);
-                       tcpm_set_charge(port, true);
+                       /* Not sink vbus if operational current is 0mA */
+                       tcpm_set_charge(port, !!pdo_max_current(port->snk_pdo[0]));
                }
                if (port->ams == HARD_RESET)
                        tcpm_ams_finish(port);
@@ -5889,12 +5860,6 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt)
                goto port_unlock;
        }
 
-       if (req_out_volt < port->pps_data.min_volt ||
-           req_out_volt > port->pps_data.max_volt) {
-               ret = -EINVAL;
-               goto port_unlock;
-       }
-
        target_mw = (port->current_limit * req_out_volt) / 1000;
        if (target_mw < port->operating_snk_mw) {
                ret = -EINVAL;
@@ -6447,11 +6412,7 @@ static int tcpm_psy_set_prop(struct power_supply *psy,
                ret = tcpm_psy_set_online(port, val);
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (val->intval < port->pps_data.min_volt * 1000 ||
-                   val->intval > port->pps_data.max_volt * 1000)
-                       ret = -EINVAL;
-               else
-                       ret = tcpm_pps_set_out_volt(port, val->intval / 1000);
+               ret = tcpm_pps_set_out_volt(port, val->intval / 1000);
                break;
        case POWER_SUPPLY_PROP_CURRENT_NOW:
                if (val->intval > port->pps_data.max_curr * 1000)
index b3bb0191987ed50496a90bd444da132d6e17e5a1..bdcb1764cfae3a71852af90669c622b056504fca 100644 (file)
@@ -4,6 +4,7 @@ config TYPEC_UCSI
        tristate "USB Type-C Connector System Software Interface driver"
        depends on !CPU_BIG_ENDIAN
        depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
+       select USB_COMMON if DEBUG_FS
        help
          USB Type-C Connector System Software Interface (UCSI) is a
          specification for an interface that allows the operating system to
index 77f09e13695613b1fef5ad8148a3429cbd61ae0b..b4679f94696b6b653703b1601a201843630a24de 100644 (file)
@@ -5,6 +5,8 @@ obj-$(CONFIG_TYPEC_UCSI)                += typec_ucsi.o
 
 typec_ucsi-y                           := ucsi.o
 
+typec_ucsi-$(CONFIG_DEBUG_FS)          += debugfs.o
+
 typec_ucsi-$(CONFIG_TRACING)           += trace.o
 
 ifneq ($(CONFIG_POWER_SUPPLY),)
diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c
new file mode 100644 (file)
index 0000000..0c7bf88
--- /dev/null
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UCSI debugfs interface
+ *
+ * Copyright (C) 2023 Intel Corporation
+ *
+ * Authors: Rajaram Regupathy <rajaram.regupathy@intel.com>
+ *         Gopal Saranya <saranya.gopal@intel.com>
+ */
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+
+#include <asm/errno.h>
+
+#include "ucsi.h"
+
+static struct dentry *ucsi_debugfs_root;
+
+static int ucsi_cmd(void *data, u64 val)
+{
+       struct ucsi *ucsi = data;
+       int ret;
+
+       memset(&ucsi->debugfs->response, 0, sizeof(ucsi->debugfs->response));
+       ucsi->debugfs->status = 0;
+
+       switch (UCSI_COMMAND(val)) {
+       case UCSI_SET_UOM:
+       case UCSI_SET_UOR:
+       case UCSI_SET_PDR:
+       case UCSI_CONNECTOR_RESET:
+               ret = ucsi_send_command(ucsi, val, NULL, 0);
+               break;
+       case UCSI_GET_CAPABILITY:
+       case UCSI_GET_CONNECTOR_CAPABILITY:
+       case UCSI_GET_ALTERNATE_MODES:
+       case UCSI_GET_CURRENT_CAM:
+       case UCSI_GET_PDOS:
+       case UCSI_GET_CABLE_PROPERTY:
+       case UCSI_GET_CONNECTOR_STATUS:
+               ret = ucsi_send_command(ucsi, val,
+                                       &ucsi->debugfs->response,
+                                       sizeof(ucsi->debugfs->response));
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+       }
+
+       if (ret < 0) {
+               ucsi->debugfs->status = ret;
+               return ret;
+       }
+
+       return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucsi_cmd_fops, NULL, ucsi_cmd, "0x%llx\n");
+
+static int ucsi_resp_show(struct seq_file *s, void *not_used)
+{
+       struct ucsi *ucsi = s->private;
+
+       if (ucsi->debugfs->status)
+               return ucsi->debugfs->status;
+
+       seq_printf(s, "0x%016llx%016llx\n", ucsi->debugfs->response.high,
+                  ucsi->debugfs->response.low);
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ucsi_resp);
+
+void ucsi_debugfs_register(struct ucsi *ucsi)
+{
+       ucsi->debugfs = kzalloc(sizeof(*ucsi->debugfs), GFP_KERNEL);
+       if (!ucsi->debugfs)
+               return;
+
+       ucsi->debugfs->dentry = debugfs_create_dir(dev_name(ucsi->dev), ucsi_debugfs_root);
+       debugfs_create_file("command", 0200, ucsi->debugfs->dentry, ucsi, &ucsi_cmd_fops);
+       debugfs_create_file("response", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_resp_fops);
+}
+
+void ucsi_debugfs_unregister(struct ucsi *ucsi)
+{
+       debugfs_remove_recursive(ucsi->debugfs->dentry);
+       kfree(ucsi->debugfs);
+}
+
+void ucsi_debugfs_init(void)
+{
+       ucsi_debugfs_root = debugfs_create_dir("ucsi", usb_debug_root);
+}
+
+void ucsi_debugfs_exit(void)
+{
+       debugfs_remove(ucsi_debugfs_root);
+}
index f6901319639d9c28cc7f5e50aa59b4f66b09cfcd..c6dfe3dff3465eddf5e106f325a3cfac9e262968 100644 (file)
@@ -1530,6 +1530,7 @@ EXPORT_SYMBOL_GPL(ucsi_create);
  */
 void ucsi_destroy(struct ucsi *ucsi)
 {
+       ucsi_debugfs_unregister(ucsi);
        kfree(ucsi);
 }
 EXPORT_SYMBOL_GPL(ucsi_destroy);
@@ -1552,6 +1553,7 @@ int ucsi_register(struct ucsi *ucsi)
 
        queue_delayed_work(system_long_wq, &ucsi->work, 0);
 
+       ucsi_debugfs_register(ucsi);
        return 0;
 }
 EXPORT_SYMBOL_GPL(ucsi_register);
@@ -1611,6 +1613,19 @@ void ucsi_unregister(struct ucsi *ucsi)
 }
 EXPORT_SYMBOL_GPL(ucsi_unregister);
 
+static int __init ucsi_module_init(void)
+{
+       ucsi_debugfs_init();
+       return 0;
+}
+module_init(ucsi_module_init);
+
+static void __exit ucsi_module_exit(void)
+{
+       ucsi_debugfs_exit();
+}
+module_exit(ucsi_module_exit);
+
 MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("USB Type-C Connector System Software Interface driver");
index c09af859f573ae5f0db7bbb94e6307305c77c7fd..474315a72c7707ab5ae96a2664ada020a906d4b0 100644 (file)
@@ -15,6 +15,7 @@
 
 struct ucsi;
 struct ucsi_altmode;
+struct dentry;
 
 /* UCSI offsets (Bytes) */
 #define UCSI_VERSION                   0
@@ -277,6 +278,16 @@ struct ucsi_connector_status {
 
 /* -------------------------------------------------------------------------- */
 
+struct ucsi_debugfs_entry {
+       u64 command;
+       struct ucsi_data {
+               u64 low;
+               u64 high;
+       } response;
+       u32 status;
+       struct dentry *dentry;
+};
+
 struct ucsi {
        u16 version;
        struct device *dev;
@@ -286,6 +297,7 @@ struct ucsi {
 
        struct ucsi_capability cap;
        struct ucsi_connector *connector;
+       struct ucsi_debugfs_entry *debugfs;
 
        struct work_struct resume_work;
        struct delayed_work work;
@@ -388,6 +400,18 @@ static inline void
 ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
 #endif /* CONFIG_TYPEC_DP_ALTMODE */
 
+#ifdef CONFIG_DEBUG_FS
+void ucsi_debugfs_init(void);
+void ucsi_debugfs_exit(void);
+void ucsi_debugfs_register(struct ucsi *ucsi);
+void ucsi_debugfs_unregister(struct ucsi *ucsi);
+#else
+static inline void ucsi_debugfs_init(void) { }
+static inline void ucsi_debugfs_exit(void) { }
+static inline void ucsi_debugfs_register(struct ucsi *ucsi) { }
+static inline void ucsi_debugfs_unregister(struct ucsi *ucsi) { }
+#endif /* CONFIG_DEBUG_FS */
+
 /*
  * NVIDIA VirtualLink (svid 0x955) has two altmode. VirtualLink
  * DP mode with vdo=0x1 and NVIDIA test mode with vdo=0x3
index 1fe9cb5b6bd96dcdcb850541181ad4ab4da261cb..bb1854b3311dc70c9450b42a64d07b83957e8a22 100644 (file)
@@ -5,7 +5,6 @@
  */
 #include <linux/auxiliary_bus.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
 #include <linux/mutex.h>
 #include <linux/property.h>
 #include <linux/soc/qcom/pdr.h>
index 2bc428f2e26108a1b1c201206d2feaa4c5235560..44b04c54c08677155b747b28f272e91ec21d0f21 100644 (file)
@@ -489,11 +489,11 @@ static void vudc_device_unusable(struct usbip_device *ud)
 
 struct vudc_device *alloc_vudc_device(int devid)
 {
-       struct vudc_device *udc_dev = NULL;
+       struct vudc_device *udc_dev;
 
        udc_dev = kzalloc(sizeof(*udc_dev), GFP_KERNEL);
        if (!udc_dev)
-               goto out;
+               return NULL;
 
        INIT_LIST_HEAD(&udc_dev->dev_entry);
 
@@ -503,7 +503,6 @@ struct vudc_device *alloc_vudc_device(int devid)
                udc_dev = NULL;
        }
 
-out:
        return udc_dev;
 }
 
index 25f8e62a30ecac5dcf9463102966e4b5d7938a6c..a21074861f91fe351027a9ce325868d7cd5ccae1 100644 (file)
@@ -25,7 +25,6 @@
 
 struct usb_device;
 struct usb_driver;
-struct wusb_dev;
 
 /*-------------------------------------------------------------------------*/
 
@@ -425,7 +424,6 @@ struct usb_host_config {
 struct usb_host_bos {
        struct usb_bos_descriptor       *desc;
 
-       /* wireless cap descriptor is handled by wusb */
        struct usb_ext_cap_descriptor   *ext_cap;
        struct usb_ss_cap_descriptor    *ss_cap;
        struct usb_ssp_cap_descriptor   *ssp_cap;
@@ -612,7 +610,6 @@ struct usb3_lpm_parameters {
  *     WUSB devices are not, until we authorize them from user space.
  *     FIXME -- complete doc
  * @authenticated: Crypto authentication passed
- * @wusb: device is Wireless USB
  * @lpm_capable: device supports LPM
  * @lpm_devinit_allow: Allow USB3 device initiated LPM, exit latency is in range
  * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM
@@ -634,8 +631,6 @@ struct usb3_lpm_parameters {
  * @do_remote_wakeup:  remote wakeup should be enabled
  * @reset_resume: needs reset instead of resume
  * @port_is_suspended: the upstream port is suspended (L2 or U3)
- * @wusb_dev: if this is a Wireless USB device, link to the WUSB
- *     specific data for the device.
  * @slot_id: Slot ID assigned by xHCI
  * @removable: Device can be physically removed from this port
  * @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout.
@@ -696,7 +691,6 @@ struct usb_device {
        unsigned have_langid:1;
        unsigned authorized:1;
        unsigned authenticated:1;
-       unsigned wusb:1;
        unsigned lpm_capable:1;
        unsigned lpm_devinit_allow:1;
        unsigned usb2_hw_lpm_capable:1;
@@ -727,7 +721,6 @@ struct usb_device {
        unsigned reset_resume:1;
        unsigned port_is_suspended:1;
 
-       struct wusb_dev *wusb_dev;
        int slot_id;
        struct usb2_lpm_parameters l1_params;
        struct usb3_lpm_parameters u1_params;
@@ -1742,11 +1735,6 @@ static inline void usb_fill_bulk_urb(struct urb *urb,
  * encoding of the endpoint interval, and express polling intervals in
  * microframes (eight per millisecond) rather than in frames (one per
  * millisecond).
- *
- * Wireless USB also uses the logarithmic encoding, but specifies it in units of
- * 128us instead of 125us.  For Wireless USB devices, the interval is passed
- * through to the host controller, rather than being translated into microframe
- * units.
  */
 static inline void usb_fill_int_urb(struct urb *urb,
                                    struct usb_device *dev,
index 969e7dba6358e2f5a8367dd5154e0c4eca1fc492..c93b410b314a16247186182762a6651a8a55c0e6 100644 (file)
@@ -3,7 +3,7 @@
  * This file holds USB constants and structures that are needed for
  * USB device APIs.  These are used by the USB device model, which is
  * defined in chapter 9 of the USB 2.0 specification and in the
- * Wireless USB 1.0 (spread around).  Linux has several APIs in C that
+ * Wireless USB 1.0 spec (now defunct).  Linux has several APIs in C that
  * need these:
  *
  * - the host side Linux-USB kernel driver API;
@@ -14,9 +14,6 @@
  * act either as a USB host or as a USB device.  That means the host and
  * device side APIs benefit from working well together.
  *
- * There's also "Wireless USB", using low power short range radios for
- * peripheral interconnection but otherwise building on the USB framework.
- *
  * Note all descriptors are declared '__attribute__((packed))' so that:
  *
  * [a] they never get padded, either internally (USB spec writers
index ee38835ed77cc4bd2b43425fffbaff280c3ab881..0b4f2d5faa080dfd122a051d8fde6b706fe6c61f 100644 (file)
@@ -63,6 +63,7 @@ struct ci_hdrc_platform_data {
 #define CI_HDRC_IMX_IS_HSIC            BIT(14)
 #define CI_HDRC_PMQOS                  BIT(15)
 #define CI_HDRC_PHY_VBUS_CONTROL       BIT(16)
+#define CI_HDRC_HAS_PORTSC_PEC_MISSED  BIT(17)
        enum usb_dr_mode        dr_mode;
 #define CI_HDRC_CONTROLLER_RESET_EVENT         0
 #define CI_HDRC_CONTROLLER_STOPPED_EVENT       1
index 07531c4f435040eb4349c1632b82eeaf00312ce8..6014340ba980de112ad4dc3f6cff6f6d1593b6ef 100644 (file)
@@ -450,29 +450,6 @@ static inline struct usb_composite_driver *to_cdriver(
  *
  * One of these devices is allocated and initialized before the
  * associated device driver's bind() is called.
- *
- * OPEN ISSUE:  it appears that some WUSB devices will need to be
- * built by combining a normal (wired) gadget with a wireless one.
- * This revision of the gadget framework should probably try to make
- * sure doing that won't hurt too much.
- *
- * One notion for how to handle Wireless USB devices involves:
- *
- * (a) a second gadget here, discovery mechanism TBD, but likely
- *     needing separate "register/unregister WUSB gadget" calls;
- * (b) updates to usb_gadget to include flags "is it wireless",
- *     "is it wired", plus (presumably in a wrapper structure)
- *     bandgroup and PHY info;
- * (c) presumably a wireless_ep wrapping a usb_ep, and reporting
- *     wireless-specific parameters like maxburst and maxsequence;
- * (d) configurations that are specific to wireless links;
- * (e) function drivers that understand wireless configs and will
- *     support wireless for (additional) function instances;
- * (f) a function to support association setup (like CBAF), not
- *     necessarily requiring a wireless adapter;
- * (g) composite device setup that can create one or more wireless
- *     configs, including appropriate association setup support;
- * (h) more, TBD.
  */
 struct usb_composite_dev {
        struct usb_gadget               *gadget;
index 4e9623e8492b38aae05f788f589d2a207d693b7a..61d4f0b793dcdc7300b2c991e53b862381edcb35 100644 (file)
@@ -154,7 +154,6 @@ struct usb_hcd {
        /* The next flag is a stopgap, to be removed when all the HCDs
         * support the new root-hub polling mechanism. */
        unsigned                uses_new_polling:1;
-       unsigned                wireless:1;     /* Wireless USB HCD */
        unsigned                has_tt:1;       /* Integrated TT in root hub */
        unsigned                amd_resume_bug:1; /* AMD remote wakeup quirk */
        unsigned                can_do_streams:1; /* HC supports streams */
@@ -249,7 +248,6 @@ struct hc_driver {
 #define        HCD_SHARED      0x0004          /* Two (or more) usb_hcds share HW */
 #define        HCD_USB11       0x0010          /* USB 1.1 */
 #define        HCD_USB2        0x0020          /* USB 2.0 */
-#define        HCD_USB25       0x0030          /* Wireless USB 1.0 (USB 2.5)*/
 #define        HCD_USB3        0x0040          /* USB 3.0 */
 #define        HCD_USB31       0x0050          /* USB 3.1 */
 #define        HCD_USB32       0x0060          /* USB 3.2 */
index e4de6bc1f69b6287cb49882c3235b824bb474d13..b513749582d775a5f6ee0e87cb0bf98fcd988959 100644 (file)
@@ -144,6 +144,10 @@ struct usb_phy {
         */
        int     (*set_wakeup)(struct usb_phy *x, bool enabled);
 
+       /* notify phy port status change */
+       int     (*notify_port_status)(struct usb_phy *x, int port,
+                                     u16 portstatus, u16 portchange);
+
        /* notify phy connect status change */
        int     (*notify_connect)(struct usb_phy *x,
                        enum usb_device_speed speed);
@@ -316,6 +320,15 @@ usb_phy_set_wakeup(struct usb_phy *x, bool enabled)
                return 0;
 }
 
+static inline int
+usb_phy_notify_port_status(struct usb_phy *x, int port, u16 portstatus, u16 portchange)
+{
+       if (x && x->notify_port_status)
+               return x->notify_port_status(x, port, portstatus, portchange);
+       else
+               return 0;
+}
+
 static inline int
 usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed)
 {
index 85e95a3251d34554644a7cd230313708914d5cc6..83376473ac76500d47e50f1be3de7e50816002b9 100644 (file)
 #define TCPC_POWER_STATUS_SINKING_VBUS BIT(0)
 
 #define TCPC_FAULT_STATUS              0x1f
+#define TCPC_FAULT_STATUS_ALL_REG_RST_TO_DEFAULT BIT(7)
 
 #define TCPC_ALERT_EXTENDED            0x21
 
index 350d49012659baba691df1442f9cb99f7bc4a2f6..28aeef8f9e7b5d9b6b55f0930dce116d02a4a3ce 100644 (file)
@@ -67,7 +67,7 @@ struct typec_altmode_ops {
 
 int typec_altmode_enter(struct typec_altmode *altmode, u32 *vdo);
 int typec_altmode_exit(struct typec_altmode *altmode);
-void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo);
+int typec_altmode_attention(struct typec_altmode *altmode, u32 vdo);
 int typec_altmode_vdm(struct typec_altmode *altmode,
                      const u32 header, const u32 *vdo, int count);
 int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf,
index fb0cd24c392c0d5e7e15117462f0ffc48dd8fbe8..ce4c83f2e66ac16ac706d4eb8ba38d484e732987 100644 (file)
 /* This is arbitrary.
  * From USB 2.0 spec Table 11-13, offset 7, a hub can
  * have up to 255 ports. The most yet reported is 10.
- *
- * Current Wireless USB host hardware (Intel i1480 for example) allows
- * up to 22 devices to connect. Upcoming hardware might raise that
- * limit. Because the arrays need to add a bit for hub status data, we
+ * Upcoming hardware might raise that limit.
+ * Because the arrays need to add a bit for hub status data, we
  * use 31, so plus one evens out to four bytes.
  */
 #define USB_MAXCHILDREN                31
index 62d318377379e1b03869db1256781cf33ff28c4a..8a147abfc6806590c3bfd91d37364478fc15f486 100644 (file)
@@ -3,7 +3,7 @@
  * This file holds USB constants and structures that are needed for
  * USB device APIs.  These are used by the USB device model, which is
  * defined in chapter 9 of the USB 2.0 specification and in the
- * Wireless USB 1.0 (spread around).  Linux has several APIs in C that
+ * Wireless USB 1.0 spec (now defunct).  Linux has several APIs in C that
  * need these:
  *
  * - the master/host side Linux-USB kernel driver API;
@@ -14,9 +14,6 @@
  * act either as a USB master/host or as a USB slave/device.  That means
  * the master and slave side APIs benefit from working well together.
  *
- * There's also "Wireless USB", using low power short range radios for
- * peripheral interconnection but otherwise building on the USB framework.
- *
  * Note all descriptors are declared '__attribute__((packed))' so that:
  *
  * [a] they never get padded, either internally (USB spec writers