summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-07-05 14:16:22 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2021-07-05 14:16:22 -0700
commit79160a603bdb51916226caf4a6616cc4e1c58a58 (patch)
tree6aa2dd8ce76799921ba56eace1c0262ab947f115
parentc932ed0adb09a7fa6d6649ee04dd78c83ab07ada (diff)
parent7756f1d6369e61d1cc47d6e51619d1e1d1681a2e (diff)
downloadlinux-rpi-79160a603bdb51916226caf4a6616cc4e1c58a58.tar.gz
linux-rpi-79160a603bdb51916226caf4a6616cc4e1c58a58.tar.bz2
linux-rpi-79160a603bdb51916226caf4a6616cc4e1c58a58.zip
Merge tag 'usb-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt updates from Greg KH: "Here is the big set of USB and Thunderbolt patches for 5.14-rc1. Nothing major here just lots of little changes for new hardware and features. Highlights are: - more USB 4 support added to the thunderbolt core - build warning fixes all over the place - usb-serial driver updates and new device support - mtu3 driver updates - gadget driver updates - dwc3 driver updates - dwc2 driver updates - isp1760 host driver updates - musb driver updates - lots of other tiny things. Full details are in the shortlog. All of these have been in linux-next for a while now with no reported issues" * tag 'usb-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (223 commits) phy: qcom-qusb2: Add configuration for SM4250 and SM6115 dt-bindings: phy: qcom,qusb2: document sm4250/6115 compatible dt-bindings: usb: qcom,dwc3: Add bindings for sm6115/4250 USB: cdc-acm: blacklist Heimann USB Appset device usb: xhci-mtk: allow multiple Start-Split in a microframe usb: ftdi-elan: remove redundant continue statement in a while-loop usb: class: cdc-wdm: return the correct errno code xhci: remove redundant continue statement usb: dwc3: Fix debugfs creation flow usb: gadget: hid: fix error return code in hid_bind() usb: gadget: eem: fix echo command packet response issue usb: gadget: f_hid: fix endianness issue with descriptors Revert "USB: misc: Add onboard_usb_hub driver" Revert "of/platform: Add stubs for of_platform_device_create/destroy()" Revert "usb: host: xhci-plat: Create platform device for onboard hubs in probe()" Revert "arm64: dts: qcom: sc7180-trogdor: Add nodes for onboard USB hub" xhci: solve a double free problem while doing s4 xhci: handle failed buffer copy to URB sg list and fix a W=1 copiler warning xhci: Add adaptive interrupt rate for isoch TRBs with XHCI_AVOID_BEI quirk xhci: Remove unused defines for ERST_SIZE and ERST_ENTRIES ...
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget-uac22
-rw-r--r--Documentation/ABI/testing/sysfs-bus-thunderbolt82
-rw-r--r--Documentation/ABI/testing/sysfs-bus-usb11
-rw-r--r--Documentation/ABI/testing/sysfs-devices-removable18
-rw-r--r--Documentation/admin-guide/thunderbolt.rst29
-rw-r--r--Documentation/devicetree/bindings/phy/allwinner,sun8i-h3-usb-phy.yaml4
-rw-r--r--Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml2
-rw-r--r--Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml3
-rw-r--r--Documentation/devicetree/bindings/usb/cdns,usb3.yaml1
-rw-r--r--Documentation/devicetree/bindings/usb/dwc2.yaml1
-rw-r--r--Documentation/devicetree/bindings/usb/nxp,isp1760.yaml69
-rw-r--r--Documentation/devicetree/bindings/usb/qcom,dwc3.yaml2
-rw-r--r--Documentation/devicetree/bindings/usb/realtek,rts5411.yaml62
-rw-r--r--Documentation/driver-api/usb/error-codes.rst3
-rw-r--r--Documentation/usb/gadget-testing.rst2
-rw-r--r--arch/arm/boot/dts/arm-realview-eb.dtsi2
-rw-r--r--arch/arm/boot/dts/arm-realview-pb1176.dts2
-rw-r--r--arch/arm/boot/dts/arm-realview-pb11mp.dts2
-rw-r--r--arch/arm/boot/dts/arm-realview-pbx.dtsi2
-rw-r--r--arch/arm/boot/dts/vexpress-v2m-rs1.dtsi2
-rw-r--r--arch/arm/boot/dts/vexpress-v2m.dtsi2
-rw-r--r--drivers/base/core.c28
-rw-r--r--drivers/pci/probe.c22
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qusb2.c34
-rw-r--r--drivers/phy/tegra/xusb-tegra186.c550
-rw-r--r--drivers/phy/tegra/xusb-tegra210.c1533
-rw-r--r--drivers/phy/tegra/xusb.c92
-rw-r--r--drivers/phy/tegra/xusb.h22
-rw-r--r--drivers/thunderbolt/Makefile2
-rw-r--r--drivers/thunderbolt/acpi.c206
-rw-r--r--drivers/thunderbolt/dma_port.c94
-rw-r--r--drivers/thunderbolt/domain.c9
-rw-r--r--drivers/thunderbolt/eeprom.c19
-rw-r--r--drivers/thunderbolt/icm.c20
-rw-r--r--drivers/thunderbolt/lc.c6
-rw-r--r--drivers/thunderbolt/nhi.c71
-rw-r--r--drivers/thunderbolt/nhi.h2
-rw-r--r--drivers/thunderbolt/nvm.c95
-rw-r--r--drivers/thunderbolt/path.c4
-rw-r--r--drivers/thunderbolt/quirks.c30
-rw-r--r--drivers/thunderbolt/retimer.c108
-rw-r--r--drivers/thunderbolt/sb_regs.h2
-rw-r--r--drivers/thunderbolt/switch.c274
-rw-r--r--drivers/thunderbolt/tb.c71
-rw-r--r--drivers/thunderbolt/tb.h116
-rw-r--r--drivers/thunderbolt/tb_regs.h4
-rw-r--r--drivers/thunderbolt/test.c559
-rw-r--r--drivers/thunderbolt/tunnel.c401
-rw-r--r--drivers/thunderbolt/tunnel.h2
-rw-r--r--drivers/thunderbolt/usb4.c438
-rw-r--r--drivers/thunderbolt/usb4_port.c280
-rw-r--r--drivers/thunderbolt/xdomain.c10
-rw-r--r--drivers/usb/atm/cxacru.c2
-rw-r--r--drivers/usb/cdns3/cdns3-ep0.c6
-rw-r--r--drivers/usb/cdns3/cdns3-gadget.c40
-rw-r--r--drivers/usb/cdns3/cdns3-imx.c2
-rw-r--r--drivers/usb/cdns3/cdns3-plat.c2
-rw-r--r--drivers/usb/cdns3/cdns3-ti.c2
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.c7
-rw-r--r--drivers/usb/cdns3/cdnsp-mem.c5
-rw-r--r--drivers/usb/cdns3/core.c4
-rw-r--r--drivers/usb/chipidea/ci.h2
-rw-r--r--drivers/usb/chipidea/core.c2
-rw-r--r--drivers/usb/chipidea/debug.c34
-rw-r--r--drivers/usb/chipidea/otg.c9
-rw-r--r--drivers/usb/chipidea/udc.c2
-rw-r--r--drivers/usb/class/cdc-acm.c5
-rw-r--r--drivers/usb/class/cdc-wdm.c5
-rw-r--r--drivers/usb/common/ulpi.c2
-rw-r--r--drivers/usb/common/usb-conn-gpio.c62
-rw-r--r--drivers/usb/core/devio.c2
-rw-r--r--drivers/usb/core/hcd.c130
-rw-r--r--drivers/usb/core/hub.c34
-rw-r--r--drivers/usb/core/message.c6
-rw-r--r--drivers/usb/core/quirks.c1
-rw-r--r--drivers/usb/core/sysfs.c24
-rw-r--r--drivers/usb/core/urb.c9
-rw-r--r--drivers/usb/dwc2/core.c30
-rw-r--r--drivers/usb/dwc2/gadget.c14
-rw-r--r--drivers/usb/dwc2/hcd_queue.c2
-rw-r--r--drivers/usb/dwc2/params.c4
-rw-r--r--drivers/usb/dwc2/pci.c2
-rw-r--r--drivers/usb/dwc2/platform.c2
-rw-r--r--drivers/usb/dwc3/core.c7
-rw-r--r--drivers/usb/dwc3/core.h2
-rw-r--r--drivers/usb/dwc3/debugfs.c8
-rw-r--r--drivers/usb/dwc3/drd.c1
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c8
-rw-r--r--drivers/usb/dwc3/gadget.c4
-rw-r--r--drivers/usb/dwc3/trace.h2
-rw-r--r--drivers/usb/gadget/function/f_eem.c43
-rw-r--r--drivers/usb/gadget/function/f_fs.c65
-rw-r--r--drivers/usb/gadget/function/f_hid.c4
-rw-r--r--drivers/usb/gadget/function/f_printer.c3
-rw-r--r--drivers/usb/gadget/function/f_uac2.c144
-rw-r--r--drivers/usb/gadget/function/u_audio.c225
-rw-r--r--drivers/usb/gadget/function/u_audio.h12
-rw-r--r--drivers/usb/gadget/function/u_hid.h4
-rw-r--r--drivers/usb/gadget/function/u_midi.h4
-rw-r--r--drivers/usb/gadget/function/u_uac2.h4
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c5
-rw-r--r--drivers/usb/gadget/legacy/hid.c4
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c7
-rw-r--r--drivers/usb/gadget/udc/core.c49
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c11
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c5
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c44
-rw-r--r--drivers/usb/gadget/udc/fsl_usb2_udc.h19
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c7
-rw-r--r--drivers/usb/gadget/udc/gr_udc.h2
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c5
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c2
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c2
-rw-r--r--drivers/usb/gadget/udc/net2272.c41
-rw-r--r--drivers/usb/gadget/udc/net2272.h1
-rw-r--r--drivers/usb/gadget/udc/net2280.c51
-rw-r--r--drivers/usb/gadget/udc/net2280.h1
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c4
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.h4
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c6
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.h4
-rw-r--r--drivers/usb/gadget/udc/s3c-hsudc.c5
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c9
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.h1
-rw-r--r--drivers/usb/gadget/udc/tegra-xudc.c30
-rw-r--r--drivers/usb/gadget/udc/trace.c2
-rw-r--r--drivers/usb/gadget/udc/trace.h2
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c4
-rw-r--r--drivers/usb/host/ehci-fsl.c8
-rw-r--r--drivers/usb/host/ehci-hcd.c8
-rw-r--r--drivers/usb/host/ehci-hub.c139
-rw-r--r--drivers/usb/host/ehci-q.c2
-rw-r--r--drivers/usb/host/fotg210-hcd.c5
-rw-r--r--drivers/usb/host/fotg210.h3
-rw-r--r--drivers/usb/host/u132-hcd.c6
-rw-r--r--drivers/usb/host/xhci-mem.c3
-rw-r--r--drivers/usb/host/xhci-mtk-sch.c60
-rw-r--r--drivers/usb/host/xhci-mtk.c2
-rw-r--r--drivers/usb/host/xhci-mtk.h10
-rw-r--r--drivers/usb/host/xhci-pci-renesas.c18
-rw-r--r--drivers/usb/host/xhci-ring.c7
-rw-r--r--drivers/usb/host/xhci-tegra.c621
-rw-r--r--drivers/usb/host/xhci.c10
-rw-r--r--drivers/usb/host/xhci.h11
-rw-r--r--drivers/usb/isp1760/Kconfig5
-rw-r--r--drivers/usb/isp1760/isp1760-core.c513
-rw-r--r--drivers/usb/isp1760/isp1760-core.h44
-rw-r--r--drivers/usb/isp1760/isp1760-hcd.c1020
-rw-r--r--drivers/usb/isp1760/isp1760-hcd.h57
-rw-r--r--drivers/usb/isp1760/isp1760-if.c41
-rw-r--r--drivers/usb/isp1760/isp1760-regs.h435
-rw-r--r--drivers/usb/isp1760/isp1760-udc.c251
-rw-r--r--drivers/usb/isp1760/isp1760-udc.h13
-rw-r--r--drivers/usb/misc/ftdi-elan.c1
-rw-r--r--drivers/usb/mtu3/mtu3.h30
-rw-r--r--drivers/usb/mtu3/mtu3_core.c20
-rw-r--r--drivers/usb/mtu3/mtu3_debugfs.c1
-rw-r--r--drivers/usb/mtu3/mtu3_dr.c169
-rw-r--r--drivers/usb/mtu3/mtu3_gadget.c2
-rw-r--r--drivers/usb/mtu3/mtu3_host.c6
-rw-r--r--drivers/usb/mtu3/mtu3_plat.c95
-rw-r--r--drivers/usb/musb/musb_core.c71
-rw-r--r--drivers/usb/musb/musb_gadget.c2
-rw-r--r--drivers/usb/musb/musb_host.c18
-rw-r--r--drivers/usb/musb/musb_host.h4
-rw-r--r--drivers/usb/musb/musb_trace.h17
-rw-r--r--drivers/usb/musb/omap2430.c32
-rw-r--r--drivers/usb/phy/phy-isp1301-omap.c2
-rw-r--r--drivers/usb/phy/phy-isp1301.c25
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c15
-rw-r--r--drivers/usb/phy/phy.c55
-rw-r--r--drivers/usb/roles/class.c9
-rw-r--r--drivers/usb/serial/cp210x.c189
-rw-r--r--drivers/usb/serial/cyberjack.c4
-rw-r--r--drivers/usb/serial/cypress_m8.c16
-rw-r--r--drivers/usb/serial/digi_acceleport.c46
-rw-r--r--drivers/usb/serial/garmin_gps.c2
-rw-r--r--drivers/usb/serial/generic.c12
-rw-r--r--drivers/usb/serial/io_edgeport.c39
-rw-r--r--drivers/usb/serial/io_ti.c12
-rw-r--r--drivers/usb/serial/ir-usb.c6
-rw-r--r--drivers/usb/serial/keyspan.c4
-rw-r--r--drivers/usb/serial/kobil_sct.c4
-rw-r--r--drivers/usb/serial/metro-usb.c12
-rw-r--r--drivers/usb/serial/mos7720.c29
-rw-r--r--drivers/usb/serial/mos7840.c17
-rw-r--r--drivers/usb/serial/opticon.c6
-rw-r--r--drivers/usb/serial/oti6858.c12
-rw-r--r--drivers/usb/serial/quatech2.c6
-rw-r--r--drivers/usb/serial/sierra.c8
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c16
-rw-r--r--drivers/usb/serial/usb-wwan.h4
-rw-r--r--drivers/usb/serial/usb_wwan.c12
-rw-r--r--drivers/usb/typec/class.c4
-rw-r--r--drivers/usb/typec/mux.c39
-rw-r--r--drivers/usb/typec/mux.h6
-rw-r--r--drivers/usb/typec/mux/intel_pmc_mux.c28
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c46
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c86
-rw-r--r--drivers/usb/typec/tcpm/wcove.c2
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c4
-rw-r--r--include/linux/device.h37
-rw-r--r--include/linux/phy/tegra/xusb.h10
-rw-r--r--include/linux/usb.h9
-rw-r--r--include/linux/usb/composite.h2
-rw-r--r--include/linux/usb/gadget.h3
-rw-r--r--include/linux/usb/hcd.h17
-rw-r--r--include/linux/usb/isp1760.h19
-rw-r--r--include/linux/usb/otg-fsm.h6
-rw-r--r--include/linux/usb/otg.h2
-rw-r--r--include/linux/usb/quirks.h2
-rw-r--r--include/linux/usb/role.h6
-rw-r--r--include/linux/usb/serial.h10
-rw-r--r--include/linux/usb/tcpm.h4
-rw-r--r--include/linux/usb/typec_dp.h2
215 files changed, 8676 insertions, 2717 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2
index d4356c8b8cd6..26fb8e9b4e61 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac2
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2
@@ -8,6 +8,8 @@ Description:
c_chmask capture channel mask
c_srate capture sampling rate
c_ssize capture sample size (bytes)
+ c_sync capture synchronization type (async/adaptive)
+ fb_max maximum extra bandwidth in async mode
p_chmask playback channel mask
p_srate playback sampling rate
p_ssize playback sample size (bytes)
diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt
index c41c68f64693..95c21d6c9a84 100644
--- a/Documentation/ABI/testing/sysfs-bus-thunderbolt
+++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt
@@ -1,4 +1,4 @@
-What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl
+What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl
Date: Jun 2018
KernelVersion: 4.17
Contact: thunderbolt-software@lists.01.org
@@ -21,7 +21,7 @@ Description: Holds a comma separated list of device unique_ids that
If a device is authorized automatically during boot its
boot attribute is set to 1.
-What: /sys/bus/thunderbolt/devices/.../domainX/deauthorization
+What: /sys/bus/thunderbolt/devices/.../domainX/deauthorization
Date: May 2021
KernelVersion: 5.12
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
@@ -30,7 +30,7 @@ Description: This attribute tells whether the system supports
de-authorize PCIe tunnel by writing 0 to authorized
attribute under each device.
-What: /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection
+What: /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection
Date: Mar 2019
KernelVersion: 4.21
Contact: thunderbolt-software@lists.01.org
@@ -39,7 +39,7 @@ Description: This attribute tells whether the system uses IOMMU
it is not (DMA protection is solely based on Thunderbolt
security levels).
-What: /sys/bus/thunderbolt/devices/.../domainX/security
+What: /sys/bus/thunderbolt/devices/.../domainX/security
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
@@ -61,7 +61,7 @@ Description: This attribute holds current Thunderbolt security level
the BIOS.
======= ==================================================
-What: /sys/bus/thunderbolt/devices/.../authorized
+What: /sys/bus/thunderbolt/devices/.../authorized
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
@@ -95,14 +95,14 @@ Description: This attribute is used to authorize Thunderbolt devices
EKEYREJECTED if the challenge response did not match.
== ========================================================
-What: /sys/bus/thunderbolt/devices/.../boot
+What: /sys/bus/thunderbolt/devices/.../boot
Date: Jun 2018
KernelVersion: 4.17
Contact: thunderbolt-software@lists.01.org
Description: This attribute contains 1 if Thunderbolt device was already
authorized on boot and 0 otherwise.
-What: /sys/bus/thunderbolt/devices/.../generation
+What: /sys/bus/thunderbolt/devices/.../generation
Date: Jan 2020
KernelVersion: 5.5
Contact: Christian Kellner <christian@kellner.me>
@@ -110,7 +110,7 @@ Description: This attribute contains the generation of the Thunderbolt
controller associated with the device. It will contain 4
for USB4.
-What: /sys/bus/thunderbolt/devices/.../key
+What: /sys/bus/thunderbolt/devices/.../key
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
@@ -213,12 +213,15 @@ Description: When new NVM image is written to the non-active NVM
restarted with the new NVM firmware. If the image
verification fails an error code is returned instead.
- This file will accept writing values "1" or "2"
+ This file will accept writing values "1", "2" or "3".
- Writing "1" will flush the image to the storage
area and authenticate the image in one action.
- Writing "2" will run some basic validation on the image
and flush it to the storage area.
+ - Writing "3" will authenticate the image that is
+ currently written in the storage area. This is only
+ supported with USB4 devices and retimers.
When read holds status of the last authentication
operation if an error occurred during the process. This
@@ -226,6 +229,20 @@ Description: When new NVM image is written to the non-active NVM
based mailbox before the device is power cycled. Writing
0 here clears the status.
+What: /sys/bus/thunderbolt/devices/.../nvm_authenticate_on_disconnect
+Date: Oct 2020
+KernelVersion: v5.9
+Contact: Mario Limonciello <mario.limonciello@dell.com>
+Description: For supported devices, automatically authenticate the new Thunderbolt
+ image when the device is disconnected from the host system.
+
+ This file will accept writing values "1" or "2"
+
+ - Writing "1" will flush the image to the storage
+ area and prepare the device for authentication on disconnect.
+ - Writing "2" will run some basic validation on the image
+ and flush it to the storage area.
+
What: /sys/bus/thunderbolt/devices/<xdomain>.<service>/key
Date: Jan 2018
KernelVersion: 4.15
@@ -276,6 +293,39 @@ Contact: thunderbolt-software@lists.01.org
Description: This contains XDomain service specific settings as
bitmask. Format: %x
+What: /sys/bus/thunderbolt/devices/usb4_portX/link
+Date: Sep 2021
+KernelVersion: v5.14
+Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
+Description: Returns the current link mode. Possible values are
+ "usb4", "tbt" and "none".
+
+What: /sys/bus/thunderbolt/devices/usb4_portX/offline
+Date: Sep 2021
+KernelVersion: v5.14
+Contact: Rajmohan Mani <rajmohan.mani@intel.com>
+Description: Writing 1 to this attribute puts the USB4 port into
+ offline mode. Only allowed when there is nothing
+ connected to the port (link attribute returns "none").
+ Once the port is in offline mode it does not receive any
+ hotplug events. This is used to update NVM firmware of
+ on-board retimers. Writing 0 puts the port back to
+ online mode.
+
+ This attribute is only visible if the platform supports
+ powering on retimers when there is no cable connected.
+
+What: /sys/bus/thunderbolt/devices/usb4_portX/rescan
+Date: Sep 2021
+KernelVersion: v5.14
+Contact: Rajmohan Mani <rajmohan.mani@intel.com>
+Description: When the USB4 port is in offline mode writing 1 to this
+ attribute forces rescan of the sideband for on-board
+ retimers. Each retimer appear under the USB4 port as if
+ the USB4 link was up. These retimers act in the same way
+ as if the cable was connected so upgrading their NVM
+ firmware can be done the usual way.
+
What: /sys/bus/thunderbolt/devices/<device>:<port>.<index>/device
Date: Oct 2020
KernelVersion: v5.9
@@ -308,17 +358,3 @@ Date: Oct 2020
KernelVersion: v5.9
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: Retimer vendor identifier read from the hardware.
-
-What: /sys/bus/thunderbolt/devices/.../nvm_authenticate_on_disconnect
-Date: Oct 2020
-KernelVersion: v5.9
-Contact: Mario Limonciello <mario.limonciello@dell.com>
-Description: For supported devices, automatically authenticate the new Thunderbolt
- image when the device is disconnected from the host system.
-
- This file will accept writing values "1" or "2"
-
- - Writing "1" will flush the image to the storage
- area and prepare the device for authentication on disconnect.
- - Writing "2" will run some basic validation on the image
- and flush it to the storage area.
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index bf2c1968525f..73eb23bc1f34 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -154,17 +154,6 @@ Description:
files hold a string value (enable or disable) indicating whether
or not USB3 hardware LPM U1 or U2 is enabled for the device.
-What: /sys/bus/usb/devices/.../removable
-Date: February 2012
-Contact: Matthew Garrett <mjg@redhat.com>
-Description:
- Some information about whether a given USB device is
- physically fixed to the platform can be inferred from a
- combination of hub descriptor bits and platform-specific data
- such as ACPI. This file will read either "removable" or
- "fixed" if the information is available, and "unknown"
- otherwise.
-
What: /sys/bus/usb/devices/.../ltm_capable
Date: July 2012
Contact: Sarah Sharp <sarah.a.sharp@linux.intel.com>
diff --git a/Documentation/ABI/testing/sysfs-devices-removable b/Documentation/ABI/testing/sysfs-devices-removable
new file mode 100644
index 000000000000..bda6c320c8d3
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-removable
@@ -0,0 +1,18 @@
+What: /sys/devices/.../removable
+Date: May 2021
+Contact: Rajat Jain <rajatxjain@gmail.com>
+Description:
+ Information about whether a given device can be removed from the
+ platform by the user. This is determined by its subsystem in a
+ bus / platform-specific way. This attribute is only present for
+ devices that can support determining such information:
+
+ "removable": device can be removed from the platform by the user
+ "fixed": device is fixed to the platform / cannot be removed
+ by the user.
+ "unknown": The information is unavailable / cannot be deduced.
+
+ Currently this is only supported by USB (which infers the
+ information from a combination of hub descriptor bits and
+ platform-specific data such as ACPI) and PCI (which gets this
+ from ACPI / device tree).
diff --git a/Documentation/admin-guide/thunderbolt.rst b/Documentation/admin-guide/thunderbolt.rst
index f18e881373c4..2ed79f41a411 100644
--- a/Documentation/admin-guide/thunderbolt.rst
+++ b/Documentation/admin-guide/thunderbolt.rst
@@ -256,6 +256,35 @@ Note names of the NVMem devices ``nvm_activeN`` and ``nvm_non_activeN``
depend on the order they are registered in the NVMem subsystem. N in
the name is the identifier added by the NVMem subsystem.
+Upgrading on-board retimer NVM when there is no cable connected
+---------------------------------------------------------------
+If the platform supports, it may be possible to upgrade the retimer NVM
+firmware even when there is nothing connected to the USB4
+ports. When this is the case the ``usb4_portX`` devices have two special
+attributes: ``offline`` and ``rescan``. The way to upgrade the firmware
+is to first put the USB4 port into offline mode::
+
+ # echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/offline
+
+This step makes sure the port does not respond to any hotplug events,
+and also ensures the retimers are powered on. The next step is to scan
+for the retimers::
+
+ # echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/rescan
+
+This enumerates and adds the on-board retimers. Now retimer NVM can be
+upgraded in the same way than with cable connected (see previous
+section). However, the retimer is not disconnected as we are offline
+mode) so after writing ``1`` to ``nvm_authenticate`` one should wait for
+5 or more seconds before running rescan again::
+
+ # echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/rescan
+
+This point if everything went fine, the port can be put back to
+functional state again::
+
+ # echo 0 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/offline
+
Upgrading NVM when host controller is in safe mode
--------------------------------------------------
If the existing NVM is not properly authenticated (or is missing) the
diff --git a/Documentation/devicetree/bindings/phy/allwinner,sun8i-h3-usb-phy.yaml b/Documentation/devicetree/bindings/phy/allwinner,sun8i-h3-usb-phy.yaml
index f80431060803..e288450e0844 100644
--- a/Documentation/devicetree/bindings/phy/allwinner,sun8i-h3-usb-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/allwinner,sun8i-h3-usb-phy.yaml
@@ -15,7 +15,9 @@ properties:
const: 1
compatible:
- const: allwinner,sun8i-h3-usb-phy
+ enum:
+ - allwinner,sun8i-h3-usb-phy
+ - allwinner,sun50i-h616-usb-phy
reg:
items:
diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml
index 930da598c969..ec9ccaaba098 100644
--- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml
@@ -23,6 +23,8 @@ properties:
- qcom,msm8998-qusb2-phy
- qcom,sdm660-qusb2-phy
- qcom,ipq6018-qusb2-phy
+ - qcom,sm4250-qusb2-phy
+ - qcom,sm6115-qusb2-phy
- items:
- enum:
- qcom,sc7180-qusb2-phy
diff --git a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml
index 0f520f17735e..933fa356d2ce 100644
--- a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml
+++ b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml
@@ -22,6 +22,9 @@ properties:
- allwinner,sun8i-a83t-musb
- allwinner,sun50i-h6-musb
- const: allwinner,sun8i-a33-musb
+ - items:
+ - const: allwinner,sun50i-h616-musb
+ - const: allwinner,sun8i-h3-musb
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml
index 8dedfa16c992..dc9d6ed0781d 100644
--- a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml
+++ b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml
@@ -75,6 +75,7 @@ required:
- reg
- reg-names
- interrupts
+ - interrupt-names
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/usb/dwc2.yaml b/Documentation/devicetree/bindings/usb/dwc2.yaml
index e5ee51b7b470..10c7d9b6cc53 100644
--- a/Documentation/devicetree/bindings/usb/dwc2.yaml
+++ b/Documentation/devicetree/bindings/usb/dwc2.yaml
@@ -24,6 +24,7 @@ properties:
- rockchip,rk3188-usb
- rockchip,rk3228-usb
- rockchip,rk3288-usb
+ - rockchip,rk3308-usb
- rockchip,rk3328-usb
- rockchip,rk3368-usb
- rockchip,rv1108-usb
diff --git a/Documentation/devicetree/bindings/usb/nxp,isp1760.yaml b/Documentation/devicetree/bindings/usb/nxp,isp1760.yaml
new file mode 100644
index 000000000000..a88f99adfe8e
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/nxp,isp1760.yaml
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/nxp,isp1760.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP ISP1760 family controller bindings
+
+maintainers:
+ - Sebastian Siewior <bigeasy@linutronix.de>
+ - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+
+description: |
+ NXP ISP1760 family, which includes ISP1760/1761/1763 devicetree controller
+ bindings
+
+properties:
+ compatible:
+ enum:
+ - nxp,usb-isp1760
+ - nxp,usb-isp1761
+ - nxp,usb-isp1763
+ reg:
+ maxItems: 1
+
+ interrupts:
+ minItems: 1
+ maxItems: 2
+ items:
+ - description: Host controller interrupt
+ - description: Device controller interrupt in isp1761
+
+ interrupt-names:
+ minItems: 1
+ maxItems: 2
+ items:
+ - const: host
+ - const: peripheral
+
+ bus-width:
+ description:
+ Number of data lines.
+ enum: [8, 16, 32]
+ default: 32
+
+ dr_mode:
+ enum:
+ - host
+ - peripheral
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ usb@40200000 {
+ compatible = "nxp,usb-isp1763";
+ reg = <0x40200000 0x100000>;
+ interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
+ bus-width = <16>;
+ dr_mode = "host";
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
index 413299b5fe2b..4e6451789806 100644
--- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
+++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
@@ -19,6 +19,8 @@ properties:
- qcom,sc7280-dwc3
- qcom,sdm845-dwc3
- qcom,sdx55-dwc3
+ - qcom,sm4250-dwc3
+ - qcom,sm6115-dwc3
- qcom,sm8150-dwc3
- qcom,sm8250-dwc3
- qcom,sm8350-dwc3
diff --git a/Documentation/devicetree/bindings/usb/realtek,rts5411.yaml b/Documentation/devicetree/bindings/usb/realtek,rts5411.yaml
new file mode 100644
index 000000000000..04ee255eb4f0
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/realtek,rts5411.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/realtek,rts5411.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Binding for the Realtek RTS5411 USB 3.0 hub controller
+
+maintainers:
+ - Matthias Kaehlcke <mka@chromium.org>
+
+allOf:
+ - $ref: usb-device.yaml#
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - usbbda,5411
+ - usbbda,411
+
+ reg: true
+
+ vdd-supply:
+ description:
+ phandle to the regulator that provides power to the hub.
+
+ companion-hub:
+ $ref: '/schemas/types.yaml#/definitions/phandle'
+ description:
+ phandle to the companion hub on the controller.
+
+required:
+ - companion-hub
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ usb {
+ dr_mode = "host";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* 2.0 hub on port 1 */
+ hub_2_0: hub@1 {
+ compatible = "usbbda,5411";
+ reg = <1>;
+ vdd-supply = <&pp3300_hub>;
+ companion-hub = <&hub_3_0>;
+ };
+
+ /* 3.0 hub on port 2 */
+ hub_3_0: hub@2 {
+ compatible = "usbbda,411";
+ reg = <2>;
+ vdd-supply = <&pp3300_hub>;
+ companion-hub = <&hub_2_0>;
+ };
+ };
diff --git a/Documentation/driver-api/usb/error-codes.rst b/Documentation/driver-api/usb/error-codes.rst
index a3e84bfac776..8f9790c2d6f8 100644
--- a/Documentation/driver-api/usb/error-codes.rst
+++ b/Documentation/driver-api/usb/error-codes.rst
@@ -61,6 +61,9 @@ USB-specific:
(c) requested data transfer length is invalid: negative
or too large for the host controller.
+``-EBADR`` The wLength value in a control URB's setup packet does
+ not match the URB's transfer_buffer_length.
+
``-ENOSPC`` This request would overcommit the usb bandwidth reserved
for periodic transfers (interrupt, isochronous).
diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
index 2085e7b24eeb..9d6276f82774 100644
--- a/Documentation/usb/gadget-testing.rst
+++ b/Documentation/usb/gadget-testing.rst
@@ -728,6 +728,8 @@ The uac2 function provides these attributes in its function directory:
c_chmask capture channel mask
c_srate capture sampling rate
c_ssize capture sample size (bytes)
+ c_sync capture synchronization type (async/adaptive)
+ fb_max maximum extra bandwidth in async mode
p_chmask playback channel mask
p_srate playback sampling rate
p_ssize playback sample size (bytes)
diff --git a/arch/arm/boot/dts/arm-realview-eb.dtsi b/arch/arm/boot/dts/arm-realview-eb.dtsi
index a534a8e444d9..04e8a27ba1eb 100644
--- a/arch/arm/boot/dts/arm-realview-eb.dtsi
+++ b/arch/arm/boot/dts/arm-realview-eb.dtsi
@@ -148,7 +148,7 @@
usb: usb@4f000000 {
compatible = "nxp,usb-isp1761";
reg = <0x4f000000 0x20000>;
- port1-otg;
+ dr_mode = "peripheral";
};
bridge {
diff --git a/arch/arm/boot/dts/arm-realview-pb1176.dts b/arch/arm/boot/dts/arm-realview-pb1176.dts
index f925782f8560..366687fb1ee3 100644
--- a/arch/arm/boot/dts/arm-realview-pb1176.dts
+++ b/arch/arm/boot/dts/arm-realview-pb1176.dts
@@ -166,7 +166,7 @@
reg = <0x3b000000 0x20000>;
interrupt-parent = <&intc_fpga1176>;
interrupts = <0 11 IRQ_TYPE_LEVEL_HIGH>;
- port1-otg;
+ dr_mode = "peripheral";
};
bridge {
diff --git a/arch/arm/boot/dts/arm-realview-pb11mp.dts b/arch/arm/boot/dts/arm-realview-pb11mp.dts
index 0c7dabef4a5f..228a51a38f95 100644
--- a/arch/arm/boot/dts/arm-realview-pb11mp.dts
+++ b/arch/arm/boot/dts/arm-realview-pb11mp.dts
@@ -712,7 +712,7 @@
reg = <0x4f000000 0x20000>;
interrupt-parent = <&intc_tc11mp>;
interrupts = <0 3 IRQ_TYPE_LEVEL_HIGH>;
- port1-otg;
+ dr_mode = "peripheral";
};
};
};
diff --git a/arch/arm/boot/dts/arm-realview-pbx.dtsi b/arch/arm/boot/dts/arm-realview-pbx.dtsi
index ac95667ed781..ccf6f756b6ed 100644
--- a/arch/arm/boot/dts/arm-realview-pbx.dtsi
+++ b/arch/arm/boot/dts/arm-realview-pbx.dtsi
@@ -164,7 +164,7 @@
usb: usb@4f000000 {
compatible = "nxp,usb-isp1761";
reg = <0x4f000000 0x20000>;
- port1-otg;
+ dr_mode = "peripheral";
};
bridge {
diff --git a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
index 4f7220b11f2d..2ad9fd7c94ec 100644
--- a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
+++ b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
@@ -144,7 +144,7 @@
compatible = "nxp,usb-isp1761";
reg = <2 0x03000000 0x20000>;
interrupts = <16>;
- port1-otg;
+ dr_mode = "peripheral";
};
iofpga-bus@300000000 {
diff --git a/arch/arm/boot/dts/vexpress-v2m.dtsi b/arch/arm/boot/dts/vexpress-v2m.dtsi
index 2ac41ed3a57c..ec13ceb9ed36 100644
--- a/arch/arm/boot/dts/vexpress-v2m.dtsi
+++ b/arch/arm/boot/dts/vexpress-v2m.dtsi
@@ -62,7 +62,7 @@
compatible = "nxp,usb-isp1761";
reg = <3 0x03000000 0x20000>;
interrupts = <16>;
- port1-otg;
+ dr_mode = "peripheral";
};
iofpga@7,00000000 {
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 0027a2d1de99..cadcade65825 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -2409,6 +2409,25 @@ static ssize_t online_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RW(online);
+static ssize_t removable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ const char *loc;
+
+ switch (dev->removable) {
+ case DEVICE_REMOVABLE:
+ loc = "removable";
+ break;
+ case DEVICE_FIXED:
+ loc = "fixed";
+ break;
+ default:
+ loc = "unknown";
+ }
+ return sysfs_emit(buf, "%s\n", loc);
+}
+static DEVICE_ATTR_RO(removable);
+
int device_add_groups(struct device *dev, const struct attribute_group **groups)
{
return sysfs_create_groups(&dev->kobj, groups);
@@ -2586,8 +2605,16 @@ static int device_add_attrs(struct device *dev)
goto err_remove_dev_online;
}
+ if (dev_removable_is_valid(dev)) {
+ error = device_create_file(dev, &dev_attr_removable);
+ if (error)
+ goto err_remove_dev_waiting_for_supplier;
+ }
+
return 0;
+ err_remove_dev_waiting_for_supplier:
+ device_remove_file(dev, &dev_attr_waiting_for_supplier);
err_remove_dev_online:
device_remove_file(dev, &dev_attr_online);
err_remove_dev_groups:
@@ -2607,6 +2634,7 @@ static void device_remove_attrs(struct device *dev)
struct class *class = dev->class;
const struct device_type *type = dev->type;
+ device_remove_file(dev, &dev_attr_removable);
device_remove_file(dev, &dev_attr_waiting_for_supplier);
device_remove_file(dev, &dev_attr_online);
device_remove_groups(dev, dev->groups);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 275204646c68..76a2f33c6b48 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1576,6 +1576,26 @@ static void set_pcie_untrusted(struct pci_dev *dev)
dev->untrusted = true;
}
+static void pci_set_removable(struct pci_dev *dev)
+{
+ struct pci_dev *parent = pci_upstream_bridge(dev);
+
+ /*
+ * We (only) consider everything downstream from an external_facing
+ * device to be removable by the user. We're mainly concerned with
+ * consumer platforms with user accessible thunderbolt ports that are
+ * vulnerable to DMA attacks, and we expect those ports to be marked by
+ * the firmware as external_facing. Devices in traditional hotplug
+ * slots can technically be removed, but the expectation is that unless
+ * the port is marked with external_facing, such devices are less
+ * accessible to user / may not be removed by end user, and thus not
+ * exposed as "removable" to userspace.
+ */
+ if (parent &&
+ (parent->external_facing || dev_is_removable(&parent->dev)))
+ dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
+}
+
/**
* pci_ext_cfg_is_aliased - Is ext config space just an alias of std config?
* @dev: PCI device
@@ -1823,6 +1843,8 @@ int pci_setup_device(struct pci_dev *dev)
/* Early fixups, before probing the BARs */
pci_fixup_device(pci_fixup_early, dev);
+ pci_set_removable(dev);
+
pci_info(dev, "[%04x:%04x] type %02x class %#08x\n",
dev->vendor, dev->device, dev->hdr_type, dev->class);
diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c
index 8f1bf7e2186b..3c1d3b71c825 100644
--- a/drivers/phy/qualcomm/phy-qcom-qusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c
@@ -219,6 +219,22 @@ static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = {
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19),
};
+static const struct qusb2_phy_init_tbl sm6115_init_tbl[] = {
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x53),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x81),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0x17),
+
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
+
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14),
+
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
+};
+
static const unsigned int qusb2_v2_regs_layout[] = {
[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
[QUSB2PHY_PLL_STATUS] = 0x1a0,
@@ -342,6 +358,18 @@ static const struct qusb2_phy_cfg sdm660_phy_cfg = {
.autoresume_en = BIT(3),
};
+static const struct qusb2_phy_cfg sm6115_phy_cfg = {
+ .tbl = sm6115_init_tbl,
+ .tbl_num = ARRAY_SIZE(sm6115_init_tbl),
+ .regs = msm8996_regs_layout,
+
+ .has_pll_test = true,
+ .se_clk_scheme_default = true,
+ .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN),
+ .mask_core_ready = PLL_LOCKED,
+ .autoresume_en = BIT(3),
+};
+
static const char * const qusb2_phy_vreg_names[] = {
"vdda-pll", "vdda-phy-dpdm",
};
@@ -889,6 +917,12 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
.compatible = "qcom,sdm660-qusb2-phy",
.data = &sdm660_phy_cfg,
}, {
+ .compatible = "qcom,sm4250-qusb2-phy",
+ .data = &sm6115_phy_cfg,
+ }, {
+ .compatible = "qcom,sm6115-qusb2-phy",
+ .data = &sm6115_phy_cfg,
+ }, {
/*
* Deprecated. Only here to support legacy device
* trees that didn't include "qcom,qusb2-v2-phy"
diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
index 5d64f69b39a9..ae3915ed9fef 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/delay.h>
@@ -113,6 +113,117 @@
#define ID_OVERRIDE_FLOATING ID_OVERRIDE(8)
#define ID_OVERRIDE_GROUNDED ID_OVERRIDE(0)
+/* XUSB AO registers */
+#define XUSB_AO_USB_DEBOUNCE_DEL (0x4)
+#define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 4)
+#define UTMIP_LINE_DEB_CNT(x) ((x) & 0xf)
+
+#define XUSB_AO_UTMIP_TRIGGERS(x) (0x40 + (x) * 4)
+#define CLR_WALK_PTR BIT(0)
+#define CAP_CFG BIT(1)
+#define CLR_WAKE_ALARM BIT(3)
+
+#define XUSB_AO_UHSIC_TRIGGERS(x) (0x60 + (x) * 4)
+#define HSIC_CLR_WALK_PTR BIT(0)
+#define HSIC_CLR_WAKE_ALARM BIT(3)
+#define HSIC_CAP_CFG BIT(4)
+
+#define XUSB_AO_UTMIP_SAVED_STATE(x) (0x70 + (x) * 4)
+#define SPEED(x) ((x) & 0x3)
+#define UTMI_HS SPEED(0)
+#define UTMI_FS SPEED(1)
+#define UTMI_LS SPEED(2)
+#define UTMI_RST SPEED(3)
+
+#define XUSB_AO_UHSIC_SAVED_STATE(x) (0x90 + (x) * 4)
+#define MODE(x) ((x) & 0x1)
+#define MODE_HS MODE(0)
+#define MODE_RST MODE(1)
+
+#define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4)
+#define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4)
+#define FAKE_USBOP_VAL BIT(0)
+#define FAKE_USBON_VAL BIT(1)
+#define FAKE_USBOP_EN BIT(2)
+#define FAKE_USBON_EN BIT(3)
+#define FAKE_STROBE_VAL BIT(0)
+#define FAKE_DATA_VAL BIT(1)
+#define FAKE_STROBE_EN BIT(2)
+#define FAKE_DATA_EN BIT(3)
+#define WAKE_WALK_EN BIT(14)
+#define MASTER_ENABLE BIT(15)
+#define LINEVAL_WALK_EN BIT(16)
+#define WAKE_VAL(x) (((x) & 0xf) << 17)
+#define WAKE_VAL_NONE WAKE_VAL(12)
+#define WAKE_VAL_ANY WAKE_VAL(15)
+#define WAKE_VAL_DS10 WAKE_VAL(2)
+#define LINE_WAKEUP_EN BIT(21)
+#define MASTER_CFG_SEL BIT(22)
+
+#define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4)
+/* phase A */
+#define USBOP_RPD_A BIT(0)
+#define USBON_RPD_A BIT(1)
+#define AP_A BIT(4)
+#define AN_A BIT(5)
+#define HIGHZ_A BIT(6)
+/* phase B */
+#define USBOP_RPD_B BIT(8)
+#define USBON_RPD_B BIT(9)
+#define AP_B BIT(12)
+#define AN_B BIT(13)
+#define HIGHZ_B BIT(14)
+/* phase C */
+#define USBOP_RPD_C BIT(16)
+#define USBON_RPD_C BIT(17)
+#define AP_C BIT(20)
+#define AN_C BIT(21)
+#define HIGHZ_C BIT(22)
+/* phase D */
+#define USBOP_RPD_D BIT(24)
+#define USBON_RPD_D BIT(25)
+#define AP_D BIT(28)
+#define AN_D BIT(29)
+#define HIGHZ_D BIT(30)
+
+#define XUSB_AO_UHSIC_SLEEPWALK(x) (0x120 + (x) * 4)
+/* phase A */
+#define RPD_STROBE_A BIT(0)
+#define RPD_DATA0_A BIT(1)
+#define RPU_STROBE_A BIT(2)
+#define RPU_DATA0_A BIT(3)
+/* phase B */
+#define RPD_STROBE_B BIT(8)
+#define RPD_DATA0_B BIT(9)
+#define RPU_STROBE_B BIT(10)
+#define RPU_DATA0_B BIT(11)
+/* phase C */
+#define RPD_STROBE_C BIT(16)
+#define RPD_DATA0_C BIT(17)
+#define RPU_STROBE_C BIT(18)
+#define RPU_DATA0_C BIT(19)
+/* phase D */
+#define RPD_STROBE_D BIT(24)
+#define RPD_DATA0_D BIT(25)
+#define RPU_STROBE_D BIT(26)
+#define RPU_DATA0_D BIT(27)
+
+#define XUSB_AO_UTMIP_PAD_CFG(x) (0x130 + (x) * 4)
+#define FSLS_USE_XUSB_AO BIT(3)
+#define TRK_CTRL_USE_XUSB_AO BIT(4)
+#define RPD_CTRL_USE_XUSB_AO BIT(5)
+#define RPU_USE_XUSB_AO BIT(6)
+#define VREG_USE_XUSB_AO BIT(7)
+#define USBOP_VAL_PD BIT(8)
+#define USBON_VAL_PD BIT(9)
+#define E_DPD_OVRD_EN BIT(10)
+#define E_DPD_OVRD_VAL BIT(11)
+
+#define XUSB_AO_UHSIC_PAD_CFG(x) (0x150 + (x) * 4)
+#define STROBE_VAL_PD BIT(0)
+#define DATA0_VAL_PD BIT(1)
+#define USE_XUSB_AO BIT(4)
+
#define TEGRA186_LANE(_name, _offset, _shift, _mask, _type) \
{ \
.name = _name, \
@@ -130,16 +241,37 @@ struct tegra_xusb_fuse_calibration {
u32 rpd_ctrl;
};
+struct tegra186_xusb_padctl_context {
+ u32 vbus_id;
+ u32 usb2_pad_mux;
+ u32 usb2_port_cap;
+ u32 ss_port_cap;
+};
+
struct tegra186_xusb_padctl {
struct tegra_xusb_padctl base;
+ void __iomem *ao_regs;
struct tegra_xusb_fuse_calibration calib;
/* UTMI bias and tracking */
struct clk *usb2_trk_clk;
unsigned int bias_pad_enable;
+
+ /* padctl context */
+ struct tegra186_xusb_padctl_context context;
};
+static inline void ao_writel(struct tegra186_xusb_padctl *priv, u32 value, unsigned int offset)
+{
+ writel(value, priv->ao_regs + offset);
+}
+
+static inline u32 ao_readl(struct tegra186_xusb_padctl *priv, unsigned int offset)
+{
+ return readl(priv->ao_regs + offset);
+}
+
static inline struct tegra186_xusb_padctl *
to_tegra186_xusb_padctl(struct tegra_xusb_padctl *padctl)
{
@@ -180,9 +312,264 @@ static void tegra186_usb2_lane_remove(struct tegra_xusb_lane *lane)
kfree(usb2);
}
+static int tegra186_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ /* ensure sleepwalk logic is disabled */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~MASTER_ENABLE;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* ensure sleepwalk logics are in low power mode */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value |= MASTER_CFG_SEL;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* set debounce time */
+ value = ao_readl(priv, XUSB_AO_USB_DEBOUNCE_DEL);
+ value &= ~UTMIP_LINE_DEB_CNT(~0);
+ value |= UTMIP_LINE_DEB_CNT(1);
+ ao_writel(priv, value, XUSB_AO_USB_DEBOUNCE_DEL);
+
+ /* ensure fake events of sleepwalk logic are desiabled */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~(FAKE_USBOP_VAL | FAKE_USBON_VAL |
+ FAKE_USBOP_EN | FAKE_USBON_EN);
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* ensure wake events of sleepwalk logic are not latched */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~LINE_WAKEUP_EN;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~WAKE_VAL(~0);
+ value |= WAKE_VAL_NONE;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* power down the line state detectors of the pad */
+ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index));
+ value |= (USBOP_VAL_PD | USBON_VAL_PD);
+ ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index));
+
+ /* save state per speed */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SAVED_STATE(index));
+ value &= ~SPEED(~0);
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ value |= UTMI_HS;
+ break;
+
+ case USB_SPEED_FULL:
+ value |= UTMI_FS;
+ break;
+
+ case USB_SPEED_LOW:
+ value |= UTMI_LS;
+ break;
+
+ default:
+ value |= UTMI_RST;
+ break;
+ }
+
+ ao_writel(priv, value, XUSB_AO_UTMIP_SAVED_STATE(index));
+
+ /* enable the trigger of the sleepwalk logic */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value |= LINEVAL_WALK_EN;
+ value &= ~WAKE_WALK_EN;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* reset the walk pointer and clear the alarm of the sleepwalk logic,
+ * as well as capture the configuration of the USB2.0 pad
+ */
+ value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index));
+ value |= (CLR_WALK_PTR | CLR_WAKE_ALARM | CAP_CFG);
+ ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index));
+
+ /* setup the pull-ups and pull-downs of the signals during the four
+ * stages of sleepwalk.
+ * if device is connected, program sleepwalk logic to maintain a J and
+ * keep driving K upon seeing remote wake.
+ */
+ value = USBOP_RPD_A | USBOP_RPD_B | USBOP_RPD_C | USBOP_RPD_D;
+ value |= USBON_RPD_A | USBON_RPD_B | USBON_RPD_C | USBON_RPD_D;
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ case USB_SPEED_FULL:
+ /* J state: D+/D- = high/low, K state: D+/D- = low/high */
+ value |= HIGHZ_A;
+ value |= AP_A;
+ value |= AN_B | AN_C | AN_D;
+ break;
+
+ case USB_SPEED_LOW:
+ /* J state: D+/D- = low/high, K state: D+/D- = high/low */
+ value |= HIGHZ_A;
+ value |= AN_A;
+ value |= AP_B | AP_C | AP_D;
+ break;
+
+ default:
+ value |= HIGHZ_A | HIGHZ_B | HIGHZ_C | HIGHZ_D;
+ break;
+ }
+
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK(index));
+
+ /* power up the line state detectors of the pad */
+ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index));
+ value &= ~(USBOP_VAL_PD | USBON_VAL_PD);
+ ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index));
+
+ usleep_range(150, 200);
+
+ /* switch the electric control of the USB2.0 pad to XUSB_AO */
+ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index));
+ value |= FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO |
+ RPU_USE_XUSB_AO | VREG_USE_XUSB_AO;
+ ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index));
+
+ /* set the wake signaling trigger events */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~WAKE_VAL(~0);
+ value |= WAKE_VAL_ANY;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* enable the wake detection */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value |= MASTER_ENABLE | LINE_WAKEUP_EN;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_utmi_disable_phy_sleepwalk(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ /* disable the wake detection */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~(MASTER_ENABLE | LINE_WAKEUP_EN);
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* switch the electric control of the USB2.0 pad to XUSB vcore logic */
+ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index));
+ value &= ~(FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO |
+ RPU_USE_XUSB_AO | VREG_USE_XUSB_AO);
+ ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index));
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~WAKE_VAL(~0);
+ value |= WAKE_VAL_NONE;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* power down the line state detectors of the port */
+ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index));
+ value |= USBOP_VAL_PD | USBON_VAL_PD;
+ ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index));
+
+ /* clear alarm of the sleepwalk logic */
+ value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index));
+ value |= CLR_WAKE_ALARM;
+ ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index));
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_utmi_enable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_utmi_disable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static bool tegra186_utmi_phy_remote_wake_detected(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(index)) &&
+ (value & USB2_PORT_WAKEUP_EVENT(index)))
+ return true;
+
+ return false;
+}
+
static const struct tegra_xusb_lane_ops tegra186_usb2_lane_ops = {
.probe = tegra186_usb2_lane_probe,
.remove = tegra186_usb2_lane_remove,
+ .enable_phy_sleepwalk = tegra186_utmi_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra186_utmi_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra186_utmi_enable_phy_wake,
+ .disable_phy_wake = tegra186_utmi_disable_phy_wake,
+ .remote_wake_detected = tegra186_utmi_phy_remote_wake_detected,
};
static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl)
@@ -656,10 +1043,128 @@ static void tegra186_usb3_lane_remove(struct tegra_xusb_lane *lane)
kfree(usb3);
}
+static int tegra186_usb3_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+ value |= SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+ value |= SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+ usleep_range(250, 350);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_usb3_disable_phy_sleepwalk(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+ value &= ~SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+ value &= ~SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_usb3_enable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_usb3_disable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static bool tegra186_usb3_phy_remote_wake_detected(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & SS_PORT_WAKEUP_EVENT(index)))
+ return true;
+
+ return false;
+}
+
static const struct tegra_xusb_lane_ops tegra186_usb3_lane_ops = {
.probe = tegra186_usb3_lane_probe,
.remove = tegra186_usb3_lane_remove,
+ .enable_phy_sleepwalk = tegra186_usb3_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra186_usb3_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra186_usb3_enable_phy_wake,
+ .disable_phy_wake = tegra186_usb3_disable_phy_wake,
+ .remote_wake_detected = tegra186_usb3_phy_remote_wake_detected,
};
+
static int tegra186_usb3_port_enable(struct tegra_xusb_port *port)
{
return 0;
@@ -913,7 +1418,9 @@ static struct tegra_xusb_padctl *
tegra186_xusb_padctl_probe(struct device *dev,
const struct tegra_xusb_padctl_soc *soc)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct tegra186_xusb_padctl *priv;
+ struct resource *res;
int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -923,6 +1430,11 @@ tegra186_xusb_padctl_probe(struct device *dev,
priv->base.dev = dev;
priv->base.soc = soc;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ao");
+ priv->ao_regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->ao_regs))
+ return ERR_CAST(priv->ao_regs);
+
err = tegra186_xusb_read_fuse_calibration(priv);
if (err < 0)
return ERR_PTR(err);
@@ -930,6 +1442,40 @@ tegra186_xusb_padctl_probe(struct device *dev,
return &priv->base;
}
+static void tegra186_xusb_padctl_save(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+
+ priv->context.vbus_id = padctl_readl(padctl, USB2_VBUS_ID);
+ priv->context.usb2_pad_mux = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
+ priv->context.usb2_port_cap = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
+ priv->context.ss_port_cap = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP);
+}
+
+static void tegra186_xusb_padctl_restore(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+
+ padctl_writel(padctl, priv->context.usb2_pad_mux, XUSB_PADCTL_USB2_PAD_MUX);
+ padctl_writel(padctl, priv->context.usb2_port_cap, XUSB_PADCTL_USB2_PORT_CAP);
+ padctl_writel(padctl, priv->context.ss_port_cap, XUSB_PADCTL_SS_PORT_CAP);
+ padctl_writel(padctl, priv->context.vbus_id, USB2_VBUS_ID);
+}
+
+static int tegra186_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl)
+{
+ tegra186_xusb_padctl_save(padctl);
+
+ return 0;
+}
+
+static int tegra186_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl)
+{
+ tegra186_xusb_padctl_restore(padctl);
+
+ return 0;
+}
+
static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
{
}
@@ -937,6 +1483,8 @@ static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = {
.probe = tegra186_xusb_padctl_probe,
.remove = tegra186_xusb_padctl_remove,
+ .suspend_noirq = tegra186_xusb_padctl_suspend_noirq,
+ .resume_noirq = tegra186_xusb_padctl_resume_noirq,
.vbus_override = tegra186_xusb_padctl_vbus_override,
};
diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
index 66bd4613835b..eedfc7c2cc05 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
* Copyright (C) 2015 Google, Inc.
*/
@@ -11,8 +11,10 @@
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
@@ -52,6 +54,20 @@
#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5))
#define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7
+#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20
+#define USB2_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x))
+#define USB2_PORT_WAKEUP_EVENT(x) BIT((x) + 7)
+#define SS_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 14)
+#define SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21)
+#define USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28)
+#define USB2_HSIC_PORT_WAKEUP_EVENT(x) BIT((x) + 30)
+#define ALL_WAKE_EVENTS ( \
+ USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \
+ USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \
+ SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \
+ SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \
+ USB2_HSIC_PORT_WAKEUP_EVENT(0))
+
#define XUSB_PADCTL_ELPG_PROGRAM1 0x024
#define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31)
#define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30)
@@ -90,6 +106,8 @@
#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1)
#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0)
+#define RPD_CTRL(x) (((x) & 0x1f) << 26)
+#define RPD_CTRL_VALUE(x) (((x) >> 26) & 0x1f)
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11)
@@ -108,6 +126,8 @@
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e
+#define TCTRL_VALUE(x) (((x) & 0x3f) >> 0)
+#define PCTRL_VALUE(x) (((x) >> 6) & 0x3f)
#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20)
#define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18)
@@ -198,6 +218,18 @@
#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN BIT(18)
#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD BIT(13)
+#define XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(x) (0x464 + (x) * 0x40)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ BIT(0)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD BIT(1)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK GENMASK(5, 4)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL GENMASK(5, 4)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD BIT(24)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ BIT(8)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD BIT(9)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK GENMASK(13, 12)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL GENMASK(13, 12)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD BIT(25)
+
#define XUSB_PADCTL_UPHY_PLL_S0_CTL1 0x860
#define XUSB_PADCTL_UPHY_PLL_S0_CTL2 0x864
@@ -209,6 +241,7 @@
#define XUSB_PADCTL_UPHY_PLL_S0_CTL8 0x87c
#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1 0x960
+#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2 0x964
#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(x) (0xa60 + (x) * 0x40)
#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT 16
@@ -238,16 +271,161 @@
#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8
#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0
+/* USB2 SLEEPWALK registers */
+#define UTMIP(_port, _offset1, _offset2) \
+ (((_port) <= 2) ? (_offset1) : (_offset2))
+
+#define PMC_UTMIP_UHSIC_SLEEP_CFG(x) UTMIP(x, 0x1fc, 0x4d0)
+#define UTMIP_MASTER_ENABLE(x) UTMIP(x, BIT(8 * (x)), BIT(0))
+#define UTMIP_FSLS_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 1), \
+ BIT(1))
+#define UTMIP_PCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 2), \
+ BIT(2))
+#define UTMIP_TCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 3), \
+ BIT(3))
+#define UTMIP_WAKE_VAL(_port, _value) (((_value) & 0xf) << \
+ (UTMIP(_port, 8 * (_port) + 4, 4)))
+#define UTMIP_WAKE_VAL_NONE(_port) UTMIP_WAKE_VAL(_port, 12)
+#define UTMIP_WAKE_VAL_ANY(_port) UTMIP_WAKE_VAL(_port, 15)
+
+#define PMC_UTMIP_UHSIC_SLEEP_CFG1 (0x4d0)
+#define UTMIP_RPU_SWITC_LOW_USE_PMC_PX(x) BIT((x) + 8)
+#define UTMIP_RPD_CTRL_USE_PMC_PX(x) BIT((x) + 16)
+
+#define PMC_UTMIP_MASTER_CONFIG (0x274)
+#define UTMIP_PWR(x) UTMIP(x, BIT(x), BIT(4))
+#define UHSIC_PWR BIT(3)
+
+#define PMC_USB_DEBOUNCE_DEL (0xec)
+#define DEBOUNCE_VAL(x) (((x) & 0xffff) << 0)
+#define UTMIP_LINE_DEB_CNT(x) (((x) & 0xf) << 16)
+#define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 20)
+
+#define PMC_UTMIP_UHSIC_FAKE(x) UTMIP(x, 0x218, 0x294)
+#define UTMIP_FAKE_USBOP_VAL(x) UTMIP(x, BIT(4 * (x)), BIT(8))
+#define UTMIP_FAKE_USBON_VAL(x) UTMIP(x, BIT(4 * (x) + 1), \
+ BIT(9))
+#define UTMIP_FAKE_USBOP_EN(x) UTMIP(x, BIT(4 * (x) + 2), \
+ BIT(10))
+#define UTMIP_FAKE_USBON_EN(x) UTMIP(x, BIT(4 * (x) + 3), \
+ BIT(11))
+
+#define PMC_UTMIP_UHSIC_SLEEPWALK_CFG(x) UTMIP(x, 0x200, 0x288)
+#define UTMIP_LINEVAL_WALK_EN(x) UTMIP(x, BIT(8 * (x) + 7), \
+ BIT(15))
+
+#define PMC_USB_AO (0xf0)
+#define USBOP_VAL_PD(x) UTMIP(x, BIT(4 * (x)), BIT(20))
+#define USBON_VAL_PD(x) UTMIP(x, BIT(4 * (x) + 1), \
+ BIT(21))
+#define STROBE_VAL_PD BIT(12)
+#define DATA0_VAL_PD BIT(13)
+#define DATA1_VAL_PD BIT(24)
+
+#define PMC_UTMIP_UHSIC_SAVED_STATE(x) UTMIP(x, 0x1f0, 0x280)
+#define SPEED(_port, _value) (((_value) & 0x3) << \
+ (UTMIP(_port, 8 * (_port), 8)))
+#define UTMI_HS(_port) SPEED(_port, 0)
+#define UTMI_FS(_port) SPEED(_port, 1)
+#define UTMI_LS(_port) SPEED(_port, 2)
+#define UTMI_RST(_port) SPEED(_port, 3)
+
+#define PMC_UTMIP_UHSIC_TRIGGERS (0x1ec)
+#define UTMIP_CLR_WALK_PTR(x) UTMIP(x, BIT(x), BIT(16))
+#define UTMIP_CAP_CFG(x) UTMIP(x, BIT((x) + 4), BIT(17))
+#define UTMIP_CLR_WAKE_ALARM(x) UTMIP(x, BIT((x) + 12), \
+ BIT(19))
+#define UHSIC_CLR_WALK_PTR BIT(3)
+#define UHSIC_CLR_WAKE_ALARM BIT(15)
+
+#define PMC_UTMIP_SLEEPWALK_PX(x) UTMIP(x, 0x204 + (4 * (x)), \
+ 0x4e0)
+/* phase A */
+#define UTMIP_USBOP_RPD_A BIT(0)
+#define UTMIP_USBON_RPD_A BIT(1)
+#define UTMIP_AP_A BIT(4)
+#define UTMIP_AN_A BIT(5)
+#define UTMIP_HIGHZ_A BIT(6)
+/* phase B */
+#define UTMIP_USBOP_RPD_B BIT(8)
+#define UTMIP_USBON_RPD_B BIT(9)
+#define UTMIP_AP_B BIT(12)
+#define UTMIP_AN_B BIT(13)
+#define UTMIP_HIGHZ_B BIT(14)
+/* phase C */
+#define UTMIP_USBOP_RPD_C BIT(16)
+#define UTMIP_USBON_RPD_C BIT(17)
+#define UTMIP_AP_C BIT(20)
+#define UTMIP_AN_C BIT(21)
+#define UTMIP_HIGHZ_C BIT(22)
+/* phase D */
+#define UTMIP_USBOP_RPD_D BIT(24)
+#define UTMIP_USBON_RPD_D BIT(25)
+#define UTMIP_AP_D BIT(28)
+#define UTMIP_AN_D BIT(29)
+#define UTMIP_HIGHZ_D BIT(30)
+
+#define PMC_UTMIP_UHSIC_LINE_WAKEUP (0x26c)
+#define UTMIP_LINE_WAKEUP_EN(x) UTMIP(x, BIT(x), BIT(4))
+#define UHSIC_LINE_WAKEUP_EN BIT(3)
+
+#define PMC_UTMIP_TERM_PAD_CFG (0x1f8)
+#define PCTRL_VAL(x) (((x) & 0x3f) << 1)
+#define TCTRL_VAL(x) (((x) & 0x3f) << 7)
+
+#define PMC_UTMIP_PAD_CFGX(x) (0x4c0 + (4 * (x)))
+#define RPD_CTRL_PX(x) (((x) & 0x1f) << 22)
+
+#define PMC_UHSIC_SLEEP_CFG PMC_UTMIP_UHSIC_SLEEP_CFG(0)
+#define UHSIC_MASTER_ENABLE BIT(24)
+#define UHSIC_WAKE_VAL(_value) (((_value) & 0xf) << 28)
+#define UHSIC_WAKE_VAL_SD10 UHSIC_WAKE_VAL(2)
+#define UHSIC_WAKE_VAL_NONE UHSIC_WAKE_VAL(12)
+
+#define PMC_UHSIC_FAKE PMC_UTMIP_UHSIC_FAKE(0)
+#define UHSIC_FAKE_STROBE_VAL BIT(12)
+#define UHSIC_FAKE_DATA_VAL BIT(13)
+#define UHSIC_FAKE_STROBE_EN BIT(14)
+#define UHSIC_FAKE_DATA_EN BIT(15)
+
+#define PMC_UHSIC_SAVED_STATE PMC_UTMIP_UHSIC_SAVED_STATE(0)
+#define UHSIC_MODE(_value) (((_value) & 0x1) << 24)
+#define UHSIC_HS UHSIC_MODE(0)
+#define UHSIC_RST UHSIC_MODE(1)
+
+#define PMC_UHSIC_SLEEPWALK_CFG PMC_UTMIP_UHSIC_SLEEPWALK_CFG(0)
+#define UHSIC_WAKE_WALK_EN BIT(30)
+#define UHSIC_LINEVAL_WALK_EN BIT(31)
+
+#define PMC_UHSIC_SLEEPWALK_P0 (0x210)
+#define UHSIC_DATA0_RPD_A BIT(1)
+#define UHSIC_DATA0_RPU_B BIT(11)
+#define UHSIC_DATA0_RPU_C BIT(19)
+#define UHSIC_DATA0_RPU_D BIT(27)
+#define UHSIC_STROBE_RPU_A BIT(2)
+#define UHSIC_STROBE_RPD_B BIT(8)
+#define UHSIC_STROBE_RPD_C BIT(16)
+#define UHSIC_STROBE_RPD_D BIT(24)
+
struct tegra210_xusb_fuse_calibration {
u32 hs_curr_level[4];
u32 hs_term_range_adj;
u32 rpd_ctrl;
};
+struct tegra210_xusb_padctl_context {
+ u32 usb2_pad_mux;
+ u32 usb2_port_cap;
+ u32 ss_port_map;
+ u32 usb3_pad_mux;
+};
+
struct tegra210_xusb_padctl {
struct tegra_xusb_padctl base;
+ struct regmap *regmap;
struct tegra210_xusb_fuse_calibration fuse;
+ struct tegra210_xusb_padctl_context context;
};
static inline struct tegra210_xusb_padctl *
@@ -256,23 +434,51 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)
return container_of(padctl, struct tegra210_xusb_padctl, base);
}
+static const struct tegra_xusb_lane_map tegra210_usb3_map[] = {
+ { 0, "pcie", 6 },
+ { 1, "pcie", 5 },
+ { 2, "pcie", 0 },
+ { 2, "pcie", 3 },
+ { 3, "pcie", 4 },
+ { 3, "sata", 0 },
+ { 0, NULL, 0 }
+};
+
+static int tegra210_usb3_lane_map(struct tegra_xusb_lane *lane)
+{
+ const struct tegra_xusb_lane_map *map;
+
+ for (map = tegra210_usb3_map; map->type; map++) {
+ if (map->index == lane->index &&
+ strcmp(map->type, lane->pad->soc->name) == 0) {
+ dev_dbg(lane->pad->padctl->dev, "lane = %s map to port = usb3-%d\n",
+ lane->pad->soc->lanes[lane->index].name, map->port);
+ return map->port;
+ }
+ }
+
+ return -EINVAL;
+}
+
/* must be called under padctl->lock */
static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
unsigned long timeout;
u32 value;
+ unsigned int i;
int err;
- if (pcie->enable > 0) {
- pcie->enable++;
+ if (pcie->enable)
return 0;
- }
err = clk_prepare_enable(pcie->pll);
if (err < 0)
return err;
+ if (tegra210_plle_hw_sequence_is_enabled())
+ goto skip_pll_init;
+
err = reset_control_deassert(pcie->rst);
if (err < 0)
goto disable;
@@ -455,7 +661,14 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
tegra210_xusb_pll_hw_sequence_start();
- pcie->enable++;
+skip_pll_init:
+ pcie->enable = true;
+
+ for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ }
return 0;
@@ -469,34 +682,44 @@ disable:
static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
+ u32 value;
+ unsigned int i;
- mutex_lock(&padctl->lock);
+ if (WARN_ON(!pcie->enable))
+ return;
- if (WARN_ON(pcie->enable == 0))
- goto unlock;
+ pcie->enable = false;
- if (--pcie->enable > 0)
- goto unlock;
+ for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ }
- reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
-
-unlock:
- mutex_unlock(&padctl->lock);
}
/* must be called under padctl->lock */
-static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb)
+static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+ struct tegra_xusb_lane *lane = tegra_xusb_find_lane(padctl, "sata", 0);
unsigned long timeout;
u32 value;
+ unsigned int i;
int err;
+ bool usb;
- if (sata->enable > 0) {
- sata->enable++;
+ if (sata->enable)
return 0;
- }
+
+ if (IS_ERR(lane))
+ return 0;
+
+ if (tegra210_plle_hw_sequence_is_enabled())
+ goto skip_pll_init;
+
+ usb = tegra_xusb_lane_check(lane, "usb3-ss");
err = clk_prepare_enable(sata->pll);
if (err < 0)
@@ -697,7 +920,14 @@ static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb)
tegra210_sata_pll_hw_sequence_start();
- sata->enable++;
+skip_pll_init:
+ sata->enable = true;
+
+ for (i = 0; i < padctl->sata->soc->num_lanes; i++) {
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(i);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ }
return 0;
@@ -711,31 +941,27 @@ disable:
static void tegra210_sata_uphy_disable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+ u32 value;
+ unsigned int i;
- mutex_lock(&padctl->lock);
+ if (WARN_ON(!sata->enable))
+ return;
- if (WARN_ON(sata->enable == 0))
- goto unlock;
+ sata->enable = false;
- if (--sata->enable > 0)
- goto unlock;
+ for (i = 0; i < padctl->sata->soc->num_lanes; i++) {
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(i);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ }
- reset_control_assert(sata->rst);
clk_disable_unprepare(sata->pll);
-
-unlock:
- mutex_unlock(&padctl->lock);
}
-static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
+static void tegra210_aux_mux_lp0_clamp_disable(struct tegra_xusb_padctl *padctl)
{
u32 value;
- mutex_lock(&padctl->lock);
-
- if (padctl->enable++ > 0)
- goto out;
-
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN;
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
@@ -751,24 +977,12 @@ static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN;
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
-out:
- mutex_unlock(&padctl->lock);
- return 0;
}
-static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)
+static void tegra210_aux_mux_lp0_clamp_enable(struct tegra_xusb_padctl *padctl)
{
u32 value;
- mutex_lock(&padctl->lock);
-
- if (WARN_ON(padctl->enable == 0))
- goto out;
-
- if (--padctl->enable > 0)
- goto out;
-
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN;
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
@@ -784,12 +998,38 @@ static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN;
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+}
+
+static int tegra210_uphy_init(struct tegra_xusb_padctl *padctl)
+{
+ if (padctl->pcie)
+ tegra210_pex_uphy_enable(padctl);
+
+ if (padctl->sata)
+ tegra210_sata_uphy_enable(padctl);
+
+ if (!tegra210_plle_hw_sequence_is_enabled())
+ tegra210_plle_hw_sequence_start();
+ else
+ dev_dbg(padctl->dev, "PLLE is already in HW control\n");
+
+ tegra210_aux_mux_lp0_clamp_disable(padctl);
-out:
- mutex_unlock(&padctl->lock);
return 0;
}
+static void __maybe_unused
+tegra210_uphy_deinit(struct tegra_xusb_padctl *padctl)
+{
+ tegra210_aux_mux_lp0_clamp_enable(padctl);
+
+ if (padctl->sata)
+ tegra210_sata_uphy_disable(padctl);
+
+ if (padctl->pcie)
+ tegra210_pex_uphy_disable(padctl);
+}
+
static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl,
unsigned int index, bool idle)
{
@@ -815,6 +1055,643 @@ static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl,
return 0;
}
+static int tegra210_usb3_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ int port = tegra210_usb3_lane_map(lane);
+ struct device *dev = padctl->dev;
+ u32 value;
+
+ if (port < 0) {
+ dev_err(dev, "invalid usb3 port number\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(250, 350);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra210_usb3_disable_phy_sleepwalk(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ int port = tegra210_usb3_lane_map(lane);
+ struct device *dev = padctl->dev;
+ u32 value;
+
+ if (port < 0) {
+ dev_err(dev, "invalid usb3 port number\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra210_usb3_enable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ int port = tegra210_usb3_lane_map(lane);
+ struct device *dev = padctl->dev;
+ u32 value;
+
+ if (port < 0) {
+ dev_err(dev, "invalid usb3 port number\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKEUP_EVENT(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKE_INTERRUPT_ENABLE(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra210_usb3_disable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ int port = tegra210_usb3_lane_map(lane);
+ struct device *dev = padctl->dev;
+ u32 value;
+
+ if (port < 0) {
+ dev_err(dev, "invalid usb3 port number\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKEUP_EVENT(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static bool tegra210_usb3_phy_remote_wake_detected(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ int index = tegra210_usb3_lane_map(lane);
+ u32 value;
+
+ if (index < 0)
+ return false;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & SS_PORT_WAKEUP_EVENT(index)))
+ return true;
+
+ return false;
+}
+
+static int tegra210_utmi_enable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra210_utmi_disable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static bool tegra210_utmi_phy_remote_wake_detected(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(index)) &&
+ (value & USB2_PORT_WAKEUP_EVENT(index)))
+ return true;
+
+ return false;
+}
+
+static int tegra210_hsic_enable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_HSIC_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra210_hsic_disable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value &= ~USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_HSIC_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static bool tegra210_hsic_phy_remote_wake_detected(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ if ((value & USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index)) &&
+ (value & USB2_HSIC_PORT_WAKEUP_EVENT(index)))
+ return true;
+
+ return false;
+}
+
+#define padctl_pmc_readl(_priv, _offset) \
+({ \
+ u32 value; \
+ WARN(regmap_read(_priv->regmap, _offset, &value), "read %s failed\n", #_offset);\
+ value; \
+})
+
+#define padctl_pmc_writel(_priv, _value, _offset) \
+ WARN(regmap_write(_priv->regmap, _offset, _value), "write %s failed\n", #_offset)
+
+static int tegra210_pmc_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+ unsigned int port = lane->index;
+ u32 value, tctrl, pctrl, rpd_ctrl;
+
+ if (!priv->regmap)
+ return -EOPNOTSUPP;
+
+ if (speed > USB_SPEED_HIGH)
+ return -EINVAL;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+ tctrl = TCTRL_VALUE(value);
+ pctrl = PCTRL_VALUE(value);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));
+ rpd_ctrl = RPD_CTRL_VALUE(value);
+
+ /* ensure sleepwalk logic is disabled */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~UTMIP_MASTER_ENABLE(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ /* ensure sleepwalk logics are in low power mode */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG);
+ value |= UTMIP_PWR(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG);
+
+ /* set debounce time */
+ value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL);
+ value &= ~UTMIP_LINE_DEB_CNT(~0);
+ value |= UTMIP_LINE_DEB_CNT(0x1);
+ padctl_pmc_writel(priv, value, PMC_USB_DEBOUNCE_DEL);
+
+ /* ensure fake events of sleepwalk logic are desiabled */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_FAKE(port));
+ value &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) |
+ UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port));
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_FAKE(port));
+
+ /* ensure wake events of sleepwalk logic are not latched */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value &= ~UTMIP_LINE_WAKEUP_EN(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~UTMIP_WAKE_VAL(port, ~0);
+ value |= UTMIP_WAKE_VAL_NONE(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ /* power down the line state detectors of the pad */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ /* save state per speed */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SAVED_STATE(port));
+ value &= ~SPEED(port, ~0);
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ value |= UTMI_HS(port);
+ break;
+
+ case USB_SPEED_FULL:
+ value |= UTMI_FS(port);
+ break;
+
+ case USB_SPEED_LOW:
+ value |= UTMI_LS(port);
+ break;
+
+ default:
+ value |= UTMI_RST(port);
+ break;
+ }
+
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SAVED_STATE(port));
+
+ /* enable the trigger of the sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));
+ value |= UTMIP_LINEVAL_WALK_EN(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));
+
+ /*
+ * Reset the walk pointer and clear the alarm of the sleepwalk logic,
+ * as well as capture the configuration of the USB2.0 pad.
+ */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
+ value |= UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) | UTMIP_CAP_CFG(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS);
+
+ /* program electrical parameters read from XUSB PADCTL */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_TERM_PAD_CFG);
+ value &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0));
+ value |= (TCTRL_VAL(tctrl) | PCTRL_VAL(pctrl));
+ padctl_pmc_writel(priv, value, PMC_UTMIP_TERM_PAD_CFG);
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_PAD_CFGX(port));
+ value &= ~RPD_CTRL_PX(~0);
+ value |= RPD_CTRL_PX(rpd_ctrl);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_PAD_CFGX(port));
+
+ /*
+ * Set up the pull-ups and pull-downs of the signals during the four
+ * stages of sleepwalk. If a device is connected, program sleepwalk
+ * logic to maintain a J and keep driving K upon seeing remote wake.
+ */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_SLEEPWALK_PX(port));
+ value = UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C | UTMIP_USBOP_RPD_D;
+ value |= UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C | UTMIP_USBON_RPD_D;
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ case USB_SPEED_FULL:
+ /* J state: D+/D- = high/low, K state: D+/D- = low/high */
+ value |= UTMIP_HIGHZ_A;
+ value |= UTMIP_AP_A;
+ value |= UTMIP_AN_B | UTMIP_AN_C | UTMIP_AN_D;
+ break;
+
+ case USB_SPEED_LOW:
+ /* J state: D+/D- = low/high, K state: D+/D- = high/low */
+ value |= UTMIP_HIGHZ_A;
+ value |= UTMIP_AN_A;
+ value |= UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D;
+ break;
+
+ default:
+ value |= UTMIP_HIGHZ_A | UTMIP_HIGHZ_B | UTMIP_HIGHZ_C | UTMIP_HIGHZ_D;
+ break;
+ }
+
+ padctl_pmc_writel(priv, value, PMC_UTMIP_SLEEPWALK_PX(port));
+
+ /* power up the line state detectors of the pad */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port));
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ usleep_range(50, 100);
+
+ /* switch the electric control of the USB2.0 pad to PMC */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value |= UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | UTMIP_TCTRL_USE_PMC(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+ value |= UTMIP_RPD_CTRL_USE_PMC_PX(port) | UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+
+ /* set the wake signaling trigger events */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~UTMIP_WAKE_VAL(port, ~0);
+ value |= UTMIP_WAKE_VAL_ANY(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ /* enable the wake detection */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value |= UTMIP_MASTER_ENABLE(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value |= UTMIP_LINE_WAKEUP_EN(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ return 0;
+}
+
+static int tegra210_pmc_utmi_disable_phy_sleepwalk(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+ unsigned int port = lane->index;
+ u32 value;
+
+ if (!priv->regmap)
+ return -EOPNOTSUPP;
+
+ /* disable the wake detection */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~UTMIP_MASTER_ENABLE(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value &= ~UTMIP_LINE_WAKEUP_EN(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ /* switch the electric control of the USB2.0 pad to XUSB or USB2 */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~(UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) |
+ UTMIP_TCTRL_USE_PMC(port));
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+ value &= ~(UTMIP_RPD_CTRL_USE_PMC_PX(port) | UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port));
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~UTMIP_WAKE_VAL(port, ~0);
+ value |= UTMIP_WAKE_VAL_NONE(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ /* power down the line state detectors of the port */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ /* clear alarm of the sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
+ value |= UTMIP_CLR_WAKE_ALARM(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS);
+
+ return 0;
+}
+
+static int tegra210_pmc_hsic_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+ u32 value;
+
+ if (!priv->regmap)
+ return -EOPNOTSUPP;
+
+ /* ensure sleepwalk logic is disabled */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value &= ~UHSIC_MASTER_ENABLE;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ /* ensure sleepwalk logics are in low power mode */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG);
+ value |= UHSIC_PWR;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG);
+
+ /* set debounce time */
+ value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL);
+ value &= ~UHSIC_LINE_DEB_CNT(~0);
+ value |= UHSIC_LINE_DEB_CNT(0x1);
+ padctl_pmc_writel(priv, value, PMC_USB_DEBOUNCE_DEL);
+
+ /* ensure fake events of sleepwalk logic are desiabled */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_FAKE);
+ value &= ~(UHSIC_FAKE_STROBE_VAL | UHSIC_FAKE_DATA_VAL |
+ UHSIC_FAKE_STROBE_EN | UHSIC_FAKE_DATA_EN);
+ padctl_pmc_writel(priv, value, PMC_UHSIC_FAKE);
+
+ /* ensure wake events of sleepwalk logic are not latched */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value &= ~UHSIC_LINE_WAKEUP_EN;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value &= ~UHSIC_WAKE_VAL(~0);
+ value |= UHSIC_WAKE_VAL_NONE;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ /* power down the line state detectors of the port */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value |= STROBE_VAL_PD | DATA0_VAL_PD | DATA1_VAL_PD;
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ /* save state, HSIC always comes up as HS */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SAVED_STATE);
+ value &= ~UHSIC_MODE(~0);
+ value |= UHSIC_HS;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SAVED_STATE);
+
+ /* enable the trigger of the sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_CFG);
+ value |= UHSIC_WAKE_WALK_EN | UHSIC_LINEVAL_WALK_EN;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_CFG);
+
+ /*
+ * Reset the walk pointer and clear the alarm of the sleepwalk logic,
+ * as well as capture the configuration of the USB2.0 port.
+ */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
+ value |= UHSIC_CLR_WALK_PTR | UHSIC_CLR_WAKE_ALARM;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS);
+
+ /*
+ * Set up the pull-ups and pull-downs of the signals during the four
+ * stages of sleepwalk. Maintain a HSIC IDLE and keep driving HSIC
+ * RESUME upon remote wake.
+ */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_P0);
+ value = UHSIC_DATA0_RPD_A | UHSIC_DATA0_RPU_B | UHSIC_DATA0_RPU_C | UHSIC_DATA0_RPU_D |
+ UHSIC_STROBE_RPU_A | UHSIC_STROBE_RPD_B | UHSIC_STROBE_RPD_C | UHSIC_STROBE_RPD_D;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_P0);
+
+ /* power up the line state detectors of the port */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value &= ~(STROBE_VAL_PD | DATA0_VAL_PD | DATA1_VAL_PD);
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ usleep_range(50, 100);
+
+ /* set the wake signaling trigger events */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value &= ~UHSIC_WAKE_VAL(~0);
+ value |= UHSIC_WAKE_VAL_SD10;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ /* enable the wake detection */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value |= UHSIC_MASTER_ENABLE;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value |= UHSIC_LINE_WAKEUP_EN;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ return 0;
+}
+
+static int tegra210_pmc_hsic_disable_phy_sleepwalk(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+ u32 value;
+
+ if (!priv->regmap)
+ return -EOPNOTSUPP;
+
+ /* disable the wake detection */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value &= ~UHSIC_MASTER_ENABLE;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value &= ~UHSIC_LINE_WAKEUP_EN;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value &= ~UHSIC_WAKE_VAL(~0);
+ value |= UHSIC_WAKE_VAL_NONE;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ /* power down the line state detectors of the port */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value |= STROBE_VAL_PD | DATA0_VAL_PD | DATA1_VAL_PD;
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ /* clear alarm of the sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
+ value |= UHSIC_CLR_WAKE_ALARM;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS);
+
+ return 0;
+}
+
static int tegra210_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
unsigned int index, bool enable)
{
@@ -911,14 +1788,36 @@ static void tegra210_usb2_lane_remove(struct tegra_xusb_lane *lane)
static const struct tegra_xusb_lane_ops tegra210_usb2_lane_ops = {
.probe = tegra210_usb2_lane_probe,
.remove = tegra210_usb2_lane_remove,
+ .enable_phy_sleepwalk = tegra210_pmc_utmi_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra210_pmc_utmi_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra210_utmi_enable_phy_wake,
+ .disable_phy_wake = tegra210_utmi_disable_phy_wake,
+ .remote_wake_detected = tegra210_utmi_phy_remote_wake_detected,
};
static int tegra210_usb2_phy_init(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ struct tegra_xusb_usb2_port *port;
+ int err;
u32 value;
+ port = tegra_xusb_find_usb2_port(padctl, index);
+ if (!port) {
+ dev_err(&phy->dev, "no port found for USB2 lane %u\n", index);
+ return -ENODEV;
+ }
+
+ if (port->supply && port->mode == USB_DR_MODE_HOST) {
+ err = regulator_enable(port->supply);
+ if (err)
+ return err;
+ }
+
+ mutex_lock(&padctl->lock);
+
value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK <<
XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT);
@@ -926,14 +1825,31 @@ static int tegra210_usb2_phy_init(struct phy *phy)
XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
- return tegra210_xusb_padctl_enable(padctl);
+ mutex_unlock(&padctl->lock);
+
+ return 0;
}
static int tegra210_usb2_phy_exit(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra_xusb_usb2_port *port;
+ int err;
- return tegra210_xusb_padctl_disable(lane->pad->padctl);
+ port = tegra_xusb_find_usb2_port(padctl, lane->index);
+ if (!port) {
+ dev_err(&phy->dev, "no port found for USB2 lane %u\n", lane->index);
+ return -ENODEV;
+ }
+
+ if (port->supply && port->mode == USB_DR_MODE_HOST) {
+ err = regulator_disable(port->supply);
+ if (err)
+ return err;
+ }
+
+ return 0;
}
static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
@@ -1053,6 +1969,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
priv = to_tegra210_xusb_padctl(padctl);
+ mutex_lock(&padctl->lock);
+
if (port->usb3_port_fake != -1) {
value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(
@@ -1146,14 +2064,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
padctl_writel(padctl, value,
XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
- if (port->supply && port->mode == USB_DR_MODE_HOST) {
- err = regulator_enable(port->supply);
- if (err)
- return err;
- }
-
- mutex_lock(&padctl->lock);
-
if (pad->enable > 0) {
pad->enable++;
mutex_unlock(&padctl->lock);
@@ -1162,7 +2072,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
err = clk_prepare_enable(pad->clk);
if (err)
- goto disable_regulator;
+ goto out;
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK <<
@@ -1194,8 +2104,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
return 0;
-disable_regulator:
- regulator_disable(port->supply);
+out:
mutex_unlock(&padctl->lock);
return err;
}
@@ -1254,7 +2163,6 @@ static int tegra210_usb2_phy_power_off(struct phy *phy)
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
out:
- regulator_disable(port->supply);
mutex_unlock(&padctl->lock);
return 0;
}
@@ -1376,6 +2284,11 @@ static void tegra210_hsic_lane_remove(struct tegra_xusb_lane *lane)
static const struct tegra_xusb_lane_ops tegra210_hsic_lane_ops = {
.probe = tegra210_hsic_lane_probe,
.remove = tegra210_hsic_lane_remove,
+ .enable_phy_sleepwalk = tegra210_pmc_hsic_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra210_pmc_hsic_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra210_hsic_enable_phy_wake,
+ .disable_phy_wake = tegra210_hsic_disable_phy_wake,
+ .remote_wake_detected = tegra210_hsic_phy_remote_wake_detected,
};
static int tegra210_hsic_phy_init(struct phy *phy)
@@ -1391,14 +2304,12 @@ static int tegra210_hsic_phy_init(struct phy *phy)
XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
- return tegra210_xusb_padctl_enable(padctl);
+ return 0;
}
static int tegra210_hsic_phy_exit(struct phy *phy)
{
- struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
-
- return tegra210_xusb_padctl_disable(lane->pad->padctl);
+ return 0;
}
static int tegra210_hsic_phy_power_on(struct phy *phy)
@@ -1582,6 +2493,55 @@ static const struct tegra_xusb_pad_soc tegra210_hsic_pad = {
.ops = &tegra210_hsic_ops,
};
+static void tegra210_uphy_lane_iddq_enable(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+ padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+static void tegra210_uphy_lane_iddq_disable(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+ padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+#define TEGRA210_UPHY_LANE(_name, _offset, _shift, _mask, _type, _misc) \
+ { \
+ .name = _name, \
+ .offset = _offset, \
+ .shift = _shift, \
+ .mask = _mask, \
+ .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions), \
+ .funcs = tegra210_##_type##_functions, \
+ .regs.misc_ctl2 = _misc, \
+ }
+
static const char *tegra210_pcie_functions[] = {
"pcie-x1",
"usb3-ss",
@@ -1590,15 +2550,137 @@ static const char *tegra210_pcie_functions[] = {
};
static const struct tegra_xusb_lane_soc tegra210_pcie_lanes[] = {
- TEGRA210_LANE("pcie-0", 0x028, 12, 0x3, pcie),
- TEGRA210_LANE("pcie-1", 0x028, 14, 0x3, pcie),
- TEGRA210_LANE("pcie-2", 0x028, 16, 0x3, pcie),
- TEGRA210_LANE("pcie-3", 0x028, 18, 0x3, pcie),
- TEGRA210_LANE("pcie-4", 0x028, 20, 0x3, pcie),
- TEGRA210_LANE("pcie-5", 0x028, 22, 0x3, pcie),
- TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie),
+ TEGRA210_UPHY_LANE("pcie-0", 0x028, 12, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(0)),
+ TEGRA210_UPHY_LANE("pcie-1", 0x028, 14, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(1)),
+ TEGRA210_UPHY_LANE("pcie-2", 0x028, 16, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(2)),
+ TEGRA210_UPHY_LANE("pcie-3", 0x028, 18, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(3)),
+ TEGRA210_UPHY_LANE("pcie-4", 0x028, 20, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(4)),
+ TEGRA210_UPHY_LANE("pcie-5", 0x028, 22, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(5)),
+ TEGRA210_UPHY_LANE("pcie-6", 0x028, 24, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(6)),
};
+static struct tegra_xusb_usb3_port *
+tegra210_lane_to_usb3_port(struct tegra_xusb_lane *lane)
+{
+ int port;
+
+ if (!lane || !lane->pad || !lane->pad->padctl)
+ return NULL;
+
+ port = tegra210_usb3_lane_map(lane);
+ if (port < 0)
+ return NULL;
+
+ return tegra_xusb_find_usb3_port(lane->pad->padctl, port);
+}
+
+static int tegra210_usb3_phy_power_on(struct phy *phy)
+{
+ struct device *dev = &phy->dev;
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane);
+ unsigned int index;
+ u32 value;
+
+ if (!usb3) {
+ dev_err(dev, "no USB3 port found for lane %u\n", lane->index);
+ return -ENODEV;
+ }
+
+ index = usb3->base.index;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+
+ if (!usb3->internal)
+ value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+ else
+ value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+
+ value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
+ value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port);
+ padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
+ value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT);
+ value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
+ value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT);
+ value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
+
+ padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL,
+ XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
+ value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT);
+ value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
+
+ padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL,
+ XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ return 0;
+}
+
+static int tegra210_usb3_phy_power_off(struct phy *phy)
+{
+ struct device *dev = &phy->dev;
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane);
+ unsigned int index;
+ u32 value;
+
+ if (!usb3) {
+ dev_err(dev, "no USB3 port found for lane %u\n", lane->index);
+ return -ENODEV;
+ }
+
+ index = usb3->base.index;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(250, 350);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ return 0;
+}
static struct tegra_xusb_lane *
tegra210_pcie_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
unsigned int index)
@@ -1635,40 +2717,40 @@ static void tegra210_pcie_lane_remove(struct tegra_xusb_lane *lane)
static const struct tegra_xusb_lane_ops tegra210_pcie_lane_ops = {
.probe = tegra210_pcie_lane_probe,
.remove = tegra210_pcie_lane_remove,
+ .iddq_enable = tegra210_uphy_lane_iddq_enable,
+ .iddq_disable = tegra210_uphy_lane_iddq_disable,
+ .enable_phy_sleepwalk = tegra210_usb3_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra210_usb3_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra210_usb3_enable_phy_wake,
+ .disable_phy_wake = tegra210_usb3_disable_phy_wake,
+ .remote_wake_detected = tegra210_usb3_phy_remote_wake_detected,
};
static int tegra210_pcie_phy_init(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- return tegra210_xusb_padctl_enable(lane->pad->padctl);
-}
+ mutex_lock(&padctl->lock);
-static int tegra210_pcie_phy_exit(struct phy *phy)
-{
- struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ tegra210_uphy_init(padctl);
+
+ mutex_unlock(&padctl->lock);
- return tegra210_xusb_padctl_disable(lane->pad->padctl);
+ return 0;
}
static int tegra210_pcie_phy_power_on(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- u32 value;
- int err;
+ int err = 0;
mutex_lock(&padctl->lock);
- err = tegra210_pex_uphy_enable(padctl);
- if (err < 0)
- goto unlock;
-
- value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
- value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index);
- padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ if (tegra_xusb_lane_check(lane, "usb3-ss"))
+ err = tegra210_usb3_phy_power_on(phy);
-unlock:
mutex_unlock(&padctl->lock);
return err;
}
@@ -1677,20 +2759,19 @@ static int tegra210_pcie_phy_power_off(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- u32 value;
+ int err = 0;
- value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
- value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index);
- padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ mutex_lock(&padctl->lock);
- tegra210_pex_uphy_disable(padctl);
+ if (tegra_xusb_lane_check(lane, "usb3-ss"))
+ err = tegra210_usb3_phy_power_off(phy);
- return 0;
+ mutex_unlock(&padctl->lock);
+ return err;
}
static const struct phy_ops tegra210_pcie_phy_ops = {
.init = tegra210_pcie_phy_init,
- .exit = tegra210_pcie_phy_exit,
.power_on = tegra210_pcie_phy_power_on,
.power_off = tegra210_pcie_phy_power_off,
.owner = THIS_MODULE,
@@ -1767,7 +2848,7 @@ static const struct tegra_xusb_pad_soc tegra210_pcie_pad = {
};
static const struct tegra_xusb_lane_soc tegra210_sata_lanes[] = {
- TEGRA210_LANE("sata-0", 0x028, 30, 0x3, pcie),
+ TEGRA210_UPHY_LANE("sata-0", 0x028, 30, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2),
};
static struct tegra_xusb_lane *
@@ -1806,40 +2887,39 @@ static void tegra210_sata_lane_remove(struct tegra_xusb_lane *lane)
static const struct tegra_xusb_lane_ops tegra210_sata_lane_ops = {
.probe = tegra210_sata_lane_probe,
.remove = tegra210_sata_lane_remove,
+ .iddq_enable = tegra210_uphy_lane_iddq_enable,
+ .iddq_disable = tegra210_uphy_lane_iddq_disable,
+ .enable_phy_sleepwalk = tegra210_usb3_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra210_usb3_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra210_usb3_enable_phy_wake,
+ .disable_phy_wake = tegra210_usb3_disable_phy_wake,
+ .remote_wake_detected = tegra210_usb3_phy_remote_wake_detected,
};
static int tegra210_sata_phy_init(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- return tegra210_xusb_padctl_enable(lane->pad->padctl);
-}
+ mutex_lock(&padctl->lock);
-static int tegra210_sata_phy_exit(struct phy *phy)
-{
- struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ tegra210_uphy_init(padctl);
- return tegra210_xusb_padctl_disable(lane->pad->padctl);
+ mutex_unlock(&padctl->lock);
+ return 0;
}
static int tegra210_sata_phy_power_on(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- u32 value;
- int err;
+ int err = 0;
mutex_lock(&padctl->lock);
- err = tegra210_sata_uphy_enable(padctl, false);
- if (err < 0)
- goto unlock;
-
- value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
- value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index);
- padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ if (tegra_xusb_lane_check(lane, "usb3-ss"))
+ err = tegra210_usb3_phy_power_on(phy);
-unlock:
mutex_unlock(&padctl->lock);
return err;
}
@@ -1848,20 +2928,19 @@ static int tegra210_sata_phy_power_off(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- u32 value;
+ int err = 0;
- value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
- value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index);
- padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ mutex_lock(&padctl->lock);
- tegra210_sata_uphy_disable(lane->pad->padctl);
+ if (tegra_xusb_lane_check(lane, "usb3-ss"))
+ err = tegra210_usb3_phy_power_off(phy);
- return 0;
+ mutex_unlock(&padctl->lock);
+ return err;
}
static const struct phy_ops tegra210_sata_phy_ops = {
.init = tegra210_sata_phy_init,
- .exit = tegra210_sata_phy_exit,
.power_on = tegra210_sata_phy_power_on,
.power_off = tegra210_sata_phy_power_off,
.owner = THIS_MODULE,
@@ -1984,137 +3063,13 @@ static const struct tegra_xusb_port_ops tegra210_hsic_port_ops = {
static int tegra210_usb3_port_enable(struct tegra_xusb_port *port)
{
- struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
- struct tegra_xusb_padctl *padctl = port->padctl;
- struct tegra_xusb_lane *lane = usb3->base.lane;
- unsigned int index = port->index;
- u32 value;
- int err;
-
- value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
-
- if (!usb3->internal)
- value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
- else
- value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
-
- value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
- value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port);
- padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
-
- /*
- * TODO: move this code into the PCIe/SATA PHY ->power_on() callbacks
- * and conditionalize based on mux function? This seems to work, but
- * might not be the exact proper sequence.
- */
- err = regulator_enable(usb3->supply);
- if (err < 0)
- return err;
-
- value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
- value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT);
- value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT;
- padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
-
- value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
- value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT);
- value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT;
- padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
-
- padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL,
- XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index));
-
- value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
- value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT);
- value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT;
- padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
-
- padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL,
- XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index));
-
- if (lane->pad == padctl->sata)
- err = tegra210_sata_uphy_enable(padctl, true);
- else
- err = tegra210_pex_uphy_enable(padctl);
-
- if (err) {
- dev_err(&port->dev, "%s: failed to enable UPHY: %d\n",
- __func__, err);
- return err;
- }
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
- usleep_range(100, 200);
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
- usleep_range(100, 200);
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
return 0;
}
static void tegra210_usb3_port_disable(struct tegra_xusb_port *port)
{
- struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
- struct tegra_xusb_padctl *padctl = port->padctl;
- struct tegra_xusb_lane *lane = port->lane;
- unsigned int index = port->index;
- u32 value;
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
- usleep_range(100, 200);
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
- usleep_range(250, 350);
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
- if (lane->pad == padctl->sata)
- tegra210_sata_uphy_disable(padctl);
- else
- tegra210_pex_uphy_disable(padctl);
-
- regulator_disable(usb3->supply);
-
- value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
- value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
- value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, 0x7);
- padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
}
-static const struct tegra_xusb_lane_map tegra210_usb3_map[] = {
- { 0, "pcie", 6 },
- { 1, "pcie", 5 },
- { 2, "pcie", 0 },
- { 2, "pcie", 3 },
- { 3, "pcie", 4 },
- { 3, "pcie", 4 },
- { 0, NULL, 0 }
-};
-
static struct tegra_xusb_lane *
tegra210_usb3_port_map(struct tegra_xusb_port *port)
{
@@ -2188,6 +3143,8 @@ tegra210_xusb_padctl_probe(struct device *dev,
const struct tegra_xusb_padctl_soc *soc)
{
struct tegra210_xusb_padctl *padctl;
+ struct platform_device *pdev;
+ struct device_node *np;
int err;
padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL);
@@ -2201,6 +3158,26 @@ tegra210_xusb_padctl_probe(struct device *dev,
if (err < 0)
return ERR_PTR(err);
+ np = of_parse_phandle(dev->of_node, "nvidia,pmc", 0);
+ if (!np) {
+ dev_warn(dev, "nvidia,pmc property is missing\n");
+ goto out;
+ }
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev) {
+ dev_warn(dev, "PMC device is not available\n");
+ goto out;
+ }
+
+ if (!platform_get_drvdata(pdev))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ padctl->regmap = dev_get_regmap(&pdev->dev, "usb_sleepwalk");
+ if (!padctl->regmap)
+ dev_info(dev, "failed to find PMC regmap\n");
+
+out:
return &padctl->base;
}
@@ -2208,9 +3185,75 @@ static void tegra210_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
{
}
+static void tegra210_xusb_padctl_save(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+
+ priv->context.usb2_pad_mux =
+ padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
+ priv->context.usb2_port_cap =
+ padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
+ priv->context.ss_port_map =
+ padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+ priv->context.usb3_pad_mux =
+ padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+}
+
+static void tegra210_xusb_padctl_restore(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+ struct tegra_xusb_lane *lane;
+
+ padctl_writel(padctl, priv->context.usb2_pad_mux,
+ XUSB_PADCTL_USB2_PAD_MUX);
+ padctl_writel(padctl, priv->context.usb2_port_cap,
+ XUSB_PADCTL_USB2_PORT_CAP);
+ padctl_writel(padctl, priv->context.ss_port_map,
+ XUSB_PADCTL_SS_PORT_MAP);
+
+ list_for_each_entry(lane, &padctl->lanes, list) {
+ if (lane->pad->ops->iddq_enable)
+ tegra210_uphy_lane_iddq_enable(lane);
+ }
+
+ padctl_writel(padctl, priv->context.usb3_pad_mux,
+ XUSB_PADCTL_USB3_PAD_MUX);
+
+ list_for_each_entry(lane, &padctl->lanes, list) {
+ if (lane->pad->ops->iddq_disable)
+ tegra210_uphy_lane_iddq_disable(lane);
+ }
+}
+
+static int tegra210_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl)
+{
+ mutex_lock(&padctl->lock);
+
+ tegra210_uphy_deinit(padctl);
+
+ tegra210_xusb_padctl_save(padctl);
+
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
+static int tegra210_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl)
+{
+ mutex_lock(&padctl->lock);
+
+ tegra210_xusb_padctl_restore(padctl);
+
+ tegra210_uphy_init(padctl);
+
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = {
.probe = tegra210_xusb_padctl_probe,
.remove = tegra210_xusb_padctl_remove,
+ .suspend_noirq = tegra210_xusb_padctl_suspend_noirq,
+ .resume_noirq = tegra210_xusb_padctl_resume_noirq,
.usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect,
.hsic_set_idle = tegra210_hsic_set_idle,
.vbus_override = tegra210_xusb_padctl_vbus_override,
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index 941006f503e4..0aadac678191 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/delay.h>
@@ -321,11 +321,17 @@ static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane)
if (soc->num_funcs < 2)
return;
+ if (lane->pad->ops->iddq_enable)
+ lane->pad->ops->iddq_enable(lane);
+
/* choose function */
value = padctl_readl(padctl, soc->offset);
value &= ~(soc->mask << soc->shift);
value |= lane->function << soc->shift;
padctl_writel(padctl, value, soc->offset);
+
+ if (lane->pad->ops->iddq_disable)
+ lane->pad->ops->iddq_disable(lane);
}
static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad)
@@ -376,7 +382,7 @@ static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl)
return 0;
}
-static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane,
+bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane,
const char *function)
{
const char *func = lane->soc->funcs[lane->function];
@@ -1267,10 +1273,36 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
return err;
}
+static int tegra_xusb_padctl_suspend_noirq(struct device *dev)
+{
+ struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+ if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq)
+ return padctl->soc->ops->suspend_noirq(padctl);
+
+ return 0;
+}
+
+static int tegra_xusb_padctl_resume_noirq(struct device *dev)
+{
+ struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+ if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq)
+ return padctl->soc->ops->resume_noirq(padctl);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq,
+ tegra_xusb_padctl_resume_noirq)
+};
+
static struct platform_driver tegra_xusb_padctl_driver = {
.driver = {
.name = "tegra-xusb-padctl",
.of_match_table = tegra_xusb_padctl_of_match,
+ .pm = &tegra_xusb_padctl_pm_ops,
},
.probe = tegra_xusb_padctl_probe,
.remove = tegra_xusb_padctl_remove,
@@ -1337,6 +1369,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl,
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
+int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ if (lane->pad->ops->enable_phy_sleepwalk)
+ return lane->pad->ops->enable_phy_sleepwalk(lane, speed);
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk);
+
+int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ if (lane->pad->ops->disable_phy_sleepwalk)
+ return lane->pad->ops->disable_phy_sleepwalk(lane);
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk);
+
+int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ if (lane->pad->ops->enable_phy_wake)
+ return lane->pad->ops->enable_phy_wake(lane);
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake);
+
+int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ if (lane->pad->ops->disable_phy_wake)
+ return lane->pad->ops->disable_phy_wake(lane);
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake);
+
+bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ if (lane->pad->ops->remote_wake_detected)
+ return lane->pad->ops->remote_wake_detected(lane);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_remote_wake_detected);
+
int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
unsigned int port, bool enable)
{
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index ea35af747066..034f7a2c28d6 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2015, Google Inc.
*/
@@ -11,6 +11,7 @@
#include <linux/mutex.h>
#include <linux/workqueue.h>
+#include <linux/usb/ch9.h>
#include <linux/usb/otg.h>
#include <linux/usb/role.h>
@@ -35,6 +36,10 @@ struct tegra_xusb_lane_soc {
const char * const *funcs;
unsigned int num_funcs;
+
+ struct {
+ unsigned int misc_ctl2;
+ } regs;
};
struct tegra_xusb_lane {
@@ -126,8 +131,17 @@ struct tegra_xusb_lane_ops {
struct device_node *np,
unsigned int index);
void (*remove)(struct tegra_xusb_lane *lane);
+ void (*iddq_enable)(struct tegra_xusb_lane *lane);
+ void (*iddq_disable)(struct tegra_xusb_lane *lane);
+ int (*enable_phy_sleepwalk)(struct tegra_xusb_lane *lane, enum usb_device_speed speed);
+ int (*disable_phy_sleepwalk)(struct tegra_xusb_lane *lane);
+ int (*enable_phy_wake)(struct tegra_xusb_lane *lane);
+ int (*disable_phy_wake)(struct tegra_xusb_lane *lane);
+ bool (*remote_wake_detected)(struct tegra_xusb_lane *lane);
};
+bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function);
+
/*
* pads
*/
@@ -230,7 +244,7 @@ struct tegra_xusb_pcie_pad {
struct reset_control *rst;
struct clk *pll;
- unsigned int enable;
+ bool enable;
};
static inline struct tegra_xusb_pcie_pad *
@@ -245,7 +259,7 @@ struct tegra_xusb_sata_pad {
struct reset_control *rst;
struct clk *pll;
- unsigned int enable;
+ bool enable;
};
static inline struct tegra_xusb_sata_pad *
@@ -388,6 +402,8 @@ struct tegra_xusb_padctl_ops {
const struct tegra_xusb_padctl_soc *soc);
void (*remove)(struct tegra_xusb_padctl *padctl);
+ int (*suspend_noirq)(struct tegra_xusb_padctl *padctl);
+ int (*resume_noirq)(struct tegra_xusb_padctl *padctl);
int (*usb3_save_context)(struct tegra_xusb_padctl *padctl,
unsigned int index);
int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl,
diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index 7aa48f6c41d9..da19d7987d00 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -2,7 +2,7 @@
obj-${CONFIG_USB4} := thunderbolt.o
thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o
thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o
-thunderbolt-objs += nvm.o retimer.o quirks.o
+thunderbolt-objs += usb4_port.o nvm.o retimer.o quirks.o
thunderbolt-${CONFIG_ACPI} += acpi.o
thunderbolt-$(CONFIG_DEBUG_FS) += debugfs.o
diff --git a/drivers/thunderbolt/acpi.c b/drivers/thunderbolt/acpi.c
index 35fa17f7e599..b67e72d5644b 100644
--- a/drivers/thunderbolt/acpi.c
+++ b/drivers/thunderbolt/acpi.c
@@ -180,3 +180,209 @@ bool tb_acpi_is_xdomain_allowed(void)
return osc_sb_native_usb4_control & OSC_USB_XDOMAIN;
return true;
}
+
+/* UUID for retimer _DSM: e0053122-795b-4122-8a5e-57be1d26acb3 */
+static const guid_t retimer_dsm_guid =
+ GUID_INIT(0xe0053122, 0x795b, 0x4122,
+ 0x8a, 0x5e, 0x57, 0xbe, 0x1d, 0x26, 0xac, 0xb3);
+
+#define RETIMER_DSM_QUERY_ONLINE_STATE 1
+#define RETIMER_DSM_SET_ONLINE_STATE 2
+
+static int tb_acpi_retimer_set_power(struct tb_port *port, bool power)
+{
+ struct usb4_port *usb4 = port->usb4;
+ union acpi_object argv4[2];
+ struct acpi_device *adev;
+ union acpi_object *obj;
+ int ret;
+
+ if (!usb4->can_offline)
+ return 0;
+
+ adev = ACPI_COMPANION(&usb4->dev);
+ if (WARN_ON(!adev))
+ return 0;
+
+ /* Check if we are already powered on (and in correct mode) */
+ obj = acpi_evaluate_dsm_typed(adev->handle, &retimer_dsm_guid, 1,
+ RETIMER_DSM_QUERY_ONLINE_STATE, NULL,
+ ACPI_TYPE_INTEGER);
+ if (!obj) {
+ tb_port_warn(port, "ACPI: query online _DSM failed\n");
+ return -EIO;
+ }
+
+ ret = obj->integer.value;
+ ACPI_FREE(obj);
+
+ if (power == ret)
+ return 0;
+
+ tb_port_dbg(port, "ACPI: calling _DSM to power %s retimers\n",
+ power ? "on" : "off");
+
+ argv4[0].type = ACPI_TYPE_PACKAGE;
+ argv4[0].package.count = 1;
+ argv4[0].package.elements = &argv4[1];
+ argv4[1].integer.type = ACPI_TYPE_INTEGER;
+ argv4[1].integer.value = power;
+
+ obj = acpi_evaluate_dsm_typed(adev->handle, &retimer_dsm_guid, 1,
+ RETIMER_DSM_SET_ONLINE_STATE, argv4,
+ ACPI_TYPE_INTEGER);
+ if (!obj) {
+ tb_port_warn(port,
+ "ACPI: set online state _DSM evaluation failed\n");
+ return -EIO;
+ }
+
+ ret = obj->integer.value;
+ ACPI_FREE(obj);
+
+ if (ret >= 0) {
+ if (power)
+ return ret == 1 ? 0 : -EBUSY;
+ return 0;
+ }
+
+ tb_port_warn(port, "ACPI: set online state _DSM failed with error %d\n", ret);
+ return -EIO;
+}
+
+/**
+ * tb_acpi_power_on_retimers() - Call platform to power on retimers
+ * @port: USB4 port
+ *
+ * Calls platform to turn on power to all retimers behind this USB4
+ * port. After this function returns successfully the caller can
+ * continue with the normal retimer flows (as specified in the USB4
+ * spec). Note if this returns %-EBUSY it means the type-C port is in
+ * non-USB4/TBT mode (there is non-USB4/TBT device connected).
+ *
+ * This should only be called if the USB4/TBT link is not up.
+ *
+ * Returns %0 on success.
+ */
+int tb_acpi_power_on_retimers(struct tb_port *port)
+{
+ return tb_acpi_retimer_set_power(port, true);
+}
+
+/**
+ * tb_acpi_power_off_retimers() - Call platform to power off retimers
+ * @port: USB4 port
+ *
+ * This is the opposite of tb_acpi_power_on_retimers(). After returning
+ * successfully the normal operations with the @port can continue.
+ *
+ * Returns %0 on success.
+ */
+int tb_acpi_power_off_retimers(struct tb_port *port)
+{
+ return tb_acpi_retimer_set_power(port, false);
+}
+
+static bool tb_acpi_bus_match(struct device *dev)
+{
+ return tb_is_switch(dev) || tb_is_usb4_port_device(dev);
+}
+
+static struct acpi_device *tb_acpi_find_port(struct acpi_device *adev,
+ const struct tb_port *port)
+{
+ struct acpi_device *port_adev;
+
+ if (!adev)
+ return NULL;
+
+ /*
+ * Device routers exists under the downstream facing USB4 port
+ * of the parent router. Their _ADR is always 0.
+ */
+ list_for_each_entry(port_adev, &adev->children, node) {
+ if (acpi_device_adr(port_adev) == port->port)
+ return port_adev;
+ }
+
+ return NULL;
+}
+
+static struct acpi_device *tb_acpi_switch_find_companion(struct tb_switch *sw)
+{
+ struct acpi_device *adev = NULL;
+ struct tb_switch *parent_sw;
+
+ parent_sw = tb_switch_parent(sw);
+ if (parent_sw) {
+ struct tb_port *port = tb_port_at(tb_route(sw), parent_sw);
+ struct acpi_device *port_adev;
+
+ port_adev = tb_acpi_find_port(ACPI_COMPANION(&parent_sw->dev), port);
+ if (port_adev)
+ adev = acpi_find_child_device(port_adev, 0, false);
+ } else {
+ struct tb_nhi *nhi = sw->tb->nhi;
+ struct acpi_device *parent_adev;
+
+ parent_adev = ACPI_COMPANION(&nhi->pdev->dev);
+ if (parent_adev)
+ adev = acpi_find_child_device(parent_adev, 0, false);
+ }
+
+ return adev;
+}
+
+static struct acpi_device *tb_acpi_find_companion(struct device *dev)
+{
+ /*
+ * The Thunderbolt/USB4 hierarchy looks like following:
+ *
+ * Device (NHI)
+ * Device (HR) // Host router _ADR == 0
+ * Device (DFP0) // Downstream port _ADR == lane 0 adapter
+ * Device (DR) // Device router _ADR == 0
+ * Device (UFP) // Upstream port _ADR == lane 0 adapter
+ * Device (DFP1) // Downstream port _ADR == lane 0 adapter number
+ *
+ * At the moment we bind the host router to the corresponding
+ * Linux device.
+ */
+ if (tb_is_switch(dev))
+ return tb_acpi_switch_find_companion(tb_to_switch(dev));
+ else if (tb_is_usb4_port_device(dev))
+ return tb_acpi_find_port(ACPI_COMPANION(dev->parent),
+ tb_to_usb4_port_device(dev)->port);
+ return NULL;
+}
+
+static void tb_acpi_setup(struct device *dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
+
+ if (!adev || !usb4)
+ return;
+
+ if (acpi_check_dsm(adev->handle, &retimer_dsm_guid, 1,
+ BIT(RETIMER_DSM_QUERY_ONLINE_STATE) |
+ BIT(RETIMER_DSM_SET_ONLINE_STATE)))
+ usb4->can_offline = true;
+}
+
+static struct acpi_bus_type tb_acpi_bus = {
+ .name = "thunderbolt",
+ .match = tb_acpi_bus_match,
+ .find_companion = tb_acpi_find_companion,
+ .setup = tb_acpi_setup,
+};
+
+int tb_acpi_init(void)
+{
+ return register_acpi_bus_type(&tb_acpi_bus);
+}
+
+void tb_acpi_exit(void)
+{
+ unregister_acpi_bus_type(&tb_acpi_bus);
+}
diff --git a/drivers/thunderbolt/dma_port.c b/drivers/thunderbolt/dma_port.c
index 5631319f7b20..9f20c7bbf0ce 100644
--- a/drivers/thunderbolt/dma_port.c
+++ b/drivers/thunderbolt/dma_port.c
@@ -299,15 +299,13 @@ static int dma_port_request(struct tb_dma_port *dma, u32 in,
return status_to_errno(out);
}
-static int dma_port_flash_read_block(struct tb_dma_port *dma, u32 address,
- void *buf, u32 size)
+static int dma_port_flash_read_block(void *data, unsigned int dwaddress,
+ void *buf, size_t dwords)
{
+ struct tb_dma_port *dma = data;
struct tb_switch *sw = dma->sw;
- u32 in, dwaddress, dwords;
int ret;
-
- dwaddress = address / 4;
- dwords = size / 4;
+ u32 in;
in = MAIL_IN_CMD_FLASH_READ << MAIL_IN_CMD_SHIFT;
if (dwords < MAIL_DATA_DWORDS)
@@ -323,14 +321,13 @@ static int dma_port_flash_read_block(struct tb_dma_port *dma, u32 address,
dma->base + MAIL_DATA, dwords, DMA_PORT_TIMEOUT);
}
-static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address,
- const void *buf, u32 size)
+static int dma_port_flash_write_block(void *data, unsigned int dwaddress,
+ const void *buf, size_t dwords)
{
+ struct tb_dma_port *dma = data;
struct tb_switch *sw = dma->sw;
- u32 in, dwaddress, dwords;
int ret;
-
- dwords = size / 4;
+ u32 in;
/* Write the block to MAIL_DATA registers */
ret = dma_port_write(sw->tb->ctl, buf, tb_route(sw), dma->port,
@@ -341,12 +338,8 @@ static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address,
in = MAIL_IN_CMD_FLASH_WRITE << MAIL_IN_CMD_SHIFT;
/* CSS header write is always done to the same magic address */
- if (address >= DMA_PORT_CSS_ADDRESS) {
- dwaddress = DMA_PORT_CSS_ADDRESS;
+ if (dwaddress >= DMA_PORT_CSS_ADDRESS)
in |= MAIL_IN_CSS;
- } else {
- dwaddress = address / 4;
- }
in |= ((dwords - 1) << MAIL_IN_DWORDS_SHIFT) & MAIL_IN_DWORDS_MASK;
in |= (dwaddress << MAIL_IN_ADDRESS_SHIFT) & MAIL_IN_ADDRESS_MASK;
@@ -365,36 +358,8 @@ static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address,
int dma_port_flash_read(struct tb_dma_port *dma, unsigned int address,
void *buf, size_t size)
{
- unsigned int retries = DMA_PORT_RETRIES;
-
- do {
- unsigned int offset;
- size_t nbytes;
- int ret;
-
- offset = address & 3;
- nbytes = min_t(size_t, size + offset, MAIL_DATA_DWORDS * 4);
-
- ret = dma_port_flash_read_block(dma, address, dma->buf,
- ALIGN(nbytes, 4));
- if (ret) {
- if (ret == -ETIMEDOUT) {
- if (retries--)
- continue;
- ret = -EIO;
- }
- return ret;
- }
-
- nbytes -= offset;
- memcpy(buf, dma->buf + offset, nbytes);
-
- size -= nbytes;
- address += nbytes;
- buf += nbytes;
- } while (size > 0);
-
- return 0;
+ return tb_nvm_read_data(address, buf, size, DMA_PORT_RETRIES,
+ dma_port_flash_read_block, dma);
}
/**
@@ -411,40 +376,11 @@ int dma_port_flash_read(struct tb_dma_port *dma, unsigned int address,
int dma_port_flash_write(struct tb_dma_port *dma, unsigned int address,
const void *buf, size_t size)
{
- unsigned int retries = DMA_PORT_RETRIES;
- unsigned int offset;
-
- if (address >= DMA_PORT_CSS_ADDRESS) {
- offset = 0;
- if (size > DMA_PORT_CSS_MAX_SIZE)
- return -E2BIG;
- } else {
- offset = address & 3;
- address = address & ~3;
- }
-
- do {
- u32 nbytes = min_t(u32, size, MAIL_DATA_DWORDS * 4);
- int ret;
+ if (address >= DMA_PORT_CSS_ADDRESS && size > DMA_PORT_CSS_MAX_SIZE)
+ return -E2BIG;
- memcpy(dma->buf + offset, buf, nbytes);
-
- ret = dma_port_flash_write_block(dma, address, buf, nbytes);
- if (ret) {
- if (ret == -ETIMEDOUT) {
- if (retries--)
- continue;
- ret = -EIO;
- }
- return ret;
- }
-
- size -= nbytes;
- address += nbytes;
- buf += nbytes;
- } while (size > 0);
-
- return 0;
+ return tb_nvm_write_data(address, buf, size, DMA_PORT_RETRIES,
+ dma_port_flash_write_block, dma);
}
/**
diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c
index 98f4056f89ff..a062befcb3b2 100644
--- a/drivers/thunderbolt/domain.c
+++ b/drivers/thunderbolt/domain.c
@@ -881,11 +881,12 @@ int tb_domain_init(void)
int ret;
tb_test_init();
-
tb_debugfs_init();
+ tb_acpi_init();
+
ret = tb_xdomain_init();
if (ret)
- goto err_debugfs;
+ goto err_acpi;
ret = bus_register(&tb_bus_type);
if (ret)
goto err_xdomain;
@@ -894,7 +895,8 @@ int tb_domain_init(void)
err_xdomain:
tb_xdomain_exit();
-err_debugfs:
+err_acpi:
+ tb_acpi_exit();
tb_debugfs_exit();
tb_test_exit();
@@ -907,6 +909,7 @@ void tb_domain_exit(void)
ida_destroy(&tb_domain_ida);
tb_nvm_exit();
tb_xdomain_exit();
+ tb_acpi_exit();
tb_debugfs_exit();
tb_test_exit();
}
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
index 46d0906a3070..470885e6f1c8 100644
--- a/drivers/thunderbolt/eeprom.c
+++ b/drivers/thunderbolt/eeprom.c
@@ -214,7 +214,10 @@ static u32 tb_crc32(void *data, size_t len)
return ~__crc32c_le(~0, data, len);
}
-#define TB_DROM_DATA_START 13
+#define TB_DROM_DATA_START 13
+#define TB_DROM_HEADER_SIZE 22
+#define USB4_DROM_HEADER_SIZE 16
+
struct tb_drom_header {
/* BYTE 0 */
u8 uid_crc8; /* checksum for uid */
@@ -224,9 +227,9 @@ struct tb_drom_header {
u32 data_crc32; /* checksum for data_len bytes starting at byte 13 */
/* BYTE 13 */
u8 device_rom_revision; /* should be <= 1 */
- u16 data_len:10;
- u8 __unknown1:6;
- /* BYTES 16-21 */
+ u16 data_len:12;
+ u8 reserved:4;
+ /* BYTES 16-21 - Only for TBT DROM, nonexistent in USB4 DROM */
u16 vendor_id;
u16 model_id;
u8 model_rev;
@@ -401,10 +404,10 @@ static int tb_drom_parse_entry_port(struct tb_switch *sw,
*
* Drom must have been copied to sw->drom.
*/
-static int tb_drom_parse_entries(struct tb_switch *sw)
+static int tb_drom_parse_entries(struct tb_switch *sw, size_t header_size)
{
struct tb_drom_header *header = (void *) sw->drom;
- u16 pos = sizeof(*header);
+ u16 pos = header_size;
u16 drom_size = header->data_len + TB_DROM_DATA_START;
int res;
@@ -566,7 +569,7 @@ static int tb_drom_parse(struct tb_switch *sw)
header->data_crc32, crc);
}
- return tb_drom_parse_entries(sw);
+ return tb_drom_parse_entries(sw, TB_DROM_HEADER_SIZE);
}
static int usb4_drom_parse(struct tb_switch *sw)
@@ -583,7 +586,7 @@ static int usb4_drom_parse(struct tb_switch *sw)
return -EINVAL;
}
- return tb_drom_parse_entries(sw);
+ return tb_drom_parse_entries(sw, USB4_DROM_HEADER_SIZE);
}
/**
diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index 2f30b816705a..6255f1ef9599 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -1677,14 +1677,18 @@ static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr)
static bool icm_tgl_is_supported(struct tb *tb)
{
- u32 val;
+ unsigned long end = jiffies + msecs_to_jiffies(10);
- /*
- * If the firmware is not running use software CM. This platform
- * should fully support both.
- */
- val = ioread32(tb->nhi->iobase + REG_FW_STS);
- return !!(val & REG_FW_STS_NVM_AUTH_DONE);
+ do {
+ u32 val;
+
+ val = ioread32(tb->nhi->iobase + REG_FW_STS);
+ if (val & REG_FW_STS_NVM_AUTH_DONE)
+ return true;
+ usleep_range(100, 500);
+ } while (time_before(jiffies, end));
+
+ return false;
}
static void icm_handle_notification(struct work_struct *work)
@@ -2505,6 +2509,8 @@ struct tb *icm_probe(struct tb_nhi *nhi)
case PCI_DEVICE_ID_INTEL_TGL_NHI1:
case PCI_DEVICE_ID_INTEL_TGL_H_NHI0:
case PCI_DEVICE_ID_INTEL_TGL_H_NHI1:
+ case PCI_DEVICE_ID_INTEL_ADL_NHI0:
+ case PCI_DEVICE_ID_INTEL_ADL_NHI1:
icm->is_supported = icm_tgl_is_supported;
icm->driver_ready = icm_icl_driver_ready;
icm->set_uuid = icm_icl_set_uuid;
diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c
index bc671730a11f..c178f0d7beab 100644
--- a/drivers/thunderbolt/lc.c
+++ b/drivers/thunderbolt/lc.c
@@ -208,8 +208,8 @@ static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
if (ret)
return ret;
- ctrl &= ~(TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD | TB_LC_SX_CTRL_WOP |
- TB_LC_SX_CTRL_WOU4);
+ ctrl &= ~(TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD | TB_LC_SX_CTRL_WODPC |
+ TB_LC_SX_CTRL_WODPD | TB_LC_SX_CTRL_WOP | TB_LC_SX_CTRL_WOU4);
if (flags & TB_WAKE_ON_CONNECT)
ctrl |= TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD;
@@ -217,6 +217,8 @@ static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
ctrl |= TB_LC_SX_CTRL_WOU4;
if (flags & TB_WAKE_ON_PCIE)
ctrl |= TB_LC_SX_CTRL_WOP;
+ if (flags & TB_WAKE_ON_DP)
+ ctrl |= TB_LC_SX_CTRL_WODPC | TB_LC_SX_CTRL_WODPD;
return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, offset + TB_LC_SX_CTRL, 1);
}
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index a0386d1e3fc9..fa44332845a1 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -17,7 +17,6 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/property.h>
-#include <linux/platform_data/x86/apple.h>
#include "nhi.h"
#include "nhi_regs.h"
@@ -1127,69 +1126,6 @@ static bool nhi_imr_valid(struct pci_dev *pdev)
return true;
}
-/*
- * During suspend the Thunderbolt controller is reset and all PCIe
- * tunnels are lost. The NHI driver will try to reestablish all tunnels
- * during resume. This adds device links between the tunneled PCIe
- * 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)
-{
- struct pci_dev *upstream, *pdev;
-
- if (!x86_apple_machine)
- return;
-
- switch (nhi->pdev->device) {
- case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
- case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
- case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
- case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
- break;
- default:
- return;
- }
-
- upstream = pci_upstream_bridge(nhi->pdev);
- while (upstream) {
- if (!pci_is_pcie(upstream))
- return;
- if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
- break;
- upstream = pci_upstream_bridge(upstream);
- }
-
- if (!upstream)
- return;
-
- /*
- * For each hotplug downstream port, create add device link
- * back to NHI so that PCIe tunnels can be re-established after
- * sleep.
- */
- for_each_pci_bridge(pdev, upstream->subordinate) {
- const struct device_link *link;
-
- if (!pci_is_pcie(pdev))
- continue;
- if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
- !pdev->is_hotplug_bridge)
- continue;
-
- link = device_link_add(&pdev->dev, &nhi->pdev->dev,
- DL_FLAG_AUTOREMOVE_SUPPLIER |
- DL_FLAG_PM_RUNTIME);
- if (link) {
- dev_dbg(&nhi->pdev->dev, "created link from %s\n",
- dev_name(&pdev->dev));
- } else {
- dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
- dev_name(&pdev->dev));
- }
- }
-}
-
static struct tb *nhi_select_cm(struct tb_nhi *nhi)
{
struct tb *tb;
@@ -1278,9 +1214,6 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return res;
}
- tb_apple_add_links(nhi);
- tb_acpi_add_links(nhi);
-
tb = nhi_select_cm(nhi);
if (!tb) {
dev_err(&nhi->pdev->dev,
@@ -1400,6 +1333,10 @@ static struct pci_device_id nhi_ids[] = {
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_H_NHI1),
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI0),
+ .driver_data = (kernel_ulong_t)&icl_nhi_ops },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI1),
+ .driver_data = (kernel_ulong_t)&icl_nhi_ops },
/* Any USB4 compliant host */
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) },
diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h
index 69770beca792..69083aab2736 100644
--- a/drivers/thunderbolt/nhi.h
+++ b/drivers/thunderbolt/nhi.h
@@ -72,6 +72,8 @@ extern const struct tb_nhi_ops icl_nhi_ops;
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE 0x15ea
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI 0x15eb
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE 0x15ef
+#define PCI_DEVICE_ID_INTEL_ADL_NHI0 0x463e
+#define PCI_DEVICE_ID_INTEL_ADL_NHI1 0x466d
#define PCI_DEVICE_ID_INTEL_ICL_NHI1 0x8a0d
#define PCI_DEVICE_ID_INTEL_ICL_NHI0 0x8a17
#define PCI_DEVICE_ID_INTEL_TGL_NHI0 0x9a1b
diff --git a/drivers/thunderbolt/nvm.c b/drivers/thunderbolt/nvm.c
index 29de6d95c6e7..3a5336913cca 100644
--- a/drivers/thunderbolt/nvm.c
+++ b/drivers/thunderbolt/nvm.c
@@ -164,6 +164,101 @@ void tb_nvm_free(struct tb_nvm *nvm)
kfree(nvm);
}
+/**
+ * tb_nvm_read_data() - Read data from NVM
+ * @address: Start address on the flash
+ * @buf: Buffer where the read data is copied
+ * @size: Size of the buffer in bytes
+ * @retries: Number of retries if block read fails
+ * @read_block: Function that reads block from the flash
+ * @read_block_data: Data passsed to @read_block
+ *
+ * This is a generic function that reads data from NVM or NVM like
+ * device.
+ *
+ * Returns %0 on success and negative errno otherwise.
+ */
+int tb_nvm_read_data(unsigned int address, void *buf, size_t size,
+ unsigned int retries, read_block_fn read_block,
+ void *read_block_data)
+{
+ do {
+ unsigned int dwaddress, dwords, offset;
+ u8 data[NVM_DATA_DWORDS * 4];
+ size_t nbytes;
+ int ret;
+
+ offset = address & 3;
+ nbytes = min_t(size_t, size + offset, NVM_DATA_DWORDS * 4);
+
+ dwaddress = address / 4;
+ dwords = ALIGN(nbytes, 4) / 4;
+
+ ret = read_block(read_block_data, dwaddress, data, dwords);
+ if (ret) {
+ if (ret != -ENODEV && retries--)
+ continue;
+ return ret;
+ }
+
+ nbytes -= offset;
+ memcpy(buf, data + offset, nbytes);
+
+ size -= nbytes;
+ address += nbytes;
+ buf += nbytes;
+ } while (size > 0);
+
+ return 0;
+}
+
+/**
+ * tb_nvm_write_data() - Write data to NVM
+ * @address: Start address on the flash
+ * @buf: Buffer where the data is copied from
+ * @size: Size of the buffer in bytes
+ * @retries: Number of retries if the block write fails
+ * @write_block: Function that writes block to the flash
+ * @write_block_data: Data passwd to @write_block
+ *
+ * This is generic function that writes data to NVM or NVM like device.
+ *
+ * Returns %0 on success and negative errno otherwise.
+ */
+int tb_nvm_write_data(unsigned int address, const void *buf, size_t size,
+ unsigned int retries, write_block_fn write_block,
+ void *write_block_data)
+{
+ do {
+ unsigned int offset, dwaddress;
+ u8 data[NVM_DATA_DWORDS * 4];
+ size_t nbytes;
+ int ret;
+
+ offset = address & 3;
+ nbytes = min_t(u32, size + offset, NVM_DATA_DWORDS * 4);
+
+ memcpy(data + offset, buf, nbytes);
+
+ dwaddress = address / 4;
+ ret = write_block(write_block_data, dwaddress, data, nbytes / 4);
+ if (ret) {
+ if (ret == -ETIMEDOUT) {
+ if (retries--)
+ continue;
+ ret = -EIO;
+ }
+ return ret;
+ }
+
+ size -= nbytes;
+ address += nbytes;
+ buf += nbytes;
+ } while (size > 0);
+
+ return 0;
+}
+
void tb_nvm_exit(void)
{
ida_destroy(&nvm_ida);
diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c
index f63e205a35d9..564e2f42cebd 100644
--- a/drivers/thunderbolt/path.c
+++ b/drivers/thunderbolt/path.c
@@ -367,7 +367,7 @@ static void __tb_path_deallocate_nfc(struct tb_path *path, int first_hop)
int i, res;
for (i = first_hop; i < path->path_length; i++) {
res = tb_port_add_nfc_credits(path->hops[i].in_port,
- -path->nfc_credits);
+ -path->hops[i].nfc_credits);
if (res)
tb_port_warn(path->hops[i].in_port,
"nfc credits deallocation failed for hop %d\n",
@@ -502,7 +502,7 @@ int tb_path_activate(struct tb_path *path)
/* Add non flow controlled credits. */
for (i = path->path_length - 1; i >= 0; i--) {
res = tb_port_add_nfc_credits(path->hops[i].in_port,
- path->nfc_credits);
+ path->hops[i].nfc_credits);
if (res) {
__tb_path_deallocate_nfc(path, i);
goto err;
diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c
index 57e2978a3c21..b5f2ec79c4d6 100644
--- a/drivers/thunderbolt/quirks.c
+++ b/drivers/thunderbolt/quirks.c
@@ -12,7 +12,17 @@ static void quirk_force_power_link(struct tb_switch *sw)
sw->quirks |= QUIRK_FORCE_POWER_LINK_CONTROLLER;
}
+static void quirk_dp_credit_allocation(struct tb_switch *sw)
+{
+ if (sw->credit_allocation && sw->min_dp_main_credits == 56) {
+ sw->min_dp_main_credits = 18;
+ tb_sw_dbg(sw, "quirked DP main: %u\n", sw->min_dp_main_credits);
+ }
+}
+
struct tb_quirk {
+ u16 hw_vendor_id;
+ u16 hw_device_id;
u16 vendor;
u16 device;
void (*hook)(struct tb_switch *sw);
@@ -20,7 +30,13 @@ struct tb_quirk {
static const struct tb_quirk tb_quirks[] = {
/* Dell WD19TB supports self-authentication on unplug */
- { 0x00d4, 0xb070, quirk_force_power_link },
+ { 0x0000, 0x0000, 0x00d4, 0xb070, quirk_force_power_link },
+ { 0x0000, 0x0000, 0x00d4, 0xb071, quirk_force_power_link },
+ /*
+ * Intel Goshen Ridge NVM 27 and before report wrong number of
+ * DP buffers.
+ */
+ { 0x8087, 0x0b26, 0x0000, 0x0000, quirk_dp_credit_allocation },
};
/**
@@ -36,7 +52,15 @@ void tb_check_quirks(struct tb_switch *sw)
for (i = 0; i < ARRAY_SIZE(tb_quirks); i++) {
const struct tb_quirk *q = &tb_quirks[i];
- if (sw->device == q->device && sw->vendor == q->vendor)
- q->hook(sw);
+ if (q->hw_vendor_id && q->hw_vendor_id != sw->config.vendor_id)
+ continue;
+ if (q->hw_device_id && q->hw_device_id != sw->config.device_id)
+ continue;
+ if (q->vendor && q->vendor != sw->vendor)
+ continue;
+ if (q->device && q->device != sw->device)
+ continue;
+
+ q->hook(sw);
}
}
diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c
index c44fad2b9fbb..722694052f4a 100644
--- a/drivers/thunderbolt/retimer.c
+++ b/drivers/thunderbolt/retimer.c
@@ -103,6 +103,7 @@ static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt)
unsigned int image_size, hdr_size;
const u8 *buf = rt->nvm->buf;
u16 ds_size, device;
+ int ret;
image_size = rt->nvm->buf_data_size;
if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
@@ -140,8 +141,43 @@ static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt)
buf += hdr_size;
image_size -= hdr_size;
- return usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf,
- image_size);
+ ret = usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf,
+ image_size);
+ if (!ret)
+ rt->nvm->flushed = true;
+
+ return ret;
+}
+
+static int tb_retimer_nvm_authenticate(struct tb_retimer *rt, bool auth_only)
+{
+ u32 status;
+ int ret;
+
+ if (auth_only) {
+ ret = usb4_port_retimer_nvm_set_offset(rt->port, rt->index, 0);
+ if (ret)
+ return ret;
+ }
+
+ ret = usb4_port_retimer_nvm_authenticate(rt->port, rt->index);
+ if (ret)
+ return ret;
+
+ usleep_range(100, 150);
+
+ /*
+ * Check the status now if we still can access the retimer. It
+ * is expected that the below fails.
+ */
+ ret = usb4_port_retimer_nvm_authenticate_status(rt->port, rt->index,
+ &status);
+ if (!ret) {
+ rt->auth_status = status;
+ return status ? -EINVAL : 0;
+ }
+
+ return 0;
}
static ssize_t device_show(struct device *dev, struct device_attribute *attr,
@@ -176,8 +212,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct tb_retimer *rt = tb_to_retimer(dev);
- bool val;
- int ret;
+ int val, ret;
pm_runtime_get_sync(&rt->dev);
@@ -191,7 +226,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
goto exit_unlock;
}
- ret = kstrtobool(buf, &val);
+ ret = kstrtoint(buf, 10, &val);
if (ret)
goto exit_unlock;
@@ -199,16 +234,22 @@ static ssize_t nvm_authenticate_store(struct device *dev,
rt->auth_status = 0;
if (val) {
- if (!rt->nvm->buf) {
- ret = -EINVAL;
- goto exit_unlock;
+ if (val == AUTHENTICATE_ONLY) {
+ ret = tb_retimer_nvm_authenticate(rt, true);
+ } else {
+ if (!rt->nvm->flushed) {
+ if (!rt->nvm->buf) {
+ ret = -EINVAL;
+ goto exit_unlock;
+ }
+
+ ret = tb_retimer_nvm_validate_and_write(rt);
+ if (ret || val == WRITE_ONLY)
+ goto exit_unlock;
+ }
+ if (val == WRITE_AND_AUTHENTICATE)
+ ret = tb_retimer_nvm_authenticate(rt, false);
}
-
- ret = tb_retimer_nvm_validate_and_write(rt);
- if (ret)
- goto exit_unlock;
-
- ret = usb4_port_retimer_nvm_authenticate(rt->port, rt->index);
}
exit_unlock:
@@ -283,11 +324,13 @@ struct device_type tb_retimer_type = {
static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
{
+ struct usb4_port *usb4;
struct tb_retimer *rt;
u32 vendor, device;
int ret;
- if (!port->cap_usb4)
+ usb4 = port->usb4;
+ if (!usb4)
return -EINVAL;
ret = usb4_port_retimer_read(port, index, USB4_SB_VENDOR_ID, &vendor,
@@ -331,7 +374,7 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
rt->port = port;
rt->tb = port->sw->tb;
- rt->dev.parent = &port->sw->dev;
+ rt->dev.parent = &usb4->dev;
rt->dev.bus = &tb_bus_type;
rt->dev.type = &tb_retimer_type;
dev_set_name(&rt->dev, "%s:%u.%u", dev_name(&port->sw->dev),
@@ -389,7 +432,7 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index)
struct tb_retimer_lookup lookup = { .port = port, .index = index };
struct device *dev;
- dev = device_find_child(&port->sw->dev, &lookup, retimer_match);
+ dev = device_find_child(&port->usb4->dev, &lookup, retimer_match);
if (dev)
return tb_to_retimer(dev);
@@ -399,19 +442,18 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index)
/**
* tb_retimer_scan() - Scan for on-board retimers under port
* @port: USB4 port to scan
+ * @add: If true also registers found retimers
*
- * Tries to enumerate on-board retimers connected to @port. Found
- * retimers are registered as children of @port. Does not scan for cable
- * retimers for now.
+ * Brings the sideband into a state where retimers can be accessed.
+ * Then Tries to enumerate on-board retimers connected to @port. Found
+ * retimers are registered as children of @port if @add is set. Does
+ * not scan for cable retimers for now.
*/
-int tb_retimer_scan(struct tb_port *port)
+int tb_retimer_scan(struct tb_port *port, bool add)
{
u32 status[TB_MAX_RETIMER_INDEX + 1] = {};
int ret, i, last_idx = 0;
- if (!port->cap_usb4)
- return 0;
-
/*
* Send broadcast RT to make sure retimer indices facing this
* port are set.
@@ -421,6 +463,13 @@ int tb_retimer_scan(struct tb_port *port)
return ret;
/*
+ * Enable sideband channel for each retimer. We can do this
+ * regardless whether there is device connected or not.
+ */
+ for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++)
+ usb4_port_retimer_set_inbound_sbtx(port, i);
+
+ /*
* Before doing anything else, read the authentication status.
* If the retimer has it set, store it for the new retimer
* device instance.
@@ -451,10 +500,10 @@ int tb_retimer_scan(struct tb_port *port)
rt = tb_port_find_retimer(port, i);
if (rt) {
put_device(&rt->dev);
- } else {
+ } else if (add) {
ret = tb_retimer_add(port, i, status[i]);
if (ret && ret != -EOPNOTSUPP)
- return ret;
+ break;
}
}
@@ -479,7 +528,10 @@ static int remove_retimer(struct device *dev, void *data)
*/
void tb_retimer_remove_all(struct tb_port *port)
{
- if (port->cap_usb4)
- device_for_each_child_reverse(&port->sw->dev, port,
+ struct usb4_port *usb4;
+
+ usb4 = port->usb4;
+ if (usb4)
+ device_for_each_child_reverse(&usb4->dev, port,
remove_retimer);
}
diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h
index 9dafd696612f..bda889ff3bda 100644
--- a/drivers/thunderbolt/sb_regs.h
+++ b/drivers/thunderbolt/sb_regs.h
@@ -17,7 +17,9 @@
enum usb4_sb_opcode {
USB4_SB_OPCODE_ERR = 0x20525245, /* "ERR " */
USB4_SB_OPCODE_ONS = 0x444d4321, /* "!CMD" */
+ USB4_SB_OPCODE_ROUTER_OFFLINE = 0x4e45534c, /* "LSEN" */
USB4_SB_OPCODE_ENUMERATE_RETIMERS = 0x4d554e45, /* "ENUM" */
+ USB4_SB_OPCODE_SET_INBOUND_SBTX = 0x5055534c, /* "LSUP" */
USB4_SB_OPCODE_QUERY_LAST_RETIMER = 0x5453414c, /* "LAST" */
USB4_SB_OPCODE_GET_NVM_SECTOR_SIZE = 0x53534e47, /* "GNSS" */
USB4_SB_OPCODE_NVM_SET_OFFSET = 0x53504f42, /* "BOPS" */
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index e73cd296db7e..83b1ef3d5d03 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -26,11 +26,6 @@ struct nvm_auth_status {
u32 status;
};
-enum nvm_write_ops {
- WRITE_AND_AUTHENTICATE = 1,
- WRITE_ONLY = 2,
-};
-
/*
* Hold NVM authentication failure status per switch This information
* needs to stay around even when the switch gets power cycled so we
@@ -308,13 +303,23 @@ static inline int nvm_read(struct tb_switch *sw, unsigned int address,
return dma_port_flash_read(sw->dma_port, address, buf, size);
}
-static int nvm_authenticate(struct tb_switch *sw)
+static int nvm_authenticate(struct tb_switch *sw, bool auth_only)
{
int ret;
- if (tb_switch_is_usb4(sw))
+ if (tb_switch_is_usb4(sw)) {
+ if (auth_only) {
+ ret = usb4_switch_nvm_set_offset(sw, 0);
+ if (ret)
+ return ret;
+ }
+ sw->nvm->authenticating = true;
return usb4_switch_nvm_authenticate(sw);
+ } else if (auth_only) {
+ return -EOPNOTSUPP;
+ }
+ sw->nvm->authenticating = true;
if (!tb_route(sw)) {
nvm_authenticate_start_dma_port(sw);
ret = nvm_authenticate_host_dma_port(sw);
@@ -459,7 +464,7 @@ static void tb_switch_nvm_remove(struct tb_switch *sw)
/* port utility functions */
-static const char *tb_port_type(struct tb_regs_port_header *port)
+static const char *tb_port_type(const struct tb_regs_port_header *port)
{
switch (port->type >> 16) {
case 0:
@@ -488,17 +493,21 @@ static const char *tb_port_type(struct tb_regs_port_header *port)
}
}
-static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port)
+static void tb_dump_port(struct tb *tb, const struct tb_port *port)
{
+ const struct tb_regs_port_header *regs = &port->config;
+
tb_dbg(tb,
" Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n",
- port->port_number, port->vendor_id, port->device_id,
- port->revision, port->thunderbolt_version, tb_port_type(port),
- port->type);
+ regs->port_number, regs->vendor_id, regs->device_id,
+ regs->revision, regs->thunderbolt_version, tb_port_type(regs),
+ regs->type);
tb_dbg(tb, " Max hop id (in/out): %d/%d\n",
- port->max_in_hop_id, port->max_out_hop_id);
- tb_dbg(tb, " Max counters: %d\n", port->max_counters);
- tb_dbg(tb, " NFC Credits: %#x\n", port->nfc_credits);
+ regs->max_in_hop_id, regs->max_out_hop_id);
+ tb_dbg(tb, " Max counters: %d\n", regs->max_counters);
+ tb_dbg(tb, " NFC Credits: %#x\n", regs->nfc_credits);
+ tb_dbg(tb, " Credits (total/control): %u/%u\n", port->total_credits,
+ port->ctl_credits);
}
/**
@@ -738,13 +747,32 @@ static int tb_init_port(struct tb_port *port)
cap = tb_port_find_cap(port, TB_PORT_CAP_USB4);
if (cap > 0)
port->cap_usb4 = cap;
+
+ /*
+ * USB4 ports the buffers allocated for the control path
+ * can be read from the path config space. Legacy
+ * devices we use hard-coded value.
+ */
+ if (tb_switch_is_usb4(port->sw)) {
+ struct tb_regs_hop hop;
+
+ if (!tb_port_read(port, &hop, TB_CFG_HOPS, 0, 2))
+ port->ctl_credits = hop.initial_credits;
+ }
+ if (!port->ctl_credits)
+ port->ctl_credits = 2;
+
} else if (port->port != 0) {
cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP);
if (cap > 0)
port->cap_adap = cap;
}
- tb_dump_port(port->sw->tb, &port->config);
+ port->total_credits =
+ (port->config.nfc_credits & ADP_CS_4_TOTAL_BUFFERS_MASK) >>
+ ADP_CS_4_TOTAL_BUFFERS_SHIFT;
+
+ tb_dump_port(port->sw->tb, port);
INIT_LIST_HEAD(&port->list);
return 0;
@@ -991,8 +1019,11 @@ static int tb_port_set_link_width(struct tb_port *port, unsigned int width)
* tb_port_lane_bonding_enable() - Enable bonding on port
* @port: port to enable
*
- * Enable bonding by setting the link width of the port and the
- * other port in case of dual link port.
+ * Enable bonding by setting the link width of the port and the other
+ * port in case of dual link port. Does not wait for the link to
+ * actually reach the bonded state so caller needs to call
+ * tb_port_wait_for_link_width() before enabling any paths through the
+ * link to make sure the link is in expected state.
*
* Return: %0 in case of success and negative errno in case of error
*/
@@ -1043,6 +1074,79 @@ void tb_port_lane_bonding_disable(struct tb_port *port)
tb_port_set_link_width(port, 1);
}
+/**
+ * tb_port_wait_for_link_width() - Wait until link reaches specific width
+ * @port: Port to wait for
+ * @width: Expected link width (%1 or %2)
+ * @timeout_msec: Timeout in ms how long to wait
+ *
+ * Should be used after both ends of the link have been bonded (or
+ * bonding has been disabled) to wait until the link actually reaches
+ * the expected state. Returns %-ETIMEDOUT if the @width was not reached
+ * within the given timeout, %0 if it did.
+ */
+int tb_port_wait_for_link_width(struct tb_port *port, int width,
+ int timeout_msec)
+{
+ ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
+ int ret;
+
+ do {
+ ret = tb_port_get_link_width(port);
+ if (ret < 0)
+ return ret;
+ else if (ret == width)
+ return 0;
+
+ usleep_range(1000, 2000);
+ } while (ktime_before(ktime_get(), timeout));
+
+ return -ETIMEDOUT;
+}
+
+static int tb_port_do_update_credits(struct tb_port *port)
+{
+ u32 nfc_credits;
+ int ret;
+
+ ret = tb_port_read(port, &nfc_credits, TB_CFG_PORT, ADP_CS_4, 1);
+ if (ret)
+ return ret;
+
+ if (nfc_credits != port->config.nfc_credits) {
+ u32 total;
+
+ total = (nfc_credits & ADP_CS_4_TOTAL_BUFFERS_MASK) >>
+ ADP_CS_4_TOTAL_BUFFERS_SHIFT;
+
+ tb_port_dbg(port, "total credits changed %u -> %u\n",
+ port->total_credits, total);
+
+ port->config.nfc_credits = nfc_credits;
+ port->total_credits = total;
+ }
+
+ return 0;
+}
+
+/**
+ * tb_port_update_credits() - Re-read port total credits
+ * @port: Port to update
+ *
+ * After the link is bonded (or bonding was disabled) the port total
+ * credits may change, so this function needs to be called to re-read
+ * the credits. Updates also the second lane adapter.
+ */
+int tb_port_update_credits(struct tb_port *port)
+{
+ int ret;
+
+ ret = tb_port_do_update_credits(port);
+ if (ret)
+ return ret;
+ return tb_port_do_update_credits(port->dual_link_port);
+}
+
static int tb_port_start_lane_initialization(struct tb_port *port)
{
int ret;
@@ -1054,6 +1158,33 @@ static int tb_port_start_lane_initialization(struct tb_port *port)
return ret == -EINVAL ? 0 : ret;
}
+/*
+ * Returns true if the port had something (router, XDomain) connected
+ * before suspend.
+ */
+static bool tb_port_resume(struct tb_port *port)
+{
+ bool has_remote = tb_port_has_remote(port);
+
+ if (port->usb4) {
+ usb4_port_device_resume(port->usb4);
+ } else if (!has_remote) {
+ /*
+ * For disconnected downstream lane adapters start lane
+ * initialization now so we detect future connects.
+ *
+ * For XDomain start the lane initialzation now so the
+ * link gets re-established.
+ *
+ * This is only needed for non-USB4 ports.
+ */
+ if (!tb_is_upstream_port(port) || port->xdomain)
+ tb_port_start_lane_initialization(port);
+ }
+
+ return has_remote || port->xdomain;
+}
+
/**
* tb_port_is_enabled() - Is the adapter port enabled
* @port: Port to check
@@ -1592,8 +1723,7 @@ static ssize_t nvm_authenticate_sysfs(struct device *dev, const char *buf,
bool disconnect)
{
struct tb_switch *sw = tb_to_switch(dev);
- int val;
- int ret;
+ int val, ret;
pm_runtime_get_sync(&sw->dev);
@@ -1616,22 +1746,27 @@ static ssize_t nvm_authenticate_sysfs(struct device *dev, const char *buf,
nvm_clear_auth_status(sw);
if (val > 0) {
- if (!sw->nvm->flushed) {
- if (!sw->nvm->buf) {
+ if (val == AUTHENTICATE_ONLY) {
+ if (disconnect)
ret = -EINVAL;
- goto exit_unlock;
+ else
+ ret = nvm_authenticate(sw, true);
+ } else {
+ if (!sw->nvm->flushed) {
+ if (!sw->nvm->buf) {
+ ret = -EINVAL;
+ goto exit_unlock;
+ }
+
+ ret = nvm_validate_and_write(sw);
+ if (ret || val == WRITE_ONLY)
+ goto exit_unlock;
}
-
- ret = nvm_validate_and_write(sw);
- if (ret || val == WRITE_ONLY)
- goto exit_unlock;
- }
- if (val == WRITE_AND_AUTHENTICATE) {
- if (disconnect) {
- ret = tb_lc_force_power(sw);
- } else {
- sw->nvm->authenticating = true;
- ret = nvm_authenticate(sw);
+ if (val == WRITE_AND_AUTHENTICATE) {
+ if (disconnect)
+ ret = tb_lc_force_power(sw);
+ else
+ ret = nvm_authenticate(sw, false);
}
}
}
@@ -2432,6 +2567,14 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw)
return ret;
}
+ ret = tb_port_wait_for_link_width(down, 2, 100);
+ if (ret) {
+ tb_port_warn(down, "timeout enabling lane bonding\n");
+ return ret;
+ }
+
+ tb_port_update_credits(down);
+ tb_port_update_credits(up);
tb_switch_update_link_attributes(sw);
tb_sw_dbg(sw, "lane bonding enabled\n");
@@ -2462,7 +2605,17 @@ void tb_switch_lane_bonding_disable(struct tb_switch *sw)
tb_port_lane_bonding_disable(up);
tb_port_lane_bonding_disable(down);
+ /*
+ * It is fine if we get other errors as the router might have
+ * been unplugged.
+ */
+ if (tb_port_wait_for_link_width(down, 1, 100) == -ETIMEDOUT)
+ tb_sw_warn(sw, "timeout disabling lane bonding\n");
+
+ tb_port_update_credits(down);
+ tb_port_update_credits(up);
tb_switch_update_link_attributes(sw);
+
tb_sw_dbg(sw, "lane bonding disabled\n");
}
@@ -2529,6 +2682,16 @@ void tb_switch_unconfigure_link(struct tb_switch *sw)
tb_lc_unconfigure_port(down);
}
+static void tb_switch_credits_init(struct tb_switch *sw)
+{
+ if (tb_switch_is_icm(sw))
+ return;
+ if (!tb_switch_is_usb4(sw))
+ return;
+ if (usb4_switch_credits_init(sw))
+ tb_sw_info(sw, "failed to determine preferred buffer allocation, using defaults\n");
+}
+
/**
* tb_switch_add() - Add a switch to the domain
* @sw: Switch to add
@@ -2559,6 +2722,8 @@ int tb_switch_add(struct tb_switch *sw)
}
if (!sw->safe_mode) {
+ tb_switch_credits_init(sw);
+
/* read drom */
ret = tb_drom_read(sw);
if (ret) {
@@ -2612,11 +2777,16 @@ int tb_switch_add(struct tb_switch *sw)
sw->device_name);
}
+ ret = usb4_switch_add_ports(sw);
+ if (ret) {
+ dev_err(&sw->dev, "failed to add USB4 ports\n");
+ goto err_del;
+ }
+
ret = tb_switch_nvm_add(sw);
if (ret) {
dev_err(&sw->dev, "failed to add NVM devices\n");
- device_del(&sw->dev);
- return ret;
+ goto err_ports;
}
/*
@@ -2637,6 +2807,13 @@ int tb_switch_add(struct tb_switch *sw)
tb_switch_debugfs_init(sw);
return 0;
+
+err_ports:
+ usb4_switch_remove_ports(sw);
+err_del:
+ device_del(&sw->dev);
+
+ return ret;
}
/**
@@ -2676,6 +2853,7 @@ void tb_switch_remove(struct tb_switch *sw)
tb_plug_events_active(sw, false);
tb_switch_nvm_remove(sw);
+ usb4_switch_remove_ports(sw);
if (tb_route(sw))
dev_info(&sw->dev, "device disconnected\n");
@@ -2773,22 +2951,11 @@ int tb_switch_resume(struct tb_switch *sw)
/* check for surviving downstream switches */
tb_switch_for_each_port(sw, port) {
- if (!tb_port_has_remote(port) && !port->xdomain) {
- /*
- * For disconnected downstream lane adapters
- * start lane initialization now so we detect
- * future connects.
- */
- if (!tb_is_upstream_port(port) && tb_port_is_null(port))
- tb_port_start_lane_initialization(port);
+ if (!tb_port_is_null(port))
+ continue;
+
+ if (!tb_port_resume(port))
continue;
- } else if (port->xdomain) {
- /*
- * Start lane initialization for XDomain so the
- * link gets re-established.
- */
- tb_port_start_lane_initialization(port);
- }
if (tb_wait_for_port(port, true) <= 0) {
tb_port_warn(port,
@@ -2797,7 +2964,7 @@ int tb_switch_resume(struct tb_switch *sw)
tb_sw_set_unplugged(port->remote->sw);
else if (port->xdomain)
port->xdomain->is_unplugged = true;
- } else if (tb_port_has_remote(port) || port->xdomain) {
+ } else {
/*
* Always unlock the port so the downstream
* switch/domain is accessible.
@@ -2844,7 +3011,8 @@ void tb_switch_suspend(struct tb_switch *sw, bool runtime)
if (runtime) {
/* Trigger wake when something is plugged in/out */
flags |= TB_WAKE_ON_CONNECT | TB_WAKE_ON_DISCONNECT;
- flags |= TB_WAKE_ON_USB4 | TB_WAKE_ON_USB3 | TB_WAKE_ON_PCIE;
+ flags |= TB_WAKE_ON_USB4;
+ flags |= TB_WAKE_ON_USB3 | TB_WAKE_ON_PCIE | TB_WAKE_ON_DP;
} else if (device_may_wakeup(&sw->dev)) {
flags |= TB_WAKE_ON_USB4 | TB_WAKE_ON_USB3 | TB_WAKE_ON_PCIE;
}
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 7e6dc2b03bed..2897a77d44c3 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -10,6 +10,7 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
+#include <linux/platform_data/x86/apple.h>
#include "tb.h"
#include "tb_regs.h"
@@ -595,7 +596,7 @@ static void tb_scan_port(struct tb_port *port)
return;
}
- tb_retimer_scan(port);
+ tb_retimer_scan(port, true);
sw = tb_switch_alloc(port->sw->tb, &port->sw->dev,
tb_downstream_route(port));
@@ -662,7 +663,7 @@ static void tb_scan_port(struct tb_port *port)
tb_sw_warn(sw, "failed to enable TMU\n");
/* Scan upstream retimers */
- tb_retimer_scan(upstream_port);
+ tb_retimer_scan(upstream_port, true);
/*
* Create USB 3.x tunnels only when the switch is plugged to the
@@ -1571,6 +1572,69 @@ static const struct tb_cm_ops tb_cm_ops = {
.disconnect_xdomain_paths = tb_disconnect_xdomain_paths,
};
+/*
+ * During suspend the Thunderbolt controller is reset and all PCIe
+ * tunnels are lost. The NHI driver will try to reestablish all tunnels
+ * during resume. This adds device links between the tunneled PCIe
+ * 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)
+{
+ struct pci_dev *upstream, *pdev;
+
+ if (!x86_apple_machine)
+ return;
+
+ switch (nhi->pdev->device) {
+ case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
+ case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
+ case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
+ case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
+ break;
+ default:
+ return;
+ }
+
+ upstream = pci_upstream_bridge(nhi->pdev);
+ while (upstream) {
+ if (!pci_is_pcie(upstream))
+ return;
+ if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
+ break;
+ upstream = pci_upstream_bridge(upstream);
+ }
+
+ if (!upstream)
+ return;
+
+ /*
+ * For each hotplug downstream port, create add device link
+ * back to NHI so that PCIe tunnels can be re-established after
+ * sleep.
+ */
+ for_each_pci_bridge(pdev, upstream->subordinate) {
+ const struct device_link *link;
+
+ if (!pci_is_pcie(pdev))
+ continue;
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
+ !pdev->is_hotplug_bridge)
+ continue;
+
+ link = device_link_add(&pdev->dev, &nhi->pdev->dev,
+ DL_FLAG_AUTOREMOVE_SUPPLIER |
+ DL_FLAG_PM_RUNTIME);
+ if (link) {
+ dev_dbg(&nhi->pdev->dev, "created link from %s\n",
+ dev_name(&pdev->dev));
+ } else {
+ dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
+ dev_name(&pdev->dev));
+ }
+ }
+}
+
struct tb *tb_probe(struct tb_nhi *nhi)
{
struct tb_cm *tcm;
@@ -1594,5 +1658,8 @@ 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);
+
return tb;
}
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 9790e9f13d2b..725104c83e3d 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -20,6 +20,7 @@
#define NVM_MIN_SIZE SZ_32K
#define NVM_MAX_SIZE SZ_512K
+#define NVM_DATA_DWORDS 16
/* Intel specific NVM offsets */
#define NVM_DEVID 0x05
@@ -57,6 +58,12 @@ struct tb_nvm {
bool flushed;
};
+enum tb_nvm_write_ops {
+ WRITE_AND_AUTHENTICATE = 1,
+ WRITE_ONLY = 2,
+ AUTHENTICATE_ONLY = 3,
+};
+
#define TB_SWITCH_KEY_SIZE 32
#define TB_SWITCH_MAX_DEPTH 6
#define USB4_SWITCH_MAX_DEPTH 5
@@ -135,6 +142,12 @@ struct tb_switch_tmu {
* @rpm_complete: Completion used to wait for runtime resume to
* complete (ICM only)
* @quirks: Quirks used for this Thunderbolt switch
+ * @credit_allocation: Are the below buffer allocation parameters valid
+ * @max_usb3_credits: Router preferred number of buffers for USB 3.x
+ * @min_dp_aux_credits: Router preferred minimum number of buffers for DP AUX
+ * @min_dp_main_credits: Router preferred minimum number of buffers for DP MAIN
+ * @max_pcie_credits: Router preferred number of buffers for PCIe
+ * @max_dma_credits: Router preferred number of buffers for DMA/P2P
*
* When the switch is being added or removed to the domain (other
* switches) you need to have domain lock held.
@@ -177,6 +190,12 @@ struct tb_switch {
u8 depth;
struct completion rpm_complete;
unsigned long quirks;
+ bool credit_allocation;
+ unsigned int max_usb3_credits;
+ unsigned int min_dp_aux_credits;
+ unsigned int min_dp_main_credits;
+ unsigned int max_pcie_credits;
+ unsigned int max_dma_credits;
};
/**
@@ -189,6 +208,7 @@ struct tb_switch {
* @cap_tmu: Offset of the adapter specific TMU capability (%0 if not present)
* @cap_adap: Offset of the adapter specific capability (%0 if not present)
* @cap_usb4: Offset to the USB4 port capability (%0 if not present)
+ * @usb4: Pointer to the USB4 port structure (only if @cap_usb4 is != %0)
* @port: Port number on switch
* @disabled: Disabled by eeprom or enabled but not implemented
* @bonded: true if the port is bonded (two lanes combined as one)
@@ -198,6 +218,10 @@ struct tb_switch {
* @in_hopids: Currently allocated input HopIDs
* @out_hopids: Currently allocated output HopIDs
* @list: Used to link ports to DP resources list
+ * @total_credits: Total number of buffers available for this port
+ * @ctl_credits: Buffers reserved for control path
+ * @dma_credits: Number of credits allocated for DMA tunneling for all
+ * DMA paths through this port.
*
* In USB4 terminology this structure represents an adapter (protocol or
* lane adapter).
@@ -211,6 +235,7 @@ struct tb_port {
int cap_tmu;
int cap_adap;
int cap_usb4;
+ struct usb4_port *usb4;
u8 port;
bool disabled;
bool bonded;
@@ -219,6 +244,24 @@ struct tb_port {
struct ida in_hopids;
struct ida out_hopids;
struct list_head list;
+ unsigned int total_credits;
+ unsigned int ctl_credits;
+ unsigned int dma_credits;
+};
+
+/**
+ * struct usb4_port - USB4 port device
+ * @dev: Device for the port
+ * @port: Pointer to the lane 0 adapter
+ * @can_offline: Does the port have necessary platform support to moved
+ * it into offline mode and back
+ * @offline: The port is currently in offline mode
+ */
+struct usb4_port {
+ struct device dev;
+ struct tb_port *port;
+ bool can_offline;
+ bool offline;
};
/**
@@ -255,6 +298,8 @@ struct tb_retimer {
* @next_hop_index: HopID of the packet when it is routed out from @out_port
* @initial_credits: Number of initial flow control credits allocated for
* the path
+ * @nfc_credits: Number of non-flow controlled buffers allocated for the
+ * @in_port.
*
* Hop configuration is always done on the IN port of a switch.
* in_port and out_port have to be on the same switch. Packets arriving on
@@ -274,6 +319,7 @@ struct tb_path_hop {
int in_counter_index;
int next_hop_index;
unsigned int initial_credits;
+ unsigned int nfc_credits;
};
/**
@@ -296,7 +342,6 @@ enum tb_path_port {
* struct tb_path - a unidirectional path between two ports
* @tb: Pointer to the domain structure
* @name: Name of the path (used for debugging)
- * @nfc_credits: Number of non flow controlled credits allocated for the path
* @ingress_shared_buffer: Shared buffering used for ingress ports on the path
* @egress_shared_buffer: Shared buffering used for egress ports on the path
* @ingress_fc_enable: Flow control for ingress ports on the path
@@ -317,7 +362,6 @@ enum tb_path_port {
struct tb_path {
struct tb *tb;
const char *name;
- int nfc_credits;
enum tb_path_port ingress_shared_buffer;
enum tb_path_port egress_shared_buffer;
enum tb_path_port ingress_fc_enable;
@@ -346,6 +390,7 @@ struct tb_path {
#define TB_WAKE_ON_USB4 BIT(2)
#define TB_WAKE_ON_USB3 BIT(3)
#define TB_WAKE_ON_PCIE BIT(4)
+#define TB_WAKE_ON_DP BIT(5)
/**
* struct tb_cm_ops - Connection manager specific operations vector
@@ -623,6 +668,7 @@ struct tb *tb_probe(struct tb_nhi *nhi);
extern struct device_type tb_domain_type;
extern struct device_type tb_retimer_type;
extern struct device_type tb_switch_type;
+extern struct device_type usb4_port_device_type;
int tb_domain_init(void);
void tb_domain_exit(void);
@@ -674,6 +720,16 @@ int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
void tb_nvm_free(struct tb_nvm *nvm);
void tb_nvm_exit(void);
+typedef int (*read_block_fn)(void *, unsigned int, void *, size_t);
+typedef int (*write_block_fn)(void *, unsigned int, const void *, size_t);
+
+int tb_nvm_read_data(unsigned int address, void *buf, size_t size,
+ unsigned int retries, read_block_fn read_block,
+ void *read_block_data);
+int tb_nvm_write_data(unsigned int address, const void *buf, size_t size,
+ unsigned int retries, write_block_fn write_next_block,
+ void *write_block_data);
+
struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
u64 route);
struct tb_switch *tb_switch_alloc_safe_mode(struct tb *tb,
@@ -853,6 +909,11 @@ void tb_port_release_out_hopid(struct tb_port *port, int hopid);
struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end,
struct tb_port *prev);
+static inline bool tb_port_use_credit_allocation(const struct tb_port *port)
+{
+ return tb_port_is_null(port) && port->sw->credit_allocation;
+}
+
/**
* tb_for_each_port_on_path() - Iterate over each port on path
* @src: Source port
@@ -870,6 +931,9 @@ int tb_port_get_link_width(struct tb_port *port);
int tb_port_state(struct tb_port *port);
int tb_port_lane_bonding_enable(struct tb_port *port);
void tb_port_lane_bonding_disable(struct tb_port *port);
+int tb_port_wait_for_link_width(struct tb_port *port, int width,
+ int timeout_msec);
+int tb_port_update_credits(struct tb_port *port);
int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap);
@@ -904,6 +968,17 @@ bool tb_path_is_invalid(struct tb_path *path);
bool tb_path_port_on_path(const struct tb_path *path,
const struct tb_port *port);
+/**
+ * tb_path_for_each_hop() - Iterate over each hop on path
+ * @path: Path whose hops to iterate
+ * @hop: Hop used as iterator
+ *
+ * Iterates over each hop on path.
+ */
+#define tb_path_for_each_hop(path, hop) \
+ for ((hop) = &(path)->hops[0]; \
+ (hop) <= &(path)->hops[(path)->path_length - 1]; (hop)++)
+
int tb_drom_read(struct tb_switch *sw);
int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid);
@@ -950,7 +1025,7 @@ void tb_xdomain_remove(struct tb_xdomain *xd);
struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
u8 depth);
-int tb_retimer_scan(struct tb_port *port);
+int tb_retimer_scan(struct tb_port *port, bool add);
void tb_retimer_remove_all(struct tb_port *port);
static inline bool tb_is_retimer(const struct device *dev)
@@ -975,10 +1050,12 @@ int usb4_switch_set_sleep(struct tb_switch *sw);
int usb4_switch_nvm_sector_size(struct tb_switch *sw);
int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
size_t size);
+int usb4_switch_nvm_set_offset(struct tb_switch *sw, unsigned int address);
int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address,
const void *buf, size_t size);
int usb4_switch_nvm_authenticate(struct tb_switch *sw);
int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status);
+int usb4_switch_credits_init(struct tb_switch *sw);
bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in);
int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in);
int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in);
@@ -986,20 +1063,27 @@ struct tb_port *usb4_switch_map_pcie_down(struct tb_switch *sw,
const struct tb_port *port);
struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw,
const struct tb_port *port);
+int usb4_switch_add_ports(struct tb_switch *sw);
+void usb4_switch_remove_ports(struct tb_switch *sw);
int usb4_port_unlock(struct tb_port *port);
int usb4_port_configure(struct tb_port *port);
void usb4_port_unconfigure(struct tb_port *port);
int usb4_port_configure_xdomain(struct tb_port *port);
void usb4_port_unconfigure_xdomain(struct tb_port *port);
+int usb4_port_router_offline(struct tb_port *port);
+int usb4_port_router_online(struct tb_port *port);
int usb4_port_enumerate_retimers(struct tb_port *port);
+int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
u8 size);
int usb4_port_retimer_write(struct tb_port *port, u8 index, u8 reg,
const void *buf, u8 size);
int usb4_port_retimer_is_last(struct tb_port *port, u8 index);
int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index);
+int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
+ unsigned int address);
int usb4_port_retimer_nvm_write(struct tb_port *port, u8 index,
unsigned int address, const void *buf,
size_t size);
@@ -1018,6 +1102,22 @@ int usb4_usb3_port_allocate_bandwidth(struct tb_port *port, int *upstream_bw,
int usb4_usb3_port_release_bandwidth(struct tb_port *port, int *upstream_bw,
int *downstream_bw);
+static inline bool tb_is_usb4_port_device(const struct device *dev)
+{
+ return dev->type == &usb4_port_device_type;
+}
+
+static inline struct usb4_port *tb_to_usb4_port_device(struct device *dev)
+{
+ if (tb_is_usb4_port_device(dev))
+ return container_of(dev, struct usb4_port, dev);
+ return NULL;
+}
+
+struct usb4_port *usb4_port_device_add(struct tb_port *port);
+void usb4_port_device_remove(struct usb4_port *usb4);
+int usb4_port_device_resume(struct usb4_port *usb4);
+
/* Keep link controller awake during update */
#define QUIRK_FORCE_POWER_LINK_CONTROLLER BIT(0)
@@ -1031,6 +1131,11 @@ bool tb_acpi_may_tunnel_usb3(void);
bool tb_acpi_may_tunnel_dp(void);
bool tb_acpi_may_tunnel_pcie(void);
bool tb_acpi_is_xdomain_allowed(void);
+
+int tb_acpi_init(void);
+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) { }
@@ -1039,6 +1144,11 @@ static inline bool tb_acpi_may_tunnel_usb3(void) { return true; }
static inline bool tb_acpi_may_tunnel_dp(void) { return true; }
static inline bool tb_acpi_may_tunnel_pcie(void) { return true; }
static inline bool tb_acpi_is_xdomain_allowed(void) { return true; }
+
+static inline int tb_acpi_init(void) { return 0; }
+static inline void tb_acpi_exit(void) { }
+static inline int tb_acpi_power_on_retimers(struct tb_port *port) { return 0; }
+static inline int tb_acpi_power_off_retimers(struct tb_port *port) { return 0; }
#endif
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index 626751e06292..484f25be2849 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -195,6 +195,7 @@ struct tb_regs_switch_header {
#define ROUTER_CS_5_SLP BIT(0)
#define ROUTER_CS_5_WOP BIT(1)
#define ROUTER_CS_5_WOU BIT(2)
+#define ROUTER_CS_5_WOD BIT(3)
#define ROUTER_CS_5_C3S BIT(23)
#define ROUTER_CS_5_PTO BIT(24)
#define ROUTER_CS_5_UTO BIT(25)
@@ -228,6 +229,7 @@ enum usb4_switch_op {
USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23,
USB4_SWITCH_OP_DROM_READ = 0x24,
USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25,
+ USB4_SWITCH_OP_BUFFER_ALLOC = 0x33,
};
/* Router TMU configuration */
@@ -458,6 +460,8 @@ struct tb_regs_hop {
#define TB_LC_SX_CTRL 0x96
#define TB_LC_SX_CTRL_WOC BIT(1)
#define TB_LC_SX_CTRL_WOD BIT(2)
+#define TB_LC_SX_CTRL_WODPC BIT(3)
+#define TB_LC_SX_CTRL_WODPD BIT(4)
#define TB_LC_SX_CTRL_WOU4 BIT(5)
#define TB_LC_SX_CTRL_WOP BIT(6)
#define TB_LC_SX_CTRL_L1C BIT(16)
diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c
index 3cc36ef639f3..eca0ef311bde 100644
--- a/drivers/thunderbolt/test.c
+++ b/drivers/thunderbolt/test.c
@@ -87,22 +87,30 @@ static struct tb_switch *alloc_host(struct kunit *test)
sw->ports[1].config.type = TB_TYPE_PORT;
sw->ports[1].config.max_in_hop_id = 19;
sw->ports[1].config.max_out_hop_id = 19;
+ sw->ports[1].total_credits = 60;
+ sw->ports[1].ctl_credits = 2;
sw->ports[1].dual_link_port = &sw->ports[2];
sw->ports[2].config.type = TB_TYPE_PORT;
sw->ports[2].config.max_in_hop_id = 19;
sw->ports[2].config.max_out_hop_id = 19;
+ sw->ports[2].total_credits = 60;
+ sw->ports[2].ctl_credits = 2;
sw->ports[2].dual_link_port = &sw->ports[1];
sw->ports[2].link_nr = 1;
sw->ports[3].config.type = TB_TYPE_PORT;
sw->ports[3].config.max_in_hop_id = 19;
sw->ports[3].config.max_out_hop_id = 19;
+ sw->ports[3].total_credits = 60;
+ sw->ports[3].ctl_credits = 2;
sw->ports[3].dual_link_port = &sw->ports[4];
sw->ports[4].config.type = TB_TYPE_PORT;
sw->ports[4].config.max_in_hop_id = 19;
sw->ports[4].config.max_out_hop_id = 19;
+ sw->ports[4].total_credits = 60;
+ sw->ports[4].ctl_credits = 2;
sw->ports[4].dual_link_port = &sw->ports[3];
sw->ports[4].link_nr = 1;
@@ -143,6 +151,25 @@ static struct tb_switch *alloc_host(struct kunit *test)
return sw;
}
+static struct tb_switch *alloc_host_usb4(struct kunit *test)
+{
+ struct tb_switch *sw;
+
+ sw = alloc_host(test);
+ if (!sw)
+ return NULL;
+
+ sw->generation = 4;
+ sw->credit_allocation = true;
+ sw->max_usb3_credits = 32;
+ sw->min_dp_aux_credits = 1;
+ sw->min_dp_main_credits = 0;
+ sw->max_pcie_credits = 64;
+ sw->max_dma_credits = 14;
+
+ return sw;
+}
+
static struct tb_switch *alloc_dev_default(struct kunit *test,
struct tb_switch *parent,
u64 route, bool bonded)
@@ -164,44 +191,60 @@ static struct tb_switch *alloc_dev_default(struct kunit *test,
sw->ports[1].config.type = TB_TYPE_PORT;
sw->ports[1].config.max_in_hop_id = 19;
sw->ports[1].config.max_out_hop_id = 19;
+ sw->ports[1].total_credits = 60;
+ sw->ports[1].ctl_credits = 2;
sw->ports[1].dual_link_port = &sw->ports[2];
sw->ports[2].config.type = TB_TYPE_PORT;
sw->ports[2].config.max_in_hop_id = 19;
sw->ports[2].config.max_out_hop_id = 19;
+ sw->ports[2].total_credits = 60;
+ sw->ports[2].ctl_credits = 2;
sw->ports[2].dual_link_port = &sw->ports[1];
sw->ports[2].link_nr = 1;
sw->ports[3].config.type = TB_TYPE_PORT;
sw->ports[3].config.max_in_hop_id = 19;
sw->ports[3].config.max_out_hop_id = 19;
+ sw->ports[3].total_credits = 60;
+ sw->ports[3].ctl_credits = 2;
sw->ports[3].dual_link_port = &sw->ports[4];
sw->ports[4].config.type = TB_TYPE_PORT;
sw->ports[4].config.max_in_hop_id = 19;
sw->ports[4].config.max_out_hop_id = 19;
+ sw->ports[4].total_credits = 60;
+ sw->ports[4].ctl_credits = 2;
sw->ports[4].dual_link_port = &sw->ports[3];
sw->ports[4].link_nr = 1;
sw->ports[5].config.type = TB_TYPE_PORT;
sw->ports[5].config.max_in_hop_id = 19;
sw->ports[5].config.max_out_hop_id = 19;
+ sw->ports[5].total_credits = 60;
+ sw->ports[5].ctl_credits = 2;
sw->ports[5].dual_link_port = &sw->ports[6];
sw->ports[6].config.type = TB_TYPE_PORT;
sw->ports[6].config.max_in_hop_id = 19;
sw->ports[6].config.max_out_hop_id = 19;
+ sw->ports[6].total_credits = 60;
+ sw->ports[6].ctl_credits = 2;
sw->ports[6].dual_link_port = &sw->ports[5];
sw->ports[6].link_nr = 1;
sw->ports[7].config.type = TB_TYPE_PORT;
sw->ports[7].config.max_in_hop_id = 19;
sw->ports[7].config.max_out_hop_id = 19;
+ sw->ports[7].total_credits = 60;
+ sw->ports[7].ctl_credits = 2;
sw->ports[7].dual_link_port = &sw->ports[8];
sw->ports[8].config.type = TB_TYPE_PORT;
sw->ports[8].config.max_in_hop_id = 19;
sw->ports[8].config.max_out_hop_id = 19;
+ sw->ports[8].total_credits = 60;
+ sw->ports[8].ctl_credits = 2;
sw->ports[8].dual_link_port = &sw->ports[7];
sw->ports[8].link_nr = 1;
@@ -260,14 +303,18 @@ static struct tb_switch *alloc_dev_default(struct kunit *test,
if (port->dual_link_port && upstream_port->dual_link_port) {
port->dual_link_port->remote = upstream_port->dual_link_port;
upstream_port->dual_link_port->remote = port->dual_link_port;
- }
- if (bonded) {
- /* Bonding is used */
- port->bonded = true;
- port->dual_link_port->bonded = true;
- upstream_port->bonded = true;
- upstream_port->dual_link_port->bonded = true;
+ if (bonded) {
+ /* Bonding is used */
+ port->bonded = true;
+ port->total_credits *= 2;
+ port->dual_link_port->bonded = true;
+ port->dual_link_port->total_credits = 0;
+ upstream_port->bonded = true;
+ upstream_port->total_credits *= 2;
+ upstream_port->dual_link_port->bonded = true;
+ upstream_port->dual_link_port->total_credits = 0;
+ }
}
return sw;
@@ -294,6 +341,27 @@ static struct tb_switch *alloc_dev_with_dpin(struct kunit *test,
return sw;
}
+static struct tb_switch *alloc_dev_usb4(struct kunit *test,
+ struct tb_switch *parent,
+ u64 route, bool bonded)
+{
+ struct tb_switch *sw;
+
+ sw = alloc_dev_default(test, parent, route, bonded);
+ if (!sw)
+ return NULL;
+
+ sw->generation = 4;
+ sw->credit_allocation = true;
+ sw->max_usb3_credits = 14;
+ sw->min_dp_aux_credits = 1;
+ sw->min_dp_main_credits = 18;
+ sw->max_pcie_credits = 32;
+ sw->max_dma_credits = 14;
+
+ return sw;
+}
+
static void tb_test_path_basic(struct kunit *test)
{
struct tb_port *src_port, *dst_port, *p;
@@ -1829,6 +1897,475 @@ static void tb_test_tunnel_dma_match(struct kunit *test)
tb_tunnel_free(tunnel);
}
+static void tb_test_credit_alloc_legacy_not_bonded(struct kunit *test)
+{
+ struct tb_switch *host, *dev;
+ struct tb_port *up, *down;
+ struct tb_tunnel *tunnel;
+ struct tb_path *path;
+
+ host = alloc_host(test);
+ dev = alloc_dev_default(test, host, 0x1, false);
+
+ down = &host->ports[8];
+ up = &dev->ports[9];
+ tunnel = tb_tunnel_alloc_pci(NULL, up, down);
+ KUNIT_ASSERT_TRUE(test, tunnel != NULL);
+ KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
+
+ path = tunnel->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 16U);
+
+ path = tunnel->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 16U);
+
+ tb_tunnel_free(tunnel);
+}
+
+static void tb_test_credit_alloc_legacy_bonded(struct kunit *test)
+{
+ struct tb_switch *host, *dev;
+ struct tb_port *up, *down;
+ struct tb_tunnel *tunnel;
+ struct tb_path *path;
+
+ host = alloc_host(test);
+ dev = alloc_dev_default(test, host, 0x1, true);
+
+ down = &host->ports[8];
+ up = &dev->ports[9];
+ tunnel = tb_tunnel_alloc_pci(NULL, up, down);
+ KUNIT_ASSERT_TRUE(test, tunnel != NULL);
+ KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
+
+ path = tunnel->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
+
+ path = tunnel->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
+
+ tb_tunnel_free(tunnel);
+}
+
+static void tb_test_credit_alloc_pcie(struct kunit *test)
+{
+ struct tb_switch *host, *dev;
+ struct tb_port *up, *down;
+ struct tb_tunnel *tunnel;
+ struct tb_path *path;
+
+ host = alloc_host_usb4(test);
+ dev = alloc_dev_usb4(test, host, 0x1, true);
+
+ down = &host->ports[8];
+ up = &dev->ports[9];
+ tunnel = tb_tunnel_alloc_pci(NULL, up, down);
+ KUNIT_ASSERT_TRUE(test, tunnel != NULL);
+ KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
+
+ path = tunnel->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
+
+ path = tunnel->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
+
+ tb_tunnel_free(tunnel);
+}
+
+static void tb_test_credit_alloc_dp(struct kunit *test)
+{
+ struct tb_switch *host, *dev;
+ struct tb_port *in, *out;
+ struct tb_tunnel *tunnel;
+ struct tb_path *path;
+
+ host = alloc_host_usb4(test);
+ dev = alloc_dev_usb4(test, host, 0x1, true);
+
+ in = &host->ports[5];
+ out = &dev->ports[14];
+
+ tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+ KUNIT_ASSERT_TRUE(test, tunnel != NULL);
+ KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3);
+
+ /* Video (main) path */
+ path = tunnel->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 12U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 18U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 0U);
+
+ /* AUX TX */
+ path = tunnel->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
+
+ /* AUX RX */
+ path = tunnel->paths[2];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
+
+ tb_tunnel_free(tunnel);
+}
+
+static void tb_test_credit_alloc_usb3(struct kunit *test)
+{
+ struct tb_switch *host, *dev;
+ struct tb_port *up, *down;
+ struct tb_tunnel *tunnel;
+ struct tb_path *path;
+
+ host = alloc_host_usb4(test);
+ dev = alloc_dev_usb4(test, host, 0x1, true);
+
+ down = &host->ports[12];
+ up = &dev->ports[16];
+ tunnel = tb_tunnel_alloc_usb3(NULL, up, down, 0, 0);
+ KUNIT_ASSERT_TRUE(test, tunnel != NULL);
+ KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
+
+ path = tunnel->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
+
+ path = tunnel->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
+
+ tb_tunnel_free(tunnel);
+}
+
+static void tb_test_credit_alloc_dma(struct kunit *test)
+{
+ struct tb_switch *host, *dev;
+ struct tb_port *nhi, *port;
+ struct tb_tunnel *tunnel;
+ struct tb_path *path;
+
+ host = alloc_host_usb4(test);
+ dev = alloc_dev_usb4(test, host, 0x1, true);
+
+ nhi = &host->ports[7];
+ port = &dev->ports[3];
+
+ tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1);
+ KUNIT_ASSERT_TRUE(test, tunnel != NULL);
+ KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
+
+ /* DMA RX */
+ path = tunnel->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
+
+ /* DMA TX */
+ path = tunnel->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
+
+ tb_tunnel_free(tunnel);
+}
+
+static void tb_test_credit_alloc_dma_multiple(struct kunit *test)
+{
+ struct tb_tunnel *tunnel1, *tunnel2, *tunnel3;
+ struct tb_switch *host, *dev;
+ struct tb_port *nhi, *port;
+ struct tb_path *path;
+
+ host = alloc_host_usb4(test);
+ dev = alloc_dev_usb4(test, host, 0x1, true);
+
+ nhi = &host->ports[7];
+ port = &dev->ports[3];
+
+ /*
+ * Create three DMA tunnels through the same ports. With the
+ * default buffers we should be able to create two and the last
+ * one fails.
+ *
+ * For default host we have following buffers for DMA:
+ *
+ * 120 - (2 + 2 * (1 + 0) + 32 + 64 + spare) = 20
+ *
+ * For device we have following:
+ *
+ * 120 - (2 + 2 * (1 + 18) + 14 + 32 + spare) = 34
+ *
+ * spare = 14 + 1 = 15
+ *
+ * So on host the first tunnel gets 14 and the second gets the
+ * remaining 1 and then we run out of buffers.
+ */
+ tunnel1 = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1);
+ KUNIT_ASSERT_TRUE(test, tunnel1 != NULL);
+ KUNIT_ASSERT_EQ(test, tunnel1->npaths, (size_t)2);
+
+ path = tunnel1->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
+
+ path = tunnel1->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
+
+ tunnel2 = tb_tunnel_alloc_dma(NULL, nhi, port, 9, 2, 9, 2);
+ KUNIT_ASSERT_TRUE(test, tunnel2 != NULL);
+ KUNIT_ASSERT_EQ(test, tunnel2->npaths, (size_t)2);
+
+ path = tunnel2->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
+
+ path = tunnel2->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
+
+ tunnel3 = tb_tunnel_alloc_dma(NULL, nhi, port, 10, 3, 10, 3);
+ KUNIT_ASSERT_TRUE(test, tunnel3 == NULL);
+
+ /*
+ * Release the first DMA tunnel. That should make 14 buffers
+ * available for the next tunnel.
+ */
+ tb_tunnel_free(tunnel1);
+
+ tunnel3 = tb_tunnel_alloc_dma(NULL, nhi, port, 10, 3, 10, 3);
+ KUNIT_ASSERT_TRUE(test, tunnel3 != NULL);
+
+ path = tunnel3->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
+
+ path = tunnel3->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
+
+ tb_tunnel_free(tunnel3);
+ tb_tunnel_free(tunnel2);
+}
+
+static void tb_test_credit_alloc_all(struct kunit *test)
+{
+ struct tb_port *up, *down, *in, *out, *nhi, *port;
+ struct tb_tunnel *pcie_tunnel, *dp_tunnel1, *dp_tunnel2, *usb3_tunnel;
+ struct tb_tunnel *dma_tunnel1, *dma_tunnel2;
+ struct tb_switch *host, *dev;
+ struct tb_path *path;
+
+ /*
+ * Create PCIe, 2 x DP, USB 3.x and two DMA tunnels from host to
+ * device. Expectation is that all these can be established with
+ * the default credit allocation found in Intel hardware.
+ */
+
+ host = alloc_host_usb4(test);
+ dev = alloc_dev_usb4(test, host, 0x1, true);
+
+ down = &host->ports[8];
+ up = &dev->ports[9];
+ pcie_tunnel = tb_tunnel_alloc_pci(NULL, up, down);
+ KUNIT_ASSERT_TRUE(test, pcie_tunnel != NULL);
+ KUNIT_ASSERT_EQ(test, pcie_tunnel->npaths, (size_t)2);
+
+ path = pcie_tunnel->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
+
+ path = pcie_tunnel->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
+
+ in = &host->ports[5];
+ out = &dev->ports[13];
+
+ dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+ KUNIT_ASSERT_TRUE(test, dp_tunnel1 != NULL);
+ KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3);
+
+ path = dp_tunnel1->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 12U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 18U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 0U);
+
+ path = dp_tunnel1->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
+
+ path = dp_tunnel1->paths[2];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
+
+ in = &host->ports[6];
+ out = &dev->ports[14];
+
+ dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+ KUNIT_ASSERT_TRUE(test, dp_tunnel2 != NULL);
+ KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3);
+
+ path = dp_tunnel2->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 12U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 18U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 0U);
+
+ path = dp_tunnel2->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
+
+ path = dp_tunnel2->paths[2];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
+
+ down = &host->ports[12];
+ up = &dev->ports[16];
+ usb3_tunnel = tb_tunnel_alloc_usb3(NULL, up, down, 0, 0);
+ KUNIT_ASSERT_TRUE(test, usb3_tunnel != NULL);
+ KUNIT_ASSERT_EQ(test, usb3_tunnel->npaths, (size_t)2);
+
+ path = usb3_tunnel->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
+
+ path = usb3_tunnel->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
+
+ nhi = &host->ports[7];
+ port = &dev->ports[3];
+
+ dma_tunnel1 = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1);
+ KUNIT_ASSERT_TRUE(test, dma_tunnel1 != NULL);
+ KUNIT_ASSERT_EQ(test, dma_tunnel1->npaths, (size_t)2);
+
+ path = dma_tunnel1->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
+
+ path = dma_tunnel1->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
+
+ dma_tunnel2 = tb_tunnel_alloc_dma(NULL, nhi, port, 9, 2, 9, 2);
+ KUNIT_ASSERT_TRUE(test, dma_tunnel2 != NULL);
+ KUNIT_ASSERT_EQ(test, dma_tunnel2->npaths, (size_t)2);
+
+ path = dma_tunnel2->paths[0];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
+
+ path = dma_tunnel2->paths[1];
+ KUNIT_ASSERT_EQ(test, path->path_length, 2);
+ KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+ KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
+
+ tb_tunnel_free(dma_tunnel2);
+ tb_tunnel_free(dma_tunnel1);
+ tb_tunnel_free(usb3_tunnel);
+ tb_tunnel_free(dp_tunnel2);
+ tb_tunnel_free(dp_tunnel1);
+ tb_tunnel_free(pcie_tunnel);
+}
+
static const u32 root_directory[] = {
0x55584401, /* "UXD" v1 */
0x00000018, /* Root directory length */
@@ -2105,6 +2642,14 @@ static struct kunit_case tb_test_cases[] = {
KUNIT_CASE(tb_test_tunnel_dma_tx),
KUNIT_CASE(tb_test_tunnel_dma_chain),
KUNIT_CASE(tb_test_tunnel_dma_match),
+ KUNIT_CASE(tb_test_credit_alloc_legacy_not_bonded),
+ KUNIT_CASE(tb_test_credit_alloc_legacy_bonded),
+ KUNIT_CASE(tb_test_credit_alloc_pcie),
+ KUNIT_CASE(tb_test_credit_alloc_dp),
+ KUNIT_CASE(tb_test_credit_alloc_usb3),
+ KUNIT_CASE(tb_test_credit_alloc_dma),
+ KUNIT_CASE(tb_test_credit_alloc_dma_multiple),
+ KUNIT_CASE(tb_test_credit_alloc_all),
KUNIT_CASE(tb_test_property_parse),
KUNIT_CASE(tb_test_property_format),
KUNIT_CASE(tb_test_property_copy),
diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index e1979bed7146..bb5cc480fc9a 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -34,6 +34,16 @@
#define TB_DP_AUX_PATH_OUT 1
#define TB_DP_AUX_PATH_IN 2
+/* Minimum number of credits needed for PCIe path */
+#define TB_MIN_PCIE_CREDITS 6U
+/*
+ * Number of credits we try to allocate for each DMA path if not limited
+ * by the host router baMaxHI.
+ */
+#define TB_DMA_CREDITS 14U
+/* Minimum number of credits for DMA path */
+#define TB_MIN_DMA_CREDITS 1U
+
static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" };
#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
@@ -57,6 +67,55 @@ static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" };
#define tb_tunnel_dbg(tunnel, fmt, arg...) \
__TB_TUNNEL_PRINT(tb_dbg, tunnel, fmt, ##arg)
+static inline unsigned int tb_usable_credits(const struct tb_port *port)
+{
+ return port->total_credits - port->ctl_credits;
+}
+
+/**
+ * tb_available_credits() - Available credits for PCIe and DMA
+ * @port: Lane adapter to check
+ * @max_dp_streams: If non-%NULL stores maximum number of simultaneous DP
+ * streams possible through this lane adapter
+ */
+static unsigned int tb_available_credits(const struct tb_port *port,
+ size_t *max_dp_streams)
+{
+ const struct tb_switch *sw = port->sw;
+ int credits, usb3, pcie, spare;
+ size_t ndp;
+
+ usb3 = tb_acpi_may_tunnel_usb3() ? sw->max_usb3_credits : 0;
+ pcie = tb_acpi_may_tunnel_pcie() ? sw->max_pcie_credits : 0;
+
+ if (tb_acpi_is_xdomain_allowed()) {
+ spare = min_not_zero(sw->max_dma_credits, TB_DMA_CREDITS);
+ /* Add some credits for potential second DMA tunnel */
+ spare += TB_MIN_DMA_CREDITS;
+ } else {
+ spare = 0;
+ }
+
+ credits = tb_usable_credits(port);
+ if (tb_acpi_may_tunnel_dp()) {
+ /*
+ * Maximum number of DP streams possible through the
+ * lane adapter.
+ */
+ ndp = (credits - (usb3 + pcie + spare)) /
+ (sw->min_dp_aux_credits + sw->min_dp_main_credits);
+ } else {
+ ndp = 0;
+ }
+ credits -= ndp * (sw->min_dp_aux_credits + sw->min_dp_main_credits);
+ credits -= usb3;
+
+ if (max_dp_streams)
+ *max_dp_streams = ndp;
+
+ return credits > 0 ? credits : 0;
+}
+
static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths,
enum tb_tunnel_type type)
{
@@ -94,24 +153,37 @@ static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate)
return 0;
}
-static int tb_initial_credits(const struct tb_switch *sw)
+static int tb_pci_init_credits(struct tb_path_hop *hop)
{
- /* If the path is complete sw is not NULL */
- if (sw) {
- /* More credits for faster link */
- switch (sw->link_speed * sw->link_width) {
- case 40:
- return 32;
- case 20:
- return 24;
- }
+ struct tb_port *port = hop->in_port;
+ struct tb_switch *sw = port->sw;
+ unsigned int credits;
+
+ if (tb_port_use_credit_allocation(port)) {
+ unsigned int available;
+
+ available = tb_available_credits(port, NULL);
+ credits = min(sw->max_pcie_credits, available);
+
+ if (credits < TB_MIN_PCIE_CREDITS)
+ return -ENOSPC;
+
+ credits = max(TB_MIN_PCIE_CREDITS, credits);
+ } else {
+ if (tb_port_is_null(port))
+ credits = port->bonded ? 32 : 16;
+ else
+ credits = 7;
}
- return 16;
+ hop->initial_credits = credits;
+ return 0;
}
-static void tb_pci_init_path(struct tb_path *path)
+static int tb_pci_init_path(struct tb_path *path)
{
+ struct tb_path_hop *hop;
+
path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
path->egress_shared_buffer = TB_PATH_NONE;
path->ingress_fc_enable = TB_PATH_ALL;
@@ -119,11 +191,16 @@ static void tb_pci_init_path(struct tb_path *path)
path->priority = 3;
path->weight = 1;
path->drop_packages = 0;
- path->nfc_credits = 0;
- path->hops[0].initial_credits = 7;
- if (path->path_length > 1)
- path->hops[1].initial_credits =
- tb_initial_credits(path->hops[1].in_port->sw);
+
+ tb_path_for_each_hop(path, hop) {
+ int ret;
+
+ ret = tb_pci_init_credits(hop);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
/**
@@ -163,14 +240,16 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down)
goto err_free;
}
tunnel->paths[TB_PCI_PATH_UP] = path;
- tb_pci_init_path(tunnel->paths[TB_PCI_PATH_UP]);
+ if (tb_pci_init_path(tunnel->paths[TB_PCI_PATH_UP]))
+ goto err_free;
path = tb_path_discover(tunnel->dst_port, -1, down, TB_PCI_HOPID, NULL,
"PCIe Down");
if (!path)
goto err_deactivate;
tunnel->paths[TB_PCI_PATH_DOWN] = path;
- tb_pci_init_path(tunnel->paths[TB_PCI_PATH_DOWN]);
+ if (tb_pci_init_path(tunnel->paths[TB_PCI_PATH_DOWN]))
+ goto err_deactivate;
/* Validate that the tunnel is complete */
if (!tb_port_is_pcie_up(tunnel->dst_port)) {
@@ -228,23 +307,25 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
path = tb_path_alloc(tb, down, TB_PCI_HOPID, up, TB_PCI_HOPID, 0,
"PCIe Down");
- if (!path) {
- tb_tunnel_free(tunnel);
- return NULL;
- }
- tb_pci_init_path(path);
+ if (!path)
+ goto err_free;
tunnel->paths[TB_PCI_PATH_DOWN] = path;
+ if (tb_pci_init_path(path))
+ goto err_free;
path = tb_path_alloc(tb, up, TB_PCI_HOPID, down, TB_PCI_HOPID, 0,
"PCIe Up");
- if (!path) {
- tb_tunnel_free(tunnel);
- return NULL;
- }
- tb_pci_init_path(path);
+ if (!path)
+ goto err_free;
tunnel->paths[TB_PCI_PATH_UP] = path;
+ if (tb_pci_init_path(path))
+ goto err_free;
return tunnel;
+
+err_free:
+ tb_tunnel_free(tunnel);
+ return NULL;
}
static bool tb_dp_is_usb4(const struct tb_switch *sw)
@@ -599,9 +680,20 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
return 0;
}
+static void tb_dp_init_aux_credits(struct tb_path_hop *hop)
+{
+ struct tb_port *port = hop->in_port;
+ struct tb_switch *sw = port->sw;
+
+ if (tb_port_use_credit_allocation(port))
+ hop->initial_credits = sw->min_dp_aux_credits;
+ else
+ hop->initial_credits = 1;
+}
+
static void tb_dp_init_aux_path(struct tb_path *path)
{
- int i;
+ struct tb_path_hop *hop;
path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
path->egress_shared_buffer = TB_PATH_NONE;
@@ -610,13 +702,42 @@ static void tb_dp_init_aux_path(struct tb_path *path)
path->priority = 2;
path->weight = 1;
- for (i = 0; i < path->path_length; i++)
- path->hops[i].initial_credits = 1;
+ tb_path_for_each_hop(path, hop)
+ tb_dp_init_aux_credits(hop);
}
-static void tb_dp_init_video_path(struct tb_path *path, bool discover)
+static int tb_dp_init_video_credits(struct tb_path_hop *hop)
{
- u32 nfc_credits = path->hops[0].in_port->config.nfc_credits;
+ struct tb_port *port = hop->in_port;
+ struct tb_switch *sw = port->sw;
+
+ if (tb_port_use_credit_allocation(port)) {
+ unsigned int nfc_credits;
+ size_t max_dp_streams;
+
+ tb_available_credits(port, &max_dp_streams);
+ /*
+ * Read the number of currently allocated NFC credits
+ * from the lane adapter. Since we only use them for DP
+ * tunneling we can use that to figure out how many DP
+ * tunnels already go through the lane adapter.
+ */
+ nfc_credits = port->config.nfc_credits &
+ ADP_CS_4_NFC_BUFFERS_MASK;
+ if (nfc_credits / sw->min_dp_main_credits > max_dp_streams)
+ return -ENOSPC;
+
+ hop->nfc_credits = sw->min_dp_main_credits;
+ } else {
+ hop->nfc_credits = min(port->total_credits - 2, 12U);
+ }
+
+ return 0;
+}
+
+static int tb_dp_init_video_path(struct tb_path *path)
+{
+ struct tb_path_hop *hop;
path->egress_fc_enable = TB_PATH_NONE;
path->egress_shared_buffer = TB_PATH_NONE;
@@ -625,16 +746,15 @@ static void tb_dp_init_video_path(struct tb_path *path, bool discover)
path->priority = 1;
path->weight = 1;
- if (discover) {
- path->nfc_credits = nfc_credits & ADP_CS_4_NFC_BUFFERS_MASK;
- } else {
- u32 max_credits;
+ tb_path_for_each_hop(path, hop) {
+ int ret;
- max_credits = (nfc_credits & ADP_CS_4_TOTAL_BUFFERS_MASK) >>
- ADP_CS_4_TOTAL_BUFFERS_SHIFT;
- /* Leave some credits for AUX path */
- path->nfc_credits = min(max_credits - 2, 12U);
+ ret = tb_dp_init_video_credits(hop);
+ if (ret)
+ return ret;
}
+
+ return 0;
}
/**
@@ -674,7 +794,8 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in)
goto err_free;
}
tunnel->paths[TB_DP_VIDEO_PATH_OUT] = path;
- tb_dp_init_video_path(tunnel->paths[TB_DP_VIDEO_PATH_OUT], true);
+ if (tb_dp_init_video_path(tunnel->paths[TB_DP_VIDEO_PATH_OUT]))
+ goto err_free;
path = tb_path_discover(in, TB_DP_AUX_TX_HOPID, NULL, -1, NULL, "AUX TX");
if (!path)
@@ -761,7 +882,7 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
1, "Video");
if (!path)
goto err_free;
- tb_dp_init_video_path(path, false);
+ tb_dp_init_video_path(path);
paths[TB_DP_VIDEO_PATH_OUT] = path;
path = tb_path_alloc(tb, in, TB_DP_AUX_TX_HOPID, out,
@@ -785,20 +906,92 @@ err_free:
return NULL;
}
-static u32 tb_dma_credits(struct tb_port *nhi)
+static unsigned int tb_dma_available_credits(const struct tb_port *port)
{
- u32 max_credits;
+ const struct tb_switch *sw = port->sw;
+ int credits;
- max_credits = (nhi->config.nfc_credits & ADP_CS_4_TOTAL_BUFFERS_MASK) >>
- ADP_CS_4_TOTAL_BUFFERS_SHIFT;
- return min(max_credits, 13U);
+ credits = tb_available_credits(port, NULL);
+ if (tb_acpi_may_tunnel_pcie())
+ credits -= sw->max_pcie_credits;
+ credits -= port->dma_credits;
+
+ return credits > 0 ? credits : 0;
}
-static void tb_dma_init_path(struct tb_path *path, unsigned int efc, u32 credits)
+static int tb_dma_reserve_credits(struct tb_path_hop *hop, unsigned int credits)
{
- int i;
+ struct tb_port *port = hop->in_port;
- path->egress_fc_enable = efc;
+ if (tb_port_use_credit_allocation(port)) {
+ unsigned int available = tb_dma_available_credits(port);
+
+ /*
+ * Need to have at least TB_MIN_DMA_CREDITS, otherwise
+ * DMA path cannot be established.
+ */
+ if (available < TB_MIN_DMA_CREDITS)
+ return -ENOSPC;
+
+ while (credits > available)
+ credits--;
+
+ tb_port_dbg(port, "reserving %u credits for DMA path\n",
+ credits);
+
+ port->dma_credits += credits;
+ } else {
+ if (tb_port_is_null(port))
+ credits = port->bonded ? 14 : 6;
+ else
+ credits = min(port->total_credits, credits);
+ }
+
+ hop->initial_credits = credits;
+ return 0;
+}
+
+/* Path from lane adapter to NHI */
+static int tb_dma_init_rx_path(struct tb_path *path, unsigned int credits)
+{
+ struct tb_path_hop *hop;
+ unsigned int i, tmp;
+
+ path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
+ path->ingress_fc_enable = TB_PATH_ALL;
+ path->egress_shared_buffer = TB_PATH_NONE;
+ path->ingress_shared_buffer = TB_PATH_NONE;
+ path->priority = 5;
+ path->weight = 1;
+ path->clear_fc = true;
+
+ /*
+ * First lane adapter is the one connected to the remote host.
+ * We don't tunnel other traffic over this link so can use all
+ * the credits (except the ones reserved for control traffic).
+ */
+ hop = &path->hops[0];
+ tmp = min(tb_usable_credits(hop->in_port), credits);
+ hop->initial_credits = tmp;
+ hop->in_port->dma_credits += tmp;
+
+ for (i = 1; i < path->path_length; i++) {
+ int ret;
+
+ ret = tb_dma_reserve_credits(&path->hops[i], credits);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Path from NHI to lane adapter */
+static int tb_dma_init_tx_path(struct tb_path *path, unsigned int credits)
+{
+ struct tb_path_hop *hop;
+
+ path->egress_fc_enable = TB_PATH_ALL;
path->ingress_fc_enable = TB_PATH_ALL;
path->egress_shared_buffer = TB_PATH_NONE;
path->ingress_shared_buffer = TB_PATH_NONE;
@@ -806,8 +999,46 @@ static void tb_dma_init_path(struct tb_path *path, unsigned int efc, u32 credits
path->weight = 1;
path->clear_fc = true;
- for (i = 0; i < path->path_length; i++)
- path->hops[i].initial_credits = credits;
+ tb_path_for_each_hop(path, hop) {
+ int ret;
+
+ ret = tb_dma_reserve_credits(hop, credits);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void tb_dma_release_credits(struct tb_path_hop *hop)
+{
+ struct tb_port *port = hop->in_port;
+
+ if (tb_port_use_credit_allocation(port)) {
+ port->dma_credits -= hop->initial_credits;
+
+ tb_port_dbg(port, "released %u DMA path credits\n",
+ hop->initial_credits);
+ }
+}
+
+static void tb_dma_deinit_path(struct tb_path *path)
+{
+ struct tb_path_hop *hop;
+
+ tb_path_for_each_hop(path, hop)
+ tb_dma_release_credits(hop);
+}
+
+static void tb_dma_deinit(struct tb_tunnel *tunnel)
+{
+ int i;
+
+ for (i = 0; i < tunnel->npaths; i++) {
+ if (!tunnel->paths[i])
+ continue;
+ tb_dma_deinit_path(tunnel->paths[i]);
+ }
}
/**
@@ -832,7 +1063,7 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
struct tb_tunnel *tunnel;
size_t npaths = 0, i = 0;
struct tb_path *path;
- u32 credits;
+ int credits;
if (receive_ring > 0)
npaths++;
@@ -848,32 +1079,39 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
tunnel->src_port = nhi;
tunnel->dst_port = dst;
+ tunnel->deinit = tb_dma_deinit;
- credits = tb_dma_credits(nhi);
+ credits = min_not_zero(TB_DMA_CREDITS, nhi->sw->max_dma_credits);
if (receive_ring > 0) {
path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0,
"DMA RX");
- if (!path) {
- tb_tunnel_free(tunnel);
- return NULL;
- }
- tb_dma_init_path(path, TB_PATH_SOURCE | TB_PATH_INTERNAL, credits);
+ if (!path)
+ goto err_free;
tunnel->paths[i++] = path;
+ if (tb_dma_init_rx_path(path, credits)) {
+ tb_tunnel_dbg(tunnel, "not enough buffers for RX path\n");
+ goto err_free;
+ }
}
if (transmit_ring > 0) {
path = tb_path_alloc(tb, nhi, transmit_ring, dst, transmit_path, 0,
"DMA TX");
- if (!path) {
- tb_tunnel_free(tunnel);
- return NULL;
- }
- tb_dma_init_path(path, TB_PATH_ALL, credits);
+ if (!path)
+ goto err_free;
tunnel->paths[i++] = path;
+ if (tb_dma_init_tx_path(path, credits)) {
+ tb_tunnel_dbg(tunnel, "not enough buffers for TX path\n");
+ goto err_free;
+ }
}
return tunnel;
+
+err_free:
+ tb_tunnel_free(tunnel);
+ return NULL;
}
/**
@@ -1067,8 +1305,28 @@ static void tb_usb3_reclaim_available_bandwidth(struct tb_tunnel *tunnel,
tunnel->allocated_up, tunnel->allocated_down);
}
+static void tb_usb3_init_credits(struct tb_path_hop *hop)
+{
+ struct tb_port *port = hop->in_port;
+ struct tb_switch *sw = port->sw;
+ unsigned int credits;
+
+ if (tb_port_use_credit_allocation(port)) {
+ credits = sw->max_usb3_credits;
+ } else {
+ if (tb_port_is_null(port))
+ credits = port->bonded ? 32 : 16;
+ else
+ credits = 7;
+ }
+
+ hop->initial_credits = credits;
+}
+
static void tb_usb3_init_path(struct tb_path *path)
{
+ struct tb_path_hop *hop;
+
path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
path->egress_shared_buffer = TB_PATH_NONE;
path->ingress_fc_enable = TB_PATH_ALL;
@@ -1076,11 +1334,9 @@ static void tb_usb3_init_path(struct tb_path *path)
path->priority = 3;
path->weight = 3;
path->drop_packages = 0;
- path->nfc_credits = 0;
- path->hops[0].initial_credits = 7;
- if (path->path_length > 1)
- path->hops[1].initial_credits =
- tb_initial_credits(path->hops[1].in_port->sw);
+
+ tb_path_for_each_hop(path, hop)
+ tb_usb3_init_credits(hop);
}
/**
@@ -1280,6 +1536,9 @@ void tb_tunnel_free(struct tb_tunnel *tunnel)
if (!tunnel)
return;
+ if (tunnel->deinit)
+ tunnel->deinit(tunnel);
+
for (i = 0; i < tunnel->npaths; i++) {
if (tunnel->paths[i])
tb_path_free(tunnel->paths[i]);
diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h
index a66994fb4e60..eea14e24f7e0 100644
--- a/drivers/thunderbolt/tunnel.h
+++ b/drivers/thunderbolt/tunnel.h
@@ -27,6 +27,7 @@ enum tb_tunnel_type {
* @paths: All paths required by the tunnel
* @npaths: Number of paths in @paths
* @init: Optional tunnel specific initialization
+ * @deinit: Optional tunnel specific de-initialization
* @activate: Optional tunnel specific activation/deactivation
* @consumed_bandwidth: Return how much bandwidth the tunnel consumes
* @release_unused_bandwidth: Release all unused bandwidth
@@ -47,6 +48,7 @@ struct tb_tunnel {
struct tb_path **paths;
size_t npaths;
int (*init)(struct tb_tunnel *tunnel);
+ void (*deinit)(struct tb_tunnel *tunnel);
int (*activate)(struct tb_tunnel *tunnel, bool activate);
int (*consumed_bandwidth)(struct tb_tunnel *tunnel, int *consumed_up,
int *consumed_down);
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index 671d72af8ba1..ceddbe7e9f93 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -13,7 +13,6 @@
#include "sb_regs.h"
#include "tb.h"
-#define USB4_DATA_DWORDS 16
#define USB4_DATA_RETRIES 3
enum usb4_sb_target {
@@ -37,8 +36,19 @@ enum usb4_sb_target {
#define USB4_NVM_SECTOR_SIZE_MASK GENMASK(23, 0)
-typedef int (*read_block_fn)(void *, unsigned int, void *, size_t);
-typedef int (*write_block_fn)(void *, const void *, size_t);
+#define USB4_BA_LENGTH_MASK GENMASK(7, 0)
+#define USB4_BA_INDEX_MASK GENMASK(15, 0)
+
+enum usb4_ba_index {
+ USB4_BA_MAX_USB3 = 0x1,
+ USB4_BA_MIN_DP_AUX = 0x2,
+ USB4_BA_MIN_DP_MAIN = 0x3,
+ USB4_BA_MAX_PCIE = 0x4,
+ USB4_BA_MAX_HI = 0x5,
+};
+
+#define USB4_BA_VALUE_MASK GENMASK(31, 16)
+#define USB4_BA_VALUE_SHIFT 16
static int usb4_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
u32 value, int timeout_msec)
@@ -62,76 +72,6 @@ static int usb4_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
return -ETIMEDOUT;
}
-static int usb4_do_read_data(u16 address, void *buf, size_t size,
- read_block_fn read_block, void *read_block_data)
-{
- unsigned int retries = USB4_DATA_RETRIES;
- unsigned int offset;
-
- do {
- unsigned int dwaddress, dwords;
- u8 data[USB4_DATA_DWORDS * 4];
- size_t nbytes;
- int ret;
-
- offset = address & 3;
- nbytes = min_t(size_t, size + offset, USB4_DATA_DWORDS * 4);
-
- dwaddress = address / 4;
- dwords = ALIGN(nbytes, 4) / 4;
-
- ret = read_block(read_block_data, dwaddress, data, dwords);
- if (ret) {
- if (ret != -ENODEV && retries--)
- continue;
- return ret;
- }
-
- nbytes -= offset;
- memcpy(buf, data + offset, nbytes);
-
- size -= nbytes;
- address += nbytes;
- buf += nbytes;
- } while (size > 0);
-
- return 0;
-}
-
-static int usb4_do_write_data(unsigned int address, const void *buf, size_t size,
- write_block_fn write_next_block, void *write_block_data)
-{
- unsigned int retries = USB4_DATA_RETRIES;
- unsigned int offset;
-
- offset = address & 3;
- address = address & ~3;
-
- do {
- u32 nbytes = min_t(u32, size, USB4_DATA_DWORDS * 4);
- u8 data[USB4_DATA_DWORDS * 4];
- int ret;
-
- memcpy(data + offset, buf, nbytes);
-
- ret = write_next_block(write_block_data, data, nbytes / 4);
- if (ret) {
- if (ret == -ETIMEDOUT) {
- if (retries--)
- continue;
- ret = -EIO;
- }
- return ret;
- }
-
- size -= nbytes;
- address += nbytes;
- buf += nbytes;
- } while (size > 0);
-
- return 0;
-}
-
static int usb4_native_switch_op(struct tb_switch *sw, u16 opcode,
u32 *metadata, u8 *status,
const void *tx_data, size_t tx_dwords,
@@ -193,7 +133,7 @@ static int __usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata,
{
const struct tb_cm_ops *cm_ops = sw->tb->cm_ops;
- if (tx_dwords > USB4_DATA_DWORDS || rx_dwords > USB4_DATA_DWORDS)
+ if (tx_dwords > NVM_DATA_DWORDS || rx_dwords > NVM_DATA_DWORDS)
return -EINVAL;
/*
@@ -320,7 +260,7 @@ int usb4_switch_setup(struct tb_switch *sw)
parent = tb_switch_parent(sw);
downstream_port = tb_port_at(tb_route(sw), parent);
sw->link_usb4 = link_is_usb4(downstream_port);
- tb_sw_dbg(sw, "link: %s\n", sw->link_usb4 ? "USB4" : "TBT3");
+ tb_sw_dbg(sw, "link: %s\n", sw->link_usb4 ? "USB4" : "TBT");
xhci = val & ROUTER_CS_6_HCI;
tbt3 = !(val & ROUTER_CS_6_TNS);
@@ -414,8 +354,8 @@ static int usb4_switch_drom_read_block(void *data,
int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf,
size_t size)
{
- return usb4_do_read_data(address, buf, size,
- usb4_switch_drom_read_block, sw);
+ return tb_nvm_read_data(address, buf, size, USB4_DATA_RETRIES,
+ usb4_switch_drom_read_block, sw);
}
/**
@@ -473,12 +413,18 @@ int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags)
val &= ~(PORT_CS_19_WOC | PORT_CS_19_WOD | PORT_CS_19_WOU4);
- if (flags & TB_WAKE_ON_CONNECT)
- val |= PORT_CS_19_WOC;
- if (flags & TB_WAKE_ON_DISCONNECT)
- val |= PORT_CS_19_WOD;
- if (flags & TB_WAKE_ON_USB4)
+ if (tb_is_upstream_port(port)) {
val |= PORT_CS_19_WOU4;
+ } else {
+ bool configured = val & PORT_CS_19_PC;
+
+ if ((flags & TB_WAKE_ON_CONNECT) && !configured)
+ val |= PORT_CS_19_WOC;
+ if ((flags & TB_WAKE_ON_DISCONNECT) && configured)
+ val |= PORT_CS_19_WOD;
+ if ((flags & TB_WAKE_ON_USB4) && configured)
+ val |= PORT_CS_19_WOU4;
+ }
ret = tb_port_write(port, &val, TB_CFG_PORT,
port->cap_usb4 + PORT_CS_19, 1);
@@ -487,7 +433,7 @@ int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags)
}
/*
- * Enable wakes from PCIe and USB 3.x on this router. Only
+ * Enable wakes from PCIe, USB 3.x and DP on this router. Only
* needed for device routers.
*/
if (route) {
@@ -495,11 +441,13 @@ int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags)
if (ret)
return ret;
- val &= ~(ROUTER_CS_5_WOP | ROUTER_CS_5_WOU);
+ val &= ~(ROUTER_CS_5_WOP | ROUTER_CS_5_WOU | ROUTER_CS_5_WOD);
if (flags & TB_WAKE_ON_USB3)
val |= ROUTER_CS_5_WOU;
if (flags & TB_WAKE_ON_PCIE)
val |= ROUTER_CS_5_WOP;
+ if (flags & TB_WAKE_ON_DP)
+ val |= ROUTER_CS_5_WOD;
ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1);
if (ret)
@@ -595,12 +543,21 @@ static int usb4_switch_nvm_read_block(void *data,
int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
size_t size)
{
- return usb4_do_read_data(address, buf, size,
- usb4_switch_nvm_read_block, sw);
+ return tb_nvm_read_data(address, buf, size, USB4_DATA_RETRIES,
+ usb4_switch_nvm_read_block, sw);
}
-static int usb4_switch_nvm_set_offset(struct tb_switch *sw,
- unsigned int address)
+/**
+ * usb4_switch_nvm_set_offset() - Set NVM write offset
+ * @sw: USB4 router
+ * @address: Start offset
+ *
+ * Explicitly sets NVM write offset. Normally when writing to NVM this
+ * is done automatically by usb4_switch_nvm_write().
+ *
+ * Returns %0 in success and negative errno if there was a failure.
+ */
+int usb4_switch_nvm_set_offset(struct tb_switch *sw, unsigned int address)
{
u32 metadata, dwaddress;
u8 status = 0;
@@ -618,8 +575,8 @@ static int usb4_switch_nvm_set_offset(struct tb_switch *sw,
return status ? -EIO : 0;
}
-static int usb4_switch_nvm_write_next_block(void *data, const void *buf,
- size_t dwords)
+static int usb4_switch_nvm_write_next_block(void *data, unsigned int dwaddress,
+ const void *buf, size_t dwords)
{
struct tb_switch *sw = data;
u8 status;
@@ -652,8 +609,8 @@ int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address,
if (ret)
return ret;
- return usb4_do_write_data(address, buf, size,
- usb4_switch_nvm_write_next_block, sw);
+ return tb_nvm_write_data(address, buf, size, USB4_DATA_RETRIES,
+ usb4_switch_nvm_write_next_block, sw);
}
/**
@@ -736,6 +693,147 @@ int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status)
}
/**
+ * usb4_switch_credits_init() - Read buffer allocation parameters
+ * @sw: USB4 router
+ *
+ * Reads @sw buffer allocation parameters and initializes @sw buffer
+ * allocation fields accordingly. Specifically @sw->credits_allocation
+ * is set to %true if these parameters can be used in tunneling.
+ *
+ * Returns %0 on success and negative errno otherwise.
+ */
+int usb4_switch_credits_init(struct tb_switch *sw)
+{
+ int max_usb3, min_dp_aux, min_dp_main, max_pcie, max_dma;
+ int ret, length, i, nports;
+ const struct tb_port *port;
+ u32 data[NVM_DATA_DWORDS];
+ u32 metadata = 0;
+ u8 status = 0;
+
+ memset(data, 0, sizeof(data));
+ ret = usb4_switch_op_data(sw, USB4_SWITCH_OP_BUFFER_ALLOC, &metadata,
+ &status, NULL, 0, data, ARRAY_SIZE(data));
+ if (ret)
+ return ret;
+ if (status)
+ return -EIO;
+
+ length = metadata & USB4_BA_LENGTH_MASK;
+ if (WARN_ON(length > ARRAY_SIZE(data)))
+ return -EMSGSIZE;
+
+ max_usb3 = -1;
+ min_dp_aux = -1;
+ min_dp_main = -1;
+ max_pcie = -1;
+ max_dma = -1;
+
+ tb_sw_dbg(sw, "credit allocation parameters:\n");
+
+ for (i = 0; i < length; i++) {
+ u16 index, value;
+
+ index = data[i] & USB4_BA_INDEX_MASK;
+ value = (data[i] & USB4_BA_VALUE_MASK) >> USB4_BA_VALUE_SHIFT;
+
+ switch (index) {
+ case USB4_BA_MAX_USB3:
+ tb_sw_dbg(sw, " USB3: %u\n", value);
+ max_usb3 = value;
+ break;
+ case USB4_BA_MIN_DP_AUX:
+ tb_sw_dbg(sw, " DP AUX: %u\n", value);
+ min_dp_aux = value;
+ break;
+ case USB4_BA_MIN_DP_MAIN:
+ tb_sw_dbg(sw, " DP main: %u\n", value);
+ min_dp_main = value;
+ break;
+ case USB4_BA_MAX_PCIE:
+ tb_sw_dbg(sw, " PCIe: %u\n", value);
+ max_pcie = value;
+ break;
+ case USB4_BA_MAX_HI:
+ tb_sw_dbg(sw, " DMA: %u\n", value);
+ max_dma = value;
+ break;
+ default:
+ tb_sw_dbg(sw, " unknown credit allocation index %#x, skipping\n",
+ index);
+ break;
+ }
+ }
+
+ /*
+ * Validate the buffer allocation preferences. If we find
+ * issues, log a warning and fall back using the hard-coded
+ * values.
+ */
+
+ /* Host router must report baMaxHI */
+ if (!tb_route(sw) && max_dma < 0) {
+ tb_sw_warn(sw, "host router is missing baMaxHI\n");
+ goto err_invalid;
+ }
+
+ nports = 0;
+ tb_switch_for_each_port(sw, port) {
+ if (tb_port_is_null(port))
+ nports++;
+ }
+
+ /* Must have DP buffer allocation (multiple USB4 ports) */
+ if (nports > 2 && (min_dp_aux < 0 || min_dp_main < 0)) {
+ tb_sw_warn(sw, "multiple USB4 ports require baMinDPaux/baMinDPmain\n");
+ goto err_invalid;
+ }
+
+ tb_switch_for_each_port(sw, port) {
+ if (tb_port_is_dpout(port) && min_dp_main < 0) {
+ tb_sw_warn(sw, "missing baMinDPmain");
+ goto err_invalid;
+ }
+ if ((tb_port_is_dpin(port) || tb_port_is_dpout(port)) &&
+ min_dp_aux < 0) {
+ tb_sw_warn(sw, "missing baMinDPaux");
+ goto err_invalid;
+ }
+ if ((tb_port_is_usb3_down(port) || tb_port_is_usb3_up(port)) &&
+ max_usb3 < 0) {
+ tb_sw_warn(sw, "missing baMaxUSB3");
+ goto err_invalid;
+ }
+ if ((tb_port_is_pcie_down(port) || tb_port_is_pcie_up(port)) &&
+ max_pcie < 0) {
+ tb_sw_warn(sw, "missing baMaxPCIe");
+ goto err_invalid;
+ }
+ }
+
+ /*
+ * Buffer allocation passed the validation so we can use it in
+ * path creation.
+ */
+ sw->credit_allocation = true;
+ if (max_usb3 > 0)
+ sw->max_usb3_credits = max_usb3;
+ if (min_dp_aux > 0)
+ sw->min_dp_aux_credits = min_dp_aux;
+ if (min_dp_main > 0)
+ sw->min_dp_main_credits = min_dp_main;
+ if (max_pcie > 0)
+ sw->max_pcie_credits = max_pcie;
+ if (max_dma > 0)
+ sw->max_dma_credits = max_dma;
+
+ return 0;
+
+err_invalid:
+ return -EINVAL;
+}
+
+/**
* usb4_switch_query_dp_resource() - Query availability of DP IN resource
* @sw: USB4 router
* @in: DP IN adapter
@@ -897,6 +995,60 @@ struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw,
}
/**
+ * usb4_switch_add_ports() - Add USB4 ports for this router
+ * @sw: USB4 router
+ *
+ * For USB4 router finds all USB4 ports and registers devices for each.
+ * Can be called to any router.
+ *
+ * Return %0 in case of success and negative errno in case of failure.
+ */
+int usb4_switch_add_ports(struct tb_switch *sw)
+{
+ struct tb_port *port;
+
+ if (tb_switch_is_icm(sw) || !tb_switch_is_usb4(sw))
+ return 0;
+
+ tb_switch_for_each_port(sw, port) {
+ struct usb4_port *usb4;
+
+ if (!tb_port_is_null(port))
+ continue;
+ if (!port->cap_usb4)
+ continue;
+
+ usb4 = usb4_port_device_add(port);
+ if (IS_ERR(usb4)) {
+ usb4_switch_remove_ports(sw);
+ return PTR_ERR(usb4);
+ }
+
+ port->usb4 = usb4;
+ }
+
+ return 0;
+}
+
+/**
+ * usb4_switch_remove_ports() - Removes USB4 ports from this router
+ * @sw: USB4 router
+ *
+ * Unregisters previously registered USB4 ports.
+ */
+void usb4_switch_remove_ports(struct tb_switch *sw)
+{
+ struct tb_port *port;
+
+ tb_switch_for_each_port(sw, port) {
+ if (port->usb4) {
+ usb4_port_device_remove(port->usb4);
+ port->usb4 = NULL;
+ }
+ }
+}
+
+/**
* usb4_port_unlock() - Unlock USB4 downstream port
* @port: USB4 port to unlock
*
@@ -1029,7 +1181,7 @@ static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit,
static int usb4_port_read_data(struct tb_port *port, void *data, size_t dwords)
{
- if (dwords > USB4_DATA_DWORDS)
+ if (dwords > NVM_DATA_DWORDS)
return -EINVAL;
return tb_port_read(port, data, TB_CFG_PORT, port->cap_usb4 + PORT_CS_2,
@@ -1039,7 +1191,7 @@ static int usb4_port_read_data(struct tb_port *port, void *data, size_t dwords)
static int usb4_port_write_data(struct tb_port *port, const void *data,
size_t dwords)
{
- if (dwords > USB4_DATA_DWORDS)
+ if (dwords > NVM_DATA_DWORDS)
return -EINVAL;
return tb_port_write(port, data, TB_CFG_PORT, port->cap_usb4 + PORT_CS_2,
@@ -1175,6 +1327,48 @@ static int usb4_port_sb_op(struct tb_port *port, enum usb4_sb_target target,
return -ETIMEDOUT;
}
+static int usb4_port_set_router_offline(struct tb_port *port, bool offline)
+{
+ u32 val = !offline;
+ int ret;
+
+ ret = usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0,
+ USB4_SB_METADATA, &val, sizeof(val));
+ if (ret)
+ return ret;
+
+ val = USB4_SB_OPCODE_ROUTER_OFFLINE;
+ return usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0,
+ USB4_SB_OPCODE, &val, sizeof(val));
+}
+
+/**
+ * usb4_port_router_offline() - Put the USB4 port to offline mode
+ * @port: USB4 port
+ *
+ * This function puts the USB4 port into offline mode. In this mode the
+ * port does not react on hotplug events anymore. This needs to be
+ * called before retimer access is done when the USB4 links is not up.
+ *
+ * Returns %0 in case of success and negative errno if there was an
+ * error.
+ */
+int usb4_port_router_offline(struct tb_port *port)
+{
+ return usb4_port_set_router_offline(port, true);
+}
+
+/**
+ * usb4_port_router_online() - Put the USB4 port back to online
+ * @port: USB4 port
+ *
+ * Makes the USB4 port functional again.
+ */
+int usb4_port_router_online(struct tb_port *port)
+{
+ return usb4_port_set_router_offline(port, false);
+}
+
/**
* usb4_port_enumerate_retimers() - Send RT broadcast transaction
* @port: USB4 port
@@ -1201,6 +1395,33 @@ static inline int usb4_port_retimer_op(struct tb_port *port, u8 index,
}
/**
+ * usb4_port_retimer_set_inbound_sbtx() - Enable sideband channel transactions
+ * @port: USB4 port
+ * @index: Retimer index
+ *
+ * Enables sideband channel transations on SBTX. Can be used when USB4
+ * link does not go up, for example if there is no device connected.
+ */
+int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index)
+{
+ int ret;
+
+ ret = usb4_port_retimer_op(port, index, USB4_SB_OPCODE_SET_INBOUND_SBTX,
+ 500);
+
+ if (ret != -ENODEV)
+ return ret;
+
+ /*
+ * Per the USB4 retimer spec, the retimer is not required to
+ * send an RT (Retimer Transaction) response for the first
+ * SET_INBOUND_SBTX command
+ */
+ return usb4_port_retimer_op(port, index, USB4_SB_OPCODE_SET_INBOUND_SBTX,
+ 500);
+}
+
+/**
* usb4_port_retimer_read() - Read from retimer sideband registers
* @port: USB4 port
* @index: Retimer index
@@ -1292,8 +1513,19 @@ int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index)
return ret ? ret : metadata & USB4_NVM_SECTOR_SIZE_MASK;
}
-static int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
- unsigned int address)
+/**
+ * usb4_port_retimer_nvm_set_offset() - Set NVM write offset
+ * @port: USB4 port
+ * @index: Retimer index
+ * @address: Start offset
+ *
+ * Exlicitly sets NVM write offset. Normally when writing to NVM this is
+ * done automatically by usb4_port_retimer_nvm_write().
+ *
+ * Returns %0 in success and negative errno if there was a failure.
+ */
+int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
+ unsigned int address)
{
u32 metadata, dwaddress;
int ret;
@@ -1316,8 +1548,8 @@ struct retimer_info {
u8 index;
};
-static int usb4_port_retimer_nvm_write_next_block(void *data, const void *buf,
- size_t dwords)
+static int usb4_port_retimer_nvm_write_next_block(void *data,
+ unsigned int dwaddress, const void *buf, size_t dwords)
{
const struct retimer_info *info = data;
@@ -1357,8 +1589,8 @@ int usb4_port_retimer_nvm_write(struct tb_port *port, u8 index, unsigned int add
if (ret)
return ret;
- return usb4_do_write_data(address, buf, size,
- usb4_port_retimer_nvm_write_next_block, &info);
+ return tb_nvm_write_data(address, buf, size, USB4_DATA_RETRIES,
+ usb4_port_retimer_nvm_write_next_block, &info);
}
/**
@@ -1442,7 +1674,7 @@ static int usb4_port_retimer_nvm_read_block(void *data, unsigned int dwaddress,
int ret;
metadata = dwaddress << USB4_NVM_READ_OFFSET_SHIFT;
- if (dwords < USB4_DATA_DWORDS)
+ if (dwords < NVM_DATA_DWORDS)
metadata |= dwords << USB4_NVM_READ_LENGTH_SHIFT;
ret = usb4_port_retimer_write(port, index, USB4_SB_METADATA, &metadata,
@@ -1475,8 +1707,8 @@ int usb4_port_retimer_nvm_read(struct tb_port *port, u8 index,
{
struct retimer_info info = { .port = port, .index = index };
- return usb4_do_read_data(address, buf, size,
- usb4_port_retimer_nvm_read_block, &info);
+ return tb_nvm_read_data(address, buf, size, USB4_DATA_RETRIES,
+ usb4_port_retimer_nvm_read_block, &info);
}
/**
diff --git a/drivers/thunderbolt/usb4_port.c b/drivers/thunderbolt/usb4_port.c
new file mode 100644
index 000000000000..29e2a4f9c9f5
--- /dev/null
+++ b/drivers/thunderbolt/usb4_port.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB4 port device
+ *
+ * Copyright (C) 2021, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#include <linux/pm_runtime.h>
+
+#include "tb.h"
+
+static ssize_t link_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
+ struct tb_port *port = usb4->port;
+ struct tb *tb = port->sw->tb;
+ const char *link;
+
+ if (mutex_lock_interruptible(&tb->lock))
+ return -ERESTARTSYS;
+
+ if (tb_is_upstream_port(port))
+ link = port->sw->link_usb4 ? "usb4" : "tbt";
+ else if (tb_port_has_remote(port))
+ link = port->remote->sw->link_usb4 ? "usb4" : "tbt";
+ else
+ link = "none";
+
+ mutex_unlock(&tb->lock);
+
+ return sysfs_emit(buf, "%s\n", link);
+}
+static DEVICE_ATTR_RO(link);
+
+static struct attribute *common_attrs[] = {
+ &dev_attr_link.attr,
+ NULL
+};
+
+static const struct attribute_group common_group = {
+ .attrs = common_attrs,
+};
+
+static int usb4_port_offline(struct usb4_port *usb4)
+{
+ struct tb_port *port = usb4->port;
+ int ret;
+
+ ret = tb_acpi_power_on_retimers(port);
+ if (ret)
+ return ret;
+
+ ret = usb4_port_router_offline(port);
+ if (ret) {
+ tb_acpi_power_off_retimers(port);
+ return ret;
+ }
+
+ ret = tb_retimer_scan(port, false);
+ if (ret) {
+ usb4_port_router_online(port);
+ tb_acpi_power_off_retimers(port);
+ }
+
+ return ret;
+}
+
+static void usb4_port_online(struct usb4_port *usb4)
+{
+ struct tb_port *port = usb4->port;
+
+ usb4_port_router_online(port);
+ tb_acpi_power_off_retimers(port);
+}
+
+static ssize_t offline_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
+
+ return sysfs_emit(buf, "%d\n", usb4->offline);
+}
+
+static ssize_t offline_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
+ struct tb_port *port = usb4->port;
+ struct tb *tb = port->sw->tb;
+ bool val;
+ int ret;
+
+ ret = kstrtobool(buf, &val);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_sync(&usb4->dev);
+
+ if (mutex_lock_interruptible(&tb->lock)) {
+ ret = -ERESTARTSYS;
+ goto out_rpm;
+ }
+
+ if (val == usb4->offline)
+ goto out_unlock;
+
+ /* Offline mode works only for ports that are not connected */
+ if (tb_port_has_remote(port)) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ if (val) {
+ ret = usb4_port_offline(usb4);
+ if (ret)
+ goto out_unlock;
+ } else {
+ usb4_port_online(usb4);
+ tb_retimer_remove_all(port);
+ }
+
+ usb4->offline = val;
+ tb_port_dbg(port, "%s offline mode\n", val ? "enter" : "exit");
+
+out_unlock:
+ mutex_unlock(&tb->lock);
+out_rpm:
+ pm_runtime_mark_last_busy(&usb4->dev);
+ pm_runtime_put_autosuspend(&usb4->dev);
+
+ return ret ? ret : count;
+}
+static DEVICE_ATTR_RW(offline);
+
+static ssize_t rescan_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
+ struct tb_port *port = usb4->port;
+ struct tb *tb = port->sw->tb;
+ bool val;
+ int ret;
+
+ ret = kstrtobool(buf, &val);
+ if (ret)
+ return ret;
+
+ if (!val)
+ return count;
+
+ pm_runtime_get_sync(&usb4->dev);
+
+ if (mutex_lock_interruptible(&tb->lock)) {
+ ret = -ERESTARTSYS;
+ goto out_rpm;
+ }
+
+ /* Must be in offline mode already */
+ if (!usb4->offline) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ tb_retimer_remove_all(port);
+ ret = tb_retimer_scan(port, true);
+
+out_unlock:
+ mutex_unlock(&tb->lock);
+out_rpm:
+ pm_runtime_mark_last_busy(&usb4->dev);
+ pm_runtime_put_autosuspend(&usb4->dev);
+
+ return ret ? ret : count;
+}
+static DEVICE_ATTR_WO(rescan);
+
+static struct attribute *service_attrs[] = {
+ &dev_attr_offline.attr,
+ &dev_attr_rescan.attr,
+ NULL
+};
+
+static umode_t service_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
+
+ /*
+ * Always need some platform help to cycle the modes so that
+ * retimers can be accessed through the sideband.
+ */
+ return usb4->can_offline ? attr->mode : 0;
+}
+
+static const struct attribute_group service_group = {
+ .attrs = service_attrs,
+ .is_visible = service_attr_is_visible,
+};
+
+static const struct attribute_group *usb4_port_device_groups[] = {
+ &common_group,
+ &service_group,
+ NULL
+};
+
+static void usb4_port_device_release(struct device *dev)
+{
+ struct usb4_port *usb4 = container_of(dev, struct usb4_port, dev);
+
+ kfree(usb4);
+}
+
+struct device_type usb4_port_device_type = {
+ .name = "usb4_port",
+ .groups = usb4_port_device_groups,
+ .release = usb4_port_device_release,
+};
+
+/**
+ * usb4_port_device_add() - Add USB4 port device
+ * @port: Lane 0 adapter port to add the USB4 port
+ *
+ * Creates and registers a USB4 port device for @port. Returns the new
+ * USB4 port device pointer or ERR_PTR() in case of error.
+ */
+struct usb4_port *usb4_port_device_add(struct tb_port *port)
+{
+ struct usb4_port *usb4;
+ int ret;
+
+ usb4 = kzalloc(sizeof(*usb4), GFP_KERNEL);
+ if (!usb4)
+ return ERR_PTR(-ENOMEM);
+
+ usb4->port = port;
+ usb4->dev.type = &usb4_port_device_type;
+ usb4->dev.parent = &port->sw->dev;
+ dev_set_name(&usb4->dev, "usb4_port%d", port->port);
+
+ ret = device_register(&usb4->dev);
+ if (ret) {
+ put_device(&usb4->dev);
+ return ERR_PTR(ret);
+ }
+
+ pm_runtime_no_callbacks(&usb4->dev);
+ pm_runtime_set_active(&usb4->dev);
+ pm_runtime_enable(&usb4->dev);
+ pm_runtime_set_autosuspend_delay(&usb4->dev, TB_AUTOSUSPEND_DELAY);
+ pm_runtime_mark_last_busy(&usb4->dev);
+ pm_runtime_use_autosuspend(&usb4->dev);
+
+ return usb4;
+}
+
+/**
+ * usb4_port_device_remove() - Removes USB4 port device
+ * @usb4: USB4 port device
+ *
+ * Unregisters the USB4 port device from the system. The device will be
+ * released when the last reference is dropped.
+ */
+void usb4_port_device_remove(struct usb4_port *usb4)
+{
+ device_unregister(&usb4->dev);
+}
+
+/**
+ * usb4_port_device_resume() - Resumes USB4 port device
+ * @usb4: USB4 port device
+ *
+ * Used to resume USB4 port device after sleep state.
+ */
+int usb4_port_device_resume(struct usb4_port *usb4)
+{
+ return usb4->offline ? usb4_port_offline(usb4) : 0;
+}
diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
index b21d99d59412..d66ea4d616fd 100644
--- a/drivers/thunderbolt/xdomain.c
+++ b/drivers/thunderbolt/xdomain.c
@@ -1527,6 +1527,13 @@ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd)
return ret;
}
+ ret = tb_port_wait_for_link_width(port, 2, 100);
+ if (ret) {
+ tb_port_warn(port, "timeout enabling lane bonding\n");
+ return ret;
+ }
+
+ tb_port_update_credits(port);
tb_xdomain_update_link_attributes(xd);
dev_dbg(&xd->dev, "lane bonding enabled\n");
@@ -1548,7 +1555,10 @@ void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd)
port = tb_port_at(xd->route, tb_xdomain_parent(xd));
if (port->dual_link_port) {
tb_port_lane_bonding_disable(port);
+ if (tb_port_wait_for_link_width(port, 1, 100) == -ETIMEDOUT)
+ tb_port_warn(port, "timeout disabling lane bonding\n");
tb_port_disable(port->dual_link_port);
+ tb_port_update_credits(port);
tb_xdomain_update_link_attributes(xd);
dev_dbg(&xd->dev, "lane bonding disabled\n");
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 4d3947476f0e..4ce7cba2b48a 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -180,7 +180,7 @@ struct cxacru_data {
struct mutex poll_state_serialize;
enum cxacru_poll_state poll_state;
- /* contol handles */
+ /* control handles */
struct mutex cm_serialize;
u8 *rcv_buf;
u8 *snd_buf;
diff --git a/drivers/usb/cdns3/cdns3-ep0.c b/drivers/usb/cdns3/cdns3-ep0.c
index 9a17802275d5..02ec7ab4bb48 100644
--- a/drivers/usb/cdns3/cdns3-ep0.c
+++ b/drivers/usb/cdns3/cdns3-ep0.c
@@ -677,7 +677,7 @@ static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
}
/**
- * cdns3_gadget_ep0_queue Transfer data on endpoint zero
+ * cdns3_gadget_ep0_queue - Transfer data on endpoint zero
* @ep: pointer to endpoint zero object
* @request: pointer to request object
* @gfp_flags: gfp flags
@@ -772,7 +772,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
}
/**
- * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
+ * cdns3_gadget_ep_set_wedge - Set wedge on selected endpoint
* @ep: endpoint object
*
* Returns 0
@@ -865,7 +865,7 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev)
}
/**
- * cdns3_init_ep0 Initializes software endpoint 0 of gadget
+ * cdns3_init_ep0 - Initializes software endpoint 0 of gadget
* @priv_dev: extended gadget object
* @priv_ep: extended endpoint object
*
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
index 5281f8d3fb3d..5d8c982019af 100644
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -155,7 +155,7 @@ static struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
}
/**
- * select_ep - selects endpoint
+ * cdns3_select_ep - selects endpoint
* @priv_dev: extended gadget object
* @ep: endpoint address
*/
@@ -430,9 +430,7 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev,
if (ret)
return ret;
- list_del(&request->list);
- list_add_tail(&request->list,
- &priv_ep->pending_req_list);
+ list_move_tail(&request->list, &priv_ep->pending_req_list);
if (request->stream_id != 0 || (priv_ep->flags & EP_TDLCHK_EN))
break;
}
@@ -484,7 +482,7 @@ static void __cdns3_descmiss_copy_data(struct usb_request *request,
}
/**
- * cdns3_wa2_descmiss_copy_data copy data from internal requests to
+ * cdns3_wa2_descmiss_copy_data - copy data from internal requests to
* request queued by class driver.
* @priv_ep: extended endpoint object
* @request: request object
@@ -1835,7 +1833,7 @@ __must_hold(&priv_dev->lock)
}
/**
- * cdns3_device_irq_handler- interrupt handler for device part of controller
+ * cdns3_device_irq_handler - interrupt handler for device part of controller
*
* @irq: irq number for cdns3 core device
* @data: structure of cdns3
@@ -1879,7 +1877,7 @@ static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
}
/**
- * cdns3_device_thread_irq_handler- interrupt handler for device part
+ * cdns3_device_thread_irq_handler - interrupt handler for device part
* of controller
*
* @irq: irq number for cdns3 core device
@@ -2022,7 +2020,7 @@ static void cdns3_configure_dmult(struct cdns3_device *priv_dev,
}
/**
- * cdns3_ep_config Configure hardware endpoint
+ * cdns3_ep_config - Configure hardware endpoint
* @priv_ep: extended endpoint object
* @enable: set EP_CFG_ENABLE bit in ep_cfg register.
*/
@@ -2221,7 +2219,7 @@ usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
}
/**
- * cdns3_gadget_ep_alloc_request Allocates request
+ * cdns3_gadget_ep_alloc_request - Allocates request
* @ep: endpoint object associated with request
* @gfp_flags: gfp flags
*
@@ -2244,7 +2242,7 @@ struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
}
/**
- * cdns3_gadget_ep_free_request Free memory occupied by request
+ * cdns3_gadget_ep_free_request - Free memory occupied by request
* @ep: endpoint object associated with request
* @request: request to free memory
*/
@@ -2261,7 +2259,7 @@ void cdns3_gadget_ep_free_request(struct usb_ep *ep,
}
/**
- * cdns3_gadget_ep_enable Enable endpoint
+ * cdns3_gadget_ep_enable - Enable endpoint
* @ep: endpoint object
* @desc: endpoint descriptor
*
@@ -2396,7 +2394,7 @@ exit:
}
/**
- * cdns3_gadget_ep_disable Disable endpoint
+ * cdns3_gadget_ep_disable - Disable endpoint
* @ep: endpoint object
*
* Returns 0 on success, error code elsewhere
@@ -2486,7 +2484,7 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
}
/**
- * cdns3_gadget_ep_queue Transfer data on endpoint
+ * __cdns3_gadget_ep_queue - Transfer data on endpoint
* @ep: endpoint object
* @request: request object
* @gfp_flags: gfp flags
@@ -2586,7 +2584,7 @@ static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
}
/**
- * cdns3_gadget_ep_dequeue Remove request from transfer queue
+ * cdns3_gadget_ep_dequeue - Remove request from transfer queue
* @ep: endpoint object associated with request
* @request: request object
*
@@ -2653,7 +2651,7 @@ not_found:
}
/**
- * __cdns3_gadget_ep_set_halt Sets stall on selected endpoint
+ * __cdns3_gadget_ep_set_halt - Sets stall on selected endpoint
* Should be called after acquiring spin_lock and selecting ep
* @priv_ep: endpoint object to set stall on.
*/
@@ -2674,7 +2672,7 @@ void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep)
}
/**
- * __cdns3_gadget_ep_clear_halt Clears stall on selected endpoint
+ * __cdns3_gadget_ep_clear_halt - Clears stall on selected endpoint
* Should be called after acquiring spin_lock and selecting ep
* @priv_ep: endpoint object to clear stall on
*/
@@ -2719,7 +2717,7 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
}
/**
- * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
+ * cdns3_gadget_ep_set_halt - Sets/clears stall on selected endpoint
* @ep: endpoint object to set/clear stall on
* @value: 1 for set stall, 0 for clear stall
*
@@ -2765,7 +2763,7 @@ static const struct usb_ep_ops cdns3_gadget_ep_ops = {
};
/**
- * cdns3_gadget_get_frame Returns number of actual ITP frame
+ * cdns3_gadget_get_frame - Returns number of actual ITP frame
* @gadget: gadget object
*
* Returns number of actual ITP frame
@@ -2874,7 +2872,7 @@ static void cdns3_gadget_config(struct cdns3_device *priv_dev)
}
/**
- * cdns3_gadget_udc_start Gadget start
+ * cdns3_gadget_udc_start - Gadget start
* @gadget: gadget object
* @driver: driver which operates on this gadget
*
@@ -2920,7 +2918,7 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
}
/**
- * cdns3_gadget_udc_stop Stops gadget
+ * cdns3_gadget_udc_stop - Stops gadget
* @gadget: gadget object
*
* Returns 0
@@ -2983,7 +2981,7 @@ static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
}
/**
- * cdns3_init_eps Initializes software endpoints of gadget
+ * cdns3_init_eps - Initializes software endpoints of gadget
* @priv_dev: extended gadget object
*
* Returns 0 on success, error code elsewhere
diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c
index 74e758dc0895..59860d1753fd 100644
--- a/drivers/usb/cdns3/cdns3-imx.c
+++ b/drivers/usb/cdns3/cdns3-imx.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* cdns3-imx.c - NXP i.MX specific Glue layer for Cadence USB Controller
*
* Copyright (C) 2019 NXP
diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c
index e1deeada425c..4d0f027e5bd3 100644
--- a/drivers/usb/cdns3/cdns3-plat.c
+++ b/drivers/usb/cdns3/cdns3-plat.c
@@ -170,7 +170,7 @@ err_phy3_init:
}
/**
- * cdns3_remove - unbind drd driver and clean up
+ * cdns3_plat_remove() - unbind drd driver and clean up
* @pdev: Pointer to Linux platform device
*
* Returns 0 on success otherwise negative errno
diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c
index eccb1c766bba..07c318770362 100644
--- a/drivers/usb/cdns3/cdns3-ti.c
+++ b/drivers/usb/cdns3/cdns3-ti.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* cdns3-ti.c - TI specific Glue layer for Cadence USB Controller
*
* Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c
index c083985e387b..c23f53e9b1ef 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.c
+++ b/drivers/usb/cdns3/cdnsp-gadget.c
@@ -56,7 +56,8 @@ u32 cdnsp_port_state_to_neutral(u32 state)
}
/**
- * Find the offset of the extended capabilities with capability ID id.
+ * cdnsp_find_next_ext_cap - Find the offset of the extended capabilities
+ * with capability ID id.
* @base: PCI MMIO registers base address.
* @start: Address at which to start looking, (0 or HCC_PARAMS to start at
* beginning of list)
@@ -1151,7 +1152,7 @@ static int cdnsp_gadget_ep_set_halt(struct usb_ep *ep, int value)
struct cdnsp_ep *pep = to_cdnsp_ep(ep);
struct cdnsp_device *pdev = pep->pdev;
struct cdnsp_request *preq;
- unsigned long flags = 0;
+ unsigned long flags;
int ret;
spin_lock_irqsave(&pdev->lock, flags);
@@ -1176,7 +1177,7 @@ static int cdnsp_gadget_ep_set_wedge(struct usb_ep *ep)
{
struct cdnsp_ep *pep = to_cdnsp_ep(ep);
struct cdnsp_device *pdev = pep->pdev;
- unsigned long flags = 0;
+ unsigned long flags;
int ret;
spin_lock_irqsave(&pdev->lock, flags);
diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c
index 5d4c4bfe15b7..a47948a1623f 100644
--- a/drivers/usb/cdns3/cdnsp-mem.c
+++ b/drivers/usb/cdns3/cdnsp-mem.c
@@ -1082,9 +1082,8 @@ void cdnsp_mem_cleanup(struct cdnsp_device *pdev)
dma_pool_destroy(pdev->device_pool);
pdev->device_pool = NULL;
- if (pdev->dcbaa)
- dma_free_coherent(dev, sizeof(*pdev->dcbaa),
- pdev->dcbaa, pdev->dcbaa->dma);
+ dma_free_coherent(dev, sizeof(*pdev->dcbaa),
+ pdev->dcbaa, pdev->dcbaa->dma);
pdev->dcbaa = NULL;
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index bb739d88179f..dbcdf3b24b47 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -332,7 +332,7 @@ exit:
}
/**
- * cdsn3_role_get - get current role of controller.
+ * cdns_role_get - get current role of controller.
*
* @sw: pointer to USB role switch structure
*
@@ -419,7 +419,7 @@ static irqreturn_t cdns_wakeup_irq(int irq, void *data)
}
/**
- * cdns_probe - probe for cdns3/cdnsp core device
+ * cdns_init - probe for cdns3/cdnsp core device
* @cdns: Pointer to cdns structure.
*
* Returns 0 on success otherwise negative errno
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 0697eb980e5f..99440baa6458 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -195,7 +195,6 @@ struct hw_bank {
* @phy: pointer to PHY, if any
* @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework
* @hcd: pointer to usb_hcd for ehci host driver
- * @debugfs: root dentry for this controller in debugfs
* @id_event: indicates there is an id event, and handled at ci_otg_work
* @b_sess_valid_event: indicates there is a vbus event, and handled
* at ci_otg_work
@@ -249,7 +248,6 @@ struct ci_hdrc {
/* old usb_phy interface */
struct usb_phy *usb_phy;
struct usb_hcd *hcd;
- struct dentry *debugfs;
bool id_event;
bool b_sess_valid_event;
bool imx28_write_fix;
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 3f6c21406dbd..2b18f5088ae4 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -335,7 +335,7 @@ static int _ci_usb_phy_init(struct ci_hdrc *ci)
}
/**
- * _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy
+ * ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy
* interfaces
* @ci: the controller
*/
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index da5d18cf6840..faf6b078b6c4 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -342,26 +342,20 @@ DEFINE_SHOW_ATTRIBUTE(ci_registers);
*/
void dbg_create_files(struct ci_hdrc *ci)
{
- ci->debugfs = debugfs_create_dir(dev_name(ci->dev), usb_debug_root);
-
- debugfs_create_file("device", S_IRUGO, ci->debugfs, ci,
- &ci_device_fops);
- debugfs_create_file("port_test", S_IRUGO | S_IWUSR, ci->debugfs, ci,
- &ci_port_test_fops);
- debugfs_create_file("qheads", S_IRUGO, ci->debugfs, ci,
- &ci_qheads_fops);
- debugfs_create_file("requests", S_IRUGO, ci->debugfs, ci,
- &ci_requests_fops);
-
- if (ci_otg_is_fsm_mode(ci)) {
- debugfs_create_file("otg", S_IRUGO, ci->debugfs, ci,
- &ci_otg_fops);
- }
+ struct dentry *dir;
+
+ dir = debugfs_create_dir(dev_name(ci->dev), usb_debug_root);
+
+ debugfs_create_file("device", S_IRUGO, dir, ci, &ci_device_fops);
+ debugfs_create_file("port_test", S_IRUGO | S_IWUSR, dir, ci, &ci_port_test_fops);
+ debugfs_create_file("qheads", S_IRUGO, dir, ci, &ci_qheads_fops);
+ debugfs_create_file("requests", S_IRUGO, dir, ci, &ci_requests_fops);
+
+ if (ci_otg_is_fsm_mode(ci))
+ debugfs_create_file("otg", S_IRUGO, dir, ci, &ci_otg_fops);
- debugfs_create_file("role", S_IRUGO | S_IWUSR, ci->debugfs, ci,
- &ci_role_fops);
- debugfs_create_file("registers", S_IRUGO, ci->debugfs, ci,
- &ci_registers_fops);
+ debugfs_create_file("role", S_IRUGO | S_IWUSR, dir, ci, &ci_role_fops);
+ debugfs_create_file("registers", S_IRUGO, dir, ci, &ci_registers_fops);
}
/**
@@ -370,5 +364,5 @@ void dbg_create_files(struct ci_hdrc *ci)
*/
void dbg_remove_files(struct ci_hdrc *ci)
{
- debugfs_remove_recursive(ci->debugfs);
+ debugfs_remove(debugfs_lookup(dev_name(ci->dev), usb_debug_root));
}
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index d3aada3ce7ec..8dd59282827b 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -22,7 +22,7 @@
#include "otg_fsm.h"
/**
- * hw_read_otgsc returns otgsc register bits value.
+ * hw_read_otgsc - returns otgsc register bits value.
* @ci: the controller
* @mask: bitfield mask
*/
@@ -75,7 +75,7 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
}
/**
- * hw_write_otgsc updates target bits of OTGSC register.
+ * hw_write_otgsc - updates target bits of OTGSC register.
* @ci: the controller
* @mask: bitfield mask
* @data: to be written
@@ -140,8 +140,9 @@ void ci_handle_vbus_change(struct ci_hdrc *ci)
}
/**
- * When we switch to device mode, the vbus value should be lower
- * than OTGSC_BSV before connecting to host.
+ * hw_wait_vbus_lower_bsv - When we switch to device mode, the vbus value
+ * should be lower than OTGSC_BSV before connecting
+ * to host.
*
* @ci: the controller
*
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 393f216b9161..8834ca613721 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -238,7 +238,7 @@ static int hw_ep_set_halt(struct ci_hdrc *ci, int num, int dir, int value)
}
/**
- * hw_is_port_high_speed: test if port is high speed
+ * hw_port_is_high_speed: test if port is high speed
* @ci: the controller
*
* This function returns true if high speed port
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index c9954eb56e00..4895325b16a4 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1946,6 +1946,11 @@ static const struct usb_device_id acm_ids[] = {
.driver_info = IGNORE_DEVICE,
},
+ /* Exclude Heimann Sensor GmbH USB appset demo */
+ { USB_DEVICE(0x32a7, 0x0000),
+ .driver_info = IGNORE_DEVICE,
+ },
+
/* control interfaces without any protocol set */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_PROTO_NONE) },
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 8e5490ac13a2..fdf79bcf7eb0 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -1035,9 +1035,10 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor
INIT_WORK(&desc->rxwork, wdm_rxwork);
INIT_WORK(&desc->service_outs_intr, service_interrupt_work);
- rv = -EINVAL;
- if (!usb_endpoint_is_int_in(ep))
+ if (!usb_endpoint_is_int_in(ep)) {
+ rv = -EINVAL;
goto err;
+ }
desc->wMaxPacketSize = usb_endpoint_maxp(ep);
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index ce5e6f6711f7..7e13b74e60e5 100644
--- a/drivers/usb/common/ulpi.c
+++ b/drivers/usb/common/ulpi.c
@@ -141,7 +141,7 @@ static const struct device_type ulpi_dev_type = {
/* -------------------------------------------------------------------------- */
/**
- * ulpi_register_driver - register a driver with the ULPI bus
+ * __ulpi_register_driver - register a driver with the ULPI bus
* @drv: driver being registered
* @module: ends up being THIS_MODULE
*
diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c
index 6c4e3a19f42c..0158148cb054 100644
--- a/drivers/usb/common/usb-conn-gpio.c
+++ b/drivers/usb/common/usb-conn-gpio.c
@@ -83,11 +83,11 @@ static void usb_conn_detect_cable(struct work_struct *work)
else
role = USB_ROLE_NONE;
- dev_dbg(info->dev, "role %d/%d, gpios: id %d, vbus %d\n",
- info->last_role, role, id, vbus);
+ dev_dbg(info->dev, "role %s -> %s, gpios: id %d, vbus %d\n",
+ usb_role_string(info->last_role), usb_role_string(role), id, vbus);
if (info->last_role == role) {
- dev_warn(info->dev, "repeated role: %d\n", role);
+ dev_warn(info->dev, "repeated role: %s\n", usb_role_string(role));
return;
}
@@ -149,14 +149,32 @@ static int usb_charger_get_property(struct power_supply *psy,
return 0;
}
-static int usb_conn_probe(struct platform_device *pdev)
+static int usb_conn_psy_register(struct usb_conn_info *info)
{
- struct device *dev = &pdev->dev;
- struct power_supply_desc *desc;
- struct usb_conn_info *info;
+ struct device *dev = info->dev;
+ struct power_supply_desc *desc = &info->desc;
struct power_supply_config cfg = {
.of_node = dev->of_node,
};
+
+ desc->name = "usb-charger";
+ desc->properties = usb_charger_properties;
+ desc->num_properties = ARRAY_SIZE(usb_charger_properties);
+ desc->get_property = usb_charger_get_property;
+ desc->type = POWER_SUPPLY_TYPE_USB;
+ cfg.drv_data = info;
+
+ info->charger = devm_power_supply_register(dev, desc, &cfg);
+ if (IS_ERR(info->charger))
+ dev_err(dev, "Unable to register charger\n");
+
+ return PTR_ERR_OR_ZERO(info->charger);
+}
+
+static int usb_conn_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_conn_info *info;
bool need_vbus = true;
int ret = 0;
@@ -205,18 +223,18 @@ static int usb_conn_probe(struct platform_device *pdev)
}
if (IS_ERR(info->vbus)) {
- if (PTR_ERR(info->vbus) != -EPROBE_DEFER)
- dev_err(dev, "failed to get vbus: %ld\n", PTR_ERR(info->vbus));
- return PTR_ERR(info->vbus);
+ ret = PTR_ERR(info->vbus);
+ return dev_err_probe(dev, ret, "failed to get vbus :%d\n", ret);
}
info->role_sw = usb_role_switch_get(dev);
- if (IS_ERR(info->role_sw)) {
- if (PTR_ERR(info->role_sw) != -EPROBE_DEFER)
- dev_err(dev, "failed to get role switch\n");
+ if (IS_ERR(info->role_sw))
+ return dev_err_probe(dev, PTR_ERR(info->role_sw),
+ "failed to get role switch\n");
- return PTR_ERR(info->role_sw);
- }
+ ret = usb_conn_psy_register(info);
+ if (ret)
+ goto put_role_sw;
if (info->id_gpiod) {
info->id_irq = gpiod_to_irq(info->id_gpiod);
@@ -252,20 +270,6 @@ static int usb_conn_probe(struct platform_device *pdev)
}
}
- desc = &info->desc;
- desc->name = "usb-charger";
- desc->properties = usb_charger_properties;
- desc->num_properties = ARRAY_SIZE(usb_charger_properties);
- desc->get_property = usb_charger_get_property;
- desc->type = POWER_SUPPLY_TYPE_USB;
- cfg.drv_data = info;
-
- info->charger = devm_power_supply_register(dev, desc, &cfg);
- if (IS_ERR(info->charger)) {
- dev_err(dev, "Unable to register charger\n");
- return PTR_ERR(info->charger);
- }
-
platform_set_drvdata(pdev, info);
/* Perform initial detection */
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 2218941d35a3..b97464498763 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1162,7 +1162,7 @@ static int do_proc_control(struct usb_dev_state *ps,
tbuf, ctrl->wLength);
usb_unlock_device(dev);
- i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl->bRequest,
+ i = usb_control_msg(dev, pipe, ctrl->bRequest,
ctrl->bRequestType, ctrl->wValue, ctrl->wIndex,
tbuf, ctrl->wLength, tmo);
usb_lock_device(dev);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 6119fb41d736..0f8b7c93310e 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2111,6 +2111,136 @@ int usb_hcd_get_frame_number (struct usb_device *udev)
}
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_HCD_TEST_MODE
+
+static void usb_ehset_completion(struct urb *urb)
+{
+ struct completion *done = urb->context;
+
+ complete(done);
+}
+/*
+ * Allocate and initialize a control URB. This request will be used by the
+ * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
+ * of the GetDescriptor request are sent 15 seconds after the SETUP stage.
+ * Return NULL if failed.
+ */
+static struct urb *request_single_step_set_feature_urb(
+ struct usb_device *udev,
+ void *dr,
+ void *buf,
+ struct completion *done)
+{
+ struct urb *urb;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ struct usb_host_endpoint *ep;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return NULL;
+
+ urb->pipe = usb_rcvctrlpipe(udev, 0);
+ ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
+ [usb_pipeendpoint(urb->pipe)];
+ if (!ep) {
+ usb_free_urb(urb);
+ return NULL;
+ }
+
+ urb->ep = ep;
+ urb->dev = udev;
+ urb->setup_packet = (void *)dr;
+ urb->transfer_buffer = buf;
+ urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
+ urb->complete = usb_ehset_completion;
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+ urb->transfer_flags = URB_DIR_IN;
+ usb_get_urb(urb);
+ atomic_inc(&urb->use_count);
+ atomic_inc(&urb->dev->urbnum);
+ if (map_urb_for_dma(hcd, urb, GFP_KERNEL)) {
+ usb_put_urb(urb);
+ usb_free_urb(urb);
+ return NULL;
+ }
+
+ urb->context = done;
+ return urb;
+}
+
+int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
+{
+ int retval = -ENOMEM;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ struct usb_device *udev;
+ struct usb_device_descriptor *buf;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ /* Obtain udev of the rhub's child port */
+ udev = usb_hub_find_child(hcd->self.root_hub, port);
+ if (!udev) {
+ dev_err(hcd->self.controller, "No device attached to the RootHub\n");
+ return -ENODEV;
+ }
+ buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ if (!dr) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ /* Fill Setup packet for GetDescriptor */
+ dr->bRequestType = USB_DIR_IN;
+ dr->bRequest = USB_REQ_GET_DESCRIPTOR;
+ dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
+ dr->wIndex = 0;
+ dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
+ urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
+ if (!urb)
+ goto cleanup;
+
+ /* Submit just the SETUP stage */
+ retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 1);
+ if (retval)
+ goto out1;
+ if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
+ usb_kill_urb(urb);
+ retval = -ETIMEDOUT;
+ dev_err(hcd->self.controller,
+ "%s SETUP stage timed out on ep0\n", __func__);
+ goto out1;
+ }
+ msleep(15 * 1000);
+
+ /* Complete remaining DATA and STATUS stages using the same URB */
+ urb->status = -EINPROGRESS;
+ usb_get_urb(urb);
+ atomic_inc(&urb->use_count);
+ atomic_inc(&urb->dev->urbnum);
+ retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 0);
+ if (!retval && !wait_for_completion_timeout(&done,
+ msecs_to_jiffies(2000))) {
+ usb_kill_urb(urb);
+ retval = -ETIMEDOUT;
+ dev_err(hcd->self.controller,
+ "%s IN stage timed out on ep0\n", __func__);
+ }
+out1:
+ usb_free_urb(urb);
+cleanup:
+ kfree(dr);
+ kfree(buf);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(ehset_single_step_set_feature);
+#endif /* CONFIG_USB_HCD_TEST_MODE */
+
+/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index df8e69e60aaf..d1efc7141333 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2434,6 +2434,8 @@ static void set_usb_port_removable(struct usb_device *udev)
u16 wHubCharacteristics;
bool removable = true;
+ dev_set_removable(&udev->dev, DEVICE_REMOVABLE_UNKNOWN);
+
if (!hdev)
return;
@@ -2445,11 +2447,11 @@ static void set_usb_port_removable(struct usb_device *udev)
*/
switch (hub->ports[udev->portnum - 1]->connect_type) {
case USB_PORT_CONNECT_TYPE_HOT_PLUG:
- udev->removable = USB_DEVICE_REMOVABLE;
+ dev_set_removable(&udev->dev, DEVICE_REMOVABLE);
return;
case USB_PORT_CONNECT_TYPE_HARD_WIRED:
case USB_PORT_NOT_USED:
- udev->removable = USB_DEVICE_FIXED;
+ dev_set_removable(&udev->dev, DEVICE_FIXED);
return;
default:
break;
@@ -2474,9 +2476,9 @@ static void set_usb_port_removable(struct usb_device *udev)
}
if (removable)
- udev->removable = USB_DEVICE_REMOVABLE;
+ dev_set_removable(&udev->dev, DEVICE_REMOVABLE);
else
- udev->removable = USB_DEVICE_FIXED;
+ dev_set_removable(&udev->dev, DEVICE_FIXED);
}
@@ -2548,8 +2550,7 @@ int usb_new_device(struct usb_device *udev)
device_enable_async_suspend(&udev->dev);
/* check whether the hub or firmware marks this port as non-removable */
- if (udev->parent)
- set_usb_port_removable(udev);
+ set_usb_port_removable(udev);
/* Register the device. The device driver is responsible
* for configuring the device and invoking the add-device
@@ -3387,6 +3388,26 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
status = 0;
}
if (status) {
+ /* Check if the port has been suspended for the timeout case
+ * to prevent the suspended port from incorrect handling.
+ */
+ if (status == -ETIMEDOUT) {
+ int ret;
+ u16 portstatus, portchange;
+
+ portstatus = portchange = 0;
+ ret = hub_port_status(hub, port1, &portstatus,
+ &portchange);
+
+ dev_dbg(&port_dev->dev,
+ "suspend timeout, status %04x\n", portstatus);
+
+ if (ret == 0 && port_is_suspended(hub, portstatus)) {
+ status = 0;
+ goto suspend_done;
+ }
+ }
+
dev_dbg(&port_dev->dev, "can't suspend, status %d\n", status);
/* Try to enable USB3 LTM again */
@@ -3403,6 +3424,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
if (!PMSG_IS_AUTO(msg))
status = 0;
} else {
+ suspend_done:
dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n",
(PMSG_IS_AUTO(msg) ? "auto-" : ""),
udev->do_remote_wakeup);
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 30e9e680c74c..4d59d927ae3e 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -783,6 +783,9 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type,
int i;
int result;
+ if (size <= 0) /* No point in asking for no data */
+ return -EINVAL;
+
memset(buf, 0, size); /* Make sure we parse really received data */
for (i = 0; i < 3; ++i) {
@@ -832,6 +835,9 @@ static int usb_get_string(struct usb_device *dev, unsigned short langid,
int i;
int result;
+ if (size <= 0) /* No point in asking for no data */
+ return -EINVAL;
+
for (i = 0; i < 3; ++i) {
/* retry on length 0 or stall; some devices are flakey */
result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 21e7522655ac..6114cf83bb44 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -406,7 +406,6 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Realtek hub in Dell WD19 (Type-C) */
{ USB_DEVICE(0x0bda, 0x0487), .driver_info = USB_QUIRK_NO_LPM },
- { USB_DEVICE(0x0bda, 0x5487), .driver_info = USB_QUIRK_RESET_RESUME },
/* Generic RTL8153 based ethernet adapters */
{ USB_DEVICE(0x0bda, 0x8153), .driver_info = USB_QUIRK_NO_LPM },
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 5a168ba9fc51..fa2e49d432ff 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -301,29 +301,6 @@ static ssize_t urbnum_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(urbnum);
-static ssize_t removable_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct usb_device *udev;
- char *state;
-
- udev = to_usb_device(dev);
-
- switch (udev->removable) {
- case USB_DEVICE_REMOVABLE:
- state = "removable";
- break;
- case USB_DEVICE_FIXED:
- state = "fixed";
- break;
- default:
- state = "unknown";
- }
-
- return sprintf(buf, "%s\n", state);
-}
-static DEVICE_ATTR_RO(removable);
-
static ssize_t ltm_capable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -828,7 +805,6 @@ static struct attribute *dev_attrs[] = {
&dev_attr_avoid_reset_quirk.attr,
&dev_attr_authorized.attr,
&dev_attr_remove.attr,
- &dev_attr_removable.attr,
&dev_attr_ltm_capable.attr,
#ifdef CONFIG_OF
&dev_attr_devspec.attr,
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 357b149b20d3..30727729a44c 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -407,6 +407,15 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
return -ENOEXEC;
is_out = !(setup->bRequestType & USB_DIR_IN) ||
!setup->wLength;
+ dev_WARN_ONCE(&dev->dev, (usb_pipeout(urb->pipe) != is_out),
+ "BOGUS control dir, pipe %x doesn't match bRequestType %x\n",
+ urb->pipe, setup->bRequestType);
+ if (le16_to_cpu(setup->wLength) != urb->transfer_buffer_length) {
+ dev_dbg(&dev->dev, "BOGUS control len %d doesn't match transfer length %d\n",
+ le16_to_cpu(setup->wLength),
+ urb->transfer_buffer_length);
+ return -EBADR;
+ }
} else {
is_out = usb_endpoint_dir_out(&ep->desc);
}
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 6f70ab9577b4..272ae5722c86 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -1111,15 +1111,6 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
if (hsotg->params.phy_utmi_width == 16)
usbcfg |= GUSBCFG_PHYIF16;
-
- /* Set turnaround time */
- if (dwc2_is_device_mode(hsotg)) {
- usbcfg &= ~GUSBCFG_USBTRDTIM_MASK;
- if (hsotg->params.phy_utmi_width == 16)
- usbcfg |= 5 << GUSBCFG_USBTRDTIM_SHIFT;
- else
- usbcfg |= 9 << GUSBCFG_USBTRDTIM_SHIFT;
- }
break;
default:
dev_err(hsotg->dev, "FS PHY selected at HS!\n");
@@ -1141,6 +1132,24 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
return retval;
}
+static void dwc2_set_turnaround_time(struct dwc2_hsotg *hsotg)
+{
+ u32 usbcfg;
+
+ if (hsotg->params.phy_type != DWC2_PHY_TYPE_PARAM_UTMI)
+ return;
+
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
+
+ usbcfg &= ~GUSBCFG_USBTRDTIM_MASK;
+ if (hsotg->params.phy_utmi_width == 16)
+ usbcfg |= 5 << GUSBCFG_USBTRDTIM_SHIFT;
+ else
+ usbcfg |= 9 << GUSBCFG_USBTRDTIM_SHIFT;
+
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
+}
+
int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
{
u32 usbcfg;
@@ -1158,6 +1167,9 @@ int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
retval = dwc2_hs_phy_init(hsotg, select_phy);
if (retval)
return retval;
+
+ if (dwc2_is_device_mode(hsotg))
+ dwc2_set_turnaround_time(hsotg);
}
if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 184964174dc0..c581ee41ac81 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -1496,8 +1496,8 @@ static int dwc2_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req,
{
struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hs = hs_ep->parent;
- unsigned long flags = 0;
- int ret = 0;
+ unsigned long flags;
+ int ret;
spin_lock_irqsave(&hs->lock, flags);
ret = dwc2_hsotg_ep_queue(ep, req, gfp_flags);
@@ -3338,7 +3338,7 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
/**
- * dwc2_hsotg_core_init - issue softreset to the core
+ * dwc2_hsotg_core_init_disconnected - issue softreset to the core
* @hsotg: The device state
* @is_usb_reset: Usb resetting flag
*
@@ -4374,8 +4374,8 @@ static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
{
struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hs = hs_ep->parent;
- unsigned long flags = 0;
- int ret = 0;
+ unsigned long flags;
+ int ret;
spin_lock_irqsave(&hs->lock, flags);
ret = dwc2_hsotg_ep_sethalt(ep, value, false);
@@ -4505,7 +4505,7 @@ err:
static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
{
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
- unsigned long flags = 0;
+ unsigned long flags;
int ep;
if (!hsotg)
@@ -4577,7 +4577,7 @@ static int dwc2_hsotg_set_selfpowered(struct usb_gadget *gadget,
static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on)
{
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
- unsigned long flags = 0;
+ unsigned long flags;
dev_dbg(hsotg->dev, "%s: is_on: %d op_state: %d\n", __func__, is_on,
hsotg->op_state);
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 621a4846bd05..89a788326c56 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -675,7 +675,7 @@ static int dwc2_hs_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
}
/**
- * dwc2_ls_pmap_unschedule() - Undo work done by dwc2_hs_pmap_schedule()
+ * dwc2_hs_pmap_unschedule() - Undo work done by dwc2_hs_pmap_schedule()
*
* @hsotg: The HCD state structure for the DWC OTG controller.
* @qh: QH for the periodic transfer.
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 7a6089fa81e1..67c5eb140232 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -784,8 +784,8 @@ static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
}
/**
- * During device initialization, read various hardware configuration
- * registers and interpret the contents.
+ * dwc2_get_hwparams() - During device initialization, read various hardware
+ * configuration registers and interpret the contents.
*
* @hsotg: Programming view of the DWC_otg controller
*
diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c
index 0000151e3ca9..a93559b4ecdb 100644
--- a/drivers/usb/dwc2/pci.c
+++ b/drivers/usb/dwc2/pci.c
@@ -64,7 +64,7 @@ struct dwc2_pci_glue {
};
/**
- * dwc2_pci_probe() - Provides the cleanup entry points for the DWC_otg PCI
+ * dwc2_pci_remove() - Provides the cleanup entry points for the DWC_otg PCI
* driver
*
* @pci: The programming view of DWC_otg PCI
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 520a0beef77c..c8f18f3ba9e3 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -408,7 +408,7 @@ static bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg)
}
/**
- * Check core version
+ * dwc2_check_core_version() - Check core version
*
* @hsotg: Programming view of the DWC_otg controller
*
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 4ac397e43e19..ba74ad7f6995 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1545,6 +1545,10 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
+ ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
+ if (ret)
+ return ret;
+
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset))
return PTR_ERR(dwc->reset);
@@ -1616,17 +1620,18 @@ static int dwc3_probe(struct platform_device *pdev)
}
dwc3_check_params(dwc);
+ dwc3_debugfs_init(dwc);
ret = dwc3_core_init_mode(dwc);
if (ret)
goto err5;
- dwc3_debugfs_init(dwc);
pm_runtime_put(dev);
return 0;
err5:
+ dwc3_debugfs_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
usb_phy_shutdown(dwc->usb2_phy);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index c5d5760cdf53..dccdf13b5f9e 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1013,7 +1013,6 @@ struct dwc3_scratchpad_array {
* @link_state: link state
* @speed: device speed (super, high, full, low)
* @hwparams: copy of hwparams registers
- * @root: debugfs root folder pointer
* @regset: debugfs pointer to regdump file
* @dbg_lsp_select: current debug lsp mux register selection
* @test_mode: true when we're entering a USB test mode
@@ -1222,7 +1221,6 @@ struct dwc3 {
u8 num_eps;
struct dwc3_hwparams hwparams;
- struct dentry *root;
struct debugfs_regset32 *regset;
u32 dbg_lsp_select;
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 5dbbe53269d3..f2b7675c7f62 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -889,8 +889,10 @@ static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep,
void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep)
{
struct dentry *dir;
+ struct dentry *root;
- dir = debugfs_create_dir(dep->name, dep->dwc->root);
+ root = debugfs_lookup(dev_name(dep->dwc->dev), usb_debug_root);
+ dir = debugfs_create_dir(dep->name, root);
dwc3_debugfs_create_endpoint_files(dep, dir);
}
@@ -909,8 +911,6 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root);
- dwc->root = root;
-
debugfs_create_regset32("regdump", 0444, root, dwc->regset);
debugfs_create_file("lsp_dump", 0644, root, dwc, &dwc3_lsp_fops);
@@ -929,6 +929,6 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
void dwc3_debugfs_exit(struct dwc3 *dwc)
{
- debugfs_remove_recursive(dwc->root);
+ debugfs_remove(debugfs_lookup(dev_name(dwc->dev), usb_debug_root));
kfree(dwc->regset);
}
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index e2b68bb770d1..8fcbac10510c 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -596,7 +596,6 @@ int dwc3_drd_init(struct dwc3 *dwc)
dwc3_drd_update(dwc);
} else {
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
- dwc->current_dr_role = DWC3_GCTL_PRTCAP_OTG;
/* use OTG block to get ID event */
irq = dwc3_otg_get_irq(dwc);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 1e51460938b8..2b37bdd39a74 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -36,7 +36,7 @@
#define PCI_DEVICE_ID_INTEL_CNPH 0xa36e
#define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0
#define PCI_DEVICE_ID_INTEL_ICLLP 0x34ee
-#define PCI_DEVICE_ID_INTEL_EHLLP 0x4b7e
+#define PCI_DEVICE_ID_INTEL_EHL 0x4b7e
#define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee
#define PCI_DEVICE_ID_INTEL_TGPH 0x43ee
#define PCI_DEVICE_ID_INTEL_JSP 0x4dee
@@ -167,7 +167,7 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc)
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
if (pdev->device == PCI_DEVICE_ID_INTEL_BXT ||
pdev->device == PCI_DEVICE_ID_INTEL_BXT_M ||
- pdev->device == PCI_DEVICE_ID_INTEL_EHLLP) {
+ pdev->device == PCI_DEVICE_ID_INTEL_EHL) {
guid_parse(PCI_INTEL_BXT_DSM_GUID, &dwc->guid);
dwc->has_dsm_for_pm = true;
}
@@ -375,8 +375,8 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICLLP),
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_EHLLP),
- (kernel_ulong_t) &dwc3_pci_intel_swnode },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_EHL),
+ (kernel_ulong_t) &dwc3_pci_intel_swnode, },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGPLP),
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f14c2aa83759..af6d7f157989 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2798,7 +2798,9 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
list_del(&dep->endpoint.ep_list);
}
- debugfs_remove_recursive(debugfs_lookup(dep->name, dwc->root));
+ debugfs_remove_recursive(debugfs_lookup(dep->name,
+ debugfs_lookup(dev_name(dep->dwc->dev),
+ usb_debug_root)));
kfree(dep);
}
}
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index 51d18e8d1602..cb998ba50fea 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -222,8 +222,6 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
TP_STRUCT__entry(
__string(name, dep->name)
__field(struct dwc3_trb *, trb)
- __field(u32, allocated)
- __field(u32, queued)
__field(u32, bpl)
__field(u32, bph)
__field(u32, size)
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index 2cd9942707b4..5d38f29bda72 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -30,6 +30,11 @@ struct f_eem {
u8 ctrl_id;
};
+struct in_context {
+ struct sk_buff *skb;
+ struct usb_ep *ep;
+};
+
static inline struct f_eem *func_to_eem(struct usb_function *f)
{
return container_of(f, struct f_eem, port.func);
@@ -320,9 +325,12 @@ fail:
static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
{
- struct sk_buff *skb = (struct sk_buff *)req->context;
+ struct in_context *ctx = req->context;
- dev_kfree_skb_any(skb);
+ dev_kfree_skb_any(ctx->skb);
+ kfree(req->buf);
+ usb_ep_free_request(ctx->ep, req);
+ kfree(ctx);
}
/*
@@ -410,7 +418,9 @@ static int eem_unwrap(struct gether *port,
* b15: bmType (0 == data, 1 == command)
*/
if (header & BIT(15)) {
- struct usb_request *req = cdev->req;
+ struct usb_request *req;
+ struct in_context *ctx;
+ struct usb_ep *ep;
u16 bmEEMCmd;
/* EEM command packet format:
@@ -439,11 +449,36 @@ static int eem_unwrap(struct gether *port,
skb_trim(skb2, len);
put_unaligned_le16(BIT(15) | BIT(11) | len,
skb_push(skb2, 2));
+
+ ep = port->in_ep;
+ req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (!req) {
+ dev_kfree_skb_any(skb2);
+ goto next;
+ }
+
+ req->buf = kmalloc(skb2->len, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ dev_kfree_skb_any(skb2);
+ goto next;
+ }
+
+ ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ dev_kfree_skb_any(skb2);
+ goto next;
+ }
+ ctx->skb = skb2;
+ ctx->ep = ep;
+
skb_copy_bits(skb2, 0, req->buf, skb2->len);
req->length = skb2->len;
req->complete = eem_cmd_complete;
req->zero = 1;
- req->context = skb2;
+ req->context = ctx;
if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC))
DBG(cdev, "echo response queue fail\n");
break;
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index d4844afeaffc..9c0c393abb39 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -250,8 +250,8 @@ EXPORT_SYMBOL_GPL(ffs_lock);
static struct ffs_dev *_ffs_find_dev(const char *name);
static struct ffs_dev *_ffs_alloc_dev(void);
static void _ffs_free_dev(struct ffs_dev *dev);
-static void *ffs_acquire_dev(const char *dev_name);
-static void ffs_release_dev(struct ffs_data *ffs_data);
+static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data);
+static void ffs_release_dev(struct ffs_dev *ffs_dev);
static int ffs_ready(struct ffs_data *ffs);
static void ffs_closed(struct ffs_data *ffs);
@@ -1554,8 +1554,8 @@ unmapped_value:
static int ffs_fs_get_tree(struct fs_context *fc)
{
struct ffs_sb_fill_data *ctx = fc->fs_private;
- void *ffs_dev;
struct ffs_data *ffs;
+ int ret;
ENTER();
@@ -1574,13 +1574,12 @@ static int ffs_fs_get_tree(struct fs_context *fc)
return -ENOMEM;
}
- ffs_dev = ffs_acquire_dev(ffs->dev_name);
- if (IS_ERR(ffs_dev)) {
+ ret = ffs_acquire_dev(ffs->dev_name, ffs);
+ if (ret) {
ffs_data_put(ffs);
- return PTR_ERR(ffs_dev);
+ return ret;
}
- ffs->private_data = ffs_dev;
ctx->ffs_data = ffs;
return get_tree_nodev(fc, ffs_sb_fill);
}
@@ -1591,7 +1590,6 @@ static void ffs_fs_free_fc(struct fs_context *fc)
if (ctx) {
if (ctx->ffs_data) {
- ffs_release_dev(ctx->ffs_data);
ffs_data_put(ctx->ffs_data);
}
@@ -1630,10 +1628,8 @@ ffs_fs_kill_sb(struct super_block *sb)
ENTER();
kill_litter_super(sb);
- if (sb->s_fs_info) {
- ffs_release_dev(sb->s_fs_info);
+ if (sb->s_fs_info)
ffs_data_closed(sb->s_fs_info);
- }
}
static struct file_system_type ffs_fs_type = {
@@ -1703,6 +1699,7 @@ static void ffs_data_put(struct ffs_data *ffs)
if (refcount_dec_and_test(&ffs->ref)) {
pr_info("%s(): freeing\n", __func__);
ffs_data_clear(ffs);
+ ffs_release_dev(ffs->private_data);
BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
swait_active(&ffs->ep0req_completion.wait) ||
waitqueue_active(&ffs->wait));
@@ -3032,6 +3029,7 @@ static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
struct ffs_function *func = ffs_func_from_usb(f);
struct f_fs_opts *ffs_opts =
container_of(f->fi, struct f_fs_opts, func_inst);
+ struct ffs_data *ffs_data;
int ret;
ENTER();
@@ -3046,12 +3044,13 @@ static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
if (!ffs_opts->no_configfs)
ffs_dev_lock();
ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV;
- func->ffs = ffs_opts->dev->ffs_data;
+ ffs_data = ffs_opts->dev->ffs_data;
if (!ffs_opts->no_configfs)
ffs_dev_unlock();
if (ret)
return ERR_PTR(ret);
+ func->ffs = ffs_data;
func->conf = c;
func->gadget = c->cdev->gadget;
@@ -3506,6 +3505,7 @@ static void ffs_free_inst(struct usb_function_instance *f)
struct f_fs_opts *opts;
opts = to_f_fs_opts(f);
+ ffs_release_dev(opts->dev);
ffs_dev_lock();
_ffs_free_dev(opts->dev);
ffs_dev_unlock();
@@ -3693,47 +3693,48 @@ static void _ffs_free_dev(struct ffs_dev *dev)
{
list_del(&dev->entry);
- /* Clear the private_data pointer to stop incorrect dev access */
- if (dev->ffs_data)
- dev->ffs_data->private_data = NULL;
-
kfree(dev);
if (list_empty(&ffs_devices))
functionfs_cleanup();
}
-static void *ffs_acquire_dev(const char *dev_name)
+static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data)
{
+ int ret = 0;
struct ffs_dev *ffs_dev;
ENTER();
ffs_dev_lock();
ffs_dev = _ffs_find_dev(dev_name);
- if (!ffs_dev)
- ffs_dev = ERR_PTR(-ENOENT);
- else if (ffs_dev->mounted)
- ffs_dev = ERR_PTR(-EBUSY);
- else if (ffs_dev->ffs_acquire_dev_callback &&
- ffs_dev->ffs_acquire_dev_callback(ffs_dev))
- ffs_dev = ERR_PTR(-ENOENT);
- else
+ if (!ffs_dev) {
+ ret = -ENOENT;
+ } else if (ffs_dev->mounted) {
+ ret = -EBUSY;
+ } else if (ffs_dev->ffs_acquire_dev_callback &&
+ ffs_dev->ffs_acquire_dev_callback(ffs_dev)) {
+ ret = -ENOENT;
+ } else {
ffs_dev->mounted = true;
+ ffs_dev->ffs_data = ffs_data;
+ ffs_data->private_data = ffs_dev;
+ }
ffs_dev_unlock();
- return ffs_dev;
+ return ret;
}
-static void ffs_release_dev(struct ffs_data *ffs_data)
+static void ffs_release_dev(struct ffs_dev *ffs_dev)
{
- struct ffs_dev *ffs_dev;
-
ENTER();
ffs_dev_lock();
- ffs_dev = ffs_data->private_data;
- if (ffs_dev) {
+ if (ffs_dev && ffs_dev->mounted) {
ffs_dev->mounted = false;
+ if (ffs_dev->ffs_data) {
+ ffs_dev->ffs_data->private_data = NULL;
+ ffs_dev->ffs_data = NULL;
+ }
if (ffs_dev->ffs_release_dev_callback)
ffs_dev->ffs_release_dev_callback(ffs_dev);
@@ -3761,7 +3762,6 @@ static int ffs_ready(struct ffs_data *ffs)
}
ffs_obj->desc_ready = true;
- ffs_obj->ffs_data = ffs;
if (ffs_obj->ffs_ready_callback) {
ret = ffs_obj->ffs_ready_callback(ffs);
@@ -3789,7 +3789,6 @@ static void ffs_closed(struct ffs_data *ffs)
goto done;
ffs_obj->desc_ready = false;
- ffs_obj->ffs_data = NULL;
if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags) &&
ffs_obj->ffs_closed_callback)
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index e55699308117..02683ac0719d 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -88,7 +88,7 @@ static struct usb_interface_descriptor hidg_interface_desc = {
static struct hid_descriptor hidg_desc = {
.bLength = sizeof hidg_desc,
.bDescriptorType = HID_DT_HID,
- .bcdHID = 0x0101,
+ .bcdHID = cpu_to_le16(0x0101),
.bCountryCode = 0x00,
.bNumDescriptors = 0x1,
/*.desc[0].bDescriptorType = DYNAMIC */
@@ -1118,7 +1118,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
hidg->func.setup = hidg_setup;
hidg->func.free_func = hidg_free;
- /* this could me made configurable at some point */
+ /* this could be made configurable at some point */
hidg->qlen = 4;
return &hidg->func;
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index 59d382fe1bbf..abec5c58f525 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -667,8 +667,7 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
value = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC);
spin_lock(&dev->lock);
if (value) {
- list_del(&req->list);
- list_add(&req->list, &dev->tx_reqs);
+ list_move(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 7aa4c8bc5a1a..ae29ff2b2b68 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -44,6 +44,7 @@
#define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
+#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC)
struct f_uac2 {
struct g_audio g_audio;
@@ -273,7 +274,7 @@ static struct usb_endpoint_descriptor fs_epout_desc = {
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+ /* .bmAttributes = DYNAMIC */
/* .wMaxPacketSize = DYNAMIC */
.bInterval = 1,
};
@@ -282,7 +283,7 @@ static struct usb_endpoint_descriptor hs_epout_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
- .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+ /* .bmAttributes = DYNAMIC */
/* .wMaxPacketSize = DYNAMIC */
.bInterval = 4,
};
@@ -292,7 +293,7 @@ static struct usb_endpoint_descriptor ss_epout_desc = {
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+ /* .bmAttributes = DYNAMIC */
/* .wMaxPacketSize = DYNAMIC */
.bInterval = 4,
};
@@ -317,6 +318,37 @@ static struct uac2_iso_endpoint_descriptor as_iso_out_desc = {
.wLockDelay = 0,
};
+/* STD AS ISO IN Feedback Endpoint */
+static struct usb_endpoint_descriptor fs_epin_fback_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK,
+ .wMaxPacketSize = cpu_to_le16(3),
+ .bInterval = 1,
+};
+
+static struct usb_endpoint_descriptor hs_epin_fback_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK,
+ .wMaxPacketSize = cpu_to_le16(4),
+ .bInterval = 4,
+};
+
+static struct usb_endpoint_descriptor ss_epin_fback_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK,
+ .wMaxPacketSize = cpu_to_le16(4),
+ .bInterval = 4,
+};
+
+
/* Audio Streaming IN Interface - Alt0 */
static struct usb_interface_descriptor std_as_in_if0_desc = {
.bLength = sizeof std_as_in_if0_desc,
@@ -431,6 +463,7 @@ static struct usb_descriptor_header *fs_audio_desc[] = {
(struct usb_descriptor_header *)&as_out_fmt1_desc,
(struct usb_descriptor_header *)&fs_epout_desc,
(struct usb_descriptor_header *)&as_iso_out_desc,
+ (struct usb_descriptor_header *)&fs_epin_fback_desc,
(struct usb_descriptor_header *)&std_as_in_if0_desc,
(struct usb_descriptor_header *)&std_as_in_if1_desc,
@@ -461,6 +494,7 @@ static struct usb_descriptor_header *hs_audio_desc[] = {
(struct usb_descriptor_header *)&as_out_fmt1_desc,
(struct usb_descriptor_header *)&hs_epout_desc,
(struct usb_descriptor_header *)&as_iso_out_desc,
+ (struct usb_descriptor_header *)&hs_epin_fback_desc,
(struct usb_descriptor_header *)&std_as_in_if0_desc,
(struct usb_descriptor_header *)&std_as_in_if1_desc,
@@ -492,6 +526,7 @@ static struct usb_descriptor_header *ss_audio_desc[] = {
(struct usb_descriptor_header *)&ss_epout_desc,
(struct usb_descriptor_header *)&ss_epout_desc_comp,
(struct usb_descriptor_header *)&as_iso_out_desc,
+ (struct usb_descriptor_header *)&ss_epin_fback_desc,
(struct usb_descriptor_header *)&std_as_in_if0_desc,
(struct usb_descriptor_header *)&std_as_in_if1_desc,
@@ -549,8 +584,11 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
ssize = uac2_opts->c_ssize;
}
+ if (!is_playback && (uac2_opts->c_sync == USB_ENDPOINT_SYNC_ASYNC))
+ srate = srate * (1000 + uac2_opts->fb_max) / 1000;
+
max_size_bw = num_channels(chmask) * ssize *
- ((srate / (factor / (1 << (ep_desc->bInterval - 1)))) + 1);
+ DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1)));
ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw,
max_size_ep));
@@ -568,22 +606,26 @@ static void setup_headers(struct f_uac2_opts *opts,
struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL;
struct usb_endpoint_descriptor *epout_desc;
struct usb_endpoint_descriptor *epin_desc;
+ struct usb_endpoint_descriptor *epin_fback_desc;
int i;
switch (speed) {
case USB_SPEED_FULL:
epout_desc = &fs_epout_desc;
epin_desc = &fs_epin_desc;
+ epin_fback_desc = &fs_epin_fback_desc;
break;
case USB_SPEED_HIGH:
epout_desc = &hs_epout_desc;
epin_desc = &hs_epin_desc;
+ epin_fback_desc = &hs_epin_fback_desc;
break;
default:
epout_desc = &ss_epout_desc;
epin_desc = &ss_epin_desc;
epout_desc_comp = &ss_epout_desc_comp;
epin_desc_comp = &ss_epin_desc_comp;
+ epin_fback_desc = &ss_epin_fback_desc;
}
i = 0;
@@ -611,6 +653,9 @@ static void setup_headers(struct f_uac2_opts *opts,
headers[i++] = USBDHDR(epout_desc_comp);
headers[i++] = USBDHDR(&as_iso_out_desc);
+
+ if (EPOUT_FBACK_IN_EN(opts))
+ headers[i++] = USBDHDR(epin_fback_desc);
}
if (EPIN_EN(opts)) {
headers[i++] = USBDHDR(&std_as_in_if0_desc);
@@ -781,6 +826,23 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
std_as_out_if1_desc.bInterfaceNumber = ret;
uac2->as_out_intf = ret;
uac2->as_out_alt = 0;
+
+ if (EPOUT_FBACK_IN_EN(uac2_opts)) {
+ fs_epout_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
+ hs_epout_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
+ ss_epout_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
+ std_as_out_if1_desc.bNumEndpoints++;
+ } else {
+ fs_epout_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
+ hs_epout_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
+ ss_epout_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
+ }
}
if (EPIN_EN(uac2_opts)) {
@@ -844,6 +906,15 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -ENODEV;
}
+ if (EPOUT_FBACK_IN_EN(uac2_opts)) {
+ agdev->in_ep_fback = usb_ep_autoconfig(gadget,
+ &fs_epin_fback_desc);
+ if (!agdev->in_ep_fback) {
+ dev_err(dev, "%s:%d Error!\n",
+ __func__, __LINE__);
+ return -ENODEV;
+ }
+ }
}
if (EPIN_EN(uac2_opts)) {
@@ -867,8 +938,10 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
le16_to_cpu(ss_epout_desc.wMaxPacketSize));
hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
+ hs_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress;
hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
ss_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
+ ss_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress;
ss_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
setup_descriptor(uac2_opts);
@@ -887,6 +960,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->params.c_srate = uac2_opts->c_srate;
agdev->params.c_ssize = uac2_opts->c_ssize;
agdev->params.req_number = uac2_opts->req_number;
+ agdev->params.fb_max = uac2_opts->fb_max;
ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget");
if (ret)
goto err_free_descs;
@@ -1195,13 +1269,71 @@ end: \
\
CONFIGFS_ATTR(f_uac2_opts_, name)
+#define UAC2_ATTRIBUTE_SYNC(name) \
+static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ int result; \
+ char *str; \
+ \
+ mutex_lock(&opts->lock); \
+ switch (opts->name) { \
+ case USB_ENDPOINT_SYNC_ASYNC: \
+ str = "async"; \
+ break; \
+ case USB_ENDPOINT_SYNC_ADAPTIVE: \
+ str = "adaptive"; \
+ break; \
+ default: \
+ str = "unknown"; \
+ break; \
+ } \
+ result = sprintf(page, "%s\n", str); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ int ret = 0; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ if (!strncmp(page, "async", 5)) \
+ opts->name = USB_ENDPOINT_SYNC_ASYNC; \
+ else if (!strncmp(page, "adaptive", 8)) \
+ opts->name = USB_ENDPOINT_SYNC_ADAPTIVE; \
+ else { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ \
+ ret = len; \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac2_opts_, name)
+
UAC2_ATTRIBUTE(p_chmask);
UAC2_ATTRIBUTE(p_srate);
UAC2_ATTRIBUTE(p_ssize);
UAC2_ATTRIBUTE(c_chmask);
UAC2_ATTRIBUTE(c_srate);
+UAC2_ATTRIBUTE_SYNC(c_sync);
UAC2_ATTRIBUTE(c_ssize);
UAC2_ATTRIBUTE(req_number);
+UAC2_ATTRIBUTE(fb_max);
static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_p_chmask,
@@ -1210,7 +1342,9 @@ static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_c_chmask,
&f_uac2_opts_attr_c_srate,
&f_uac2_opts_attr_c_ssize,
+ &f_uac2_opts_attr_c_sync,
&f_uac2_opts_attr_req_number,
+ &f_uac2_opts_attr_fb_max,
NULL,
};
@@ -1248,7 +1382,9 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->c_chmask = UAC2_DEF_CCHMASK;
opts->c_srate = UAC2_DEF_CSRATE;
opts->c_ssize = UAC2_DEF_CSSIZE;
+ opts->c_sync = UAC2_DEF_CSYNC;
opts->req_number = UAC2_DEF_REQ_NUM;
+ opts->fb_max = UAC2_DEF_FB_MAX;
return &opts->func_inst;
}
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 5fbceee897a3..018dd0978995 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -16,6 +16,7 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <sound/control.h>
#include "u_audio.h"
@@ -35,9 +36,13 @@ struct uac_rtd_params {
void *rbuf;
+ unsigned int pitch; /* Stream pitch ratio to 1000000 */
unsigned int max_psize; /* MaxPacketSize of endpoint */
struct usb_request **reqs;
+
+ struct usb_request *req_fback; /* Feedback endpoint request */
+ bool fb_ep_enabled; /* if the ep is enabled */
};
struct snd_uac_chip {
@@ -70,6 +75,48 @@ static const struct snd_pcm_hardware uac_pcm_hardware = {
.periods_min = MIN_PERIODS,
};
+static void u_audio_set_fback_frequency(enum usb_device_speed speed,
+ unsigned long long freq,
+ unsigned int pitch,
+ void *buf)
+{
+ u32 ff = 0;
+
+ /*
+ * Because the pitch base is 1000000, the final divider here
+ * will be 1000 * 1000000 = 1953125 << 9
+ *
+ * Instead of dealing with big numbers lets fold this 9 left shift
+ */
+
+ if (speed == USB_SPEED_FULL) {
+ /*
+ * Full-speed feedback endpoints report frequency
+ * in samples/frame
+ * Format is encoded in Q10.10 left-justified in the 24 bits,
+ * so that it has a Q10.14 format.
+ *
+ * ff = (freq << 14) / 1000
+ */
+ freq <<= 5;
+ } else {
+ /*
+ * High-speed feedback endpoints report frequency
+ * in samples/microframe.
+ * Format is encoded in Q12.13 fitted into four bytes so that
+ * the binary point is located between the second and the third
+ * byte fromat (that is Q16.16)
+ *
+ * ff = (freq << 16) / 8000
+ */
+ freq <<= 4;
+ }
+
+ ff = DIV_ROUND_CLOSEST_ULL((freq * pitch), 1953125);
+
+ *(__le32 *)buf = cpu_to_le32(ff);
+}
+
static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
{
unsigned int pending;
@@ -173,6 +220,35 @@ exit:
dev_err(uac->card->dev, "%d Error!\n", __LINE__);
}
+static void u_audio_iso_fback_complete(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct uac_rtd_params *prm = req->context;
+ struct snd_uac_chip *uac = prm->uac;
+ struct g_audio *audio_dev = uac->audio_dev;
+ struct uac_params *params = &audio_dev->params;
+ int status = req->status;
+
+ /* i/f shutting down */
+ if (!prm->fb_ep_enabled || req->status == -ESHUTDOWN)
+ return;
+
+ /*
+ * We can't really do much about bad xfers.
+ * Afterall, the ISOCH xfers could fail legitimately.
+ */
+ if (status)
+ pr_debug("%s: iso_complete status(%d) %d/%d\n",
+ __func__, status, req->actual, req->length);
+
+ u_audio_set_fback_frequency(audio_dev->gadget->speed,
+ params->c_srate, prm->pitch,
+ req->buf);
+
+ if (usb_ep_queue(ep, req, GFP_ATOMIC))
+ dev_err(uac->card->dev, "%d Error!\n", __LINE__);
+}
+
static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
@@ -335,14 +411,33 @@ static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep)
dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
}
+static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep)
+{
+ struct snd_uac_chip *uac = prm->uac;
+
+ if (!prm->fb_ep_enabled)
+ return;
+
+ prm->fb_ep_enabled = false;
+
+ if (prm->req_fback) {
+ usb_ep_dequeue(ep, prm->req_fback);
+ kfree(prm->req_fback->buf);
+ usb_ep_free_request(ep, prm->req_fback);
+ prm->req_fback = NULL;
+ }
+
+ if (usb_ep_disable(ep))
+ dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
+}
int u_audio_start_capture(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
struct usb_gadget *gadget = audio_dev->gadget;
struct device *dev = &gadget->dev;
- struct usb_request *req;
- struct usb_ep *ep;
+ struct usb_request *req, *req_fback;
+ struct usb_ep *ep, *ep_fback;
struct uac_rtd_params *prm;
struct uac_params *params = &audio_dev->params;
int req_len, i;
@@ -374,6 +469,43 @@ int u_audio_start_capture(struct g_audio *audio_dev)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
+ ep_fback = audio_dev->in_ep_fback;
+ if (!ep_fback)
+ return 0;
+
+ /* Setup feedback endpoint */
+ config_ep_by_speed(gadget, &audio_dev->func, ep_fback);
+ prm->fb_ep_enabled = true;
+ usb_ep_enable(ep_fback);
+ req_len = ep_fback->maxpacket;
+
+ req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC);
+ if (req_fback == NULL)
+ return -ENOMEM;
+
+ prm->req_fback = req_fback;
+ req_fback->zero = 0;
+ req_fback->context = prm;
+ req_fback->length = req_len;
+ req_fback->complete = u_audio_iso_fback_complete;
+
+ req_fback->buf = kzalloc(req_len, GFP_ATOMIC);
+ if (!req_fback->buf)
+ return -ENOMEM;
+
+ /*
+ * Configure the feedback endpoint's reported frequency.
+ * Always start with original frequency since its deviation can't
+ * be meauserd at start of playback
+ */
+ prm->pitch = 1000000;
+ u_audio_set_fback_frequency(audio_dev->gadget->speed,
+ params->c_srate, prm->pitch,
+ req_fback->buf);
+
+ if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC))
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+
return 0;
}
EXPORT_SYMBOL_GPL(u_audio_start_capture);
@@ -382,6 +514,8 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
+ if (audio_dev->in_ep_fback)
+ free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback);
free_ep(&uac->c_prm, audio_dev->out_ep);
}
EXPORT_SYMBOL_GPL(u_audio_stop_capture);
@@ -463,12 +597,82 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
}
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
+static int u_audio_pitch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ struct snd_uac_chip *uac = prm->uac;
+ struct g_audio *audio_dev = uac->audio_dev;
+ struct uac_params *params = &audio_dev->params;
+ unsigned int pitch_min, pitch_max;
+
+ pitch_min = (1000 - FBACK_SLOW_MAX) * 1000;
+ pitch_max = (1000 + params->fb_max) * 1000;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = pitch_min;
+ uinfo->value.integer.max = pitch_max;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int u_audio_pitch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = prm->pitch;
+
+ return 0;
+}
+
+static int u_audio_pitch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ struct snd_uac_chip *uac = prm->uac;
+ struct g_audio *audio_dev = uac->audio_dev;
+ struct uac_params *params = &audio_dev->params;
+ unsigned int val;
+ unsigned int pitch_min, pitch_max;
+ int change = 0;
+
+ pitch_min = (1000 - FBACK_SLOW_MAX) * 1000;
+ pitch_max = (1000 + params->fb_max) * 1000;
+
+ val = ucontrol->value.integer.value[0];
+
+ if (val < pitch_min)
+ val = pitch_min;
+ if (val > pitch_max)
+ val = pitch_max;
+
+ if (prm->pitch != val) {
+ prm->pitch = val;
+ change = 1;
+ }
+
+ return change;
+}
+
+static const struct snd_kcontrol_new u_audio_controls[] = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Capture Pitch 1000000",
+ .info = u_audio_pitch_info,
+ .get = u_audio_pitch_get,
+ .put = u_audio_pitch_put,
+},
+};
+
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
const char *card_name)
{
struct snd_uac_chip *uac;
struct snd_card *card;
struct snd_pcm *pcm;
+ struct snd_kcontrol *kctl;
struct uac_params *params;
int p_chmask, c_chmask;
int err;
@@ -556,6 +760,23 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
+ if (c_chmask && g_audio->in_ep_fback) {
+ strscpy(card->mixername, card_name, sizeof(card->driver));
+
+ kctl = snd_ctl_new1(&u_audio_controls[0], &uac->c_prm);
+ if (!kctl) {
+ err = -ENOMEM;
+ goto snd_fail;
+ }
+
+ kctl->id.device = pcm->device;
+ kctl->id.subdevice = 0;
+
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ goto snd_fail;
+ }
+
strscpy(card->driver, card_name, sizeof(card->driver));
strscpy(card->shortname, card_name, sizeof(card->shortname));
sprintf(card->longname, "%s %i", card_name, card->dev->id);
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 5ea6b86f1fda..a218cdf771fe 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -11,6 +11,14 @@
#include <linux/usb/composite.h>
+/*
+ * Same maximum frequency deviation on the slower side as in
+ * sound/usb/endpoint.c. Value is expressed in per-mil deviation.
+ * The maximum deviation on the faster side will be provided as
+ * parameter, as it impacts the endpoint required bandwidth.
+ */
+#define FBACK_SLOW_MAX 250
+
struct uac_params {
/* playback */
int p_chmask; /* channel mask */
@@ -23,6 +31,7 @@ struct uac_params {
int c_ssize; /* sample size */
int req_number; /* number of preallocated requests */
+ int fb_max; /* upper frequency drift feedback limit per-mil */
};
struct g_audio {
@@ -30,7 +39,10 @@ struct g_audio {
struct usb_gadget *gadget;
struct usb_ep *in_ep;
+
struct usb_ep *out_ep;
+ /* feedback IN endpoint corresponding to out_ep */
+ struct usb_ep *in_ep_fback;
/* Max packet size for all in_ep possible speeds */
unsigned int in_ep_maxpsize;
diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h
index 84e6da302499..98d6af558c03 100644
--- a/drivers/usb/gadget/function/u_hid.h
+++ b/drivers/usb/gadget/function/u_hid.h
@@ -29,8 +29,8 @@ struct f_hid_opts {
* Protect the data form concurrent access by read/write
* and create symlink/remove symlink.
*/
- struct mutex lock;
- int refcnt;
+ struct mutex lock;
+ int refcnt;
};
int ghid_setup(struct usb_gadget *g, int count);
diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h
index f6e14af7f566..2e400b495cb8 100644
--- a/drivers/usb/gadget/function/u_midi.h
+++ b/drivers/usb/gadget/function/u_midi.h
@@ -29,8 +29,8 @@ struct f_midi_opts {
* Protect the data form concurrent access by read/write
* and create symlink/remove symlink.
*/
- struct mutex lock;
- int refcnt;
+ struct mutex lock;
+ int refcnt;
};
#endif /* U_MIDI_H */
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index b5035711172d..179d3ef6a195 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -21,7 +21,9 @@
#define UAC2_DEF_CCHMASK 0x3
#define UAC2_DEF_CSRATE 64000
#define UAC2_DEF_CSSIZE 2
+#define UAC2_DEF_CSYNC USB_ENDPOINT_SYNC_ASYNC
#define UAC2_DEF_REQ_NUM 2
+#define UAC2_DEF_FB_MAX 5
struct f_uac2_opts {
struct usb_function_instance func_inst;
@@ -31,7 +33,9 @@ struct f_uac2_opts {
int c_chmask;
int c_srate;
int c_ssize;
+ int c_sync;
int req_number;
+ int fb_max;
bool bound;
struct mutex lock;
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index cd28dec837dd..77d64031aa9c 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -914,8 +914,6 @@ static int uvcg_streaming_header_allow_link(struct config_item *src,
target_fmt = container_of(to_config_group(target), struct uvcg_format,
group);
- if (!target_fmt)
- goto out;
uvcg_format_set_indices(to_config_group(target));
@@ -955,8 +953,6 @@ static void uvcg_streaming_header_drop_link(struct config_item *src,
mutex_lock(&opts->lock);
target_fmt = container_of(to_config_group(target), struct uvcg_format,
group);
- if (!target_fmt)
- goto out;
list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry)
if (format_ptr->fmt == target_fmt) {
@@ -968,7 +964,6 @@ static void uvcg_streaming_header_drop_link(struct config_item *src,
--target_fmt->linked;
-out:
mutex_unlock(&opts->lock);
mutex_unlock(su_mutex);
}
diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c
index c4eda7fe7ab4..5b27d289443f 100644
--- a/drivers/usb/gadget/legacy/hid.c
+++ b/drivers/usb/gadget/legacy/hid.c
@@ -171,8 +171,10 @@ static int hid_bind(struct usb_composite_dev *cdev)
struct usb_descriptor_header *usb_desc;
usb_desc = usb_otg_descriptor_alloc(gadget);
- if (!usb_desc)
+ if (!usb_desc) {
+ status = -ENOMEM;
goto put;
+ }
usb_otg_descriptor_init(gadget, usb_desc);
otg_desc[0] = usb_desc;
otg_desc[1] = NULL;
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index 9cd4a70ccdd6..a9f07c59fc37 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -288,7 +288,6 @@ struct bcm63xx_req {
* @ep0_req_completed: ep0 request has completed; worker has not seen it yet.
* @ep0_reply: Pending reply from gadget driver.
* @ep0_request: Outstanding ep0 request.
- * @debugfs_root: debugfs directory: /sys/kernel/debug/<DRV_MODULE_NAME>.
*/
struct bcm63xx_udc {
spinlock_t lock;
@@ -327,8 +326,6 @@ struct bcm63xx_udc {
unsigned ep0_req_completed:1;
struct usb_request *ep0_reply;
struct usb_request *ep0_request;
-
- struct dentry *debugfs_root;
};
static const struct usb_ep_ops bcm63xx_udc_ep_ops;
@@ -2250,8 +2247,6 @@ static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc)
return;
root = debugfs_create_dir(udc->gadget.name, usb_debug_root);
- udc->debugfs_root = root;
-
debugfs_create_file("usbd", 0400, root, udc, &bcm63xx_usbd_dbg_fops);
debugfs_create_file("iudma", 0400, root, udc, &bcm63xx_iudma_dbg_fops);
}
@@ -2264,7 +2259,7 @@ static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc)
*/
static void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc)
{
- debugfs_remove_recursive(udc->debugfs_root);
+ debugfs_remove(debugfs_lookup(udc->gadget.name, usb_debug_root));
}
/***********************************************************************
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 493ff93f7dda..b7f0b1ebaaa8 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1148,6 +1148,53 @@ static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
}
/**
+ * usb_gadget_enable_async_callbacks - tell usb device controller to enable asynchronous callbacks
+ * @udc: The UDC which should enable async callbacks
+ *
+ * This routine is used when binding gadget drivers. It undoes the effect
+ * of usb_gadget_disable_async_callbacks(); the UDC driver should enable IRQs
+ * (if necessary) and resume issuing callbacks.
+ *
+ * This routine will always be called in process context.
+ */
+static inline void usb_gadget_enable_async_callbacks(struct usb_udc *udc)
+{
+ struct usb_gadget *gadget = udc->gadget;
+
+ if (gadget->ops->udc_async_callbacks)
+ gadget->ops->udc_async_callbacks(gadget, true);
+}
+
+/**
+ * usb_gadget_disable_async_callbacks - tell usb device controller to disable asynchronous callbacks
+ * @udc: The UDC which should disable async callbacks
+ *
+ * This routine is used when unbinding gadget drivers. It prevents a race:
+ * The UDC driver doesn't know when the gadget driver's ->unbind callback
+ * runs, so unless it is told to disable asynchronous callbacks, it might
+ * issue a callback (such as ->disconnect) after the unbind has completed.
+ *
+ * After this function runs, the UDC driver must suppress all ->suspend,
+ * ->resume, ->disconnect, ->reset, and ->setup callbacks to the gadget driver
+ * until async callbacks are again enabled. A simple-minded but effective
+ * way to accomplish this is to tell the UDC hardware not to generate any
+ * more IRQs.
+ *
+ * Request completion callbacks must still be issued. However, it's okay
+ * to defer them until the request is cancelled, since the pull-up will be
+ * turned off during the time period when async callbacks are disabled.
+ *
+ * This routine will always be called in process context.
+ */
+static inline void usb_gadget_disable_async_callbacks(struct usb_udc *udc)
+{
+ struct usb_gadget *gadget = udc->gadget;
+
+ if (gadget->ops->udc_async_callbacks)
+ gadget->ops->udc_async_callbacks(gadget, false);
+}
+
+/**
* usb_udc_release - release the usb_udc struct
* @dev: the dev member within usb_udc
*
@@ -1361,6 +1408,7 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
usb_gadget_disconnect(udc->gadget);
+ usb_gadget_disable_async_callbacks(udc);
if (udc->gadget->irq)
synchronize_irq(udc->gadget->irq);
udc->driver->unbind(udc->gadget);
@@ -1442,6 +1490,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
driver->unbind(udc->gadget);
goto err1;
}
+ usb_gadget_enable_async_callbacks(udc);
usb_udc_connect_control(udc);
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 7db773c87379..a2d956af42a2 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -934,6 +934,15 @@ static void dummy_udc_set_speed(struct usb_gadget *_gadget,
dummy_udc_update_ep0(dum);
}
+static void dummy_udc_async_callbacks(struct usb_gadget *_gadget, bool enable)
+{
+ struct dummy *dum = gadget_dev_to_dummy(&_gadget->dev);
+
+ spin_lock_irq(&dum->lock);
+ dum->ints_enabled = enable;
+ spin_unlock_irq(&dum->lock);
+}
+
static int dummy_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
static int dummy_udc_stop(struct usb_gadget *g);
@@ -946,6 +955,7 @@ static const struct usb_gadget_ops dummy_ops = {
.udc_start = dummy_udc_start,
.udc_stop = dummy_udc_stop,
.udc_set_speed = dummy_udc_set_speed,
+ .udc_async_callbacks = dummy_udc_async_callbacks,
};
/*-------------------------------------------------------------------------*/
@@ -1005,7 +1015,6 @@ static int dummy_udc_start(struct usb_gadget *g,
spin_lock_irq(&dum->lock);
dum->devstatus = 0;
dum->driver = driver;
- dum->ints_enabled = 1;
spin_unlock_irq(&dum->lock);
return 0;
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index fa66449b3907..8e8588933628 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -541,6 +541,7 @@ static int qe_ep_init(struct qe_udc *udc,
case USB_SPEED_HIGH:
if ((max == 128) || (max == 256) || (max == 512))
break;
+ fallthrough;
default:
switch (max) {
case 4:
@@ -562,9 +563,11 @@ static int qe_ep_init(struct qe_udc *udc,
case USB_SPEED_HIGH:
if (max <= 1024)
break;
+ fallthrough;
case USB_SPEED_FULL:
if (max <= 64)
break;
+ fallthrough;
default:
if (max <= 8)
break;
@@ -579,6 +582,7 @@ static int qe_ep_init(struct qe_udc *udc,
case USB_SPEED_HIGH:
if (max <= 1024)
break;
+ fallthrough;
case USB_SPEED_FULL:
if (max <= 1023)
break;
@@ -605,6 +609,7 @@ static int qe_ep_init(struct qe_udc *udc,
default:
goto en_done;
}
+ fallthrough;
case USB_SPEED_LOW:
switch (max) {
case 1:
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index ad6ff9c4188e..29fcb9b461d7 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -36,7 +36,6 @@
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
#include <linux/dmapool.h>
-#include <linux/delay.h>
#include <linux/of_device.h>
#include <asm/byteorder.h>
@@ -323,13 +322,11 @@ static int dr_controller_setup(struct fsl_udc *udc)
fsl_writel(tmp, &dr_regs->endptctrl[ep_num]);
}
/* Config control enable i/o output, cpu endian register */
-#ifndef CONFIG_ARCH_MXC
if (udc->pdata->have_sysif_regs) {
ctrl = __raw_readl(&usb_sys_regs->control);
ctrl |= USB_CTRL_IOENB;
__raw_writel(ctrl, &usb_sys_regs->control);
}
-#endif
#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
/* Turn on cache snooping hardware, since some PowerPC platforms
@@ -547,7 +544,7 @@ static int fsl_ep_enable(struct usb_ep *_ep,
unsigned short max = 0;
unsigned char mult = 0, zlt;
int retval = -EINVAL;
- unsigned long flags = 0;
+ unsigned long flags;
ep = container_of(_ep, struct fsl_ep, ep);
@@ -631,7 +628,7 @@ static int fsl_ep_disable(struct usb_ep *_ep)
{
struct fsl_udc *udc = NULL;
struct fsl_ep *ep = NULL;
- unsigned long flags = 0;
+ unsigned long flags;
u32 epctrl;
int ep_num;
@@ -1001,7 +998,7 @@ out: epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
{
struct fsl_ep *ep = NULL;
- unsigned long flags = 0;
+ unsigned long flags;
int status = -EOPNOTSUPP; /* operation not supported */
unsigned char ep_dir = 0, ep_num = 0;
struct fsl_udc *udc = NULL;
@@ -1938,7 +1935,7 @@ static int fsl_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
int retval = 0;
- unsigned long flags = 0;
+ unsigned long flags;
/* lock is needed but whether should use this lock or another */
spin_lock_irqsave(&udc_controller->lock, flags);
@@ -2153,7 +2150,6 @@ static int fsl_proc_read(struct seq_file *m, void *v)
tmp_reg = fsl_readl(&dr_regs->endpointprime);
seq_printf(m, "EP Prime Reg = [0x%x]\n\n", tmp_reg);
-#ifndef CONFIG_ARCH_MXC
if (udc->pdata->have_sysif_regs) {
tmp_reg = usb_sys_regs->snoop1;
seq_printf(m, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
@@ -2161,7 +2157,6 @@ static int fsl_proc_read(struct seq_file *m, void *v)
tmp_reg = usb_sys_regs->control;
seq_printf(m, "General Control Reg : = [0x%x]\n\n", tmp_reg);
}
-#endif
/* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
ep = &udc->eps[0];
@@ -2412,28 +2407,21 @@ static int fsl_udc_probe(struct platform_device *pdev)
*/
if (pdata->init && pdata->init(pdev)) {
ret = -ENODEV;
- goto err_iounmap_noclk;
+ goto err_iounmap;
}
/* Set accessors only after pdata->init() ! */
fsl_set_accessors(pdata);
-#ifndef CONFIG_ARCH_MXC
if (pdata->have_sysif_regs)
usb_sys_regs = (void *)dr_regs + USB_DR_SYS_OFFSET;
-#endif
-
- /* Initialize USB clocks */
- ret = fsl_udc_clk_init(pdev);
- if (ret < 0)
- goto err_iounmap_noclk;
/* Read Device Controller Capability Parameters register */
dccparams = fsl_readl(&dr_regs->dccparams);
if (!(dccparams & DCCPARAMS_DC)) {
ERR("This SOC doesn't support device role\n");
ret = -ENODEV;
- goto err_iounmap;
+ goto err_exit;
}
/* Get max device endpoints */
/* DEN is bidirectional ep number, max_ep doubles the number */
@@ -2442,7 +2430,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
ret = platform_get_irq(pdev, 0);
if (ret <= 0) {
ret = ret ? : -ENODEV;
- goto err_iounmap;
+ goto err_exit;
}
udc_controller->irq = ret;
@@ -2451,7 +2439,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
if (ret != 0) {
ERR("cannot request irq %d err %d\n",
udc_controller->irq, ret);
- goto err_iounmap;
+ goto err_exit;
}
/* Initialize the udc structure including QH member and other member */
@@ -2467,10 +2455,6 @@ static int fsl_udc_probe(struct platform_device *pdev)
dr_controller_setup(udc_controller);
}
- ret = fsl_udc_clk_finalize(pdev);
- if (ret)
- goto err_free_irq;
-
/* Setup gadget structure */
udc_controller->gadget.ops = &fsl_gadget_ops;
udc_controller->gadget.max_speed = USB_SPEED_HIGH;
@@ -2530,11 +2514,10 @@ err_del_udc:
dma_pool_destroy(udc_controller->td_pool);
err_free_irq:
free_irq(udc_controller->irq, udc_controller);
-err_iounmap:
+err_exit:
if (pdata->exit)
pdata->exit(pdev);
- fsl_udc_clk_release();
-err_iounmap_noclk:
+err_iounmap:
iounmap(dr_regs);
err_release_mem_region:
if (pdata->operating_mode == FSL_USB2_DR_DEVICE)
@@ -2561,8 +2544,6 @@ static int fsl_udc_remove(struct platform_device *pdev)
udc_controller->done = &done;
usb_del_gadget_udc(&udc_controller->gadget);
- fsl_udc_clk_release();
-
/* DR has been stopped in usb_gadget_unregister_driver() */
remove_proc_file();
@@ -2677,10 +2658,6 @@ static int fsl_udc_otg_resume(struct device *dev)
--------------------------------------------------------------------------*/
static const struct platform_device_id fsl_udc_devtype[] = {
{
- .name = "imx-udc-mx27",
- }, {
- .name = "imx-udc-mx51",
- }, {
.name = "fsl-usb2-udc",
}, {
/* sentinel */
@@ -2689,7 +2666,6 @@ static const struct platform_device_id fsl_udc_devtype[] = {
MODULE_DEVICE_TABLE(platform, fsl_udc_devtype);
static struct platform_driver udc_driver = {
.remove = fsl_udc_remove,
- /* Just for FSL i.mx SoC currently */
.id_table = fsl_udc_devtype,
/* these suspend and resume are not usb suspend and resume */
.suspend = fsl_udc_suspend,
diff --git a/drivers/usb/gadget/udc/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h
index 4ba651ae9048..2efc5a930b48 100644
--- a/drivers/usb/gadget/udc/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h
@@ -588,23 +588,4 @@ static inline struct ep_queue_head *get_qh_by_ep(struct fsl_ep *ep)
USB_DIR_IN) ? 1 : 0];
}
-struct platform_device;
-#ifdef CONFIG_ARCH_MXC
-int fsl_udc_clk_init(struct platform_device *pdev);
-int fsl_udc_clk_finalize(struct platform_device *pdev);
-void fsl_udc_clk_release(void);
-#else
-static inline int fsl_udc_clk_init(struct platform_device *pdev)
-{
- return 0;
-}
-static inline int fsl_udc_clk_finalize(struct platform_device *pdev)
-{
- return 0;
-}
-static inline void fsl_udc_clk_release(void)
-{
-}
-#endif
-
#endif
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index f8f3aa52383b..4b35739d3695 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -207,14 +207,15 @@ DEFINE_SHOW_ATTRIBUTE(gr_dfs);
static void gr_dfs_create(struct gr_udc *dev)
{
const char *name = "gr_udc_state";
+ struct dentry *root;
- dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), usb_debug_root);
- debugfs_create_file(name, 0444, dev->dfs_root, dev, &gr_dfs_fops);
+ root = debugfs_create_dir(dev_name(dev->dev), usb_debug_root);
+ debugfs_create_file(name, 0444, root, dev, &gr_dfs_fops);
}
static void gr_dfs_delete(struct gr_udc *dev)
{
- debugfs_remove_recursive(dev->dfs_root);
+ debugfs_remove(debugfs_lookup(dev_name(dev->dev), usb_debug_root));
}
#else /* !CONFIG_USB_GADGET_DEBUG_FS */
diff --git a/drivers/usb/gadget/udc/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h
index ac5b3f65adb5..70134239179e 100644
--- a/drivers/usb/gadget/udc/gr_udc.h
+++ b/drivers/usb/gadget/udc/gr_udc.h
@@ -215,8 +215,6 @@ struct gr_udc {
struct list_head ep_list;
spinlock_t lock; /* General lock, a.k.a. "dev->lock" in comments */
-
- struct dentry *dfs_root;
};
#define to_gr_udc(gadget) (container_of((gadget), struct gr_udc, gadget))
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index 3f1c62adce4b..a25d01c89564 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -127,7 +127,6 @@ struct lpc32xx_udc {
struct usb_gadget_driver *driver;
struct platform_device *pdev;
struct device *dev;
- struct dentry *pde;
spinlock_t lock;
struct i2c_client *isp1301_i2c_client;
@@ -528,12 +527,12 @@ DEFINE_SHOW_ATTRIBUTE(udc);
static void create_debug_file(struct lpc32xx_udc *udc)
{
- udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &udc_fops);
+ debugfs_create_file(debug_filename, 0, NULL, udc, &udc_fops);
}
static void remove_debug_file(struct lpc32xx_udc *udc)
{
- debugfs_remove(udc->pde);
+ debugfs_remove(debugfs_lookup(debug_filename, NULL));
}
#else
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index 5486f5a70868..ce3d7a3eb7e3 100644
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -941,7 +941,7 @@ mv_u3d_ep_set_stall(struct mv_u3d *u3d, u8 ep_num, u8 direction, int stall)
static int mv_u3d_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
{
struct mv_u3d_ep *ep;
- unsigned long flags = 0;
+ unsigned long flags;
int status = 0;
struct mv_u3d *u3d;
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 0fb4ef464321..7f24ce400b59 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -888,7 +888,7 @@ static int ep_is_stall(struct mv_udc *udc, u8 ep_num, u8 direction)
static int mv_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
{
struct mv_ep *ep;
- unsigned long flags = 0;
+ unsigned long flags;
int status = 0;
struct mv_udc *udc;
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index 89f479b78d80..7c38057dcb4a 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -1150,6 +1150,7 @@ net2272_pullup(struct usb_gadget *_gadget, int is_on)
static int net2272_start(struct usb_gadget *_gadget,
struct usb_gadget_driver *driver);
static int net2272_stop(struct usb_gadget *_gadget);
+static void net2272_async_callbacks(struct usb_gadget *_gadget, bool enable);
static const struct usb_gadget_ops net2272_ops = {
.get_frame = net2272_get_frame,
@@ -1158,6 +1159,7 @@ static const struct usb_gadget_ops net2272_ops = {
.pullup = net2272_pullup,
.udc_start = net2272_start,
.udc_stop = net2272_stop,
+ .udc_async_callbacks = net2272_async_callbacks,
};
/*---------------------------------------------------------------------------*/
@@ -1476,7 +1478,7 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver)
net2272_dequeue_all(&dev->ep[i]);
/* report disconnect; the driver is already quiesced */
- if (driver) {
+ if (dev->async_callbacks && driver) {
spin_unlock(&dev->lock);
driver->disconnect(&dev->gadget);
spin_lock(&dev->lock);
@@ -1501,6 +1503,15 @@ static int net2272_stop(struct usb_gadget *_gadget)
return 0;
}
+static void net2272_async_callbacks(struct usb_gadget *_gadget, bool enable)
+{
+ struct net2272 *dev = container_of(_gadget, struct net2272, gadget);
+
+ spin_lock_irq(&dev->lock);
+ dev->async_callbacks = enable;
+ spin_unlock_irq(&dev->lock);
+}
+
/*---------------------------------------------------------------------------*/
/* handle ep-a/ep-b dma completions */
static void
@@ -1910,9 +1921,11 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat)
u.r.bRequestType, u.r.bRequest,
u.r.wValue, u.r.wIndex,
net2272_ep_read(ep, EP_CFG));
- spin_unlock(&dev->lock);
- tmp = dev->driver->setup(&dev->gadget, &u.r);
- spin_lock(&dev->lock);
+ if (dev->async_callbacks) {
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &u.r);
+ spin_lock(&dev->lock);
+ }
}
/* stall ep0 on error */
@@ -1994,14 +2007,14 @@ net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat)
if (disconnect || reset) {
stop_activity(dev, dev->driver);
net2272_ep0_start(dev);
- spin_unlock(&dev->lock);
- if (reset)
- usb_gadget_udc_reset
- (&dev->gadget, dev->driver);
- else
- (dev->driver->disconnect)
- (&dev->gadget);
- spin_lock(&dev->lock);
+ if (dev->async_callbacks) {
+ spin_unlock(&dev->lock);
+ if (reset)
+ usb_gadget_udc_reset(&dev->gadget, dev->driver);
+ else
+ (dev->driver->disconnect)(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
return;
}
}
@@ -2015,14 +2028,14 @@ net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat)
if (stat & tmp) {
net2272_write(dev, IRQSTAT1, tmp);
if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) {
- if (dev->driver->suspend)
+ if (dev->async_callbacks && dev->driver->suspend)
dev->driver->suspend(&dev->gadget);
if (!enable_suspend) {
stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT);
dev_dbg(dev->dev, "Suspend disabled, ignoring\n");
}
} else {
- if (dev->driver->resume)
+ if (dev->async_callbacks && dev->driver->resume)
dev->driver->resume(&dev->gadget);
}
stat &= ~tmp;
diff --git a/drivers/usb/gadget/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h
index c669308111c2..a9994f737588 100644
--- a/drivers/usb/gadget/udc/net2272.h
+++ b/drivers/usb/gadget/udc/net2272.h
@@ -442,6 +442,7 @@ struct net2272 {
softconnect:1,
wakeup:1,
added:1,
+ async_callbacks:1,
dma_eot_polarity:1,
dma_dack_polarity:1,
dma_dreq_polarity:1,
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index fc9f99fe7f37..16e7d2db6411 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -1617,6 +1617,7 @@ static struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget,
static int net2280_start(struct usb_gadget *_gadget,
struct usb_gadget_driver *driver);
static int net2280_stop(struct usb_gadget *_gadget);
+static void net2280_async_callbacks(struct usb_gadget *_gadget, bool enable);
static const struct usb_gadget_ops net2280_ops = {
.get_frame = net2280_get_frame,
@@ -1625,6 +1626,7 @@ static const struct usb_gadget_ops net2280_ops = {
.pullup = net2280_pullup,
.udc_start = net2280_start,
.udc_stop = net2280_stop,
+ .udc_async_callbacks = net2280_async_callbacks,
.match_ep = net2280_match_ep,
};
@@ -2472,7 +2474,7 @@ static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver)
nuke(&dev->ep[i]);
/* report disconnect; the driver is already quiesced */
- if (driver) {
+ if (dev->async_callbacks && driver) {
spin_unlock(&dev->lock);
driver->disconnect(&dev->gadget);
spin_lock(&dev->lock);
@@ -2502,6 +2504,15 @@ static int net2280_stop(struct usb_gadget *_gadget)
return 0;
}
+static void net2280_async_callbacks(struct usb_gadget *_gadget, bool enable)
+{
+ struct net2280 *dev = container_of(_gadget, struct net2280, gadget);
+
+ spin_lock_irq(&dev->lock);
+ dev->async_callbacks = enable;
+ spin_unlock_irq(&dev->lock);
+}
+
/*-------------------------------------------------------------------------*/
/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
@@ -2814,8 +2825,6 @@ static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
* - Wait and try again.
*/
udelay(DEFECT_7374_PROCESSOR_WAIT_TIME);
-
- continue;
}
@@ -3042,9 +3051,11 @@ usb3_delegate:
readl(&ep->cfg->ep_cfg));
ep->responded = 0;
- spin_unlock(&dev->lock);
- tmp = dev->driver->setup(&dev->gadget, &r);
- spin_lock(&dev->lock);
+ if (dev->async_callbacks) {
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &r);
+ spin_lock(&dev->lock);
+ }
}
do_stall3:
if (tmp < 0) {
@@ -3284,9 +3295,11 @@ delegate:
w_value, w_index, w_length,
readl(&ep->cfg->ep_cfg));
ep->responded = 0;
- spin_unlock(&dev->lock);
- tmp = dev->driver->setup(&dev->gadget, &u.r);
- spin_lock(&dev->lock);
+ if (dev->async_callbacks) {
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &u.r);
+ spin_lock(&dev->lock);
+ }
}
/* stall ep0 on error */
@@ -3391,14 +3404,14 @@ __acquires(dev->lock)
if (disconnect || reset) {
stop_activity(dev, dev->driver);
ep0_start(dev);
- spin_unlock(&dev->lock);
- if (reset)
- usb_gadget_udc_reset
- (&dev->gadget, dev->driver);
- else
- (dev->driver->disconnect)
- (&dev->gadget);
- spin_lock(&dev->lock);
+ if (dev->async_callbacks) {
+ spin_unlock(&dev->lock);
+ if (reset)
+ usb_gadget_udc_reset(&dev->gadget, dev->driver);
+ else
+ (dev->driver->disconnect)(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
return;
}
}
@@ -3419,12 +3432,12 @@ __acquires(dev->lock)
writel(tmp, &dev->regs->irqstat1);
spin_unlock(&dev->lock);
if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) {
- if (dev->driver->suspend)
+ if (dev->async_callbacks && dev->driver->suspend)
dev->driver->suspend(&dev->gadget);
if (!enable_suspend)
stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT);
} else {
- if (dev->driver->resume)
+ if (dev->async_callbacks && dev->driver->resume)
dev->driver->resume(&dev->gadget);
/* at high speed, note erratum 0133 */
}
diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h
index 7da3dc1e9729..34716a9f4926 100644
--- a/drivers/usb/gadget/udc/net2280.h
+++ b/drivers/usb/gadget/udc/net2280.h
@@ -162,6 +162,7 @@ struct net2280 {
ltm_enable:1,
wakeup_enable:1,
addressed_state:1,
+ async_callbacks:1,
bug7734_patched:1;
u16 chiprev;
int enhanced_mode;
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 10324a7334fe..69ef1e669d0c 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -1338,10 +1338,10 @@ DEFINE_SHOW_ATTRIBUTE(udc_debug);
#define create_debug_files(dev) \
do { \
- dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \
+ debugfs_create_file(dev->gadget.name, \
S_IRUGO, NULL, dev, &udc_debug_fops); \
} while (0)
-#define remove_debug_files(dev) debugfs_remove(dev->debugfs_udc)
+#define remove_debug_files(dev) debugfs_remove(debugfs_lookup(dev->gadget.name, NULL))
#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h
index ccc6b921f067..aa4b68fd9fc0 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.h
+++ b/drivers/usb/gadget/udc/pxa25x_udc.h
@@ -116,10 +116,6 @@ struct pxa25x_udc {
struct usb_phy *transceiver;
u64 dma_mask;
struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS];
-
-#ifdef CONFIG_USB_GADGET_DEBUG_FS
- struct dentry *debugfs_udc;
-#endif
void __iomem *regs;
};
#define to_pxa25x(g) (container_of((g), struct pxa25x_udc, gadget))
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index ce57961dfd2d..f4b7a2a3e711 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -208,8 +208,6 @@ static void pxa_init_debugfs(struct pxa_udc *udc)
struct dentry *root;
root = debugfs_create_dir(udc->gadget.name, usb_debug_root);
- udc->debugfs_root = root;
-
debugfs_create_file("udcstate", 0400, root, udc, &state_dbg_fops);
debugfs_create_file("queues", 0400, root, udc, &queues_dbg_fops);
debugfs_create_file("epstate", 0400, root, udc, &eps_dbg_fops);
@@ -217,7 +215,7 @@ static void pxa_init_debugfs(struct pxa_udc *udc)
static void pxa_cleanup_debugfs(struct pxa_udc *udc)
{
- debugfs_remove_recursive(udc->debugfs_root);
+ debugfs_remove(debugfs_lookup(udc->gadget.name, usb_debug_root));
}
#else
@@ -1730,7 +1728,7 @@ static void udc_enable(struct pxa_udc *udc)
}
/**
- * pxa27x_start - Register gadget driver
+ * pxa27x_udc_start - Register gadget driver
* @g: gadget
* @driver: gadget driver
*
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h
index 13b2977399ab..0a6bc18a1264 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.h
+++ b/drivers/usb/gadget/udc/pxa27x_udc.h
@@ -440,7 +440,6 @@ struct udc_stats {
* @last_interface: UDC interface of the last SET_INTERFACE host request
* @last_alternate: UDC altsetting of the last SET_INTERFACE host request
* @udccsr0: save of udccsr0 in case of suspend
- * @debugfs_root: root entry of debug filesystem
* @debugfs_state: debugfs entry for "udcstate"
* @debugfs_queues: debugfs entry for "queues"
* @debugfs_eps: debugfs entry for "epstate"
@@ -474,9 +473,6 @@ struct pxa_udc {
#ifdef CONFIG_PM
unsigned udccsr0;
#endif
-#ifdef CONFIG_USB_GADGET_DEBUG_FS
- struct dentry *debugfs_root;
-#endif
};
#define to_pxa(g) (container_of((g), struct pxa_udc, gadget))
diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c
index 7bd5182ce3ef..89f1f8c9f02e 100644
--- a/drivers/usb/gadget/udc/s3c-hsudc.c
+++ b/drivers/usb/gadget/udc/s3c-hsudc.c
@@ -1220,9 +1220,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
struct s3c24xx_hsudc_platdata *pd = dev_get_platdata(&pdev->dev);
int ret, i;
- hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) +
- sizeof(struct s3c_hsudc_ep) * pd->epnum,
- GFP_KERNEL);
+ hsudc = devm_kzalloc(&pdev->dev, struct_size(hsudc, ep, pd->epnum),
+ GFP_KERNEL);
if (!hsudc)
return -ENOMEM;
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index b154b62abefa..179777cb699f 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -198,7 +198,7 @@ static inline void s3c2410_udc_set_ep0_de(void __iomem *base)
udc_writeb(base, S3C2410_UDC_EP0_CSR_DE, S3C2410_UDC_EP0_CSR_REG);
}
-inline void s3c2410_udc_set_ep0_ss(void __iomem *b)
+static inline void s3c2410_udc_set_ep0_ss(void __iomem *b)
{
udc_writeb(b, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
udc_writeb(b, S3C2410_UDC_EP0_CSR_SENDSTL, S3C2410_UDC_EP0_CSR_REG);
@@ -1843,9 +1843,8 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
if (retval)
goto err_add_udc;
- udc->regs_info = debugfs_create_file("registers", S_IRUGO,
- s3c2410_udc_debugfs_root, udc,
- &s3c2410_udc_debugfs_fops);
+ debugfs_create_file("registers", S_IRUGO, s3c2410_udc_debugfs_root, udc,
+ &s3c2410_udc_debugfs_fops);
dev_dbg(dev, "probe ok\n");
@@ -1889,7 +1888,7 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
return -EBUSY;
usb_del_gadget_udc(&udc->gadget);
- debugfs_remove(udc->regs_info);
+ debugfs_remove(debugfs_lookup("registers", s3c2410_udc_debugfs_root));
if (udc_info && !udc_info->udc_command &&
gpio_is_valid(udc_info->pullup_pin))
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.h b/drivers/usb/gadget/udc/s3c2410_udc.h
index 68bdf3e5aac2..135a5bff3c74 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.h
+++ b/drivers/usb/gadget/udc/s3c2410_udc.h
@@ -89,7 +89,6 @@ struct s3c2410_udc {
unsigned req_config : 1;
unsigned req_pending : 1;
u8 vbus;
- struct dentry *regs_info;
int irq;
};
#define to_s3c2410(g) (container_of((g), struct s3c2410_udc, gadget))
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 2319c9737c2b..a54d1cef17db 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -1907,7 +1907,7 @@ static void tegra_xudc_ep_free_request(struct usb_ep *usb_ep,
kfree(req);
}
-static struct usb_ep_ops tegra_xudc_ep_ops = {
+static const struct usb_ep_ops tegra_xudc_ep_ops = {
.enable = tegra_xudc_ep_enable,
.disable = tegra_xudc_ep_disable,
.alloc_request = tegra_xudc_ep_alloc_request,
@@ -1928,7 +1928,7 @@ static int tegra_xudc_ep0_disable(struct usb_ep *usb_ep)
return -EBUSY;
}
-static struct usb_ep_ops tegra_xudc_ep0_ops = {
+static const struct usb_ep_ops tegra_xudc_ep0_ops = {
.enable = tegra_xudc_ep0_enable,
.disable = tegra_xudc_ep0_disable,
.alloc_request = tegra_xudc_ep_alloc_request,
@@ -2168,7 +2168,7 @@ static int tegra_xudc_set_selfpowered(struct usb_gadget *gadget, int is_on)
return 0;
}
-static struct usb_gadget_ops tegra_xudc_gadget_ops = {
+static const struct usb_gadget_ops tegra_xudc_gadget_ops = {
.get_frame = tegra_xudc_gadget_get_frame,
.wakeup = tegra_xudc_gadget_wakeup,
.pullup = tegra_xudc_gadget_pullup,
@@ -3508,10 +3508,8 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc)
xudc->utmi_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
if (IS_ERR(xudc->utmi_phy[i])) {
err = PTR_ERR(xudc->utmi_phy[i]);
- if (err != -EPROBE_DEFER)
- dev_err(xudc->dev, "failed to get usb2-%d PHY: %d\n",
- i, err);
-
+ dev_err_probe(xudc->dev, err,
+ "failed to get usb2-%d PHY\n", i);
goto clean_up;
} else if (xudc->utmi_phy[i]) {
/* Get usb-phy, if utmi phy is available */
@@ -3520,8 +3518,8 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc)
&xudc->vbus_nb);
if (IS_ERR(xudc->usbphy[i])) {
err = PTR_ERR(xudc->usbphy[i]);
- dev_err(xudc->dev, "failed to get usbphy-%d: %d\n",
- i, err);
+ dev_err_probe(xudc->dev, err,
+ "failed to get usbphy-%d\n", i);
goto clean_up;
}
} else if (!xudc->utmi_phy[i]) {
@@ -3538,10 +3536,8 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc)
xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
if (IS_ERR(xudc->usb3_phy[i])) {
err = PTR_ERR(xudc->usb3_phy[i]);
- if (err != -EPROBE_DEFER)
- dev_err(xudc->dev, "failed to get usb3-%d PHY: %d\n",
- usb3, err);
-
+ dev_err_probe(xudc->dev, err,
+ "failed to get usb3-%d PHY\n", usb3);
goto clean_up;
} else if (xudc->usb3_phy[i])
dev_dbg(xudc->dev, "usb3-%d PHY registered", usb3);
@@ -3781,9 +3777,7 @@ static int tegra_xudc_probe(struct platform_device *pdev)
err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, xudc->clks);
if (err) {
- if (err != -EPROBE_DEFER)
- dev_err(xudc->dev, "failed to request clocks: %d\n", err);
-
+ dev_err_probe(xudc->dev, err, "failed to request clocks\n");
return err;
}
@@ -3798,9 +3792,7 @@ static int tegra_xudc_probe(struct platform_device *pdev)
err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies,
xudc->supplies);
if (err) {
- if (err != -EPROBE_DEFER)
- dev_err(xudc->dev, "failed to request regulators: %d\n", err);
-
+ dev_err_probe(xudc->dev, err, "failed to request regulators\n");
return err;
}
diff --git a/drivers/usb/gadget/udc/trace.c b/drivers/usb/gadget/udc/trace.c
index 7430624c0bd7..19e837de2a20 100644
--- a/drivers/usb/gadget/udc/trace.c
+++ b/drivers/usb/gadget/udc/trace.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* trace.c - USB Gadget Framework Trace Support
*
* Copyright (C) 2016 Intel Corporation
diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h
index 2d1f68b5ea76..98584f6b6c66 100644
--- a/drivers/usb/gadget/udc/trace.h
+++ b/drivers/usb/gadget/udc/trace.h
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* udc.c - Core UDC Framework
*
* Copyright (C) 2016 Intel Corporation
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index 72f2ea062d55..fb4ffedd6f0d 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -791,7 +791,7 @@ static int xudc_ep_set_halt(struct usb_ep *_ep, int value)
}
/**
- * xudc_ep_enable - Enables the given endpoint.
+ * __xudc_ep_enable - Enables the given endpoint.
* @ep: pointer to the xusb endpoint structure.
* @desc: pointer to usb endpoint descriptor.
*
@@ -987,7 +987,7 @@ static void xudc_free_request(struct usb_ep *_ep, struct usb_request *_req)
}
/**
- * xudc_ep0_queue - Adds the request to endpoint 0 queue.
+ * __xudc_ep0_queue - Adds the request to endpoint 0 queue.
* @ep0: pointer to the xusb endpoint 0 structure.
* @req: pointer to the xusb request structure.
*
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 6f7bd6641694..385be30baad3 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -387,11 +387,11 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
/* EHCI registers start at offset 0x100 */
ehci->caps = hcd->regs + 0x100;
-#ifdef CONFIG_PPC_83xx
+#if defined(CONFIG_PPC_83xx) || defined(CONFIG_PPC_85xx)
/*
- * Deal with MPC834X that need port power to be cycled after the power
- * fault condition is removed. Otherwise the state machine does not
- * reflect PORTSC[CSC] correctly.
+ * Deal with MPC834X/85XX that need port power to be cycled
+ * after the power fault condition is removed. Otherwise the
+ * state machine does not reflect PORTSC[CSC] correctly.
*/
ehci->need_oc_pp_cycle = 1;
#endif
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 94b5e64ae9a2..36f5bf6a0752 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -76,12 +76,12 @@ static const char hcd_name [] = "ehci_hcd";
#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */
/* Initial IRQ latency: faster than hw default */
-static int log2_irq_thresh = 0; // 0 to 6
+static int log2_irq_thresh; // 0 to 6
module_param (log2_irq_thresh, int, S_IRUGO);
MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
/* initial park setting: slower than hw default */
-static unsigned park = 0;
+static unsigned park;
module_param (park, uint, S_IRUGO);
MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets");
@@ -1238,6 +1238,10 @@ static const struct hc_driver ehci_hc_driver = {
* device support
*/
.free_dev = ehci_remove_device,
+#ifdef CONFIG_USB_HCD_TEST_MODE
+ /* EH SINGLE_STEP_SET_FEATURE test support */
+ .submit_single_step_set_feature = ehci_submit_single_step_set_feature,
+#endif
};
void ehci_init_driver(struct hc_driver *drv,
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 159cc27b1a36..c4f6a2559a98 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -727,145 +727,6 @@ ehci_hub_descriptor (
}
/*-------------------------------------------------------------------------*/
-#ifdef CONFIG_USB_HCD_TEST_MODE
-
-#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
-
-static void usb_ehset_completion(struct urb *urb)
-{
- struct completion *done = urb->context;
-
- complete(done);
-}
-static int submit_single_step_set_feature(
- struct usb_hcd *hcd,
- struct urb *urb,
- int is_setup
-);
-
-/*
- * Allocate and initialize a control URB. This request will be used by the
- * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
- * of the GetDescriptor request are sent 15 seconds after the SETUP stage.
- * Return NULL if failed.
- */
-static struct urb *request_single_step_set_feature_urb(
- struct usb_device *udev,
- void *dr,
- void *buf,
- struct completion *done
-) {
- struct urb *urb;
- struct usb_hcd *hcd = bus_to_hcd(udev->bus);
- struct usb_host_endpoint *ep;
-
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb)
- return NULL;
-
- urb->pipe = usb_rcvctrlpipe(udev, 0);
- ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
- [usb_pipeendpoint(urb->pipe)];
- if (!ep) {
- usb_free_urb(urb);
- return NULL;
- }
-
- urb->ep = ep;
- urb->dev = udev;
- urb->setup_packet = (void *)dr;
- urb->transfer_buffer = buf;
- urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
- urb->complete = usb_ehset_completion;
- urb->status = -EINPROGRESS;
- urb->actual_length = 0;
- urb->transfer_flags = URB_DIR_IN;
- usb_get_urb(urb);
- atomic_inc(&urb->use_count);
- atomic_inc(&urb->dev->urbnum);
- urb->setup_dma = dma_map_single(
- hcd->self.sysdev,
- urb->setup_packet,
- sizeof(struct usb_ctrlrequest),
- DMA_TO_DEVICE);
- urb->transfer_dma = dma_map_single(
- hcd->self.sysdev,
- urb->transfer_buffer,
- urb->transfer_buffer_length,
- DMA_FROM_DEVICE);
- urb->context = done;
- return urb;
-}
-
-static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
-{
- int retval = -ENOMEM;
- struct usb_ctrlrequest *dr;
- struct urb *urb;
- struct usb_device *udev;
- struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- struct usb_device_descriptor *buf;
- DECLARE_COMPLETION_ONSTACK(done);
-
- /* Obtain udev of the rhub's child port */
- udev = usb_hub_find_child(hcd->self.root_hub, port);
- if (!udev) {
- ehci_err(ehci, "No device attached to the RootHub\n");
- return -ENODEV;
- }
- buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
- if (!dr) {
- kfree(buf);
- return -ENOMEM;
- }
-
- /* Fill Setup packet for GetDescriptor */
- dr->bRequestType = USB_DIR_IN;
- dr->bRequest = USB_REQ_GET_DESCRIPTOR;
- dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
- dr->wIndex = 0;
- dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
- urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
- if (!urb)
- goto cleanup;
-
- /* Submit just the SETUP stage */
- retval = submit_single_step_set_feature(hcd, urb, 1);
- if (retval)
- goto out1;
- if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
- usb_kill_urb(urb);
- retval = -ETIMEDOUT;
- ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__);
- goto out1;
- }
- msleep(15 * 1000);
-
- /* Complete remaining DATA and STATUS stages using the same URB */
- urb->status = -EINPROGRESS;
- usb_get_urb(urb);
- atomic_inc(&urb->use_count);
- atomic_inc(&urb->dev->urbnum);
- retval = submit_single_step_set_feature(hcd, urb, 0);
- if (!retval && !wait_for_completion_timeout(&done,
- msecs_to_jiffies(2000))) {
- usb_kill_urb(urb);
- retval = -ETIMEDOUT;
- ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__);
- }
-out1:
- usb_free_urb(urb);
-cleanup:
- kfree(dr);
- kfree(buf);
- return retval;
-}
-#endif /* CONFIG_USB_HCD_TEST_MODE */
-/*-------------------------------------------------------------------------*/
int ehci_hub_control(
struct usb_hcd *hcd,
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index a826715ae9bd..2cbf4f85bff3 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1165,7 +1165,7 @@ submit_async (
* performed; TRUE - SETUP and FALSE - IN+STATUS
* Returns 0 if success
*/
-static int submit_single_step_set_feature(
+static int ehci_submit_single_step_set_feature(
struct usb_hcd *hcd,
struct urb *urb,
int is_setup
diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
index 9c2eda0918e1..05fb8d97cf02 100644
--- a/drivers/usb/host/fotg210-hcd.c
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -850,7 +850,6 @@ static inline void create_debug_files(struct fotg210_hcd *fotg210)
struct dentry *root;
root = debugfs_create_dir(bus->bus_name, fotg210_debug_root);
- fotg210->debug_dir = root;
debugfs_create_file("async", S_IRUGO, root, bus, &debug_async_fops);
debugfs_create_file("periodic", S_IRUGO, root, bus,
@@ -861,7 +860,9 @@ static inline void create_debug_files(struct fotg210_hcd *fotg210)
static inline void remove_debug_files(struct fotg210_hcd *fotg210)
{
- debugfs_remove_recursive(fotg210->debug_dir);
+ struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self;
+
+ debugfs_remove(debugfs_lookup(bus->bus_name, fotg210_debug_root));
}
/* handshake - spin reading hc until handshake completes or fails
diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h
index 6cee40ec65b4..0a91061a0551 100644
--- a/drivers/usb/host/fotg210.h
+++ b/drivers/usb/host/fotg210.h
@@ -184,9 +184,6 @@ struct fotg210_hcd { /* one per controller */
/* silicon clock */
struct clk *pclk;
-
- /* debug files */
- struct dentry *debug_dir;
};
/* convert between an HCD pointer and the corresponding FOTG210_HCD */
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index 5a783c423d8e..ae882d76612b 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -2392,8 +2392,7 @@ static int dequeue_from_overflow_chain(struct u132 *u132,
urb->error_count = 0;
usb_hcd_giveback_urb(hcd, urb, 0);
return 0;
- } else
- continue;
+ }
}
dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]=%p ring"
"[%d] %c%c usb_endp=%d usb_addr=%d size=%d next=%04X last=%04X"
@@ -2448,8 +2447,7 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
queue_scan];
break;
- } else
- continue;
+ }
}
while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
*urb_slot = endp->urb_list[ENDP_QUEUE_MASK &
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index f66815fe8482..0e312066c5c6 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1924,6 +1924,7 @@ no_bw:
xhci->hw_ports = NULL;
xhci->rh_bw = NULL;
xhci->ext_caps = NULL;
+ xhci->port_caps = NULL;
xhci->page_size = 0;
xhci->page_shift = 0;
@@ -2547,6 +2548,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Wrote ERST address to ir_set 0.");
+ xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
+
/*
* XXX: Might need to set the Interrupter Moderation Register to
* something other than the default (~1ms minimum between interrupts).
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
index 8b90da5a6ed1..cffcaf4dfa9f 100644
--- a/drivers/usb/host/xhci-mtk-sch.c
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -470,11 +470,12 @@ static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset)
static int check_sch_tt(struct mu3h_sch_ep_info *sch_ep, u32 offset)
{
- struct mu3h_sch_tt *tt = sch_ep->sch_tt;
u32 extra_cs_count;
u32 start_ss, last_ss;
u32 start_cs, last_cs;
- int i;
+
+ if (!sch_ep->sch_tt)
+ return 0;
start_ss = offset % 8;
@@ -488,10 +489,6 @@ static int check_sch_tt(struct mu3h_sch_ep_info *sch_ep, u32 offset)
if (!(start_ss == 7 || last_ss < 6))
return -ESCH_SS_Y6;
- for (i = 0; i < sch_ep->cs_count; i++)
- if (test_bit(offset + i, tt->ss_bit_map))
- return -ESCH_SS_OVERLAP;
-
} else {
u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX);
@@ -518,9 +515,6 @@ static int check_sch_tt(struct mu3h_sch_ep_info *sch_ep, u32 offset)
if (cs_count > 7)
cs_count = 7; /* HW limit */
- if (test_bit(offset, tt->ss_bit_map))
- return -ESCH_SS_OVERLAP;
-
sch_ep->cs_count = cs_count;
/* one for ss, the other for idle */
sch_ep->num_budget_microframes = cs_count + 2;
@@ -541,11 +535,9 @@ static void update_sch_tt(struct mu3h_sch_ep_info *sch_ep, bool used)
struct mu3h_sch_tt *tt = sch_ep->sch_tt;
u32 base, num_esit;
int bw_updated;
- int bits;
int i, j;
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
- bits = (sch_ep->ep_type == ISOC_OUT_EP) ? sch_ep->cs_count : 1;
if (used)
bw_updated = sch_ep->bw_cost_per_microframe;
@@ -555,13 +547,6 @@ static void update_sch_tt(struct mu3h_sch_ep_info *sch_ep, bool used)
for (i = 0; i < num_esit; i++) {
base = sch_ep->offset + i * sch_ep->esit;
- for (j = 0; j < bits; j++) {
- if (used)
- set_bit(base + j, tt->ss_bit_map);
- else
- clear_bit(base + j, tt->ss_bit_map);
- }
-
for (j = 0; j < sch_ep->cs_count; j++)
tt->fs_bus_bw[base + j] += bw_updated;
}
@@ -603,54 +588,47 @@ static u32 get_esit_boundary(struct mu3h_sch_ep_info *sch_ep)
static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw,
struct mu3h_sch_ep_info *sch_ep)
{
+ const u32 esit_boundary = get_esit_boundary(sch_ep);
+ const u32 bw_boundary = get_bw_boundary(sch_ep->speed);
u32 offset;
- u32 min_bw;
- u32 min_index;
u32 worst_bw;
- u32 bw_boundary;
- u32 esit_boundary;
- u32 min_num_budget;
- u32 min_cs_count;
+ u32 min_bw = ~0;
+ int min_index = -1;
int ret = 0;
/*
* Search through all possible schedule microframes.
* and find a microframe where its worst bandwidth is minimum.
*/
- min_bw = ~0;
- min_index = 0;
- min_cs_count = sch_ep->cs_count;
- min_num_budget = sch_ep->num_budget_microframes;
- esit_boundary = get_esit_boundary(sch_ep);
for (offset = 0; offset < sch_ep->esit; offset++) {
- if (sch_ep->sch_tt) {
- ret = check_sch_tt(sch_ep, offset);
- if (ret)
- continue;
- }
+ ret = check_sch_tt(sch_ep, offset);
+ if (ret)
+ continue;
if ((offset + sch_ep->num_budget_microframes) > esit_boundary)
break;
worst_bw = get_max_bw(sch_bw, sch_ep, offset);
+ if (worst_bw > bw_boundary)
+ continue;
+
if (min_bw > worst_bw) {
min_bw = worst_bw;
min_index = offset;
- min_cs_count = sch_ep->cs_count;
- min_num_budget = sch_ep->num_budget_microframes;
}
+
+ /* use first-fit for LS/FS */
+ if (sch_ep->sch_tt && min_index >= 0)
+ break;
+
if (min_bw == 0)
break;
}
- bw_boundary = get_bw_boundary(sch_ep->speed);
- /* check bandwidth */
- if (min_bw > bw_boundary)
+ if (min_index < 0)
return ret ? ret : -ESCH_BW_OVERFLOW;
sch_ep->offset = min_index;
- sch_ep->cs_count = min_cs_count;
- sch_ep->num_budget_microframes = min_num_budget;
return load_ep_bw(sch_bw, sch_ep, true);
}
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index b2058b3bc834..2548976bcf05 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -495,8 +495,6 @@ static int xhci_mtk_probe(struct platform_device *pdev)
goto put_usb2_hcd;
}
mtk->has_ippc = true;
- } else {
- mtk->has_ippc = false;
}
device_init_wakeup(dev, true);
diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h
index cd3a37bb73e6..ace432356c41 100644
--- a/drivers/usb/host/xhci-mtk.h
+++ b/drivers/usb/host/xhci-mtk.h
@@ -24,12 +24,10 @@
#define XHCI_MTK_MAX_ESIT 64
/**
- * @ss_bit_map: used to avoid start split microframes overlay
* @fs_bus_bw: array to keep track of bandwidth already used for FS
* @ep_list: Endpoints using this TT
*/
struct mu3h_sch_tt {
- DECLARE_BITMAP(ss_bit_map, XHCI_MTK_MAX_ESIT);
u32 fs_bus_bw[XHCI_MTK_MAX_ESIT];
struct list_head ep_list;
};
@@ -138,17 +136,17 @@ struct xhci_hcd_mtk {
struct mu3h_sch_bw_info *sch_array;
struct list_head bw_ep_chk_list;
struct mu3c_ippc_regs __iomem *ippc_regs;
- bool has_ippc;
int num_u2_ports;
int num_u3_ports;
int u3p_dis_msk;
struct regulator *vusb33;
struct regulator *vbus;
struct clk_bulk_data clks[BULK_CLKS_NUM];
- bool lpm_support;
- bool u2_lpm_disable;
+ unsigned int has_ippc:1;
+ unsigned int lpm_support:1;
+ unsigned int u2_lpm_disable:1;
/* usb remote wakeup */
- bool uwk_en;
+ unsigned int uwk_en:1;
struct regmap *uwk;
u32 uwk_reg_base;
u32 uwk_vers;
diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c
index f97ac9f52bf4..1da647961c25 100644
--- a/drivers/usb/host/xhci-pci-renesas.c
+++ b/drivers/usb/host/xhci-pci-renesas.c
@@ -197,7 +197,7 @@ static int renesas_check_rom_state(struct pci_dev *pdev)
if (err)
return pcibios_err_to_errno(err);
- if (rom_state & BIT(15)) {
+ if (rom_state & RENESAS_ROM_STATUS_ROM_EXISTS) {
/* ROM exists */
dev_dbg(&pdev->dev, "ROM exists\n");
@@ -207,7 +207,8 @@ static int renesas_check_rom_state(struct pci_dev *pdev)
return 0;
case RENESAS_ROM_STATUS_NO_RESULT: /* No result yet */
- return 0;
+ dev_dbg(&pdev->dev, "Unknown ROM status ...\n");
+ break;
case RENESAS_ROM_STATUS_ERROR: /* Error State */
default: /* All other states are marked as "Reserved states" */
@@ -224,13 +225,12 @@ static int renesas_fw_check_running(struct pci_dev *pdev)
u8 fw_state;
int err;
- /* Check if device has ROM and loaded, if so skip everything */
- err = renesas_check_rom(pdev);
- if (err) { /* we have rom */
- err = renesas_check_rom_state(pdev);
- if (!err)
- return err;
- }
+ /*
+ * Only if device has ROM and loaded FW we can skip loading and
+ * return success. Otherwise (even unknown state), attempt to load FW.
+ */
+ if (renesas_check_rom(pdev) && !renesas_check_rom_state(pdev))
+ return 0;
/*
* Test if the device is actually needing the firmware. As most
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 6acd2329e08d..8fea44bbc266 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3076,6 +3076,11 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
if (event_loop++ < TRBS_PER_SEGMENT / 2)
continue;
xhci_update_erst_dequeue(xhci, event_ring_deq);
+
+ /* ring is half-full, force isoc trbs to interrupt more often */
+ if (xhci->isoc_bei_interval > AVOID_BEI_INTERVAL_MIN)
+ xhci->isoc_bei_interval = xhci->isoc_bei_interval / 2;
+
event_loop = 0;
}
@@ -3956,7 +3961,7 @@ static bool trb_block_event_intr(struct xhci_hcd *xhci, int num_tds, int i)
* generate an event at least every 8th TD to clear the event ring
*/
if (i && xhci->quirks & XHCI_AVOID_BEI)
- return !!(i % 8);
+ return !!(i % xhci->isoc_bei_interval);
return true;
}
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index c7387677a26a..575fa89a783f 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -2,7 +2,7 @@
/*
* NVIDIA Tegra xHCI host controller driver
*
- * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
* Copyright (C) 2014 Google, Inc.
*/
@@ -15,9 +15,11 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
#include <linux/phy/phy.h>
#include <linux/phy/tegra/xusb.h>
#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
#include <linux/pm.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
@@ -224,6 +226,7 @@ struct tegra_xusb {
int xhci_irq;
int mbox_irq;
+ int padctl_irq;
void __iomem *ipfs_base;
void __iomem *fpci_base;
@@ -249,8 +252,7 @@ struct tegra_xusb {
struct device *genpd_dev_host;
struct device *genpd_dev_ss;
- struct device_link *genpd_dl_host;
- struct device_link *genpd_dl_ss;
+ bool use_genpd;
struct phy **phys;
unsigned int num_phys;
@@ -270,6 +272,7 @@ struct tegra_xusb {
dma_addr_t phys;
} fw;
+ bool suspended;
struct tegra_xusb_context context;
};
@@ -666,6 +669,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data)
mutex_lock(&tegra->lock);
+ if (pm_runtime_suspended(tegra->dev) || tegra->suspended)
+ goto out;
+
value = fpci_readl(tegra, tegra->soc->mbox.data_out);
tegra_xusb_mbox_unpack(&msg, value);
@@ -679,6 +685,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data)
tegra_xusb_mbox_handle(tegra, &msg);
+out:
mutex_unlock(&tegra->lock);
return IRQ_HANDLED;
}
@@ -819,40 +826,6 @@ static void tegra_xusb_phy_disable(struct tegra_xusb *tegra)
}
}
-static int tegra_xusb_runtime_suspend(struct device *dev)
-{
- struct tegra_xusb *tegra = dev_get_drvdata(dev);
-
- regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
- tegra_xusb_clk_disable(tegra);
-
- return 0;
-}
-
-static int tegra_xusb_runtime_resume(struct device *dev)
-{
- struct tegra_xusb *tegra = dev_get_drvdata(dev);
- int err;
-
- err = tegra_xusb_clk_enable(tegra);
- if (err) {
- dev_err(dev, "failed to enable clocks: %d\n", err);
- return err;
- }
-
- err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
- if (err) {
- dev_err(dev, "failed to enable regulators: %d\n", err);
- goto disable_clk;
- }
-
- return 0;
-
-disable_clk:
- tegra_xusb_clk_disable(tegra);
- return err;
-}
-
#ifdef CONFIG_PM_SLEEP
static int tegra_xusb_init_context(struct tegra_xusb *tegra)
{
@@ -1022,10 +995,9 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
static void tegra_xusb_powerdomain_remove(struct device *dev,
struct tegra_xusb *tegra)
{
- if (tegra->genpd_dl_ss)
- device_link_del(tegra->genpd_dl_ss);
- if (tegra->genpd_dl_host)
- device_link_del(tegra->genpd_dl_host);
+ if (!tegra->use_genpd)
+ return;
+
if (!IS_ERR_OR_NULL(tegra->genpd_dev_ss))
dev_pm_domain_detach(tegra->genpd_dev_ss, true);
if (!IS_ERR_OR_NULL(tegra->genpd_dev_host))
@@ -1051,20 +1023,84 @@ static int tegra_xusb_powerdomain_init(struct device *dev,
return err;
}
- tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host,
- DL_FLAG_PM_RUNTIME |
- DL_FLAG_STATELESS);
- if (!tegra->genpd_dl_host) {
- dev_err(dev, "adding host device link failed!\n");
- return -ENODEV;
+ tegra->use_genpd = true;
+
+ return 0;
+}
+
+static int tegra_xusb_unpowergate_partitions(struct tegra_xusb *tegra)
+{
+ struct device *dev = tegra->dev;
+ int rc;
+
+ if (tegra->use_genpd) {
+ rc = pm_runtime_get_sync(tegra->genpd_dev_ss);
+ if (rc < 0) {
+ dev_err(dev, "failed to enable XUSB SS partition\n");
+ return rc;
+ }
+
+ rc = pm_runtime_get_sync(tegra->genpd_dev_host);
+ if (rc < 0) {
+ dev_err(dev, "failed to enable XUSB Host partition\n");
+ pm_runtime_put_sync(tegra->genpd_dev_ss);
+ return rc;
+ }
+ } else {
+ rc = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA,
+ tegra->ss_clk,
+ tegra->ss_rst);
+ if (rc < 0) {
+ dev_err(dev, "failed to enable XUSB SS partition\n");
+ return rc;
+ }
+
+ rc = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,
+ tegra->host_clk,
+ tegra->host_rst);
+ if (rc < 0) {
+ dev_err(dev, "failed to enable XUSB Host partition\n");
+ tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
+ return rc;
+ }
}
- tegra->genpd_dl_ss = device_link_add(dev, tegra->genpd_dev_ss,
- DL_FLAG_PM_RUNTIME |
- DL_FLAG_STATELESS);
- if (!tegra->genpd_dl_ss) {
- dev_err(dev, "adding superspeed device link failed!\n");
- return -ENODEV;
+ return 0;
+}
+
+static int tegra_xusb_powergate_partitions(struct tegra_xusb *tegra)
+{
+ struct device *dev = tegra->dev;
+ int rc;
+
+ if (tegra->use_genpd) {
+ rc = pm_runtime_put_sync(tegra->genpd_dev_host);
+ if (rc < 0) {
+ dev_err(dev, "failed to disable XUSB Host partition\n");
+ return rc;
+ }
+
+ rc = pm_runtime_put_sync(tegra->genpd_dev_ss);
+ if (rc < 0) {
+ dev_err(dev, "failed to disable XUSB SS partition\n");
+ pm_runtime_get_sync(tegra->genpd_dev_host);
+ return rc;
+ }
+ } else {
+ rc = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
+ if (rc < 0) {
+ dev_err(dev, "failed to disable XUSB Host partition\n");
+ return rc;
+ }
+
+ rc = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
+ if (rc < 0) {
+ dev_err(dev, "failed to disable XUSB SS partition\n");
+ tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,
+ tegra->host_clk,
+ tegra->host_rst);
+ return rc;
+ }
}
return 0;
@@ -1086,6 +1122,24 @@ static int __tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
return err;
}
+static irqreturn_t tegra_xusb_padctl_irq(int irq, void *data)
+{
+ struct tegra_xusb *tegra = data;
+
+ mutex_lock(&tegra->lock);
+
+ if (tegra->suspended) {
+ mutex_unlock(&tegra->lock);
+ return IRQ_HANDLED;
+ }
+
+ mutex_unlock(&tegra->lock);
+
+ pm_runtime_resume(tegra->dev);
+
+ return IRQ_HANDLED;
+}
+
static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
{
int err;
@@ -1209,6 +1263,52 @@ static void tegra_xhci_id_work(struct work_struct *work)
}
}
+#if IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP)
+static bool is_usb2_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+ return (tegra->usbphy[index] != NULL);
+}
+
+static bool is_usb3_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+ struct tegra_xusb_padctl *padctl = tegra->padctl;
+ unsigned int i;
+ int port;
+
+ for (i = 0; i < tegra->num_usb_phys; i++) {
+ if (is_usb2_otg_phy(tegra, i)) {
+ port = tegra_xusb_padctl_get_usb3_companion(padctl, i);
+ if ((port >= 0) && (index == (unsigned int)port))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool is_host_mode_phy(struct tegra_xusb *tegra, unsigned int phy_type, unsigned int index)
+{
+ if (strcmp(tegra->soc->phy_types[phy_type].name, "hsic") == 0)
+ return true;
+
+ if (strcmp(tegra->soc->phy_types[phy_type].name, "usb2") == 0) {
+ if (is_usb2_otg_phy(tegra, index))
+ return ((index == tegra->otg_usb2_port) && tegra->host_mode);
+ else
+ return true;
+ }
+
+ if (strcmp(tegra->soc->phy_types[phy_type].name, "usb3") == 0) {
+ if (is_usb3_otg_phy(tegra, index))
+ return ((index == tegra->otg_usb3_port) && tegra->host_mode);
+ else
+ return true;
+ }
+
+ return false;
+}
+#endif
+
static int tegra_xusb_get_usb2_port(struct tegra_xusb *tegra,
struct usb_phy *usbphy)
{
@@ -1301,6 +1401,7 @@ static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra)
static int tegra_xusb_probe(struct platform_device *pdev)
{
struct tegra_xusb *tegra;
+ struct device_node *np;
struct resource *regs;
struct xhci_hcd *xhci;
unsigned int i, j, k;
@@ -1321,8 +1422,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
if (err < 0)
return err;
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- tegra->regs = devm_ioremap_resource(&pdev->dev, regs);
+ tegra->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &regs);
if (IS_ERR(tegra->regs))
return PTR_ERR(tegra->regs);
@@ -1348,6 +1448,18 @@ static int tegra_xusb_probe(struct platform_device *pdev)
if (IS_ERR(tegra->padctl))
return PTR_ERR(tegra->padctl);
+ np = of_parse_phandle(pdev->dev.of_node, "nvidia,xusb-padctl", 0);
+ if (!np) {
+ err = -ENODEV;
+ goto put_padctl;
+ }
+
+ tegra->padctl_irq = of_irq_get(np, 0);
+ if (tegra->padctl_irq <= 0) {
+ err = (tegra->padctl_irq == 0) ? -ENODEV : tegra->padctl_irq;
+ goto put_padctl;
+ }
+
tegra->host_clk = devm_clk_get(&pdev->dev, "xusb_host");
if (IS_ERR(tegra->host_clk)) {
err = PTR_ERR(tegra->host_clk);
@@ -1428,25 +1540,6 @@ static int tegra_xusb_probe(struct platform_device *pdev)
err);
goto put_padctl;
}
-
- err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA,
- tegra->ss_clk,
- tegra->ss_rst);
- if (err) {
- dev_err(&pdev->dev,
- "failed to enable XUSBA domain: %d\n", err);
- goto put_padctl;
- }
-
- err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,
- tegra->host_clk,
- tegra->host_rst);
- if (err) {
- tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
- dev_err(&pdev->dev,
- "failed to enable XUSBC domain: %d\n", err);
- goto put_padctl;
- }
} else {
err = tegra_xusb_powerdomain_init(&pdev->dev, tegra);
if (err)
@@ -1511,6 +1604,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
goto put_powerdomains;
}
+ tegra->hcd->skip_phy_initialization = 1;
tegra->hcd->regs = tegra->regs;
tegra->hcd->rsrc_start = regs->start;
tegra->hcd->rsrc_len = resource_size(regs);
@@ -1521,10 +1615,22 @@ static int tegra_xusb_probe(struct platform_device *pdev)
*/
platform_set_drvdata(pdev, tegra);
+ err = tegra_xusb_clk_enable(tegra);
+ if (err) {
+ dev_err(tegra->dev, "failed to enable clocks: %d\n", err);
+ goto put_hcd;
+ }
+
+ err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
+ if (err) {
+ dev_err(tegra->dev, "failed to enable regulators: %d\n", err);
+ goto disable_clk;
+ }
+
err = tegra_xusb_phy_enable(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err);
- goto put_hcd;
+ goto disable_regulator;
}
/*
@@ -1543,30 +1649,22 @@ static int tegra_xusb_probe(struct platform_device *pdev)
goto disable_phy;
}
- pm_runtime_enable(&pdev->dev);
-
- if (!pm_runtime_enabled(&pdev->dev))
- err = tegra_xusb_runtime_resume(&pdev->dev);
- else
- err = pm_runtime_get_sync(&pdev->dev);
-
- if (err < 0) {
- dev_err(&pdev->dev, "failed to enable device: %d\n", err);
+ err = tegra_xusb_unpowergate_partitions(tegra);
+ if (err)
goto free_firmware;
- }
tegra_xusb_config(tegra);
err = tegra_xusb_load_firmware(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to load firmware: %d\n", err);
- goto put_rpm;
+ goto powergate;
}
err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED);
if (err < 0) {
dev_err(&pdev->dev, "failed to add USB HCD: %d\n", err);
- goto put_rpm;
+ goto powergate;
}
device_wakeup_enable(tegra->hcd->self.controller);
@@ -1589,12 +1687,6 @@ static int tegra_xusb_probe(struct platform_device *pdev)
goto put_usb3;
}
- err = tegra_xusb_enable_firmware_messages(tegra);
- if (err < 0) {
- dev_err(&pdev->dev, "failed to enable messages: %d\n", err);
- goto remove_usb3;
- }
-
err = devm_request_threaded_irq(&pdev->dev, tegra->mbox_irq,
tegra_xusb_mbox_irq,
tegra_xusb_mbox_thread, 0,
@@ -1604,12 +1696,36 @@ static int tegra_xusb_probe(struct platform_device *pdev)
goto remove_usb3;
}
+ err = devm_request_threaded_irq(&pdev->dev, tegra->padctl_irq, NULL, tegra_xusb_padctl_irq,
+ IRQF_ONESHOT, dev_name(&pdev->dev), tegra);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request padctl IRQ: %d\n", err);
+ goto remove_usb3;
+ }
+
+ err = tegra_xusb_enable_firmware_messages(tegra);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to enable messages: %d\n", err);
+ goto remove_usb3;
+ }
+
err = tegra_xusb_init_usb_phy(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to init USB PHY: %d\n", err);
goto remove_usb3;
}
+ /* Enable wake for both USB 2.0 and USB 3.0 roothubs */
+ device_init_wakeup(&tegra->hcd->self.root_hub->dev, true);
+ device_init_wakeup(&xhci->shared_hcd->self.root_hub->dev, true);
+ device_init_wakeup(tegra->dev, true);
+
+ pm_runtime_use_autosuspend(tegra->dev);
+ pm_runtime_set_autosuspend_delay(tegra->dev, 2000);
+ pm_runtime_mark_last_busy(tegra->dev);
+ pm_runtime_set_active(tegra->dev);
+ pm_runtime_enable(tegra->dev);
+
return 0;
remove_usb3:
@@ -1618,25 +1734,23 @@ put_usb3:
usb_put_hcd(xhci->shared_hcd);
remove_usb2:
usb_remove_hcd(tegra->hcd);
-put_rpm:
- if (!pm_runtime_status_suspended(&pdev->dev))
- tegra_xusb_runtime_suspend(&pdev->dev);
-put_hcd:
- usb_put_hcd(tegra->hcd);
+powergate:
+ tegra_xusb_powergate_partitions(tegra);
free_firmware:
dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt,
tegra->fw.phys);
disable_phy:
tegra_xusb_phy_disable(tegra);
- pm_runtime_disable(&pdev->dev);
+disable_regulator:
+ regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
+disable_clk:
+ tegra_xusb_clk_disable(tegra);
+put_hcd:
+ usb_put_hcd(tegra->hcd);
put_powerdomains:
- if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
- tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
- tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
- } else {
- tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
- }
+ tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
put_padctl:
+ of_node_put(np);
tegra_xusb_padctl_put(tegra->padctl);
return err;
}
@@ -1648,6 +1762,7 @@ static int tegra_xusb_remove(struct platform_device *pdev)
tegra_xusb_deinit_usb_phy(tegra);
+ pm_runtime_get_sync(&pdev->dev);
usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd);
xhci->shared_hcd = NULL;
@@ -1657,24 +1772,22 @@ static int tegra_xusb_remove(struct platform_device *pdev)
dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt,
tegra->fw.phys);
- pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_put(&pdev->dev);
- if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
- tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
- tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
- } else {
- tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
- }
+ tegra_xusb_powergate_partitions(tegra);
- tegra_xusb_phy_disable(tegra);
+ tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
+ 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);
return 0;
}
-#ifdef CONFIG_PM_SLEEP
+#if IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP)
static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
{
struct device *dev = hub->hcd->self.controller;
@@ -1700,9 +1813,17 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
static int tegra_xusb_check_ports(struct tegra_xusb *tegra)
{
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ struct xhci_bus_state *bus_state = &xhci->usb2_rhub.bus_state;
unsigned long flags;
int err = 0;
+ if (bus_state->bus_suspended) {
+ /* xusb_hub_suspend() has just directed one or more USB2 port(s)
+ * to U3 state, it takes 3ms to enter U3.
+ */
+ usleep_range(3000, 4000);
+ }
+
spin_lock_irqsave(&xhci->lock, flags);
if (!xhci_hub_ports_suspended(&xhci->usb2_rhub) ||
@@ -1748,45 +1869,186 @@ static void tegra_xusb_restore_context(struct tegra_xusb *tegra)
}
}
-static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool wakeup)
+static enum usb_device_speed tegra_xhci_portsc_to_speed(struct tegra_xusb *tegra, u32 portsc)
{
+ if (DEV_LOWSPEED(portsc))
+ return USB_SPEED_LOW;
+
+ if (DEV_HIGHSPEED(portsc))
+ return USB_SPEED_HIGH;
+
+ if (DEV_FULLSPEED(portsc))
+ return USB_SPEED_FULL;
+
+ if (DEV_SUPERSPEED_ANY(portsc))
+ return USB_SPEED_SUPER;
+
+ return USB_SPEED_UNKNOWN;
+}
+
+static void tegra_xhci_enable_phy_sleepwalk_wake(struct tegra_xusb *tegra)
+{
+ struct tegra_xusb_padctl *padctl = tegra->padctl;
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ enum usb_device_speed speed;
+ struct phy *phy;
+ unsigned int index, offset;
+ unsigned int i, j, k;
+ struct xhci_hub *rhub;
+ u32 portsc;
+
+ for (i = 0, k = 0; i < tegra->soc->num_types; i++) {
+ if (strcmp(tegra->soc->phy_types[i].name, "usb3") == 0)
+ rhub = &xhci->usb3_rhub;
+ else
+ rhub = &xhci->usb2_rhub;
+
+ if (strcmp(tegra->soc->phy_types[i].name, "hsic") == 0)
+ offset = tegra->soc->ports.usb2.count;
+ else
+ offset = 0;
+
+ for (j = 0; j < tegra->soc->phy_types[i].num; j++) {
+ phy = tegra->phys[k++];
+
+ if (!phy)
+ continue;
+
+ index = j + offset;
+
+ if (index >= rhub->num_ports)
+ continue;
+
+ if (!is_host_mode_phy(tegra, i, j))
+ continue;
+
+ portsc = readl(rhub->ports[index]->addr);
+ speed = tegra_xhci_portsc_to_speed(tegra, portsc);
+ tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed);
+ tegra_xusb_padctl_enable_phy_wake(padctl, phy);
+ }
+ }
+}
+
+static void tegra_xhci_disable_phy_wake(struct tegra_xusb *tegra)
+{
+ struct tegra_xusb_padctl *padctl = tegra->padctl;
+ unsigned int i;
+
+ for (i = 0; i < tegra->num_phys; i++) {
+ if (!tegra->phys[i])
+ continue;
+
+ tegra_xusb_padctl_disable_phy_wake(padctl, tegra->phys[i]);
+ }
+}
+
+static void tegra_xhci_disable_phy_sleepwalk(struct tegra_xusb *tegra)
+{
+ struct tegra_xusb_padctl *padctl = tegra->padctl;
+ unsigned int i;
+
+ for (i = 0; i < tegra->num_phys; i++) {
+ if (!tegra->phys[i])
+ continue;
+
+ tegra_xusb_padctl_disable_phy_sleepwalk(padctl, tegra->phys[i]);
+ }
+}
+
+static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ struct device *dev = tegra->dev;
+ bool wakeup = runtime ? true : device_may_wakeup(dev);
+ unsigned int i;
int err;
+ u32 usbcmd;
+
+ dev_dbg(dev, "entering ELPG\n");
+
+ usbcmd = readl(&xhci->op_regs->command);
+ usbcmd &= ~CMD_EIE;
+ writel(usbcmd, &xhci->op_regs->command);
err = tegra_xusb_check_ports(tegra);
if (err < 0) {
dev_err(tegra->dev, "not all ports suspended: %d\n", err);
- return err;
+ goto out;
}
err = xhci_suspend(xhci, wakeup);
if (err < 0) {
dev_err(tegra->dev, "failed to suspend XHCI: %d\n", err);
- return err;
+ goto out;
}
tegra_xusb_save_context(tegra);
- tegra_xusb_phy_disable(tegra);
+
+ if (wakeup)
+ tegra_xhci_enable_phy_sleepwalk_wake(tegra);
+
+ tegra_xusb_powergate_partitions(tegra);
+
+ for (i = 0; i < tegra->num_phys; i++) {
+ if (!tegra->phys[i])
+ continue;
+
+ phy_power_off(tegra->phys[i]);
+ if (!wakeup)
+ phy_exit(tegra->phys[i]);
+ }
+
tegra_xusb_clk_disable(tegra);
- return 0;
+out:
+ if (!err)
+ dev_dbg(tegra->dev, "entering ELPG done\n");
+ else {
+ usbcmd = readl(&xhci->op_regs->command);
+ usbcmd |= CMD_EIE;
+ writel(usbcmd, &xhci->op_regs->command);
+
+ dev_dbg(tegra->dev, "entering ELPG failed\n");
+ pm_runtime_mark_last_busy(tegra->dev);
+ }
+
+ return err;
}
-static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool wakeup)
+static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
{
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ struct device *dev = tegra->dev;
+ bool wakeup = runtime ? true : device_may_wakeup(dev);
+ unsigned int i;
+ u32 usbcmd;
int err;
+ dev_dbg(dev, "exiting ELPG\n");
+ pm_runtime_mark_last_busy(tegra->dev);
+
err = tegra_xusb_clk_enable(tegra);
if (err < 0) {
dev_err(tegra->dev, "failed to enable clocks: %d\n", err);
- return err;
+ goto out;
}
- err = tegra_xusb_phy_enable(tegra);
- if (err < 0) {
- dev_err(tegra->dev, "failed to enable PHYs: %d\n", err);
- goto disable_clk;
+ err = tegra_xusb_unpowergate_partitions(tegra);
+ if (err)
+ goto disable_clks;
+
+ if (wakeup)
+ tegra_xhci_disable_phy_wake(tegra);
+
+ for (i = 0; i < tegra->num_phys; i++) {
+ if (!tegra->phys[i])
+ continue;
+
+ if (!wakeup)
+ phy_init(tegra->phys[i]);
+
+ phy_power_on(tegra->phys[i]);
}
tegra_xusb_config(tegra);
@@ -1804,31 +2066,79 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool wakeup)
goto disable_phy;
}
- err = xhci_resume(xhci, true);
+ if (wakeup)
+ tegra_xhci_disable_phy_sleepwalk(tegra);
+
+ err = xhci_resume(xhci, 0);
if (err < 0) {
dev_err(tegra->dev, "failed to resume XHCI: %d\n", err);
goto disable_phy;
}
- return 0;
+ usbcmd = readl(&xhci->op_regs->command);
+ usbcmd |= CMD_EIE;
+ writel(usbcmd, &xhci->op_regs->command);
+
+ goto out;
disable_phy:
- tegra_xusb_phy_disable(tegra);
-disable_clk:
+ for (i = 0; i < tegra->num_phys; i++) {
+ if (!tegra->phys[i])
+ continue;
+
+ phy_power_off(tegra->phys[i]);
+ if (!wakeup)
+ phy_exit(tegra->phys[i]);
+ }
+ tegra_xusb_powergate_partitions(tegra);
+disable_clks:
tegra_xusb_clk_disable(tegra);
+out:
+ if (!err)
+ dev_dbg(dev, "exiting ELPG done\n");
+ else
+ dev_dbg(dev, "exiting ELPG failed\n");
+
return err;
}
static int tegra_xusb_suspend(struct device *dev)
{
struct tegra_xusb *tegra = dev_get_drvdata(dev);
- bool wakeup = device_may_wakeup(dev);
int err;
synchronize_irq(tegra->mbox_irq);
mutex_lock(&tegra->lock);
- err = tegra_xusb_enter_elpg(tegra, wakeup);
+
+ if (pm_runtime_suspended(dev)) {
+ err = tegra_xusb_exit_elpg(tegra, true);
+ if (err < 0)
+ goto out;
+ }
+
+ err = tegra_xusb_enter_elpg(tegra, false);
+ if (err < 0) {
+ if (pm_runtime_suspended(dev)) {
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ }
+
+ goto out;
+ }
+
+out:
+ if (!err) {
+ tegra->suspended = true;
+ pm_runtime_disable(dev);
+
+ if (device_may_wakeup(dev)) {
+ if (enable_irq_wake(tegra->padctl_irq))
+ dev_err(dev, "failed to enable padctl wakes\n");
+ }
+ }
+
mutex_unlock(&tegra->lock);
return err;
@@ -1837,11 +2147,56 @@ static int tegra_xusb_suspend(struct device *dev)
static int tegra_xusb_resume(struct device *dev)
{
struct tegra_xusb *tegra = dev_get_drvdata(dev);
- bool wakeup = device_may_wakeup(dev);
int err;
mutex_lock(&tegra->lock);
- err = tegra_xusb_exit_elpg(tegra, wakeup);
+
+ if (!tegra->suspended) {
+ mutex_unlock(&tegra->lock);
+ return 0;
+ }
+
+ err = tegra_xusb_exit_elpg(tegra, false);
+ if (err < 0) {
+ mutex_unlock(&tegra->lock);
+ return err;
+ }
+
+ if (device_may_wakeup(dev)) {
+ if (disable_irq_wake(tegra->padctl_irq))
+ dev_err(dev, "failed to disable padctl wakes\n");
+ }
+ tegra->suspended = false;
+ mutex_unlock(&tegra->lock);
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int tegra_xusb_runtime_suspend(struct device *dev)
+{
+ struct tegra_xusb *tegra = dev_get_drvdata(dev);
+ int ret;
+
+ synchronize_irq(tegra->mbox_irq);
+ mutex_lock(&tegra->lock);
+ ret = tegra_xusb_enter_elpg(tegra, true);
+ mutex_unlock(&tegra->lock);
+
+ return ret;
+}
+
+static int tegra_xusb_runtime_resume(struct device *dev)
+{
+ struct tegra_xusb *tegra = dev_get_drvdata(dev);
+ int err;
+
+ mutex_lock(&tegra->lock);
+ err = tegra_xusb_exit_elpg(tegra, true);
mutex_unlock(&tegra->lock);
return err;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 27283654ca08..3618070eba78 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1361,12 +1361,17 @@ static void xhci_unmap_temp_buf(struct usb_hcd *hcd, struct urb *urb)
urb->transfer_buffer_length,
dir);
- if (usb_urb_dir_in(urb))
+ if (usb_urb_dir_in(urb)) {
len = sg_pcopy_from_buffer(urb->sg, urb->num_sgs,
urb->transfer_buffer,
buf_len,
0);
-
+ if (len != buf_len) {
+ xhci_dbg(hcd_to_xhci(hcd),
+ "Copy from tmp buf to urb sg list failed\n");
+ urb->actual_length = len;
+ }
+ }
urb->transfer_flags &= ~URB_DMA_MAP_SINGLE;
kfree(urb->transfer_buffer);
urb->transfer_buffer = NULL;
@@ -4840,7 +4845,6 @@ static int xhci_update_timeout_for_interface(struct xhci_hcd *xhci,
if (xhci_update_timeout_for_endpoint(xhci, udev,
&alt->endpoint[j].desc, state, timeout))
return -E2BIG;
- continue;
}
return 0;
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index e417f5ce13d1..3c7d281672ae 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1526,6 +1526,12 @@ static inline const char *xhci_trb_type_string(u8 type)
#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_BUFF_SIZE - \
(addr & (TRB_MAX_BUFF_SIZE - 1)))
#define MAX_SOFT_RETRY 3
+/*
+ * Limits of consecutive isoc trbs that can Block Event Interrupt (BEI) if
+ * XHCI_AVOID_BEI quirk is in use.
+ */
+#define AVOID_BEI_INTERVAL_MIN 8
+#define AVOID_BEI_INTERVAL_MAX 32
struct xhci_segment {
union xhci_trb *trbs;
@@ -1663,10 +1669,6 @@ struct urb_priv {
* meaning 64 ring segments.
* Initial allocated size of the ERST, in number of entries */
#define ERST_NUM_SEGS 1
-/* Initial allocated size of the ERST, in number of entries */
-#define ERST_SIZE 64
-/* Initial number of event segment rings allocated */
-#define ERST_ENTRIES 1
/* Poll every 60 seconds */
#define POLL_TIMEOUT 60
/* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */
@@ -1772,6 +1774,7 @@ struct xhci_hcd {
u8 isoc_threshold;
/* imod_interval in ns (I * 250ns) */
u32 imod_interval;
+ u32 isoc_bei_interval;
int event_ring_max;
/* 4KB min, 128MB max */
int page_size;
diff --git a/drivers/usb/isp1760/Kconfig b/drivers/usb/isp1760/Kconfig
index b1022cc490a2..2ed2b73291d1 100644
--- a/drivers/usb/isp1760/Kconfig
+++ b/drivers/usb/isp1760/Kconfig
@@ -1,10 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
config USB_ISP1760
- tristate "NXP ISP 1760/1761 support"
+ tristate "NXP ISP 1760/1761/1763 support"
depends on USB || USB_GADGET
+ select REGMAP_MMIO
help
- Say Y or M here if your system as an ISP1760 USB host controller
+ Say Y or M here if your system as an ISP1760/1763 USB host controller
or an ISP1761 USB dual-role controller.
This driver does not support isochronous transfers or OTG.
diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c
index fdeb4cf97cc5..ff07e2890692 100644
--- a/drivers/usb/isp1760/isp1760-core.c
+++ b/drivers/usb/isp1760/isp1760-core.c
@@ -2,12 +2,14 @@
/*
* Driver for the NXP ISP1760 chip
*
+ * Copyright 2021 Linaro, Rui Miguel Silva
* Copyright 2014 Laurent Pinchart
* Copyright 2007 Sebastian Siewior
*
* Contacts:
* Sebastian Siewior <bigeasy@linutronix.de>
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Rui Miguel Silva <rui.silva@linaro.org>
*/
#include <linux/delay.h>
@@ -15,6 +17,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/usb.h>
@@ -23,10 +26,10 @@
#include "isp1760-regs.h"
#include "isp1760-udc.h"
-static void isp1760_init_core(struct isp1760_device *isp)
+static int isp1760_init_core(struct isp1760_device *isp)
{
- u32 otgctrl;
- u32 hwmode;
+ struct isp1760_hcd *hcd = &isp->hcd;
+ struct isp1760_udc *udc = &isp->udc;
/* Low-level chip reset */
if (isp->rst_gpio) {
@@ -39,24 +42,29 @@ static void isp1760_init_core(struct isp1760_device *isp)
* Reset the host controller, including the CPU interface
* configuration.
*/
- isp1760_write32(isp->regs, HC_RESET_REG, SW_RESET_RESET_ALL);
+ isp1760_field_set(hcd->fields, SW_RESET_RESET_ALL);
msleep(100);
/* Setup HW Mode Control: This assumes a level active-low interrupt */
- hwmode = HW_DATA_BUS_32BIT;
+ if ((isp->devflags & ISP1760_FLAG_ANALOG_OC) && hcd->is_isp1763) {
+ dev_err(isp->dev, "isp1763 analog overcurrent not available\n");
+ return -EINVAL;
+ }
if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_16)
- hwmode &= ~HW_DATA_BUS_32BIT;
+ isp1760_field_clear(hcd->fields, HW_DATA_BUS_WIDTH);
+ if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_8)
+ isp1760_field_set(hcd->fields, HW_DATA_BUS_WIDTH);
if (isp->devflags & ISP1760_FLAG_ANALOG_OC)
- hwmode |= HW_ANA_DIGI_OC;
+ isp1760_field_set(hcd->fields, HW_ANA_DIGI_OC);
if (isp->devflags & ISP1760_FLAG_DACK_POL_HIGH)
- hwmode |= HW_DACK_POL_HIGH;
+ isp1760_field_set(hcd->fields, HW_DACK_POL_HIGH);
if (isp->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
- hwmode |= HW_DREQ_POL_HIGH;
+ isp1760_field_set(hcd->fields, HW_DREQ_POL_HIGH);
if (isp->devflags & ISP1760_FLAG_INTR_POL_HIGH)
- hwmode |= HW_INTR_HIGH_ACT;
+ isp1760_field_set(hcd->fields, HW_INTR_HIGH_ACT);
if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
- hwmode |= HW_INTR_EDGE_TRIG;
+ isp1760_field_set(hcd->fields, HW_INTR_EDGE_TRIG);
/*
* The ISP1761 has a dedicated DC IRQ line but supports sharing the HC
@@ -65,59 +73,425 @@ static void isp1760_init_core(struct isp1760_device *isp)
* spurious interrupts during HCD registration.
*/
if (isp->devflags & ISP1760_FLAG_ISP1761) {
- isp1760_write32(isp->regs, DC_MODE, 0);
- hwmode |= HW_COMN_IRQ;
+ isp1760_reg_write(udc->regs, ISP176x_DC_MODE, 0);
+ isp1760_field_set(hcd->fields, HW_COMN_IRQ);
}
/*
- * We have to set this first in case we're in 16-bit mode.
- * Write it twice to ensure correct upper bits if switching
- * to 16-bit mode.
- */
- isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
- isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
-
- /*
* PORT 1 Control register of the ISP1760 is the OTG control register
* on ISP1761.
*
* TODO: Really support OTG. For now we configure port 1 in device mode
- * when OTG is requested.
*/
- if ((isp->devflags & ISP1760_FLAG_ISP1761) &&
- (isp->devflags & ISP1760_FLAG_OTG_EN))
- otgctrl = ((HW_DM_PULLDOWN | HW_DP_PULLDOWN) << 16)
- | HW_OTG_DISABLE;
- else
- otgctrl = (HW_SW_SEL_HC_DC << 16)
- | (HW_VBUS_DRV | HW_SEL_CP_EXT);
-
- isp1760_write32(isp->regs, HC_PORT1_CTRL, otgctrl);
+ if (((isp->devflags & ISP1760_FLAG_ISP1761) ||
+ (isp->devflags & ISP1760_FLAG_ISP1763)) &&
+ (isp->devflags & ISP1760_FLAG_PERIPHERAL_EN)) {
+ isp1760_field_set(hcd->fields, HW_DM_PULLDOWN);
+ isp1760_field_set(hcd->fields, HW_DP_PULLDOWN);
+ isp1760_field_set(hcd->fields, HW_OTG_DISABLE);
+ } else {
+ isp1760_field_set(hcd->fields, HW_SW_SEL_HC_DC);
+ isp1760_field_set(hcd->fields, HW_VBUS_DRV);
+ isp1760_field_set(hcd->fields, HW_SEL_CP_EXT);
+ }
- dev_info(isp->dev, "bus width: %u, oc: %s\n",
+ dev_info(isp->dev, "%s bus width: %u, oc: %s\n",
+ hcd->is_isp1763 ? "isp1763" : "isp1760",
+ isp->devflags & ISP1760_FLAG_BUS_WIDTH_8 ? 8 :
isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32,
+ hcd->is_isp1763 ? "not available" :
isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital");
+
+ return 0;
}
void isp1760_set_pullup(struct isp1760_device *isp, bool enable)
{
- isp1760_write32(isp->regs, HW_OTG_CTRL_SET,
- enable ? HW_DP_PULLUP : HW_DP_PULLUP << 16);
+ struct isp1760_udc *udc = &isp->udc;
+
+ if (enable)
+ isp1760_field_set(udc->fields, HW_DP_PULLUP);
+ else
+ isp1760_field_set(udc->fields, HW_DP_PULLUP_CLEAR);
}
+/*
+ * ISP1760/61:
+ *
+ * 60kb divided in:
+ * - 32 blocks @ 256 bytes
+ * - 20 blocks @ 1024 bytes
+ * - 4 blocks @ 8192 bytes
+ */
+static const struct isp1760_memory_layout isp176x_memory_conf = {
+ .blocks[0] = 32,
+ .blocks_size[0] = 256,
+ .blocks[1] = 20,
+ .blocks_size[1] = 1024,
+ .blocks[2] = 4,
+ .blocks_size[2] = 8192,
+
+ .slot_num = 32,
+ .payload_blocks = 32 + 20 + 4,
+ .payload_area_size = 0xf000,
+};
+
+/*
+ * ISP1763:
+ *
+ * 20kb divided in:
+ * - 8 blocks @ 256 bytes
+ * - 2 blocks @ 1024 bytes
+ * - 4 blocks @ 4096 bytes
+ */
+static const struct isp1760_memory_layout isp1763_memory_conf = {
+ .blocks[0] = 8,
+ .blocks_size[0] = 256,
+ .blocks[1] = 2,
+ .blocks_size[1] = 1024,
+ .blocks[2] = 4,
+ .blocks_size[2] = 4096,
+
+ .slot_num = 16,
+ .payload_blocks = 8 + 2 + 4,
+ .payload_area_size = 0x5000,
+};
+
+static const struct regmap_range isp176x_hc_volatile_ranges[] = {
+ regmap_reg_range(ISP176x_HC_USBCMD, ISP176x_HC_ATL_PTD_LASTPTD),
+ regmap_reg_range(ISP176x_HC_BUFFER_STATUS, ISP176x_HC_MEMORY),
+ regmap_reg_range(ISP176x_HC_INTERRUPT, ISP176x_HC_OTG_CTRL_CLEAR),
+};
+
+static const struct regmap_access_table isp176x_hc_volatile_table = {
+ .yes_ranges = isp176x_hc_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(isp176x_hc_volatile_ranges),
+};
+
+static const struct regmap_config isp1760_hc_regmap_conf = {
+ .name = "isp1760-hc",
+ .reg_bits = 16,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
+ .max_register = ISP176x_HC_OTG_CTRL_CLEAR,
+ .volatile_table = &isp176x_hc_volatile_table,
+};
+
+static const struct reg_field isp1760_hc_reg_fields[] = {
+ [HCS_PPC] = REG_FIELD(ISP176x_HC_HCSPARAMS, 4, 4),
+ [HCS_N_PORTS] = REG_FIELD(ISP176x_HC_HCSPARAMS, 0, 3),
+ [HCC_ISOC_CACHE] = REG_FIELD(ISP176x_HC_HCCPARAMS, 7, 7),
+ [HCC_ISOC_THRES] = REG_FIELD(ISP176x_HC_HCCPARAMS, 4, 6),
+ [CMD_LRESET] = REG_FIELD(ISP176x_HC_USBCMD, 7, 7),
+ [CMD_RESET] = REG_FIELD(ISP176x_HC_USBCMD, 1, 1),
+ [CMD_RUN] = REG_FIELD(ISP176x_HC_USBCMD, 0, 0),
+ [STS_PCD] = REG_FIELD(ISP176x_HC_USBSTS, 2, 2),
+ [HC_FRINDEX] = REG_FIELD(ISP176x_HC_FRINDEX, 0, 13),
+ [FLAG_CF] = REG_FIELD(ISP176x_HC_CONFIGFLAG, 0, 0),
+ [HC_ISO_PTD_DONEMAP] = REG_FIELD(ISP176x_HC_ISO_PTD_DONEMAP, 0, 31),
+ [HC_ISO_PTD_SKIPMAP] = REG_FIELD(ISP176x_HC_ISO_PTD_SKIPMAP, 0, 31),
+ [HC_ISO_PTD_LASTPTD] = REG_FIELD(ISP176x_HC_ISO_PTD_LASTPTD, 0, 31),
+ [HC_INT_PTD_DONEMAP] = REG_FIELD(ISP176x_HC_INT_PTD_DONEMAP, 0, 31),
+ [HC_INT_PTD_SKIPMAP] = REG_FIELD(ISP176x_HC_INT_PTD_SKIPMAP, 0, 31),
+ [HC_INT_PTD_LASTPTD] = REG_FIELD(ISP176x_HC_INT_PTD_LASTPTD, 0, 31),
+ [HC_ATL_PTD_DONEMAP] = REG_FIELD(ISP176x_HC_ATL_PTD_DONEMAP, 0, 31),
+ [HC_ATL_PTD_SKIPMAP] = REG_FIELD(ISP176x_HC_ATL_PTD_SKIPMAP, 0, 31),
+ [HC_ATL_PTD_LASTPTD] = REG_FIELD(ISP176x_HC_ATL_PTD_LASTPTD, 0, 31),
+ [PORT_OWNER] = REG_FIELD(ISP176x_HC_PORTSC1, 13, 13),
+ [PORT_POWER] = REG_FIELD(ISP176x_HC_PORTSC1, 12, 12),
+ [PORT_LSTATUS] = REG_FIELD(ISP176x_HC_PORTSC1, 10, 11),
+ [PORT_RESET] = REG_FIELD(ISP176x_HC_PORTSC1, 8, 8),
+ [PORT_SUSPEND] = REG_FIELD(ISP176x_HC_PORTSC1, 7, 7),
+ [PORT_RESUME] = REG_FIELD(ISP176x_HC_PORTSC1, 6, 6),
+ [PORT_PE] = REG_FIELD(ISP176x_HC_PORTSC1, 2, 2),
+ [PORT_CSC] = REG_FIELD(ISP176x_HC_PORTSC1, 1, 1),
+ [PORT_CONNECT] = REG_FIELD(ISP176x_HC_PORTSC1, 0, 0),
+ [ALL_ATX_RESET] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 31, 31),
+ [HW_ANA_DIGI_OC] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 15, 15),
+ [HW_COMN_IRQ] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 10, 10),
+ [HW_DATA_BUS_WIDTH] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 8, 8),
+ [HW_DACK_POL_HIGH] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 6, 6),
+ [HW_DREQ_POL_HIGH] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 5, 5),
+ [HW_INTR_HIGH_ACT] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 2, 2),
+ [HW_INTR_EDGE_TRIG] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 1, 1),
+ [HW_GLOBAL_INTR_EN] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 0, 0),
+ [HC_CHIP_REV] = REG_FIELD(ISP176x_HC_CHIP_ID, 16, 31),
+ [HC_CHIP_ID_HIGH] = REG_FIELD(ISP176x_HC_CHIP_ID, 8, 15),
+ [HC_CHIP_ID_LOW] = REG_FIELD(ISP176x_HC_CHIP_ID, 0, 7),
+ [HC_SCRATCH] = REG_FIELD(ISP176x_HC_SCRATCH, 0, 31),
+ [SW_RESET_RESET_ALL] = REG_FIELD(ISP176x_HC_RESET, 0, 0),
+ [ISO_BUF_FILL] = REG_FIELD(ISP176x_HC_BUFFER_STATUS, 2, 2),
+ [INT_BUF_FILL] = REG_FIELD(ISP176x_HC_BUFFER_STATUS, 1, 1),
+ [ATL_BUF_FILL] = REG_FIELD(ISP176x_HC_BUFFER_STATUS, 0, 0),
+ [MEM_BANK_SEL] = REG_FIELD(ISP176x_HC_MEMORY, 16, 17),
+ [MEM_START_ADDR] = REG_FIELD(ISP176x_HC_MEMORY, 0, 15),
+ [HC_INTERRUPT] = REG_FIELD(ISP176x_HC_INTERRUPT, 0, 9),
+ [HC_ATL_IRQ_ENABLE] = REG_FIELD(ISP176x_HC_INTERRUPT_ENABLE, 8, 8),
+ [HC_INT_IRQ_ENABLE] = REG_FIELD(ISP176x_HC_INTERRUPT_ENABLE, 7, 7),
+ [HC_ISO_IRQ_MASK_OR] = REG_FIELD(ISP176x_HC_ISO_IRQ_MASK_OR, 0, 31),
+ [HC_INT_IRQ_MASK_OR] = REG_FIELD(ISP176x_HC_INT_IRQ_MASK_OR, 0, 31),
+ [HC_ATL_IRQ_MASK_OR] = REG_FIELD(ISP176x_HC_ATL_IRQ_MASK_OR, 0, 31),
+ [HC_ISO_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_ISO_IRQ_MASK_AND, 0, 31),
+ [HC_INT_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_INT_IRQ_MASK_AND, 0, 31),
+ [HC_ATL_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_ATL_IRQ_MASK_AND, 0, 31),
+ [HW_OTG_DISABLE] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 10, 10),
+ [HW_SW_SEL_HC_DC] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 7, 7),
+ [HW_VBUS_DRV] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 4, 4),
+ [HW_SEL_CP_EXT] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 3, 3),
+ [HW_DM_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 2, 2),
+ [HW_DP_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 1, 1),
+ [HW_DP_PULLUP] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 0, 0),
+ [HW_OTG_DISABLE_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 10, 10),
+ [HW_SW_SEL_HC_DC_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 7, 7),
+ [HW_VBUS_DRV_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 4, 4),
+ [HW_SEL_CP_EXT_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 3, 3),
+ [HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 2, 2),
+ [HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 1, 1),
+ [HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 0, 0),
+};
+
+static const struct reg_field isp1763_hc_reg_fields[] = {
+ [CMD_LRESET] = REG_FIELD(ISP1763_HC_USBCMD, 7, 7),
+ [CMD_RESET] = REG_FIELD(ISP1763_HC_USBCMD, 1, 1),
+ [CMD_RUN] = REG_FIELD(ISP1763_HC_USBCMD, 0, 0),
+ [STS_PCD] = REG_FIELD(ISP1763_HC_USBSTS, 2, 2),
+ [HC_FRINDEX] = REG_FIELD(ISP1763_HC_FRINDEX, 0, 13),
+ [FLAG_CF] = REG_FIELD(ISP1763_HC_CONFIGFLAG, 0, 0),
+ [HC_ISO_PTD_DONEMAP] = REG_FIELD(ISP1763_HC_ISO_PTD_DONEMAP, 0, 15),
+ [HC_ISO_PTD_SKIPMAP] = REG_FIELD(ISP1763_HC_ISO_PTD_SKIPMAP, 0, 15),
+ [HC_ISO_PTD_LASTPTD] = REG_FIELD(ISP1763_HC_ISO_PTD_LASTPTD, 0, 15),
+ [HC_INT_PTD_DONEMAP] = REG_FIELD(ISP1763_HC_INT_PTD_DONEMAP, 0, 15),
+ [HC_INT_PTD_SKIPMAP] = REG_FIELD(ISP1763_HC_INT_PTD_SKIPMAP, 0, 15),
+ [HC_INT_PTD_LASTPTD] = REG_FIELD(ISP1763_HC_INT_PTD_LASTPTD, 0, 15),
+ [HC_ATL_PTD_DONEMAP] = REG_FIELD(ISP1763_HC_ATL_PTD_DONEMAP, 0, 15),
+ [HC_ATL_PTD_SKIPMAP] = REG_FIELD(ISP1763_HC_ATL_PTD_SKIPMAP, 0, 15),
+ [HC_ATL_PTD_LASTPTD] = REG_FIELD(ISP1763_HC_ATL_PTD_LASTPTD, 0, 15),
+ [PORT_OWNER] = REG_FIELD(ISP1763_HC_PORTSC1, 13, 13),
+ [PORT_POWER] = REG_FIELD(ISP1763_HC_PORTSC1, 12, 12),
+ [PORT_LSTATUS] = REG_FIELD(ISP1763_HC_PORTSC1, 10, 11),
+ [PORT_RESET] = REG_FIELD(ISP1763_HC_PORTSC1, 8, 8),
+ [PORT_SUSPEND] = REG_FIELD(ISP1763_HC_PORTSC1, 7, 7),
+ [PORT_RESUME] = REG_FIELD(ISP1763_HC_PORTSC1, 6, 6),
+ [PORT_PE] = REG_FIELD(ISP1763_HC_PORTSC1, 2, 2),
+ [PORT_CSC] = REG_FIELD(ISP1763_HC_PORTSC1, 1, 1),
+ [PORT_CONNECT] = REG_FIELD(ISP1763_HC_PORTSC1, 0, 0),
+ [HW_DATA_BUS_WIDTH] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 4, 4),
+ [HW_DACK_POL_HIGH] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 6, 6),
+ [HW_DREQ_POL_HIGH] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 5, 5),
+ [HW_INTF_LOCK] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 3, 3),
+ [HW_INTR_HIGH_ACT] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 2, 2),
+ [HW_INTR_EDGE_TRIG] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 1, 1),
+ [HW_GLOBAL_INTR_EN] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 0, 0),
+ [SW_RESET_RESET_ATX] = REG_FIELD(ISP1763_HC_RESET, 3, 3),
+ [SW_RESET_RESET_ALL] = REG_FIELD(ISP1763_HC_RESET, 0, 0),
+ [HC_CHIP_ID_HIGH] = REG_FIELD(ISP1763_HC_CHIP_ID, 0, 15),
+ [HC_CHIP_ID_LOW] = REG_FIELD(ISP1763_HC_CHIP_REV, 8, 15),
+ [HC_CHIP_REV] = REG_FIELD(ISP1763_HC_CHIP_REV, 0, 7),
+ [HC_SCRATCH] = REG_FIELD(ISP1763_HC_SCRATCH, 0, 15),
+ [ISO_BUF_FILL] = REG_FIELD(ISP1763_HC_BUFFER_STATUS, 2, 2),
+ [INT_BUF_FILL] = REG_FIELD(ISP1763_HC_BUFFER_STATUS, 1, 1),
+ [ATL_BUF_FILL] = REG_FIELD(ISP1763_HC_BUFFER_STATUS, 0, 0),
+ [MEM_START_ADDR] = REG_FIELD(ISP1763_HC_MEMORY, 0, 15),
+ [HC_DATA] = REG_FIELD(ISP1763_HC_DATA, 0, 15),
+ [HC_INTERRUPT] = REG_FIELD(ISP1763_HC_INTERRUPT, 0, 10),
+ [HC_ATL_IRQ_ENABLE] = REG_FIELD(ISP1763_HC_INTERRUPT_ENABLE, 8, 8),
+ [HC_INT_IRQ_ENABLE] = REG_FIELD(ISP1763_HC_INTERRUPT_ENABLE, 7, 7),
+ [HC_ISO_IRQ_MASK_OR] = REG_FIELD(ISP1763_HC_ISO_IRQ_MASK_OR, 0, 15),
+ [HC_INT_IRQ_MASK_OR] = REG_FIELD(ISP1763_HC_INT_IRQ_MASK_OR, 0, 15),
+ [HC_ATL_IRQ_MASK_OR] = REG_FIELD(ISP1763_HC_ATL_IRQ_MASK_OR, 0, 15),
+ [HC_ISO_IRQ_MASK_AND] = REG_FIELD(ISP1763_HC_ISO_IRQ_MASK_AND, 0, 15),
+ [HC_INT_IRQ_MASK_AND] = REG_FIELD(ISP1763_HC_INT_IRQ_MASK_AND, 0, 15),
+ [HC_ATL_IRQ_MASK_AND] = REG_FIELD(ISP1763_HC_ATL_IRQ_MASK_AND, 0, 15),
+ [HW_HC_2_DIS] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 15, 15),
+ [HW_OTG_DISABLE] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 10, 10),
+ [HW_SW_SEL_HC_DC] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 7, 7),
+ [HW_VBUS_DRV] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 4, 4),
+ [HW_SEL_CP_EXT] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 3, 3),
+ [HW_DM_PULLDOWN] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 2, 2),
+ [HW_DP_PULLDOWN] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 1, 1),
+ [HW_DP_PULLUP] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 0, 0),
+ [HW_HC_2_DIS_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 15, 15),
+ [HW_OTG_DISABLE_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 10, 10),
+ [HW_SW_SEL_HC_DC_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 7, 7),
+ [HW_VBUS_DRV_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 4, 4),
+ [HW_SEL_CP_EXT_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 3, 3),
+ [HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 2, 2),
+ [HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 1, 1),
+ [HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 0, 0),
+};
+
+static const struct regmap_range isp1763_hc_volatile_ranges[] = {
+ regmap_reg_range(ISP1763_HC_USBCMD, ISP1763_HC_ATL_PTD_LASTPTD),
+ regmap_reg_range(ISP1763_HC_BUFFER_STATUS, ISP1763_HC_DATA),
+ regmap_reg_range(ISP1763_HC_INTERRUPT, ISP1763_HC_OTG_CTRL_CLEAR),
+};
+
+static const struct regmap_access_table isp1763_hc_volatile_table = {
+ .yes_ranges = isp1763_hc_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(isp1763_hc_volatile_ranges),
+};
+
+static const struct regmap_config isp1763_hc_regmap_conf = {
+ .name = "isp1763-hc",
+ .reg_bits = 8,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .fast_io = true,
+ .max_register = ISP1763_HC_OTG_CTRL_CLEAR,
+ .volatile_table = &isp1763_hc_volatile_table,
+};
+
+static const struct regmap_range isp176x_dc_volatile_ranges[] = {
+ regmap_reg_range(ISP176x_DC_EPMAXPKTSZ, ISP176x_DC_EPTYPE),
+ regmap_reg_range(ISP176x_DC_BUFLEN, ISP176x_DC_EPINDEX),
+};
+
+static const struct regmap_access_table isp176x_dc_volatile_table = {
+ .yes_ranges = isp176x_dc_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(isp176x_dc_volatile_ranges),
+};
+
+static const struct regmap_config isp1761_dc_regmap_conf = {
+ .name = "isp1761-dc",
+ .reg_bits = 16,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
+ .max_register = ISP176x_DC_TESTMODE,
+ .volatile_table = &isp176x_dc_volatile_table,
+};
+
+static const struct reg_field isp1761_dc_reg_fields[] = {
+ [DC_DEVEN] = REG_FIELD(ISP176x_DC_ADDRESS, 7, 7),
+ [DC_DEVADDR] = REG_FIELD(ISP176x_DC_ADDRESS, 0, 6),
+ [DC_VBUSSTAT] = REG_FIELD(ISP176x_DC_MODE, 8, 8),
+ [DC_SFRESET] = REG_FIELD(ISP176x_DC_MODE, 4, 4),
+ [DC_GLINTENA] = REG_FIELD(ISP176x_DC_MODE, 3, 3),
+ [DC_CDBGMOD_ACK] = REG_FIELD(ISP176x_DC_INTCONF, 6, 6),
+ [DC_DDBGMODIN_ACK] = REG_FIELD(ISP176x_DC_INTCONF, 4, 4),
+ [DC_DDBGMODOUT_ACK] = REG_FIELD(ISP176x_DC_INTCONF, 2, 2),
+ [DC_INTPOL] = REG_FIELD(ISP176x_DC_INTCONF, 0, 0),
+ [DC_IEPRXTX_7] = REG_FIELD(ISP176x_DC_INTENABLE, 25, 25),
+ [DC_IEPRXTX_6] = REG_FIELD(ISP176x_DC_INTENABLE, 23, 23),
+ [DC_IEPRXTX_5] = REG_FIELD(ISP176x_DC_INTENABLE, 21, 21),
+ [DC_IEPRXTX_4] = REG_FIELD(ISP176x_DC_INTENABLE, 19, 19),
+ [DC_IEPRXTX_3] = REG_FIELD(ISP176x_DC_INTENABLE, 17, 17),
+ [DC_IEPRXTX_2] = REG_FIELD(ISP176x_DC_INTENABLE, 15, 15),
+ [DC_IEPRXTX_1] = REG_FIELD(ISP176x_DC_INTENABLE, 13, 13),
+ [DC_IEPRXTX_0] = REG_FIELD(ISP176x_DC_INTENABLE, 11, 11),
+ [DC_IEP0SETUP] = REG_FIELD(ISP176x_DC_INTENABLE, 8, 8),
+ [DC_IEVBUS] = REG_FIELD(ISP176x_DC_INTENABLE, 7, 7),
+ [DC_IEHS_STA] = REG_FIELD(ISP176x_DC_INTENABLE, 5, 5),
+ [DC_IERESM] = REG_FIELD(ISP176x_DC_INTENABLE, 4, 4),
+ [DC_IESUSP] = REG_FIELD(ISP176x_DC_INTENABLE, 3, 3),
+ [DC_IEBRST] = REG_FIELD(ISP176x_DC_INTENABLE, 0, 0),
+ [DC_EP0SETUP] = REG_FIELD(ISP176x_DC_EPINDEX, 5, 5),
+ [DC_ENDPIDX] = REG_FIELD(ISP176x_DC_EPINDEX, 1, 4),
+ [DC_EPDIR] = REG_FIELD(ISP176x_DC_EPINDEX, 0, 0),
+ [DC_CLBUF] = REG_FIELD(ISP176x_DC_CTRLFUNC, 4, 4),
+ [DC_VENDP] = REG_FIELD(ISP176x_DC_CTRLFUNC, 3, 3),
+ [DC_DSEN] = REG_FIELD(ISP176x_DC_CTRLFUNC, 2, 2),
+ [DC_STATUS] = REG_FIELD(ISP176x_DC_CTRLFUNC, 1, 1),
+ [DC_STALL] = REG_FIELD(ISP176x_DC_CTRLFUNC, 0, 0),
+ [DC_BUFLEN] = REG_FIELD(ISP176x_DC_BUFLEN, 0, 15),
+ [DC_FFOSZ] = REG_FIELD(ISP176x_DC_EPMAXPKTSZ, 0, 10),
+ [DC_EPENABLE] = REG_FIELD(ISP176x_DC_EPTYPE, 3, 3),
+ [DC_ENDPTYP] = REG_FIELD(ISP176x_DC_EPTYPE, 0, 1),
+ [DC_UFRAMENUM] = REG_FIELD(ISP176x_DC_FRAMENUM, 11, 13),
+ [DC_FRAMENUM] = REG_FIELD(ISP176x_DC_FRAMENUM, 0, 10),
+ [DC_CHIP_ID_HIGH] = REG_FIELD(ISP176x_DC_CHIPID, 16, 31),
+ [DC_CHIP_ID_LOW] = REG_FIELD(ISP176x_DC_CHIPID, 0, 15),
+ [DC_SCRATCH] = REG_FIELD(ISP176x_DC_SCRATCH, 0, 15),
+};
+
+static const struct regmap_range isp1763_dc_volatile_ranges[] = {
+ regmap_reg_range(ISP1763_DC_EPMAXPKTSZ, ISP1763_DC_EPTYPE),
+ regmap_reg_range(ISP1763_DC_BUFLEN, ISP1763_DC_EPINDEX),
+};
+
+static const struct regmap_access_table isp1763_dc_volatile_table = {
+ .yes_ranges = isp1763_dc_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(isp1763_dc_volatile_ranges),
+};
+
+static const struct reg_field isp1763_dc_reg_fields[] = {
+ [DC_DEVEN] = REG_FIELD(ISP1763_DC_ADDRESS, 7, 7),
+ [DC_DEVADDR] = REG_FIELD(ISP1763_DC_ADDRESS, 0, 6),
+ [DC_VBUSSTAT] = REG_FIELD(ISP1763_DC_MODE, 8, 8),
+ [DC_SFRESET] = REG_FIELD(ISP1763_DC_MODE, 4, 4),
+ [DC_GLINTENA] = REG_FIELD(ISP1763_DC_MODE, 3, 3),
+ [DC_CDBGMOD_ACK] = REG_FIELD(ISP1763_DC_INTCONF, 6, 6),
+ [DC_DDBGMODIN_ACK] = REG_FIELD(ISP1763_DC_INTCONF, 4, 4),
+ [DC_DDBGMODOUT_ACK] = REG_FIELD(ISP1763_DC_INTCONF, 2, 2),
+ [DC_INTPOL] = REG_FIELD(ISP1763_DC_INTCONF, 0, 0),
+ [DC_IEPRXTX_7] = REG_FIELD(ISP1763_DC_INTENABLE, 25, 25),
+ [DC_IEPRXTX_6] = REG_FIELD(ISP1763_DC_INTENABLE, 23, 23),
+ [DC_IEPRXTX_5] = REG_FIELD(ISP1763_DC_INTENABLE, 21, 21),
+ [DC_IEPRXTX_4] = REG_FIELD(ISP1763_DC_INTENABLE, 19, 19),
+ [DC_IEPRXTX_3] = REG_FIELD(ISP1763_DC_INTENABLE, 17, 17),
+ [DC_IEPRXTX_2] = REG_FIELD(ISP1763_DC_INTENABLE, 15, 15),
+ [DC_IEPRXTX_1] = REG_FIELD(ISP1763_DC_INTENABLE, 13, 13),
+ [DC_IEPRXTX_0] = REG_FIELD(ISP1763_DC_INTENABLE, 11, 11),
+ [DC_IEP0SETUP] = REG_FIELD(ISP1763_DC_INTENABLE, 8, 8),
+ [DC_IEVBUS] = REG_FIELD(ISP1763_DC_INTENABLE, 7, 7),
+ [DC_IEHS_STA] = REG_FIELD(ISP1763_DC_INTENABLE, 5, 5),
+ [DC_IERESM] = REG_FIELD(ISP1763_DC_INTENABLE, 4, 4),
+ [DC_IESUSP] = REG_FIELD(ISP1763_DC_INTENABLE, 3, 3),
+ [DC_IEBRST] = REG_FIELD(ISP1763_DC_INTENABLE, 0, 0),
+ [DC_EP0SETUP] = REG_FIELD(ISP1763_DC_EPINDEX, 5, 5),
+ [DC_ENDPIDX] = REG_FIELD(ISP1763_DC_EPINDEX, 1, 4),
+ [DC_EPDIR] = REG_FIELD(ISP1763_DC_EPINDEX, 0, 0),
+ [DC_CLBUF] = REG_FIELD(ISP1763_DC_CTRLFUNC, 4, 4),
+ [DC_VENDP] = REG_FIELD(ISP1763_DC_CTRLFUNC, 3, 3),
+ [DC_DSEN] = REG_FIELD(ISP1763_DC_CTRLFUNC, 2, 2),
+ [DC_STATUS] = REG_FIELD(ISP1763_DC_CTRLFUNC, 1, 1),
+ [DC_STALL] = REG_FIELD(ISP1763_DC_CTRLFUNC, 0, 0),
+ [DC_BUFLEN] = REG_FIELD(ISP1763_DC_BUFLEN, 0, 15),
+ [DC_FFOSZ] = REG_FIELD(ISP1763_DC_EPMAXPKTSZ, 0, 10),
+ [DC_EPENABLE] = REG_FIELD(ISP1763_DC_EPTYPE, 3, 3),
+ [DC_ENDPTYP] = REG_FIELD(ISP1763_DC_EPTYPE, 0, 1),
+ [DC_UFRAMENUM] = REG_FIELD(ISP1763_DC_FRAMENUM, 11, 13),
+ [DC_FRAMENUM] = REG_FIELD(ISP1763_DC_FRAMENUM, 0, 10),
+ [DC_CHIP_ID_HIGH] = REG_FIELD(ISP1763_DC_CHIPID_HIGH, 0, 15),
+ [DC_CHIP_ID_LOW] = REG_FIELD(ISP1763_DC_CHIPID_LOW, 0, 15),
+ [DC_SCRATCH] = REG_FIELD(ISP1763_DC_SCRATCH, 0, 15),
+};
+
+static const struct regmap_config isp1763_dc_regmap_conf = {
+ .name = "isp1763-dc",
+ .reg_bits = 8,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .fast_io = true,
+ .max_register = ISP1763_DC_TESTMODE,
+ .volatile_table = &isp1763_dc_volatile_table,
+};
+
int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
struct device *dev, unsigned int devflags)
{
+ const struct regmap_config *hc_regmap;
+ const struct reg_field *hc_reg_fields;
+ const struct regmap_config *dc_regmap;
+ const struct reg_field *dc_reg_fields;
struct isp1760_device *isp;
- bool udc_disabled = !(devflags & ISP1760_FLAG_ISP1761);
+ struct isp1760_hcd *hcd;
+ struct isp1760_udc *udc;
+ struct regmap_field *f;
+ bool udc_enabled;
int ret;
+ int i;
/*
* If neither the HCD not the UDC is enabled return an error, as no
* device would be registered.
*/
+ udc_enabled = ((devflags & ISP1760_FLAG_ISP1763) ||
+ (devflags & ISP1760_FLAG_ISP1761));
+
if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) &&
- (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || udc_disabled))
+ (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || !udc_enabled))
return -ENODEV;
isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
@@ -126,28 +500,81 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
isp->dev = dev;
isp->devflags = devflags;
+ hcd = &isp->hcd;
+ udc = &isp->udc;
+
+ hcd->is_isp1763 = !!(devflags & ISP1760_FLAG_ISP1763);
+ udc->is_isp1763 = !!(devflags & ISP1760_FLAG_ISP1763);
+
+ if (!hcd->is_isp1763 && (devflags & ISP1760_FLAG_BUS_WIDTH_8)) {
+ dev_err(dev, "isp1760/61 do not support data width 8\n");
+ return -EINVAL;
+ }
+
+ if (hcd->is_isp1763) {
+ hc_regmap = &isp1763_hc_regmap_conf;
+ hc_reg_fields = &isp1763_hc_reg_fields[0];
+ dc_regmap = &isp1763_dc_regmap_conf;
+ dc_reg_fields = &isp1763_dc_reg_fields[0];
+ } else {
+ hc_regmap = &isp1760_hc_regmap_conf;
+ hc_reg_fields = &isp1760_hc_reg_fields[0];
+ dc_regmap = &isp1761_dc_regmap_conf;
+ dc_reg_fields = &isp1761_dc_reg_fields[0];
+ }
isp->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH);
if (IS_ERR(isp->rst_gpio))
return PTR_ERR(isp->rst_gpio);
- isp->regs = devm_ioremap_resource(dev, mem);
- if (IS_ERR(isp->regs))
- return PTR_ERR(isp->regs);
+ hcd->base = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(hcd->base))
+ return PTR_ERR(hcd->base);
+
+ hcd->regs = devm_regmap_init_mmio(dev, hcd->base, hc_regmap);
+ if (IS_ERR(hcd->regs))
+ return PTR_ERR(hcd->regs);
+
+ for (i = 0; i < HC_FIELD_MAX; i++) {
+ f = devm_regmap_field_alloc(dev, hcd->regs, hc_reg_fields[i]);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ hcd->fields[i] = f;
+ }
+
+ udc->regs = devm_regmap_init_mmio(dev, hcd->base, dc_regmap);
+ if (IS_ERR(udc->regs))
+ return PTR_ERR(udc->regs);
+
+ for (i = 0; i < DC_FIELD_MAX; i++) {
+ f = devm_regmap_field_alloc(dev, udc->regs, dc_reg_fields[i]);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ udc->fields[i] = f;
+ }
+
+ if (hcd->is_isp1763)
+ hcd->memory_layout = &isp1763_memory_conf;
+ else
+ hcd->memory_layout = &isp176x_memory_conf;
- isp1760_init_core(isp);
+ ret = isp1760_init_core(isp);
+ if (ret < 0)
+ return ret;
if (IS_ENABLED(CONFIG_USB_ISP1760_HCD) && !usb_disabled()) {
- ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq,
+ ret = isp1760_hcd_register(hcd, mem, irq,
irqflags | IRQF_SHARED, dev);
if (ret < 0)
return ret;
}
- if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) {
+ if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && udc_enabled) {
ret = isp1760_udc_register(isp, irq, irqflags);
if (ret < 0) {
- isp1760_hcd_unregister(&isp->hcd);
+ isp1760_hcd_unregister(hcd);
return ret;
}
}
diff --git a/drivers/usb/isp1760/isp1760-core.h b/drivers/usb/isp1760/isp1760-core.h
index d9a0a4cc467c..91e0ee3992a7 100644
--- a/drivers/usb/isp1760/isp1760-core.h
+++ b/drivers/usb/isp1760/isp1760-core.h
@@ -2,18 +2,21 @@
/*
* Driver for the NXP ISP1760 chip
*
+ * Copyright 2021 Linaro, Rui Miguel Silva
* Copyright 2014 Laurent Pinchart
* Copyright 2007 Sebastian Siewior
*
* Contacts:
* Sebastian Siewior <bigeasy@linutronix.de>
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Rui Miguel Silva <rui.silva@linaro.org>
*/
#ifndef _ISP1760_CORE_H_
#define _ISP1760_CORE_H_
#include <linux/ioport.h>
+#include <linux/regmap.h>
#include "isp1760-hcd.h"
#include "isp1760-udc.h"
@@ -27,18 +30,19 @@ struct gpio_desc;
* a sane default configuration.
*/
#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */
-#define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */
+#define ISP1760_FLAG_PERIPHERAL_EN 0x00000004 /* Port 1 supports Peripheral mode*/
#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */
#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */
#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */
#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */
#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */
#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */
+#define ISP1760_FLAG_ISP1763 0x00000200 /* Chip is ISP1763 */
+#define ISP1760_FLAG_BUS_WIDTH_8 0x00000400 /* 8-bit data bus width */
struct isp1760_device {
struct device *dev;
- void __iomem *regs;
unsigned int devflags;
struct gpio_desc *rst_gpio;
@@ -52,14 +56,42 @@ void isp1760_unregister(struct device *dev);
void isp1760_set_pullup(struct isp1760_device *isp, bool enable);
-static inline u32 isp1760_read32(void __iomem *base, u32 reg)
+static inline u32 isp1760_field_read(struct regmap_field **fields, u32 field)
{
- return readl(base + reg);
+ unsigned int val;
+
+ regmap_field_read(fields[field], &val);
+
+ return val;
+}
+
+static inline void isp1760_field_write(struct regmap_field **fields, u32 field,
+ u32 val)
+{
+ regmap_field_write(fields[field], val);
+}
+
+static inline void isp1760_field_set(struct regmap_field **fields, u32 field)
+{
+ isp1760_field_write(fields, field, 0xFFFFFFFF);
}
-static inline void isp1760_write32(void __iomem *base, u32 reg, u32 val)
+static inline void isp1760_field_clear(struct regmap_field **fields, u32 field)
{
- writel(val, base + reg);
+ isp1760_field_write(fields, field, 0);
}
+static inline u32 isp1760_reg_read(struct regmap *regs, u32 reg)
+{
+ unsigned int val;
+
+ regmap_read(regs, reg, &val);
+
+ return val;
+}
+
+static inline void isp1760_reg_write(struct regmap *regs, u32 reg, u32 val)
+{
+ regmap_write(regs, reg, val);
+}
#endif
diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c
index 33ae656c4b68..27168b4a4ef2 100644
--- a/drivers/usb/isp1760/isp1760-hcd.c
+++ b/drivers/usb/isp1760/isp1760-hcd.c
@@ -11,6 +11,8 @@
*
* (c) 2011 Arvid Brodin <arvid.brodin@enea.com>
*
+ * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org>
+ *
*/
#include <linux/gpio/consumer.h>
#include <linux/module.h>
@@ -44,6 +46,9 @@ static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
return *(struct isp1760_hcd **)hcd->hcd_priv;
}
+#define dw_to_le32(x) (cpu_to_le32((__force u32)x))
+#define le32_to_dw(x) ((__force __dw)(le32_to_cpu(x)))
+
/* urb state*/
#define DELETE_URB (0x0008)
#define NO_TRANSFER_ACTIVE (0xffffffff)
@@ -60,50 +65,69 @@ struct ptd {
__dw dw6;
__dw dw7;
};
+
+struct ptd_le32 {
+ __le32 dw0;
+ __le32 dw1;
+ __le32 dw2;
+ __le32 dw3;
+ __le32 dw4;
+ __le32 dw5;
+ __le32 dw6;
+ __le32 dw7;
+};
+
#define PTD_OFFSET 0x0400
#define ISO_PTD_OFFSET 0x0400
#define INT_PTD_OFFSET 0x0800
#define ATL_PTD_OFFSET 0x0c00
#define PAYLOAD_OFFSET 0x1000
-
-/* ATL */
-/* DW0 */
-#define DW0_VALID_BIT 1
-#define FROM_DW0_VALID(x) ((x) & 0x01)
-#define TO_DW0_LENGTH(x) (((u32) x) << 3)
-#define TO_DW0_MAXPACKET(x) (((u32) x) << 18)
-#define TO_DW0_MULTI(x) (((u32) x) << 29)
-#define TO_DW0_ENDPOINT(x) (((u32) x) << 31)
+#define ISP_BANK_0 0x00
+#define ISP_BANK_1 0x01
+#define ISP_BANK_2 0x02
+#define ISP_BANK_3 0x03
+
+#define TO_DW(x) ((__force __dw)x)
+#define TO_U32(x) ((__force u32)x)
+
+ /* ATL */
+ /* DW0 */
+#define DW0_VALID_BIT TO_DW(1)
+#define FROM_DW0_VALID(x) (TO_U32(x) & 0x01)
+#define TO_DW0_LENGTH(x) TO_DW((((u32)x) << 3))
+#define TO_DW0_MAXPACKET(x) TO_DW((((u32)x) << 18))
+#define TO_DW0_MULTI(x) TO_DW((((u32)x) << 29))
+#define TO_DW0_ENDPOINT(x) TO_DW((((u32)x) << 31))
/* DW1 */
-#define TO_DW1_DEVICE_ADDR(x) (((u32) x) << 3)
-#define TO_DW1_PID_TOKEN(x) (((u32) x) << 10)
-#define DW1_TRANS_BULK ((u32) 2 << 12)
-#define DW1_TRANS_INT ((u32) 3 << 12)
-#define DW1_TRANS_SPLIT ((u32) 1 << 14)
-#define DW1_SE_USB_LOSPEED ((u32) 2 << 16)
-#define TO_DW1_PORT_NUM(x) (((u32) x) << 18)
-#define TO_DW1_HUB_NUM(x) (((u32) x) << 25)
+#define TO_DW1_DEVICE_ADDR(x) TO_DW((((u32)x) << 3))
+#define TO_DW1_PID_TOKEN(x) TO_DW((((u32)x) << 10))
+#define DW1_TRANS_BULK TO_DW(((u32)2 << 12))
+#define DW1_TRANS_INT TO_DW(((u32)3 << 12))
+#define DW1_TRANS_SPLIT TO_DW(((u32)1 << 14))
+#define DW1_SE_USB_LOSPEED TO_DW(((u32)2 << 16))
+#define TO_DW1_PORT_NUM(x) TO_DW((((u32)x) << 18))
+#define TO_DW1_HUB_NUM(x) TO_DW((((u32)x) << 25))
/* DW2 */
-#define TO_DW2_DATA_START_ADDR(x) (((u32) x) << 8)
-#define TO_DW2_RL(x) ((x) << 25)
-#define FROM_DW2_RL(x) (((x) >> 25) & 0xf)
+#define TO_DW2_DATA_START_ADDR(x) TO_DW((((u32)x) << 8))
+#define TO_DW2_RL(x) TO_DW(((x) << 25))
+#define FROM_DW2_RL(x) ((TO_U32(x) >> 25) & 0xf)
/* DW3 */
-#define FROM_DW3_NRBYTESTRANSFERRED(x) ((x) & 0x7fff)
-#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x) ((x) & 0x07ff)
-#define TO_DW3_NAKCOUNT(x) ((x) << 19)
-#define FROM_DW3_NAKCOUNT(x) (((x) >> 19) & 0xf)
-#define TO_DW3_CERR(x) ((x) << 23)
-#define FROM_DW3_CERR(x) (((x) >> 23) & 0x3)
-#define TO_DW3_DATA_TOGGLE(x) ((x) << 25)
-#define FROM_DW3_DATA_TOGGLE(x) (((x) >> 25) & 0x1)
-#define TO_DW3_PING(x) ((x) << 26)
-#define FROM_DW3_PING(x) (((x) >> 26) & 0x1)
-#define DW3_ERROR_BIT (1 << 28)
-#define DW3_BABBLE_BIT (1 << 29)
-#define DW3_HALT_BIT (1 << 30)
-#define DW3_ACTIVE_BIT (1 << 31)
-#define FROM_DW3_ACTIVE(x) (((x) >> 31) & 0x01)
+#define FROM_DW3_NRBYTESTRANSFERRED(x) TO_U32((x) & 0x3fff)
+#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x) TO_U32((x) & 0x07ff)
+#define TO_DW3_NAKCOUNT(x) TO_DW(((x) << 19))
+#define FROM_DW3_NAKCOUNT(x) ((TO_U32(x) >> 19) & 0xf)
+#define TO_DW3_CERR(x) TO_DW(((x) << 23))
+#define FROM_DW3_CERR(x) ((TO_U32(x) >> 23) & 0x3)
+#define TO_DW3_DATA_TOGGLE(x) TO_DW(((x) << 25))
+#define FROM_DW3_DATA_TOGGLE(x) ((TO_U32(x) >> 25) & 0x1)
+#define TO_DW3_PING(x) TO_DW(((x) << 26))
+#define FROM_DW3_PING(x) ((TO_U32(x) >> 26) & 0x1)
+#define DW3_ERROR_BIT TO_DW((1 << 28))
+#define DW3_BABBLE_BIT TO_DW((1 << 29))
+#define DW3_HALT_BIT TO_DW((1 << 30))
+#define DW3_ACTIVE_BIT TO_DW((1 << 31))
+#define FROM_DW3_ACTIVE(x) ((TO_U32(x) >> 31) & 0x01)
#define INT_UNDERRUN (1 << 2)
#define INT_BABBLE (1 << 1)
@@ -116,7 +140,7 @@ struct ptd {
/* Errata 1 */
#define RL_COUNTER (0)
#define NAK_COUNTER (0)
-#define ERR_COUNTER (2)
+#define ERR_COUNTER (3)
struct isp1760_qtd {
u8 packet_type;
@@ -158,17 +182,124 @@ struct urb_listitem {
struct urb *urb;
};
+static const u32 isp1763_hc_portsc1_fields[] = {
+ [PORT_OWNER] = BIT(13),
+ [PORT_POWER] = BIT(12),
+ [PORT_LSTATUS] = BIT(10),
+ [PORT_RESET] = BIT(8),
+ [PORT_SUSPEND] = BIT(7),
+ [PORT_RESUME] = BIT(6),
+ [PORT_PE] = BIT(2),
+ [PORT_CSC] = BIT(1),
+ [PORT_CONNECT] = BIT(0),
+};
+
+/*
+ * Access functions for isp176x registers regmap fields
+ */
+static u32 isp1760_hcd_read(struct usb_hcd *hcd, u32 field)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ return isp1760_field_read(priv->fields, field);
+}
+
/*
- * Access functions for isp176x registers (addresses 0..0x03FF).
+ * We need, in isp1763, to write directly the values to the portsc1
+ * register so it will make the other values to trigger.
*/
-static u32 reg_read32(void __iomem *base, u32 reg)
+static void isp1760_hcd_portsc1_set_clear(struct isp1760_hcd *priv, u32 field,
+ u32 val)
+{
+ u32 bit = isp1763_hc_portsc1_fields[field];
+ u32 port_status = readl(priv->base + ISP1763_HC_PORTSC1);
+
+ if (val)
+ writel(port_status | bit, priv->base + ISP1763_HC_PORTSC1);
+ else
+ writel(port_status & ~bit, priv->base + ISP1763_HC_PORTSC1);
+}
+
+static void isp1760_hcd_write(struct usb_hcd *hcd, u32 field, u32 val)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ if (unlikely(priv->is_isp1763 &&
+ (field >= PORT_OWNER && field <= PORT_CONNECT)))
+ return isp1760_hcd_portsc1_set_clear(priv, field, val);
+
+ isp1760_field_write(priv->fields, field, val);
+}
+
+static void isp1760_hcd_set(struct usb_hcd *hcd, u32 field)
+{
+ isp1760_hcd_write(hcd, field, 0xFFFFFFFF);
+}
+
+static void isp1760_hcd_clear(struct usb_hcd *hcd, u32 field)
{
- return isp1760_read32(base, reg);
+ isp1760_hcd_write(hcd, field, 0);
+}
+
+static int isp1760_hcd_set_and_wait(struct usb_hcd *hcd, u32 field,
+ u32 timeout_us)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ u32 val;
+
+ isp1760_hcd_set(hcd, field);
+
+ return regmap_field_read_poll_timeout(priv->fields[field], val,
+ val, 10, timeout_us);
+}
+
+static int isp1760_hcd_set_and_wait_swap(struct usb_hcd *hcd, u32 field,
+ u32 timeout_us)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ u32 val;
+
+ isp1760_hcd_set(hcd, field);
+
+ return regmap_field_read_poll_timeout(priv->fields[field], val,
+ !val, 10, timeout_us);
+}
+
+static int isp1760_hcd_clear_and_wait(struct usb_hcd *hcd, u32 field,
+ u32 timeout_us)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ u32 val;
+
+ isp1760_hcd_clear(hcd, field);
+
+ return regmap_field_read_poll_timeout(priv->fields[field], val,
+ !val, 10, timeout_us);
+}
+
+static bool isp1760_hcd_is_set(struct usb_hcd *hcd, u32 field)
+{
+ return !!isp1760_hcd_read(hcd, field);
+}
+
+static bool isp1760_hcd_ppc_is_set(struct usb_hcd *hcd)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ if (priv->is_isp1763)
+ return true;
+
+ return isp1760_hcd_is_set(hcd, HCS_PPC);
}
-static void reg_write32(void __iomem *base, u32 reg, u32 val)
+static u32 isp1760_hcd_n_ports(struct usb_hcd *hcd)
{
- isp1760_write32(base, reg, val);
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ if (priv->is_isp1763)
+ return 1;
+
+ return isp1760_hcd_read(hcd, HCS_N_PORTS);
}
/*
@@ -176,7 +307,7 @@ static void reg_write32(void __iomem *base, u32 reg, u32 val)
*
* bank_reads8() reads memory locations prefetched by an earlier write to
* HC_MEMORY_REG (see isp176x datasheet). Unless you want to do fancy multi-
- * bank optimizations, you should use the more generic mem_reads8() below.
+ * bank optimizations, you should use the more generic mem_read() below.
*
* For access to ptd memory, use the specialized ptd_read() and ptd_write()
* below.
@@ -196,7 +327,7 @@ static void bank_reads8(void __iomem *src_base, u32 src_offset, u32 bank_addr,
if (src_offset < PAYLOAD_OFFSET) {
while (bytes >= 4) {
- *dst = le32_to_cpu(__raw_readl(src));
+ *dst = readl_relaxed(src);
bytes -= 4;
src++;
dst++;
@@ -217,7 +348,7 @@ static void bank_reads8(void __iomem *src_base, u32 src_offset, u32 bank_addr,
* allocated.
*/
if (src_offset < PAYLOAD_OFFSET)
- val = le32_to_cpu(__raw_readl(src));
+ val = readl_relaxed(src);
else
val = __raw_readl(src);
@@ -231,16 +362,59 @@ static void bank_reads8(void __iomem *src_base, u32 src_offset, u32 bank_addr,
}
}
-static void mem_reads8(void __iomem *src_base, u32 src_offset, void *dst,
- u32 bytes)
+static void isp1760_mem_read(struct usb_hcd *hcd, u32 src_offset, void *dst,
+ u32 bytes)
{
- reg_write32(src_base, HC_MEMORY_REG, src_offset + ISP_BANK(0));
- ndelay(90);
- bank_reads8(src_base, src_offset, ISP_BANK(0), dst, bytes);
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ isp1760_hcd_write(hcd, MEM_BANK_SEL, ISP_BANK_0);
+ isp1760_hcd_write(hcd, MEM_START_ADDR, src_offset);
+ ndelay(100);
+
+ bank_reads8(priv->base, src_offset, ISP_BANK_0, dst, bytes);
}
-static void mem_writes8(void __iomem *dst_base, u32 dst_offset,
- __u32 const *src, u32 bytes)
+/*
+ * ISP1763 does not have the banks direct host controller memory access,
+ * needs to use the HC_DATA register. Add data read/write according to this,
+ * and also adjust 16bit access.
+ */
+static void isp1763_mem_read(struct usb_hcd *hcd, u16 srcaddr,
+ u16 *dstptr, u32 bytes)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ /* Write the starting device address to the hcd memory register */
+ isp1760_reg_write(priv->regs, ISP1763_HC_MEMORY, srcaddr);
+ ndelay(100); /* Delay between consecutive access */
+
+ /* As long there are at least 16-bit to read ... */
+ while (bytes >= 2) {
+ *dstptr = __raw_readw(priv->base + ISP1763_HC_DATA);
+ bytes -= 2;
+ dstptr++;
+ }
+
+ /* If there are no more bytes to read, return */
+ if (bytes <= 0)
+ return;
+
+ *((u8 *)dstptr) = (u8)(readw(priv->base + ISP1763_HC_DATA) & 0xFF);
+}
+
+static void mem_read(struct usb_hcd *hcd, u32 src_offset, __u32 *dst,
+ u32 bytes)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ if (!priv->is_isp1763)
+ return isp1760_mem_read(hcd, src_offset, (u16 *)dst, bytes);
+
+ isp1763_mem_read(hcd, (u16)src_offset, (u16 *)dst, bytes);
+}
+
+static void isp1760_mem_write(void __iomem *dst_base, u32 dst_offset,
+ __u32 const *src, u32 bytes)
{
__u32 __iomem *dst;
@@ -248,7 +422,7 @@ static void mem_writes8(void __iomem *dst_base, u32 dst_offset,
if (dst_offset < PAYLOAD_OFFSET) {
while (bytes >= 4) {
- __raw_writel(cpu_to_le32(*src), dst);
+ writel_relaxed(*src, dst);
bytes -= 4;
src++;
dst++;
@@ -269,74 +443,168 @@ static void mem_writes8(void __iomem *dst_base, u32 dst_offset,
*/
if (dst_offset < PAYLOAD_OFFSET)
- __raw_writel(cpu_to_le32(*src), dst);
+ writel_relaxed(*src, dst);
else
__raw_writel(*src, dst);
}
+static void isp1763_mem_write(struct usb_hcd *hcd, u16 dstaddr, u16 *src,
+ u32 bytes)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ /* Write the starting device address to the hcd memory register */
+ isp1760_reg_write(priv->regs, ISP1763_HC_MEMORY, dstaddr);
+ ndelay(100); /* Delay between consecutive access */
+
+ while (bytes >= 2) {
+ /* Get and write the data; then adjust the data ptr and len */
+ __raw_writew(*src, priv->base + ISP1763_HC_DATA);
+ bytes -= 2;
+ src++;
+ }
+
+ /* If there are no more bytes to process, return */
+ if (bytes <= 0)
+ return;
+
+ /*
+ * The only way to get here is if there is a single byte left,
+ * get it and write it to the data reg;
+ */
+ writew(*((u8 *)src), priv->base + ISP1763_HC_DATA);
+}
+
+static void mem_write(struct usb_hcd *hcd, u32 dst_offset, __u32 *src,
+ u32 bytes)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ if (!priv->is_isp1763)
+ return isp1760_mem_write(priv->base, dst_offset, src, bytes);
+
+ isp1763_mem_write(hcd, dst_offset, (u16 *)src, bytes);
+}
+
/*
* Read and write ptds. 'ptd_offset' should be one of ISO_PTD_OFFSET,
* INT_PTD_OFFSET, and ATL_PTD_OFFSET. 'slot' should be less than 32.
*/
-static void ptd_read(void __iomem *base, u32 ptd_offset, u32 slot,
- struct ptd *ptd)
+static void isp1760_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
+ struct ptd *ptd)
{
- reg_write32(base, HC_MEMORY_REG,
- ISP_BANK(0) + ptd_offset + slot*sizeof(*ptd));
+ u16 src_offset = ptd_offset + slot * sizeof(*ptd);
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ isp1760_hcd_write(hcd, MEM_BANK_SEL, ISP_BANK_0);
+ isp1760_hcd_write(hcd, MEM_START_ADDR, src_offset);
ndelay(90);
- bank_reads8(base, ptd_offset + slot*sizeof(*ptd), ISP_BANK(0),
- (void *) ptd, sizeof(*ptd));
+
+ bank_reads8(priv->base, src_offset, ISP_BANK_0, (void *)ptd,
+ sizeof(*ptd));
+}
+
+static void isp1763_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
+ struct ptd *ptd)
+{
+ u16 src_offset = ptd_offset + slot * sizeof(*ptd);
+ struct ptd_le32 le32_ptd;
+
+ isp1763_mem_read(hcd, src_offset, (u16 *)&le32_ptd, sizeof(le32_ptd));
+ /* Normalize the data obtained */
+ ptd->dw0 = le32_to_dw(le32_ptd.dw0);
+ ptd->dw1 = le32_to_dw(le32_ptd.dw1);
+ ptd->dw2 = le32_to_dw(le32_ptd.dw2);
+ ptd->dw3 = le32_to_dw(le32_ptd.dw3);
+ ptd->dw4 = le32_to_dw(le32_ptd.dw4);
+ ptd->dw5 = le32_to_dw(le32_ptd.dw5);
+ ptd->dw6 = le32_to_dw(le32_ptd.dw6);
+ ptd->dw7 = le32_to_dw(le32_ptd.dw7);
+}
+
+static void ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
+ struct ptd *ptd)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ if (!priv->is_isp1763)
+ return isp1760_ptd_read(hcd, ptd_offset, slot, ptd);
+
+ isp1763_ptd_read(hcd, ptd_offset, slot, ptd);
+}
+
+static void isp1763_ptd_write(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
+ struct ptd *cpu_ptd)
+{
+ u16 dst_offset = ptd_offset + slot * sizeof(*cpu_ptd);
+ struct ptd_le32 ptd;
+
+ ptd.dw0 = dw_to_le32(cpu_ptd->dw0);
+ ptd.dw1 = dw_to_le32(cpu_ptd->dw1);
+ ptd.dw2 = dw_to_le32(cpu_ptd->dw2);
+ ptd.dw3 = dw_to_le32(cpu_ptd->dw3);
+ ptd.dw4 = dw_to_le32(cpu_ptd->dw4);
+ ptd.dw5 = dw_to_le32(cpu_ptd->dw5);
+ ptd.dw6 = dw_to_le32(cpu_ptd->dw6);
+ ptd.dw7 = dw_to_le32(cpu_ptd->dw7);
+
+ isp1763_mem_write(hcd, dst_offset, (u16 *)&ptd.dw0,
+ 8 * sizeof(ptd.dw0));
}
-static void ptd_write(void __iomem *base, u32 ptd_offset, u32 slot,
- struct ptd *ptd)
+static void isp1760_ptd_write(void __iomem *base, u32 ptd_offset, u32 slot,
+ struct ptd *ptd)
{
- mem_writes8(base, ptd_offset + slot*sizeof(*ptd) + sizeof(ptd->dw0),
- &ptd->dw1, 7*sizeof(ptd->dw1));
- /* Make sure dw0 gets written last (after other dw's and after payload)
- since it contains the enable bit */
+ u32 dst_offset = ptd_offset + slot * sizeof(*ptd);
+
+ /*
+ * Make sure dw0 gets written last (after other dw's and after payload)
+ * since it contains the enable bit
+ */
+ isp1760_mem_write(base, dst_offset + sizeof(ptd->dw0),
+ (__force u32 *)&ptd->dw1, 7 * sizeof(ptd->dw1));
wmb();
- mem_writes8(base, ptd_offset + slot*sizeof(*ptd), &ptd->dw0,
- sizeof(ptd->dw0));
+ isp1760_mem_write(base, dst_offset, (__force u32 *)&ptd->dw0,
+ sizeof(ptd->dw0));
}
+static void ptd_write(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
+ struct ptd *ptd)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ if (!priv->is_isp1763)
+ return isp1760_ptd_write(priv->base, ptd_offset, slot, ptd);
+
+ isp1763_ptd_write(hcd, ptd_offset, slot, ptd);
+}
/* memory management of the 60kb on the chip from 0x1000 to 0xffff */
static void init_memory(struct isp1760_hcd *priv)
{
- int i, curr;
+ const struct isp1760_memory_layout *mem = priv->memory_layout;
+ int i, j, curr;
u32 payload_addr;
payload_addr = PAYLOAD_OFFSET;
- for (i = 0; i < BLOCK_1_NUM; i++) {
- priv->memory_pool[i].start = payload_addr;
- priv->memory_pool[i].size = BLOCK_1_SIZE;
- priv->memory_pool[i].free = 1;
- payload_addr += priv->memory_pool[i].size;
- }
-
- curr = i;
- for (i = 0; i < BLOCK_2_NUM; i++) {
- priv->memory_pool[curr + i].start = payload_addr;
- priv->memory_pool[curr + i].size = BLOCK_2_SIZE;
- priv->memory_pool[curr + i].free = 1;
- payload_addr += priv->memory_pool[curr + i].size;
- }
- curr = i;
- for (i = 0; i < BLOCK_3_NUM; i++) {
- priv->memory_pool[curr + i].start = payload_addr;
- priv->memory_pool[curr + i].size = BLOCK_3_SIZE;
- priv->memory_pool[curr + i].free = 1;
- payload_addr += priv->memory_pool[curr + i].size;
+ for (i = 0, curr = 0; i < ARRAY_SIZE(mem->blocks); i++) {
+ for (j = 0; j < mem->blocks[i]; j++, curr++) {
+ priv->memory_pool[curr + j].start = payload_addr;
+ priv->memory_pool[curr + j].size = mem->blocks_size[i];
+ priv->memory_pool[curr + j].free = 1;
+ payload_addr += priv->memory_pool[curr + j].size;
+ }
}
- WARN_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE);
+ WARN_ON(payload_addr - priv->memory_pool[0].start >
+ mem->payload_area_size);
}
static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ const struct isp1760_memory_layout *mem = priv->memory_layout;
int i;
WARN_ON(qtd->payload_addr);
@@ -344,7 +612,7 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
if (!qtd->length)
return;
- for (i = 0; i < BLOCKS; i++) {
+ for (i = 0; i < mem->payload_blocks; i++) {
if (priv->memory_pool[i].size >= qtd->length &&
priv->memory_pool[i].free) {
priv->memory_pool[i].free = 0;
@@ -357,12 +625,13 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ const struct isp1760_memory_layout *mem = priv->memory_layout;
int i;
if (!qtd->payload_addr)
return;
- for (i = 0; i < BLOCKS; i++) {
+ for (i = 0; i < mem->payload_blocks; i++) {
if (priv->memory_pool[i].start == qtd->payload_addr) {
WARN_ON(priv->memory_pool[i].free);
priv->memory_pool[i].free = 1;
@@ -377,34 +646,15 @@ static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
qtd->payload_addr = 0;
}
-static int handshake(struct usb_hcd *hcd, u32 reg,
- u32 mask, u32 done, int usec)
-{
- u32 result;
- int ret;
-
- ret = readl_poll_timeout_atomic(hcd->regs + reg, result,
- ((result & mask) == done ||
- result == U32_MAX), 1, usec);
- if (result == U32_MAX)
- return -ENODEV;
-
- return ret;
-}
-
/* reset a non-running (STS_HALT == 1) controller */
static int ehci_reset(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- u32 command = reg_read32(hcd->regs, HC_USBCMD);
-
- command |= CMD_RESET;
- reg_write32(hcd->regs, HC_USBCMD, command);
hcd->state = HC_STATE_HALT;
priv->next_statechange = jiffies;
- return handshake(hcd, HC_USBCMD, CMD_RESET, 0, 250 * 1000);
+ return isp1760_hcd_set_and_wait_swap(hcd, CMD_RESET, 250 * 1000);
}
static struct isp1760_qh *qh_alloc(gfp_t flags)
@@ -432,8 +682,9 @@ static void qh_free(struct isp1760_qh *qh)
/* one-time init, only for memory state */
static int priv_init(struct usb_hcd *hcd)
{
- struct isp1760_hcd *priv = hcd_to_priv(hcd);
- u32 hcc_params;
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ u32 isoc_cache;
+ u32 isoc_thres;
int i;
spin_lock_init(&priv->lock);
@@ -447,13 +698,20 @@ static int priv_init(struct usb_hcd *hcd)
*/
priv->periodic_size = DEFAULT_I_TDPS;
+ if (priv->is_isp1763) {
+ priv->i_thresh = 2;
+ return 0;
+ }
+
/* controllers may cache some of the periodic schedule ... */
- hcc_params = reg_read32(hcd->regs, HC_HCCPARAMS);
+ isoc_cache = isp1760_hcd_read(hcd, HCC_ISOC_CACHE);
+ isoc_thres = isp1760_hcd_read(hcd, HCC_ISOC_THRES);
+
/* full frame cache */
- if (HCC_ISOC_CACHE(hcc_params))
+ if (isoc_cache)
priv->i_thresh = 8;
else /* N microframes cached */
- priv->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
+ priv->i_thresh = 2 + isoc_thres;
return 0;
}
@@ -461,15 +719,24 @@ static int priv_init(struct usb_hcd *hcd)
static int isp1760_hc_setup(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ u32 atx_reset;
int result;
- u32 scratch, hwmode;
+ u32 scratch;
+ u32 pattern;
+
+ if (priv->is_isp1763)
+ pattern = 0xcafe;
+ else
+ pattern = 0xdeadcafe;
+
+ isp1760_hcd_write(hcd, HC_SCRATCH, pattern);
- reg_write32(hcd->regs, HC_SCRATCH_REG, 0xdeadbabe);
/* Change bus pattern */
- scratch = reg_read32(hcd->regs, HC_CHIP_ID_REG);
- scratch = reg_read32(hcd->regs, HC_SCRATCH_REG);
- if (scratch != 0xdeadbabe) {
- dev_err(hcd->self.controller, "Scratch test failed.\n");
+ scratch = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH);
+ dev_err(hcd->self.controller, "Scratch test 0x%08x\n", scratch);
+ scratch = isp1760_hcd_read(hcd, HC_SCRATCH);
+ if (scratch != pattern) {
+ dev_err(hcd->self.controller, "Scratch test failed. 0x%08x\n", scratch);
return -ENODEV;
}
@@ -481,10 +748,13 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
* the host controller through the EHCI USB Command register. The device
* has been reset in core code anyway, so this shouldn't matter.
*/
- reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0);
- reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
- reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
- reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
+ isp1760_hcd_clear(hcd, ISO_BUF_FILL);
+ isp1760_hcd_clear(hcd, INT_BUF_FILL);
+ isp1760_hcd_clear(hcd, ATL_BUF_FILL);
+
+ isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP);
+ isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP);
+ isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP);
result = ehci_reset(hcd);
if (result)
@@ -493,14 +763,26 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
/* Step 11 passed */
/* ATL reset */
- hwmode = reg_read32(hcd->regs, HC_HW_MODE_CTRL) & ~ALL_ATX_RESET;
- reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET);
+ if (priv->is_isp1763)
+ atx_reset = SW_RESET_RESET_ATX;
+ else
+ atx_reset = ALL_ATX_RESET;
+
+ isp1760_hcd_set(hcd, atx_reset);
mdelay(10);
- reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
+ isp1760_hcd_clear(hcd, atx_reset);
- reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK);
+ if (priv->is_isp1763) {
+ isp1760_hcd_set(hcd, HW_OTG_DISABLE);
+ isp1760_hcd_set(hcd, HW_SW_SEL_HC_DC_CLEAR);
+ isp1760_hcd_set(hcd, HW_HC_2_DIS_CLEAR);
+ mdelay(10);
- priv->hcs_params = reg_read32(hcd->regs, HC_HCSPARAMS);
+ isp1760_hcd_set(hcd, HW_INTF_LOCK);
+ }
+
+ isp1760_hcd_set(hcd, HC_INT_IRQ_ENABLE);
+ isp1760_hcd_set(hcd, HC_ATL_IRQ_ENABLE);
return priv_init(hcd);
}
@@ -553,7 +835,7 @@ static void create_ptd_atl(struct isp1760_qh *qh,
ptd->dw0 |= TO_DW0_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe));
/* DW1 */
- ptd->dw1 = usb_pipeendpoint(qtd->urb->pipe) >> 1;
+ ptd->dw1 = TO_DW((usb_pipeendpoint(qtd->urb->pipe) >> 1));
ptd->dw1 |= TO_DW1_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe));
ptd->dw1 |= TO_DW1_PID_TOKEN(qtd->packet_type);
@@ -575,7 +857,7 @@ static void create_ptd_atl(struct isp1760_qh *qh,
/* SE bit for Split INT transfers */
if (usb_pipeint(qtd->urb->pipe) &&
(qtd->urb->dev->speed == USB_SPEED_LOW))
- ptd->dw1 |= 2 << 16;
+ ptd->dw1 |= DW1_SE_USB_LOSPEED;
rl = 0;
nak = 0;
@@ -647,14 +929,14 @@ static void transform_add_int(struct isp1760_qh *qh,
* that number come from? 0xff seems to work fine...
*/
/* ptd->dw5 = 0x1c; */
- ptd->dw5 = 0xff; /* Execute Complete Split on any uFrame */
+ ptd->dw5 = TO_DW(0xff); /* Execute Complete Split on any uFrame */
}
period = period >> 1;/* Ensure equal or shorter period than requested */
period &= 0xf8; /* Mask off too large values and lowest unused 3 bits */
- ptd->dw2 |= period;
- ptd->dw4 = usof;
+ ptd->dw2 |= TO_DW(period);
+ ptd->dw4 = TO_DW(usof);
}
static void create_ptd_int(struct isp1760_qh *qh,
@@ -720,41 +1002,45 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
struct ptd *ptd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ const struct isp1760_memory_layout *mem = priv->memory_layout;
int skip_map;
- WARN_ON((slot < 0) || (slot > 31));
+ WARN_ON((slot < 0) || (slot > mem->slot_num - 1));
WARN_ON(qtd->length && !qtd->payload_addr);
WARN_ON(slots[slot].qtd);
WARN_ON(slots[slot].qh);
WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC);
+ if (priv->is_isp1763)
+ ndelay(100);
+
/* Make sure done map has not triggered from some unlinked transfer */
if (ptd_offset == ATL_PTD_OFFSET) {
- priv->atl_done_map |= reg_read32(hcd->regs,
- HC_ATL_PTD_DONEMAP_REG);
+ skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP);
+ isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP,
+ skip_map | (1 << slot));
+ priv->atl_done_map |= isp1760_hcd_read(hcd, HC_ATL_PTD_DONEMAP);
priv->atl_done_map &= ~(1 << slot);
} else {
- priv->int_done_map |= reg_read32(hcd->regs,
- HC_INT_PTD_DONEMAP_REG);
+ skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP);
+ isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP,
+ skip_map | (1 << slot));
+ priv->int_done_map |= isp1760_hcd_read(hcd, HC_INT_PTD_DONEMAP);
priv->int_done_map &= ~(1 << slot);
}
+ skip_map &= ~(1 << slot);
qh->slot = slot;
qtd->status = QTD_XFER_STARTED;
slots[slot].timestamp = jiffies;
slots[slot].qtd = qtd;
slots[slot].qh = qh;
- ptd_write(hcd->regs, ptd_offset, slot, ptd);
+ ptd_write(hcd, ptd_offset, slot, ptd);
- if (ptd_offset == ATL_PTD_OFFSET) {
- skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
- skip_map &= ~(1 << qh->slot);
- reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map);
- } else {
- skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
- skip_map &= ~(1 << qh->slot);
- reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map);
- }
+ if (ptd_offset == ATL_PTD_OFFSET)
+ isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map);
+ else
+ isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map);
}
static int is_short_bulk(struct isp1760_qtd *qtd)
@@ -766,9 +1052,9 @@ static int is_short_bulk(struct isp1760_qtd *qtd)
static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh,
struct list_head *urb_list)
{
- int last_qtd;
struct isp1760_qtd *qtd, *qtd_next;
struct urb_listitem *urb_listitem;
+ int last_qtd;
list_for_each_entry_safe(qtd, qtd_next, &qh->qtd_list, qtd_list) {
if (qtd->status < QTD_XFER_COMPLETE)
@@ -783,9 +1069,9 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh,
if (qtd->actual_length) {
switch (qtd->packet_type) {
case IN_PID:
- mem_reads8(hcd->regs, qtd->payload_addr,
- qtd->data_buffer,
- qtd->actual_length);
+ mem_read(hcd, qtd->payload_addr,
+ qtd->data_buffer,
+ qtd->actual_length);
fallthrough;
case OUT_PID:
qtd->urb->actual_length +=
@@ -829,6 +1115,8 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh,
static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ const struct isp1760_memory_layout *mem = priv->memory_layout;
+ int slot_num = mem->slot_num;
int ptd_offset;
struct isp1760_slotinfo *slots;
int curr_slot, free_slot;
@@ -855,7 +1143,7 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
}
free_slot = -1;
- for (curr_slot = 0; curr_slot < 32; curr_slot++) {
+ for (curr_slot = 0; curr_slot < slot_num; curr_slot++) {
if ((free_slot == -1) && (slots[curr_slot].qtd == NULL))
free_slot = curr_slot;
if (slots[curr_slot].qh == qh)
@@ -870,11 +1158,10 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
if ((qtd->length) && (!qtd->payload_addr))
break;
- if ((qtd->length) &&
- ((qtd->packet_type == SETUP_PID) ||
- (qtd->packet_type == OUT_PID))) {
- mem_writes8(hcd->regs, qtd->payload_addr,
- qtd->data_buffer, qtd->length);
+ if (qtd->length && (qtd->packet_type == SETUP_PID ||
+ qtd->packet_type == OUT_PID)) {
+ mem_write(hcd, qtd->payload_addr,
+ qtd->data_buffer, qtd->length);
}
qtd->status = QTD_PAYLOAD_ALLOC;
@@ -887,7 +1174,7 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
"available for transfer\n", __func__);
*/
/* Start xfer for this endpoint if not already done */
- if ((curr_slot > 31) && (free_slot > -1)) {
+ if ((curr_slot > slot_num - 1) && (free_slot > -1)) {
if (usb_pipeint(qtd->urb->pipe))
create_ptd_int(qh, qtd, &ptd);
else
@@ -977,10 +1264,10 @@ static void schedule_ptds(struct usb_hcd *hcd)
static int check_int_transfer(struct usb_hcd *hcd, struct ptd *ptd,
struct urb *urb)
{
- __dw dw4;
+ u32 dw4;
int i;
- dw4 = ptd->dw4;
+ dw4 = TO_U32(ptd->dw4);
dw4 >>= 8;
/* FIXME: ISP1761 datasheet does not say what to do with these. Do we
@@ -1074,9 +1361,9 @@ static void handle_done_ptds(struct usb_hcd *hcd)
int modified;
int skip_map;
- skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
+ skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP);
priv->int_done_map &= ~skip_map;
- skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
+ skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP);
priv->atl_done_map &= ~skip_map;
modified = priv->int_done_map || priv->atl_done_map;
@@ -1094,7 +1381,7 @@ static void handle_done_ptds(struct usb_hcd *hcd)
continue;
}
ptd_offset = INT_PTD_OFFSET;
- ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd);
+ ptd_read(hcd, INT_PTD_OFFSET, slot, &ptd);
state = check_int_transfer(hcd, &ptd,
slots[slot].qtd->urb);
} else {
@@ -1109,7 +1396,7 @@ static void handle_done_ptds(struct usb_hcd *hcd)
continue;
}
ptd_offset = ATL_PTD_OFFSET;
- ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
+ ptd_read(hcd, ATL_PTD_OFFSET, slot, &ptd);
state = check_atl_transfer(hcd, &ptd,
slots[slot].qtd->urb);
}
@@ -1134,7 +1421,7 @@ static void handle_done_ptds(struct usb_hcd *hcd)
qtd->status = QTD_XFER_COMPLETE;
if (list_is_last(&qtd->qtd_list, &qh->qtd_list) ||
- is_short_bulk(qtd))
+ is_short_bulk(qtd))
qtd = NULL;
else
qtd = list_entry(qtd->qtd_list.next,
@@ -1202,25 +1489,30 @@ static void handle_done_ptds(struct usb_hcd *hcd)
static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- u32 imask;
irqreturn_t irqret = IRQ_NONE;
+ u32 int_reg;
+ u32 imask;
spin_lock(&priv->lock);
if (!(hcd->state & HC_STATE_RUNNING))
goto leave;
- imask = reg_read32(hcd->regs, HC_INTERRUPT_REG);
+ imask = isp1760_hcd_read(hcd, HC_INTERRUPT);
if (unlikely(!imask))
goto leave;
- reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */
- priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG);
- priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG);
+ int_reg = priv->is_isp1763 ? ISP1763_HC_INTERRUPT :
+ ISP176x_HC_INTERRUPT;
+ isp1760_reg_write(priv->regs, int_reg, imask);
+
+ priv->int_done_map |= isp1760_hcd_read(hcd, HC_INT_PTD_DONEMAP);
+ priv->atl_done_map |= isp1760_hcd_read(hcd, HC_ATL_PTD_DONEMAP);
handle_done_ptds(hcd);
irqret = IRQ_HANDLED;
+
leave:
spin_unlock(&priv->lock);
@@ -1261,17 +1553,18 @@ static void errata2_function(struct timer_list *unused)
{
struct usb_hcd *hcd = errata2_timer_hcd;
struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ const struct isp1760_memory_layout *mem = priv->memory_layout;
int slot;
struct ptd ptd;
unsigned long spinflags;
spin_lock_irqsave(&priv->lock, spinflags);
- for (slot = 0; slot < 32; slot++)
+ for (slot = 0; slot < mem->slot_num; slot++)
if (priv->atl_slots[slot].qh && time_after(jiffies,
priv->atl_slots[slot].timestamp +
msecs_to_jiffies(SLOT_TIMEOUT))) {
- ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
+ ptd_read(hcd, ATL_PTD_OFFSET, slot, &ptd);
if (!FROM_DW0_VALID(ptd.dw0) &&
!FROM_DW3_ACTIVE(ptd.dw3))
priv->atl_done_map |= 1 << slot;
@@ -1286,35 +1579,120 @@ static void errata2_function(struct timer_list *unused)
add_timer(&errata2_timer);
}
+static int isp1763_run(struct usb_hcd *hcd)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ int retval;
+ u32 chipid_h;
+ u32 chipid_l;
+ u32 chip_rev;
+ u32 ptd_atl_int;
+ u32 ptd_iso;
+
+ hcd->uses_new_polling = 1;
+ hcd->state = HC_STATE_RUNNING;
+
+ chipid_h = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH);
+ chipid_l = isp1760_hcd_read(hcd, HC_CHIP_ID_LOW);
+ chip_rev = isp1760_hcd_read(hcd, HC_CHIP_REV);
+ dev_info(hcd->self.controller, "USB ISP %02x%02x HW rev. %d started\n",
+ chipid_h, chipid_l, chip_rev);
+
+ isp1760_hcd_clear(hcd, ISO_BUF_FILL);
+ isp1760_hcd_clear(hcd, INT_BUF_FILL);
+ isp1760_hcd_clear(hcd, ATL_BUF_FILL);
+
+ isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP);
+ isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP);
+ isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP);
+ ndelay(100);
+ isp1760_hcd_clear(hcd, HC_ATL_PTD_DONEMAP);
+ isp1760_hcd_clear(hcd, HC_INT_PTD_DONEMAP);
+ isp1760_hcd_clear(hcd, HC_ISO_PTD_DONEMAP);
+
+ isp1760_hcd_set(hcd, HW_OTG_DISABLE);
+ isp1760_reg_write(priv->regs, ISP1763_HC_OTG_CTRL_CLEAR, BIT(7));
+ isp1760_reg_write(priv->regs, ISP1763_HC_OTG_CTRL_CLEAR, BIT(15));
+ mdelay(10);
+
+ isp1760_hcd_set(hcd, HC_INT_IRQ_ENABLE);
+ isp1760_hcd_set(hcd, HC_ATL_IRQ_ENABLE);
+
+ isp1760_hcd_set(hcd, HW_GLOBAL_INTR_EN);
+
+ isp1760_hcd_clear(hcd, HC_ATL_IRQ_MASK_AND);
+ isp1760_hcd_clear(hcd, HC_INT_IRQ_MASK_AND);
+ isp1760_hcd_clear(hcd, HC_ISO_IRQ_MASK_AND);
+
+ isp1760_hcd_set(hcd, HC_ATL_IRQ_MASK_OR);
+ isp1760_hcd_set(hcd, HC_INT_IRQ_MASK_OR);
+ isp1760_hcd_set(hcd, HC_ISO_IRQ_MASK_OR);
+
+ ptd_atl_int = 0x8000;
+ ptd_iso = 0x0001;
+
+ isp1760_hcd_write(hcd, HC_ATL_PTD_LASTPTD, ptd_atl_int);
+ isp1760_hcd_write(hcd, HC_INT_PTD_LASTPTD, ptd_atl_int);
+ isp1760_hcd_write(hcd, HC_ISO_PTD_LASTPTD, ptd_iso);
+
+ isp1760_hcd_set(hcd, ATL_BUF_FILL);
+ isp1760_hcd_set(hcd, INT_BUF_FILL);
+
+ isp1760_hcd_clear(hcd, CMD_LRESET);
+ isp1760_hcd_clear(hcd, CMD_RESET);
+
+ retval = isp1760_hcd_set_and_wait(hcd, CMD_RUN, 250 * 1000);
+ if (retval)
+ return retval;
+
+ down_write(&ehci_cf_port_reset_rwsem);
+ retval = isp1760_hcd_set_and_wait(hcd, FLAG_CF, 250 * 1000);
+ up_write(&ehci_cf_port_reset_rwsem);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
static int isp1760_run(struct usb_hcd *hcd)
{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
int retval;
- u32 temp;
- u32 command;
- u32 chipid;
+ u32 chipid_h;
+ u32 chipid_l;
+ u32 chip_rev;
+ u32 ptd_atl_int;
+ u32 ptd_iso;
+
+ /*
+ * ISP1763 have some differences in the setup and order to enable
+ * the ports, disable otg, setup buffers, and ATL, INT, ISO status.
+ * So, just handle it a separate sequence.
+ */
+ if (priv->is_isp1763)
+ return isp1763_run(hcd);
hcd->uses_new_polling = 1;
hcd->state = HC_STATE_RUNNING;
/* Set PTD interrupt AND & OR maps */
- reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0);
- reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff);
- reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0);
- reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff);
- reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0);
- reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff);
+ isp1760_hcd_clear(hcd, HC_ATL_IRQ_MASK_AND);
+ isp1760_hcd_clear(hcd, HC_INT_IRQ_MASK_AND);
+ isp1760_hcd_clear(hcd, HC_ISO_IRQ_MASK_AND);
+
+ isp1760_hcd_set(hcd, HC_ATL_IRQ_MASK_OR);
+ isp1760_hcd_set(hcd, HC_INT_IRQ_MASK_OR);
+ isp1760_hcd_set(hcd, HC_ISO_IRQ_MASK_OR);
+
/* step 23 passed */
- temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
- reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN);
+ isp1760_hcd_set(hcd, HW_GLOBAL_INTR_EN);
- command = reg_read32(hcd->regs, HC_USBCMD);
- command &= ~(CMD_LRESET|CMD_RESET);
- command |= CMD_RUN;
- reg_write32(hcd->regs, HC_USBCMD, command);
+ isp1760_hcd_clear(hcd, CMD_LRESET);
+ isp1760_hcd_clear(hcd, CMD_RESET);
- retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000);
+ retval = isp1760_hcd_set_and_wait(hcd, CMD_RUN, 250 * 1000);
if (retval)
return retval;
@@ -1324,9 +1702,8 @@ static int isp1760_run(struct usb_hcd *hcd)
* the semaphore while doing so.
*/
down_write(&ehci_cf_port_reset_rwsem);
- reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF);
- retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000);
+ retval = isp1760_hcd_set_and_wait(hcd, FLAG_CF, 250 * 1000);
up_write(&ehci_cf_port_reset_rwsem);
if (retval)
return retval;
@@ -1336,21 +1713,28 @@ static int isp1760_run(struct usb_hcd *hcd)
errata2_timer.expires = jiffies + msecs_to_jiffies(SLOT_CHECK_PERIOD);
add_timer(&errata2_timer);
- chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG);
- dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n",
- chipid & 0xffff, chipid >> 16);
+ chipid_h = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH);
+ chipid_l = isp1760_hcd_read(hcd, HC_CHIP_ID_LOW);
+ chip_rev = isp1760_hcd_read(hcd, HC_CHIP_REV);
+ dev_info(hcd->self.controller, "USB ISP %02x%02x HW rev. %d started\n",
+ chipid_h, chipid_l, chip_rev);
/* PTD Register Init Part 2, Step 28 */
/* Setup registers controlling PTD checking */
- reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000);
- reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000);
- reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001);
- reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff);
- reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff);
- reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff);
- reg_write32(hcd->regs, HC_BUFFER_STATUS_REG,
- ATL_BUF_FILL | INT_BUF_FILL);
+ ptd_atl_int = 0x80000000;
+ ptd_iso = 0x00000001;
+
+ isp1760_hcd_write(hcd, HC_ATL_PTD_LASTPTD, ptd_atl_int);
+ isp1760_hcd_write(hcd, HC_INT_PTD_LASTPTD, ptd_atl_int);
+ isp1760_hcd_write(hcd, HC_ISO_PTD_LASTPTD, ptd_iso);
+
+ isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP);
+ isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP);
+ isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP);
+
+ isp1760_hcd_set(hcd, ATL_BUF_FILL);
+ isp1760_hcd_set(hcd, INT_BUF_FILL);
/* GRR this is run-once init(), being done every time the HC starts.
* So long as they're part of class devices, we can't do it init()
@@ -1363,8 +1747,6 @@ static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len)
{
qtd->data_buffer = databuffer;
- if (len > MAX_PAYLOAD_SIZE)
- len = MAX_PAYLOAD_SIZE;
qtd->length = len;
return qtd->length;
@@ -1388,6 +1770,8 @@ static void qtd_list_free(struct list_head *qtd_list)
static void packetize_urb(struct usb_hcd *hcd,
struct urb *urb, struct list_head *head, gfp_t flags)
{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ const struct isp1760_memory_layout *mem = priv->memory_layout;
struct isp1760_qtd *qtd;
void *buf;
int len, maxpacketsize;
@@ -1440,6 +1824,10 @@ static void packetize_urb(struct usb_hcd *hcd,
qtd = qtd_alloc(flags, urb, packet_type);
if (!qtd)
goto cleanup;
+
+ if (len > mem->blocks_size[ISP176x_BLOCK_NUM - 1])
+ len = mem->blocks_size[ISP176x_BLOCK_NUM - 1];
+
this_qtd_len = qtd_fill(qtd, buf, len);
list_add_tail(&qtd->qtd_list, head);
@@ -1584,15 +1972,16 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb,
/* We need to forcefully reclaim the slot since some transfers never
return, e.g. interrupt transfers and NAKed bulk transfers. */
if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe)) {
- skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
+ skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP);
skip_map |= (1 << qh->slot);
- reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map);
+ isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map);
+ ndelay(100);
priv->atl_slots[qh->slot].qh = NULL;
priv->atl_slots[qh->slot].qtd = NULL;
} else {
- skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
+ skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP);
skip_map |= (1 << qh->slot);
- reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map);
+ isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map);
priv->int_slots[qh->slot].qh = NULL;
priv->int_slots[qh->slot].qtd = NULL;
}
@@ -1705,8 +2094,7 @@ out:
static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- u32 temp, status = 0;
- u32 mask;
+ u32 status = 0;
int retval = 1;
unsigned long flags;
@@ -1716,17 +2104,13 @@ static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf)
/* init status to no-changes */
buf[0] = 0;
- mask = PORT_CSC;
spin_lock_irqsave(&priv->lock, flags);
- temp = reg_read32(hcd->regs, HC_PORTSC1);
- if (temp & PORT_OWNER) {
- if (temp & PORT_CSC) {
- temp &= ~PORT_CSC;
- reg_write32(hcd->regs, HC_PORTSC1, temp);
- goto done;
- }
+ if (isp1760_hcd_is_set(hcd, PORT_OWNER) &&
+ isp1760_hcd_is_set(hcd, PORT_CSC)) {
+ isp1760_hcd_clear(hcd, PORT_CSC);
+ goto done;
}
/*
@@ -1735,11 +2119,9 @@ static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf)
* high-speed device is switched over to the companion
* controller by the user.
*/
-
- if ((temp & mask) != 0
- || ((temp & PORT_RESUME) != 0
- && time_after_eq(jiffies,
- priv->reset_done))) {
+ if (isp1760_hcd_is_set(hcd, PORT_CSC) ||
+ (isp1760_hcd_is_set(hcd, PORT_RESUME) &&
+ time_after_eq(jiffies, priv->reset_done))) {
buf [0] |= 1 << (0 + 1);
status = STS_PCD;
}
@@ -1752,9 +2134,11 @@ done:
static void isp1760_hub_descriptor(struct isp1760_hcd *priv,
struct usb_hub_descriptor *desc)
{
- int ports = HCS_N_PORTS(priv->hcs_params);
+ int ports;
u16 temp;
+ ports = isp1760_hcd_n_ports(priv->hcd);
+
desc->bDescriptorType = USB_DT_HUB;
/* priv 1.0, 2.3.9 says 20ms max */
desc->bPwrOn2PwrGood = 10;
@@ -1770,7 +2154,7 @@ static void isp1760_hub_descriptor(struct isp1760_hcd *priv,
/* per-port overcurrent reporting */
temp = HUB_CHAR_INDV_PORT_OCPM;
- if (HCS_PPC(priv->hcs_params))
+ if (isp1760_hcd_ppc_is_set(priv->hcd))
/* per-port power control */
temp |= HUB_CHAR_INDV_PORT_LPSM;
else
@@ -1781,38 +2165,37 @@ static void isp1760_hub_descriptor(struct isp1760_hcd *priv,
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
-static int check_reset_complete(struct usb_hcd *hcd, int index,
- int port_status)
+static void check_reset_complete(struct usb_hcd *hcd, int index)
{
- if (!(port_status & PORT_CONNECT))
- return port_status;
+ if (!(isp1760_hcd_is_set(hcd, PORT_CONNECT)))
+ return;
/* if reset finished and it's still not enabled -- handoff */
- if (!(port_status & PORT_PE)) {
-
+ if (!isp1760_hcd_is_set(hcd, PORT_PE)) {
dev_info(hcd->self.controller,
- "port %d full speed --> companion\n",
- index + 1);
+ "port %d full speed --> companion\n", index + 1);
- port_status |= PORT_OWNER;
- port_status &= ~PORT_RWC_BITS;
- reg_write32(hcd->regs, HC_PORTSC1, port_status);
+ isp1760_hcd_set(hcd, PORT_OWNER);
- } else
+ isp1760_hcd_clear(hcd, PORT_CSC);
+ } else {
dev_info(hcd->self.controller, "port %d high speed\n",
- index + 1);
+ index + 1);
+ }
- return port_status;
+ return;
}
static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
u16 wValue, u16 wIndex, char *buf, u16 wLength)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- int ports = HCS_N_PORTS(priv->hcs_params);
- u32 temp, status;
+ u32 status;
unsigned long flags;
int retval = 0;
+ int ports;
+
+ ports = isp1760_hcd_n_ports(hcd);
/*
* FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
@@ -1837,7 +2220,6 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
- temp = reg_read32(hcd->regs, HC_PORTSC1);
/*
* Even if OWNER is set, so the port is owned by the
@@ -1848,22 +2230,22 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
- reg_write32(hcd->regs, HC_PORTSC1, temp & ~PORT_PE);
+ isp1760_hcd_clear(hcd, PORT_PE);
break;
case USB_PORT_FEAT_C_ENABLE:
/* XXX error? */
break;
case USB_PORT_FEAT_SUSPEND:
- if (temp & PORT_RESET)
+ if (isp1760_hcd_is_set(hcd, PORT_RESET))
goto error;
- if (temp & PORT_SUSPEND) {
- if ((temp & PORT_PE) == 0)
+ if (isp1760_hcd_is_set(hcd, PORT_SUSPEND)) {
+ if (!isp1760_hcd_is_set(hcd, PORT_PE))
goto error;
/* resume signaling for 20 msec */
- temp &= ~(PORT_RWC_BITS);
- reg_write32(hcd->regs, HC_PORTSC1,
- temp | PORT_RESUME);
+ isp1760_hcd_clear(hcd, PORT_CSC);
+ isp1760_hcd_set(hcd, PORT_RESUME);
+
priv->reset_done = jiffies +
msecs_to_jiffies(USB_RESUME_TIMEOUT);
}
@@ -1872,12 +2254,11 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
/* we auto-clear this feature */
break;
case USB_PORT_FEAT_POWER:
- if (HCS_PPC(priv->hcs_params))
- reg_write32(hcd->regs, HC_PORTSC1,
- temp & ~PORT_POWER);
+ if (isp1760_hcd_ppc_is_set(hcd))
+ isp1760_hcd_clear(hcd, PORT_POWER);
break;
case USB_PORT_FEAT_C_CONNECTION:
- reg_write32(hcd->regs, HC_PORTSC1, temp | PORT_CSC);
+ isp1760_hcd_set(hcd, PORT_CSC);
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
/* XXX error ?*/
@@ -1888,7 +2269,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
default:
goto error;
}
- reg_read32(hcd->regs, HC_USBCMD);
+ isp1760_hcd_read(hcd, CMD_RUN);
break;
case GetHubDescriptor:
isp1760_hub_descriptor(priv, (struct usb_hub_descriptor *)
@@ -1903,15 +2284,13 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
goto error;
wIndex--;
status = 0;
- temp = reg_read32(hcd->regs, HC_PORTSC1);
/* wPortChange bits */
- if (temp & PORT_CSC)
+ if (isp1760_hcd_is_set(hcd, PORT_CSC))
status |= USB_PORT_STAT_C_CONNECTION << 16;
-
/* whoever resumes must GetPortStatus to complete it!! */
- if (temp & PORT_RESUME) {
+ if (isp1760_hcd_is_set(hcd, PORT_RESUME)) {
dev_err(hcd->self.controller, "Port resume should be skipped.\n");
/* Remote Wakeup received? */
@@ -1930,44 +2309,39 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
priv->reset_done = 0;
/* stop resume signaling */
- temp = reg_read32(hcd->regs, HC_PORTSC1);
- reg_write32(hcd->regs, HC_PORTSC1,
- temp & ~(PORT_RWC_BITS | PORT_RESUME));
- retval = handshake(hcd, HC_PORTSC1,
- PORT_RESUME, 0, 2000 /* 2msec */);
+ isp1760_hcd_clear(hcd, PORT_CSC);
+
+ retval = isp1760_hcd_clear_and_wait(hcd,
+ PORT_RESUME, 2000);
if (retval != 0) {
dev_err(hcd->self.controller,
"port %d resume error %d\n",
wIndex + 1, retval);
goto error;
}
- temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
}
}
/* whoever resets must GetPortStatus to complete it!! */
- if ((temp & PORT_RESET)
- && time_after_eq(jiffies,
- priv->reset_done)) {
+ if (isp1760_hcd_is_set(hcd, PORT_RESET) &&
+ time_after_eq(jiffies, priv->reset_done)) {
status |= USB_PORT_STAT_C_RESET << 16;
priv->reset_done = 0;
/* force reset to complete */
- reg_write32(hcd->regs, HC_PORTSC1, temp & ~PORT_RESET);
/* REVISIT: some hardware needs 550+ usec to clear
* this bit; seems too long to spin routinely...
*/
- retval = handshake(hcd, HC_PORTSC1,
- PORT_RESET, 0, 750);
+ retval = isp1760_hcd_clear_and_wait(hcd, PORT_RESET,
+ 750);
if (retval != 0) {
dev_err(hcd->self.controller, "port %d reset error %d\n",
- wIndex + 1, retval);
+ wIndex + 1, retval);
goto error;
}
/* see what we found out */
- temp = check_reset_complete(hcd, wIndex,
- reg_read32(hcd->regs, HC_PORTSC1));
+ check_reset_complete(hcd, wIndex);
}
/*
* Even if OWNER is set, there's no harm letting hub_wq
@@ -1975,21 +2349,22 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
* for PORT_POWER anyway).
*/
- if (temp & PORT_OWNER)
+ if (isp1760_hcd_is_set(hcd, PORT_OWNER))
dev_err(hcd->self.controller, "PORT_OWNER is set\n");
- if (temp & PORT_CONNECT) {
+ if (isp1760_hcd_is_set(hcd, PORT_CONNECT)) {
status |= USB_PORT_STAT_CONNECTION;
/* status may be from integrated TT */
status |= USB_PORT_STAT_HIGH_SPEED;
}
- if (temp & PORT_PE)
+ if (isp1760_hcd_is_set(hcd, PORT_PE))
status |= USB_PORT_STAT_ENABLE;
- if (temp & (PORT_SUSPEND|PORT_RESUME))
+ if (isp1760_hcd_is_set(hcd, PORT_SUSPEND) &&
+ isp1760_hcd_is_set(hcd, PORT_RESUME))
status |= USB_PORT_STAT_SUSPEND;
- if (temp & PORT_RESET)
+ if (isp1760_hcd_is_set(hcd, PORT_RESET))
status |= USB_PORT_STAT_RESET;
- if (temp & PORT_POWER)
+ if (isp1760_hcd_is_set(hcd, PORT_POWER))
status |= USB_PORT_STAT_POWER;
put_unaligned(cpu_to_le32(status), (__le32 *) buf);
@@ -2009,41 +2384,40 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
- temp = reg_read32(hcd->regs, HC_PORTSC1);
- if (temp & PORT_OWNER)
+
+ if (isp1760_hcd_is_set(hcd, PORT_OWNER))
break;
-/* temp &= ~PORT_RWC_BITS; */
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
- reg_write32(hcd->regs, HC_PORTSC1, temp | PORT_PE);
+ isp1760_hcd_set(hcd, PORT_PE);
break;
case USB_PORT_FEAT_SUSPEND:
- if ((temp & PORT_PE) == 0
- || (temp & PORT_RESET) != 0)
+ if (!isp1760_hcd_is_set(hcd, PORT_PE) ||
+ isp1760_hcd_is_set(hcd, PORT_RESET))
goto error;
- reg_write32(hcd->regs, HC_PORTSC1, temp | PORT_SUSPEND);
+ isp1760_hcd_set(hcd, PORT_SUSPEND);
break;
case USB_PORT_FEAT_POWER:
- if (HCS_PPC(priv->hcs_params))
- reg_write32(hcd->regs, HC_PORTSC1,
- temp | PORT_POWER);
+ if (isp1760_hcd_ppc_is_set(hcd))
+ isp1760_hcd_set(hcd, PORT_POWER);
break;
case USB_PORT_FEAT_RESET:
- if (temp & PORT_RESUME)
+ if (isp1760_hcd_is_set(hcd, PORT_RESUME))
goto error;
/* line status bits may report this as low speed,
* which can be fine if this root hub has a
* transaction translator built in.
*/
- if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
- && PORT_USB11(temp)) {
- temp |= PORT_OWNER;
+ if ((isp1760_hcd_is_set(hcd, PORT_CONNECT) &&
+ !isp1760_hcd_is_set(hcd, PORT_PE)) &&
+ (isp1760_hcd_read(hcd, PORT_LSTATUS) == 1)) {
+ isp1760_hcd_set(hcd, PORT_OWNER);
} else {
- temp |= PORT_RESET;
- temp &= ~PORT_PE;
+ isp1760_hcd_set(hcd, PORT_RESET);
+ isp1760_hcd_clear(hcd, PORT_PE);
/*
* caller must wait, then call GetPortStatus
@@ -2052,12 +2426,10 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
priv->reset_done = jiffies +
msecs_to_jiffies(50);
}
- reg_write32(hcd->regs, HC_PORTSC1, temp);
break;
default:
goto error;
}
- reg_read32(hcd->regs, HC_USBCMD);
break;
default:
@@ -2074,14 +2446,13 @@ static int isp1760_get_frame(struct usb_hcd *hcd)
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 fr;
- fr = reg_read32(hcd->regs, HC_FRINDEX);
+ fr = isp1760_hcd_read(hcd, HC_FRINDEX);
return (fr >> 3) % priv->periodic_size;
}
static void isp1760_stop(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- u32 temp;
del_timer(&errata2_timer);
@@ -2092,24 +2463,19 @@ static void isp1760_stop(struct usb_hcd *hcd)
spin_lock_irq(&priv->lock);
ehci_reset(hcd);
/* Disable IRQ */
- temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
- reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp &= ~HW_GLOBAL_INTR_EN);
+ isp1760_hcd_clear(hcd, HW_GLOBAL_INTR_EN);
spin_unlock_irq(&priv->lock);
- reg_write32(hcd->regs, HC_CONFIGFLAG, 0);
+ isp1760_hcd_clear(hcd, FLAG_CF);
}
static void isp1760_shutdown(struct usb_hcd *hcd)
{
- u32 command, temp;
-
isp1760_stop(hcd);
- temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
- reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp &= ~HW_GLOBAL_INTR_EN);
- command = reg_read32(hcd->regs, HC_USBCMD);
- command &= ~CMD_RUN;
- reg_write32(hcd->regs, HC_USBCMD, command);
+ isp1760_hcd_clear(hcd, HW_GLOBAL_INTR_EN);
+
+ isp1760_hcd_clear(hcd, CMD_RUN);
}
static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd,
@@ -2182,10 +2548,11 @@ void isp1760_deinit_kmem_cache(void)
kmem_cache_destroy(urb_listitem_cachep);
}
-int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
- struct resource *mem, int irq, unsigned long irqflags,
+int isp1760_hcd_register(struct isp1760_hcd *priv, struct resource *mem,
+ int irq, unsigned long irqflags,
struct device *dev)
{
+ const struct isp1760_memory_layout *mem_layout = priv->memory_layout;
struct usb_hcd *hcd;
int ret;
@@ -2197,10 +2564,23 @@ int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
priv->hcd = hcd;
+ priv->atl_slots = kcalloc(mem_layout->slot_num,
+ sizeof(struct isp1760_slotinfo), GFP_KERNEL);
+ if (!priv->atl_slots) {
+ ret = -ENOMEM;
+ goto put_hcd;
+ }
+
+ priv->int_slots = kcalloc(mem_layout->slot_num,
+ sizeof(struct isp1760_slotinfo), GFP_KERNEL);
+ if (!priv->int_slots) {
+ ret = -ENOMEM;
+ goto free_atl_slots;
+ }
+
init_memory(priv);
hcd->irq = irq;
- hcd->regs = regs;
hcd->rsrc_start = mem->start;
hcd->rsrc_len = resource_size(mem);
@@ -2209,13 +2589,17 @@ int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
ret = usb_add_hcd(hcd, irq, irqflags);
if (ret)
- goto error;
+ goto free_int_slots;
device_wakeup_enable(hcd->self.controller);
return 0;
-error:
+free_int_slots:
+ kfree(priv->int_slots);
+free_atl_slots:
+ kfree(priv->atl_slots);
+put_hcd:
usb_put_hcd(hcd);
return ret;
}
@@ -2227,4 +2611,6 @@ void isp1760_hcd_unregister(struct isp1760_hcd *priv)
usb_remove_hcd(priv->hcd);
usb_put_hcd(priv->hcd);
+ kfree(priv->atl_slots);
+ kfree(priv->int_slots);
}
diff --git a/drivers/usb/isp1760/isp1760-hcd.h b/drivers/usb/isp1760/isp1760-hcd.h
index f1bb2deb1ccf..ee3063a34de3 100644
--- a/drivers/usb/isp1760/isp1760-hcd.h
+++ b/drivers/usb/isp1760/isp1760-hcd.h
@@ -3,30 +3,15 @@
#define _ISP1760_HCD_H_
#include <linux/spinlock.h>
+#include <linux/regmap.h>
+
+#include "isp1760-regs.h"
struct isp1760_qh;
struct isp1760_qtd;
struct resource;
struct usb_hcd;
-/*
- * 60kb divided in:
- * - 32 blocks @ 256 bytes
- * - 20 blocks @ 1024 bytes
- * - 4 blocks @ 8192 bytes
- */
-
-#define BLOCK_1_NUM 32
-#define BLOCK_2_NUM 20
-#define BLOCK_3_NUM 4
-
-#define BLOCK_1_SIZE 256
-#define BLOCK_2_SIZE 1024
-#define BLOCK_3_SIZE 8192
-#define BLOCKS (BLOCK_1_NUM + BLOCK_2_NUM + BLOCK_3_NUM)
-#define MAX_PAYLOAD_SIZE BLOCK_3_SIZE
-#define PAYLOAD_AREA_SIZE 0xf000
-
struct isp1760_slotinfo {
struct isp1760_qh *qh;
struct isp1760_qtd *qtd;
@@ -34,6 +19,18 @@ struct isp1760_slotinfo {
};
/* chip memory management */
+#define ISP176x_BLOCK_MAX (32 + 20 + 4)
+#define ISP176x_BLOCK_NUM 3
+
+struct isp1760_memory_layout {
+ unsigned int blocks[ISP176x_BLOCK_NUM];
+ unsigned int blocks_size[ISP176x_BLOCK_NUM];
+
+ unsigned int slot_num;
+ unsigned int payload_blocks;
+ unsigned int payload_area_size;
+};
+
struct isp1760_memory_chunk {
unsigned int start;
unsigned int size;
@@ -48,16 +45,22 @@ enum isp1760_queue_head_types {
};
struct isp1760_hcd {
-#ifdef CONFIG_USB_ISP1760_HCD
struct usb_hcd *hcd;
- u32 hcs_params;
+ void __iomem *base;
+
+ struct regmap *regs;
+ struct regmap_field *fields[HC_FIELD_MAX];
+
+ bool is_isp1763;
+ const struct isp1760_memory_layout *memory_layout;
+
spinlock_t lock;
- struct isp1760_slotinfo atl_slots[32];
+ struct isp1760_slotinfo *atl_slots;
int atl_done_map;
- struct isp1760_slotinfo int_slots[32];
+ struct isp1760_slotinfo *int_slots;
int int_done_map;
- struct isp1760_memory_chunk memory_pool[BLOCKS];
+ struct isp1760_memory_chunk memory_pool[ISP176x_BLOCK_MAX];
struct list_head qh_list[QH_END];
/* periodic schedule support */
@@ -66,20 +69,18 @@ struct isp1760_hcd {
unsigned i_thresh;
unsigned long reset_done;
unsigned long next_statechange;
-#endif
};
#ifdef CONFIG_USB_ISP1760_HCD
-int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
- struct resource *mem, int irq, unsigned long irqflags,
- struct device *dev);
+int isp1760_hcd_register(struct isp1760_hcd *priv, struct resource *mem,
+ int irq, unsigned long irqflags, struct device *dev);
void isp1760_hcd_unregister(struct isp1760_hcd *priv);
int isp1760_init_kmem_once(void);
void isp1760_deinit_kmem_cache(void);
#else
static inline int isp1760_hcd_register(struct isp1760_hcd *priv,
- void __iomem *regs, struct resource *mem,
+ struct resource *mem,
int irq, unsigned long irqflags,
struct device *dev)
{
diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c
index ccd30f835888..7cc349c0b2ad 100644
--- a/drivers/usb/isp1760/isp1760-if.c
+++ b/drivers/usb/isp1760/isp1760-if.c
@@ -7,6 +7,7 @@
* - PDEV (generic platform device centralized driver model)
*
* (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
+ * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org>
*
*/
@@ -16,8 +17,8 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/usb/isp1760.h>
#include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
#include "isp1760-core.h"
#include "isp1760-regs.h"
@@ -75,9 +76,9 @@ static int isp1761_pci_init(struct pci_dev *dev)
/*by default host is in 16bit mode, so
* io operations at this stage must be 16 bit
* */
- writel(0xface, iobase + HC_SCRATCH_REG);
+ writel(0xface, iobase + ISP176x_HC_SCRATCH);
udelay(100);
- reg_data = readl(iobase + HC_SCRATCH_REG) & 0x0000ffff;
+ reg_data = readl(iobase + ISP176x_HC_SCRATCH) & 0x0000ffff;
retry_count--;
}
@@ -209,13 +210,21 @@ static int isp1760_plat_probe(struct platform_device *pdev)
if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
devflags |= ISP1760_FLAG_ISP1761;
- /* Some systems wire up only 16 of the 32 data lines */
+ if (of_device_is_compatible(dp, "nxp,usb-isp1763"))
+ devflags |= ISP1760_FLAG_ISP1763;
+
+ /*
+ * Some systems wire up only 8 of 16 data lines or
+ * 16 of the 32 data lines
+ */
of_property_read_u32(dp, "bus-width", &bus_width);
if (bus_width == 16)
devflags |= ISP1760_FLAG_BUS_WIDTH_16;
+ else if (bus_width == 8)
+ devflags |= ISP1760_FLAG_BUS_WIDTH_8;
- if (of_property_read_bool(dp, "port1-otg"))
- devflags |= ISP1760_FLAG_OTG_EN;
+ if (usb_get_dr_mode(&pdev->dev) == USB_DR_MODE_PERIPHERAL)
+ devflags |= ISP1760_FLAG_PERIPHERAL_EN;
if (of_property_read_bool(dp, "analog-oc"))
devflags |= ISP1760_FLAG_ANALOG_OC;
@@ -225,22 +234,9 @@ static int isp1760_plat_probe(struct platform_device *pdev)
if (of_property_read_bool(dp, "dreq-polarity"))
devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
- } else if (dev_get_platdata(&pdev->dev)) {
- struct isp1760_platform_data *pdata =
- dev_get_platdata(&pdev->dev);
-
- if (pdata->is_isp1761)
- devflags |= ISP1760_FLAG_ISP1761;
- if (pdata->bus_width_16)
- devflags |= ISP1760_FLAG_BUS_WIDTH_16;
- if (pdata->port1_otg)
- devflags |= ISP1760_FLAG_OTG_EN;
- if (pdata->analog_oc)
- devflags |= ISP1760_FLAG_ANALOG_OC;
- if (pdata->dack_polarity_high)
- devflags |= ISP1760_FLAG_DACK_POL_HIGH;
- if (pdata->dreq_polarity_high)
- devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
+ } else {
+ pr_err("isp1760: no platform data\n");
+ return -ENXIO;
}
ret = isp1760_register(mem_res, irq_res->start, irqflags, &pdev->dev,
@@ -263,6 +259,7 @@ static int isp1760_plat_remove(struct platform_device *pdev)
static const struct of_device_id isp1760_of_match[] = {
{ .compatible = "nxp,usb-isp1760", },
{ .compatible = "nxp,usb-isp1761", },
+ { .compatible = "nxp,usb-isp1763", },
{ },
};
MODULE_DEVICE_TABLE(of, isp1760_of_match);
diff --git a/drivers/usb/isp1760/isp1760-regs.h b/drivers/usb/isp1760/isp1760-regs.h
index fedc4f5cded0..94ea60c20b2a 100644
--- a/drivers/usb/isp1760/isp1760-regs.h
+++ b/drivers/usb/isp1760/isp1760-regs.h
@@ -2,226 +2,291 @@
/*
* Driver for the NXP ISP1760 chip
*
+ * Copyright 2021 Linaro, Rui Miguel Silva
* Copyright 2014 Laurent Pinchart
* Copyright 2007 Sebastian Siewior
*
* Contacts:
* Sebastian Siewior <bigeasy@linutronix.de>
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Rui Miguel Silva <rui.silva@linaro.org>
*/
-#ifndef _ISP1760_REGS_H_
-#define _ISP1760_REGS_H_
+#ifndef _ISP176x_REGS_H_
+#define _ISP176x_REGS_H_
/* -----------------------------------------------------------------------------
* Host Controller
*/
+/* ISP1760/31 */
/* EHCI capability registers */
-#define HC_CAPLENGTH 0x000
-#define HC_LENGTH(p) (((p) >> 00) & 0x00ff) /* bits 7:0 */
-#define HC_VERSION(p) (((p) >> 16) & 0xffff) /* bits 31:16 */
+#define ISP176x_HC_VERSION 0x002
+#define ISP176x_HC_HCSPARAMS 0x004
+#define ISP176x_HC_HCCPARAMS 0x008
-#define HC_HCSPARAMS 0x004
-#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* true: has port indicators */
-#define HCS_PPC(p) ((p) & (1 << 4)) /* true: port power control */
-#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) /* bits 3:0, ports on HC */
+/* EHCI operational registers */
+#define ISP176x_HC_USBCMD 0x020
+#define ISP176x_HC_USBSTS 0x024
+#define ISP176x_HC_FRINDEX 0x02c
+
+#define ISP176x_HC_CONFIGFLAG 0x060
+#define ISP176x_HC_PORTSC1 0x064
+
+#define ISP176x_HC_ISO_PTD_DONEMAP 0x130
+#define ISP176x_HC_ISO_PTD_SKIPMAP 0x134
+#define ISP176x_HC_ISO_PTD_LASTPTD 0x138
+#define ISP176x_HC_INT_PTD_DONEMAP 0x140
+#define ISP176x_HC_INT_PTD_SKIPMAP 0x144
+#define ISP176x_HC_INT_PTD_LASTPTD 0x148
+#define ISP176x_HC_ATL_PTD_DONEMAP 0x150
+#define ISP176x_HC_ATL_PTD_SKIPMAP 0x154
+#define ISP176x_HC_ATL_PTD_LASTPTD 0x158
-#define HC_HCCPARAMS 0x008
-#define HCC_ISOC_CACHE(p) ((p) & (1 << 7)) /* true: can cache isoc frame */
-#define HCC_ISOC_THRES(p) (((p) >> 4) & 0x7) /* bits 6:4, uframes cached */
+/* Configuration Register */
+#define ISP176x_HC_HW_MODE_CTRL 0x300
+#define ISP176x_HC_CHIP_ID 0x304
+#define ISP176x_HC_SCRATCH 0x308
+#define ISP176x_HC_RESET 0x30c
+#define ISP176x_HC_BUFFER_STATUS 0x334
+#define ISP176x_HC_MEMORY 0x33c
+/* Interrupt Register */
+#define ISP176x_HC_INTERRUPT 0x310
+#define ISP176x_HC_INTERRUPT_ENABLE 0x314
+#define ISP176x_HC_ISO_IRQ_MASK_OR 0x318
+#define ISP176x_HC_INT_IRQ_MASK_OR 0x31c
+#define ISP176x_HC_ATL_IRQ_MASK_OR 0x320
+#define ISP176x_HC_ISO_IRQ_MASK_AND 0x324
+#define ISP176x_HC_INT_IRQ_MASK_AND 0x328
+#define ISP176x_HC_ATL_IRQ_MASK_AND 0x32c
+
+#define ISP176x_HC_OTG_CTRL_SET 0x374
+#define ISP176x_HC_OTG_CTRL_CLEAR 0x376
+
+enum isp176x_host_controller_fields {
+ /* HC_PORTSC1 */
+ PORT_OWNER, PORT_POWER, PORT_LSTATUS, PORT_RESET, PORT_SUSPEND,
+ PORT_RESUME, PORT_PE, PORT_CSC, PORT_CONNECT,
+ /* HC_HCSPARAMS */
+ HCS_PPC, HCS_N_PORTS,
+ /* HC_HCCPARAMS */
+ HCC_ISOC_CACHE, HCC_ISOC_THRES,
+ /* HC_USBCMD */
+ CMD_LRESET, CMD_RESET, CMD_RUN,
+ /* HC_USBSTS */
+ STS_PCD,
+ /* HC_FRINDEX */
+ HC_FRINDEX,
+ /* HC_CONFIGFLAG */
+ FLAG_CF,
+ /* ISO/INT/ATL PTD */
+ HC_ISO_PTD_DONEMAP, HC_ISO_PTD_SKIPMAP, HC_ISO_PTD_LASTPTD,
+ HC_INT_PTD_DONEMAP, HC_INT_PTD_SKIPMAP, HC_INT_PTD_LASTPTD,
+ HC_ATL_PTD_DONEMAP, HC_ATL_PTD_SKIPMAP, HC_ATL_PTD_LASTPTD,
+ /* HC_HW_MODE_CTRL */
+ ALL_ATX_RESET, HW_ANA_DIGI_OC, HW_DEV_DMA, HW_COMN_IRQ, HW_COMN_DMA,
+ HW_DATA_BUS_WIDTH, HW_DACK_POL_HIGH, HW_DREQ_POL_HIGH, HW_INTR_HIGH_ACT,
+ HW_INTF_LOCK, HW_INTR_EDGE_TRIG, HW_GLOBAL_INTR_EN,
+ /* HC_CHIP_ID */
+ HC_CHIP_ID_HIGH, HC_CHIP_ID_LOW, HC_CHIP_REV,
+ /* HC_SCRATCH */
+ HC_SCRATCH,
+ /* HC_RESET */
+ SW_RESET_RESET_ATX, SW_RESET_RESET_HC, SW_RESET_RESET_ALL,
+ /* HC_BUFFER_STATUS */
+ ISO_BUF_FILL, INT_BUF_FILL, ATL_BUF_FILL,
+ /* HC_MEMORY */
+ MEM_BANK_SEL, MEM_START_ADDR,
+ /* HC_DATA */
+ HC_DATA,
+ /* HC_INTERRUPT */
+ HC_INTERRUPT,
+ /* HC_INTERRUPT_ENABLE */
+ HC_INT_IRQ_ENABLE, HC_ATL_IRQ_ENABLE,
+ /* INTERRUPT MASKS */
+ HC_ISO_IRQ_MASK_OR, HC_INT_IRQ_MASK_OR, HC_ATL_IRQ_MASK_OR,
+ HC_ISO_IRQ_MASK_AND, HC_INT_IRQ_MASK_AND, HC_ATL_IRQ_MASK_AND,
+ /* HW_OTG_CTRL_SET */
+ HW_OTG_DISABLE, HW_SW_SEL_HC_DC, HW_VBUS_DRV, HW_SEL_CP_EXT,
+ HW_DM_PULLDOWN, HW_DP_PULLDOWN, HW_DP_PULLUP, HW_HC_2_DIS,
+ /* HW_OTG_CTRL_CLR */
+ HW_OTG_DISABLE_CLEAR, HW_SW_SEL_HC_DC_CLEAR, HW_VBUS_DRV_CLEAR,
+ HW_SEL_CP_EXT_CLEAR, HW_DM_PULLDOWN_CLEAR, HW_DP_PULLDOWN_CLEAR,
+ HW_DP_PULLUP_CLEAR, HW_HC_2_DIS_CLEAR,
+ /* Last element */
+ HC_FIELD_MAX,
+};
+
+/* ISP1763 */
/* EHCI operational registers */
-#define HC_USBCMD 0x020
-#define CMD_LRESET (1 << 7) /* partial reset (no ports, etc) */
-#define CMD_RESET (1 << 1) /* reset HC not bus */
-#define CMD_RUN (1 << 0) /* start/stop HC */
-
-#define HC_USBSTS 0x024
-#define STS_PCD (1 << 2) /* port change detect */
-
-#define HC_FRINDEX 0x02c
-
-#define HC_CONFIGFLAG 0x060
-#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */
-
-#define HC_PORTSC1 0x064
-#define PORT_OWNER (1 << 13) /* true: companion hc owns this port */
-#define PORT_POWER (1 << 12) /* true: has power (see PPC) */
-#define PORT_USB11(x) (((x) & (3 << 10)) == (1 << 10)) /* USB 1.1 device */
-#define PORT_RESET (1 << 8) /* reset port */
-#define PORT_SUSPEND (1 << 7) /* suspend port */
-#define PORT_RESUME (1 << 6) /* resume it */
-#define PORT_PE (1 << 2) /* port enable */
-#define PORT_CSC (1 << 1) /* connect status change */
-#define PORT_CONNECT (1 << 0) /* device connected */
-#define PORT_RWC_BITS (PORT_CSC)
-
-#define HC_ISO_PTD_DONEMAP_REG 0x130
-#define HC_ISO_PTD_SKIPMAP_REG 0x134
-#define HC_ISO_PTD_LASTPTD_REG 0x138
-#define HC_INT_PTD_DONEMAP_REG 0x140
-#define HC_INT_PTD_SKIPMAP_REG 0x144
-#define HC_INT_PTD_LASTPTD_REG 0x148
-#define HC_ATL_PTD_DONEMAP_REG 0x150
-#define HC_ATL_PTD_SKIPMAP_REG 0x154
-#define HC_ATL_PTD_LASTPTD_REG 0x158
+#define ISP1763_HC_USBCMD 0x8c
+#define ISP1763_HC_USBSTS 0x90
+#define ISP1763_HC_FRINDEX 0x98
+
+#define ISP1763_HC_CONFIGFLAG 0x9c
+#define ISP1763_HC_PORTSC1 0xa0
+
+#define ISP1763_HC_ISO_PTD_DONEMAP 0xa4
+#define ISP1763_HC_ISO_PTD_SKIPMAP 0xa6
+#define ISP1763_HC_ISO_PTD_LASTPTD 0xa8
+#define ISP1763_HC_INT_PTD_DONEMAP 0xaa
+#define ISP1763_HC_INT_PTD_SKIPMAP 0xac
+#define ISP1763_HC_INT_PTD_LASTPTD 0xae
+#define ISP1763_HC_ATL_PTD_DONEMAP 0xb0
+#define ISP1763_HC_ATL_PTD_SKIPMAP 0xb2
+#define ISP1763_HC_ATL_PTD_LASTPTD 0xb4
/* Configuration Register */
-#define HC_HW_MODE_CTRL 0x300
-#define ALL_ATX_RESET (1 << 31)
-#define HW_ANA_DIGI_OC (1 << 15)
-#define HW_DEV_DMA (1 << 11)
-#define HW_COMN_IRQ (1 << 10)
-#define HW_COMN_DMA (1 << 9)
-#define HW_DATA_BUS_32BIT (1 << 8)
-#define HW_DACK_POL_HIGH (1 << 6)
-#define HW_DREQ_POL_HIGH (1 << 5)
-#define HW_INTR_HIGH_ACT (1 << 2)
-#define HW_INTR_EDGE_TRIG (1 << 1)
-#define HW_GLOBAL_INTR_EN (1 << 0)
-
-#define HC_CHIP_ID_REG 0x304
-#define HC_SCRATCH_REG 0x308
-
-#define HC_RESET_REG 0x30c
-#define SW_RESET_RESET_HC (1 << 1)
-#define SW_RESET_RESET_ALL (1 << 0)
-
-#define HC_BUFFER_STATUS_REG 0x334
-#define ISO_BUF_FILL (1 << 2)
-#define INT_BUF_FILL (1 << 1)
-#define ATL_BUF_FILL (1 << 0)
-
-#define HC_MEMORY_REG 0x33c
-#define ISP_BANK(x) ((x) << 16)
-
-#define HC_PORT1_CTRL 0x374
-#define PORT1_POWER (3 << 3)
-#define PORT1_INIT1 (1 << 7)
-#define PORT1_INIT2 (1 << 23)
-#define HW_OTG_CTRL_SET 0x374
-#define HW_OTG_CTRL_CLR 0x376
-#define HW_OTG_DISABLE (1 << 10)
-#define HW_OTG_SE0_EN (1 << 9)
-#define HW_BDIS_ACON_EN (1 << 8)
-#define HW_SW_SEL_HC_DC (1 << 7)
-#define HW_VBUS_CHRG (1 << 6)
-#define HW_VBUS_DISCHRG (1 << 5)
-#define HW_VBUS_DRV (1 << 4)
-#define HW_SEL_CP_EXT (1 << 3)
-#define HW_DM_PULLDOWN (1 << 2)
-#define HW_DP_PULLDOWN (1 << 1)
-#define HW_DP_PULLUP (1 << 0)
+#define ISP1763_HC_HW_MODE_CTRL 0xb6
+#define ISP1763_HC_CHIP_REV 0x70
+#define ISP1763_HC_CHIP_ID 0x72
+#define ISP1763_HC_SCRATCH 0x78
+#define ISP1763_HC_RESET 0xb8
+#define ISP1763_HC_BUFFER_STATUS 0xba
+#define ISP1763_HC_MEMORY 0xc4
+#define ISP1763_HC_DATA 0xc6
/* Interrupt Register */
-#define HC_INTERRUPT_REG 0x310
-
-#define HC_INTERRUPT_ENABLE 0x314
-#define HC_ISO_INT (1 << 9)
-#define HC_ATL_INT (1 << 8)
-#define HC_INTL_INT (1 << 7)
-#define HC_EOT_INT (1 << 3)
-#define HC_SOT_INT (1 << 1)
-#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT)
-
-#define HC_ISO_IRQ_MASK_OR_REG 0x318
-#define HC_INT_IRQ_MASK_OR_REG 0x31c
-#define HC_ATL_IRQ_MASK_OR_REG 0x320
-#define HC_ISO_IRQ_MASK_AND_REG 0x324
-#define HC_INT_IRQ_MASK_AND_REG 0x328
-#define HC_ATL_IRQ_MASK_AND_REG 0x32c
+#define ISP1763_HC_INTERRUPT 0xd4
+#define ISP1763_HC_INTERRUPT_ENABLE 0xd6
+#define ISP1763_HC_ISO_IRQ_MASK_OR 0xd8
+#define ISP1763_HC_INT_IRQ_MASK_OR 0xda
+#define ISP1763_HC_ATL_IRQ_MASK_OR 0xdc
+#define ISP1763_HC_ISO_IRQ_MASK_AND 0xde
+#define ISP1763_HC_INT_IRQ_MASK_AND 0xe0
+#define ISP1763_HC_ATL_IRQ_MASK_AND 0xe2
+
+#define ISP1763_HC_OTG_CTRL_SET 0xe4
+#define ISP1763_HC_OTG_CTRL_CLEAR 0xe6
/* -----------------------------------------------------------------------------
* Peripheral Controller
*/
-/* Initialization Registers */
-#define DC_ADDRESS 0x0200
-#define DC_DEVEN (1 << 7)
-
-#define DC_MODE 0x020c
-#define DC_DMACLKON (1 << 9)
-#define DC_VBUSSTAT (1 << 8)
-#define DC_CLKAON (1 << 7)
-#define DC_SNDRSU (1 << 6)
-#define DC_GOSUSP (1 << 5)
-#define DC_SFRESET (1 << 4)
-#define DC_GLINTENA (1 << 3)
-#define DC_WKUPCS (1 << 2)
-
-#define DC_INTCONF 0x0210
-#define DC_CDBGMOD_ACK_NAK (0 << 6)
-#define DC_CDBGMOD_ACK (1 << 6)
-#define DC_CDBGMOD_ACK_1NAK (2 << 6)
-#define DC_DDBGMODIN_ACK_NAK (0 << 4)
-#define DC_DDBGMODIN_ACK (1 << 4)
-#define DC_DDBGMODIN_ACK_1NAK (2 << 4)
-#define DC_DDBGMODOUT_ACK_NYET_NAK (0 << 2)
-#define DC_DDBGMODOUT_ACK_NYET (1 << 2)
-#define DC_DDBGMODOUT_ACK_NYET_1NAK (2 << 2)
-#define DC_INTLVL (1 << 1)
-#define DC_INTPOL (1 << 0)
-
-#define DC_DEBUG 0x0212
-#define DC_INTENABLE 0x0214
#define DC_IEPTX(n) (1 << (11 + 2 * (n)))
#define DC_IEPRX(n) (1 << (10 + 2 * (n)))
#define DC_IEPRXTX(n) (3 << (10 + 2 * (n)))
-#define DC_IEP0SETUP (1 << 8)
-#define DC_IEVBUS (1 << 7)
-#define DC_IEDMA (1 << 6)
-#define DC_IEHS_STA (1 << 5)
-#define DC_IERESM (1 << 4)
-#define DC_IESUSP (1 << 3)
-#define DC_IEPSOF (1 << 2)
-#define DC_IESOF (1 << 1)
-#define DC_IEBRST (1 << 0)
+
+#define ISP176x_DC_CDBGMOD_ACK BIT(6)
+#define ISP176x_DC_DDBGMODIN_ACK BIT(4)
+#define ISP176x_DC_DDBGMODOUT_ACK BIT(2)
+
+#define ISP176x_DC_IEP0SETUP BIT(8)
+#define ISP176x_DC_IEVBUS BIT(7)
+#define ISP176x_DC_IEHS_STA BIT(5)
+#define ISP176x_DC_IERESM BIT(4)
+#define ISP176x_DC_IESUSP BIT(3)
+#define ISP176x_DC_IEBRST BIT(0)
+
+#define ISP176x_DC_ENDPTYP_ISOC 0x01
+#define ISP176x_DC_ENDPTYP_BULK 0x02
+#define ISP176x_DC_ENDPTYP_INTERRUPT 0x03
+
+/* Initialization Registers */
+#define ISP176x_DC_ADDRESS 0x0200
+#define ISP176x_DC_MODE 0x020c
+#define ISP176x_DC_INTCONF 0x0210
+#define ISP176x_DC_DEBUG 0x0212
+#define ISP176x_DC_INTENABLE 0x0214
+
+/* Data Flow Registers */
+#define ISP176x_DC_EPMAXPKTSZ 0x0204
+#define ISP176x_DC_EPTYPE 0x0208
+
+#define ISP176x_DC_BUFLEN 0x021c
+#define ISP176x_DC_BUFSTAT 0x021e
+#define ISP176x_DC_DATAPORT 0x0220
+
+#define ISP176x_DC_CTRLFUNC 0x0228
+#define ISP176x_DC_EPINDEX 0x022c
+
+/* DMA Registers */
+#define ISP176x_DC_DMACMD 0x0230
+#define ISP176x_DC_DMATXCOUNT 0x0234
+#define ISP176x_DC_DMACONF 0x0238
+#define ISP176x_DC_DMAHW 0x023c
+#define ISP176x_DC_DMAINTREASON 0x0250
+#define ISP176x_DC_DMAINTEN 0x0254
+#define ISP176x_DC_DMAEP 0x0258
+#define ISP176x_DC_DMABURSTCOUNT 0x0264
+
+/* General Registers */
+#define ISP176x_DC_INTERRUPT 0x0218
+#define ISP176x_DC_CHIPID 0x0270
+#define ISP176x_DC_FRAMENUM 0x0274
+#define ISP176x_DC_SCRATCH 0x0278
+#define ISP176x_DC_UNLOCKDEV 0x027c
+#define ISP176x_DC_INTPULSEWIDTH 0x0280
+#define ISP176x_DC_TESTMODE 0x0284
+
+enum isp176x_device_controller_fields {
+ /* DC_ADDRESS */
+ DC_DEVEN, DC_DEVADDR,
+ /* DC_MODE */
+ DC_VBUSSTAT, DC_SFRESET, DC_GLINTENA,
+ /* DC_INTCONF */
+ DC_CDBGMOD_ACK, DC_DDBGMODIN_ACK, DC_DDBGMODOUT_ACK, DC_INTPOL,
+ /* DC_INTENABLE */
+ DC_IEPRXTX_7, DC_IEPRXTX_6, DC_IEPRXTX_5, DC_IEPRXTX_4, DC_IEPRXTX_3,
+ DC_IEPRXTX_2, DC_IEPRXTX_1, DC_IEPRXTX_0,
+ DC_IEP0SETUP, DC_IEVBUS, DC_IEHS_STA, DC_IERESM, DC_IESUSP, DC_IEBRST,
+ /* DC_EPINDEX */
+ DC_EP0SETUP, DC_ENDPIDX, DC_EPDIR,
+ /* DC_CTRLFUNC */
+ DC_CLBUF, DC_VENDP, DC_DSEN, DC_STATUS, DC_STALL,
+ /* DC_BUFLEN */
+ DC_BUFLEN,
+ /* DC_EPMAXPKTSZ */
+ DC_FFOSZ,
+ /* DC_EPTYPE */
+ DC_EPENABLE, DC_ENDPTYP,
+ /* DC_FRAMENUM */
+ DC_FRAMENUM, DC_UFRAMENUM,
+ /* DC_CHIP_ID */
+ DC_CHIP_ID_HIGH, DC_CHIP_ID_LOW,
+ /* DC_SCRATCH */
+ DC_SCRATCH,
+ /* Last element */
+ DC_FIELD_MAX,
+};
+
+/* ISP1763 */
+/* Initialization Registers */
+#define ISP1763_DC_ADDRESS 0x00
+#define ISP1763_DC_MODE 0x0c
+#define ISP1763_DC_INTCONF 0x10
+#define ISP1763_DC_INTENABLE 0x14
/* Data Flow Registers */
-#define DC_EPINDEX 0x022c
-#define DC_EP0SETUP (1 << 5)
-#define DC_ENDPIDX(n) ((n) << 1)
-#define DC_EPDIR (1 << 0)
-
-#define DC_CTRLFUNC 0x0228
-#define DC_CLBUF (1 << 4)
-#define DC_VENDP (1 << 3)
-#define DC_DSEN (1 << 2)
-#define DC_STATUS (1 << 1)
-#define DC_STALL (1 << 0)
-
-#define DC_DATAPORT 0x0220
-#define DC_BUFLEN 0x021c
-#define DC_DATACOUNT_MASK 0xffff
-#define DC_BUFSTAT 0x021e
-#define DC_EPMAXPKTSZ 0x0204
-
-#define DC_EPTYPE 0x0208
-#define DC_NOEMPKT (1 << 4)
-#define DC_EPENABLE (1 << 3)
-#define DC_DBLBUF (1 << 2)
-#define DC_ENDPTYP_ISOC (1 << 0)
-#define DC_ENDPTYP_BULK (2 << 0)
-#define DC_ENDPTYP_INTERRUPT (3 << 0)
+#define ISP1763_DC_EPMAXPKTSZ 0x04
+#define ISP1763_DC_EPTYPE 0x08
+
+#define ISP1763_DC_BUFLEN 0x1c
+#define ISP1763_DC_BUFSTAT 0x1e
+#define ISP1763_DC_DATAPORT 0x20
+
+#define ISP1763_DC_CTRLFUNC 0x28
+#define ISP1763_DC_EPINDEX 0x2c
/* DMA Registers */
-#define DC_DMACMD 0x0230
-#define DC_DMATXCOUNT 0x0234
-#define DC_DMACONF 0x0238
-#define DC_DMAHW 0x023c
-#define DC_DMAINTREASON 0x0250
-#define DC_DMAINTEN 0x0254
-#define DC_DMAEP 0x0258
-#define DC_DMABURSTCOUNT 0x0264
+#define ISP1763_DC_DMACMD 0x30
+#define ISP1763_DC_DMATXCOUNT 0x34
+#define ISP1763_DC_DMACONF 0x38
+#define ISP1763_DC_DMAHW 0x3c
+#define ISP1763_DC_DMAINTREASON 0x50
+#define ISP1763_DC_DMAINTEN 0x54
+#define ISP1763_DC_DMAEP 0x58
+#define ISP1763_DC_DMABURSTCOUNT 0x64
/* General Registers */
-#define DC_INTERRUPT 0x0218
-#define DC_CHIPID 0x0270
-#define DC_FRAMENUM 0x0274
-#define DC_SCRATCH 0x0278
-#define DC_UNLOCKDEV 0x027c
-#define DC_INTPULSEWIDTH 0x0280
-#define DC_TESTMODE 0x0284
+#define ISP1763_DC_INTERRUPT 0x18
+#define ISP1763_DC_CHIPID_LOW 0x70
+#define ISP1763_DC_CHIPID_HIGH 0x72
+#define ISP1763_DC_FRAMENUM 0x74
+#define ISP1763_DC_SCRATCH 0x78
+#define ISP1763_DC_UNLOCKDEV 0x7c
+#define ISP1763_DC_INTPULSEWIDTH 0x80
+#define ISP1763_DC_TESTMODE 0x84
#endif
diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c
index 1714b2258b54..a78da59d6417 100644
--- a/drivers/usb/isp1760/isp1760-udc.c
+++ b/drivers/usb/isp1760/isp1760-udc.c
@@ -2,10 +2,12 @@
/*
* Driver for the NXP ISP1761 device controller
*
+ * Copyright 2021 Linaro, Rui Miguel Silva
* Copyright 2014 Ideas on Board Oy
*
* Contacts:
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Rui Miguel Silva <rui.silva@linaro.org>
*/
#include <linux/interrupt.h>
@@ -45,16 +47,62 @@ static inline struct isp1760_request *req_to_udc_req(struct usb_request *req)
return container_of(req, struct isp1760_request, req);
}
-static inline u32 isp1760_udc_read(struct isp1760_udc *udc, u16 reg)
+static u32 isp1760_udc_read(struct isp1760_udc *udc, u16 field)
{
- return isp1760_read32(udc->regs, reg);
+ return isp1760_field_read(udc->fields, field);
}
-static inline void isp1760_udc_write(struct isp1760_udc *udc, u16 reg, u32 val)
+static void isp1760_udc_write(struct isp1760_udc *udc, u16 field, u32 val)
{
- isp1760_write32(udc->regs, reg, val);
+ isp1760_field_write(udc->fields, field, val);
}
+static u32 isp1760_udc_read_raw(struct isp1760_udc *udc, u16 reg)
+{
+ __le32 val;
+
+ regmap_raw_read(udc->regs, reg, &val, 4);
+
+ return le32_to_cpu(val);
+}
+
+static u16 isp1760_udc_read_raw16(struct isp1760_udc *udc, u16 reg)
+{
+ __le16 val;
+
+ regmap_raw_read(udc->regs, reg, &val, 2);
+
+ return le16_to_cpu(val);
+}
+
+static void isp1760_udc_write_raw(struct isp1760_udc *udc, u16 reg, u32 val)
+{
+ __le32 val_le = cpu_to_le32(val);
+
+ regmap_raw_write(udc->regs, reg, &val_le, 4);
+}
+
+static void isp1760_udc_write_raw16(struct isp1760_udc *udc, u16 reg, u16 val)
+{
+ __le16 val_le = cpu_to_le16(val);
+
+ regmap_raw_write(udc->regs, reg, &val_le, 2);
+}
+
+static void isp1760_udc_set(struct isp1760_udc *udc, u32 field)
+{
+ isp1760_udc_write(udc, field, 0xFFFFFFFF);
+}
+
+static void isp1760_udc_clear(struct isp1760_udc *udc, u32 field)
+{
+ isp1760_udc_write(udc, field, 0);
+}
+
+static bool isp1760_udc_is_set(struct isp1760_udc *udc, u32 field)
+{
+ return !!isp1760_udc_read(udc, field);
+}
/* -----------------------------------------------------------------------------
* Endpoint Management
*/
@@ -75,16 +123,21 @@ static struct isp1760_ep *isp1760_udc_find_ep(struct isp1760_udc *udc,
return NULL;
}
-static void __isp1760_udc_select_ep(struct isp1760_ep *ep, int dir)
+static void __isp1760_udc_select_ep(struct isp1760_udc *udc,
+ struct isp1760_ep *ep, int dir)
{
- isp1760_udc_write(ep->udc, DC_EPINDEX,
- DC_ENDPIDX(ep->addr & USB_ENDPOINT_NUMBER_MASK) |
- (dir == USB_DIR_IN ? DC_EPDIR : 0));
+ isp1760_udc_write(udc, DC_ENDPIDX, ep->addr & USB_ENDPOINT_NUMBER_MASK);
+
+ if (dir == USB_DIR_IN)
+ isp1760_udc_set(udc, DC_EPDIR);
+ else
+ isp1760_udc_clear(udc, DC_EPDIR);
}
/**
* isp1760_udc_select_ep - Select an endpoint for register access
* @ep: The endpoint
+ * @udc: Reference to the device controller
*
* The ISP1761 endpoint registers are banked. This function selects the target
* endpoint for banked register access. The selection remains valid until the
@@ -93,9 +146,10 @@ static void __isp1760_udc_select_ep(struct isp1760_ep *ep, int dir)
*
* Called with the UDC spinlock held.
*/
-static void isp1760_udc_select_ep(struct isp1760_ep *ep)
+static void isp1760_udc_select_ep(struct isp1760_udc *udc,
+ struct isp1760_ep *ep)
{
- __isp1760_udc_select_ep(ep, ep->addr & USB_ENDPOINT_DIR_MASK);
+ __isp1760_udc_select_ep(udc, ep, ep->addr & USB_ENDPOINT_DIR_MASK);
}
/* Called with the UDC spinlock held. */
@@ -108,9 +162,13 @@ static void isp1760_udc_ctrl_send_status(struct isp1760_ep *ep, int dir)
* the direction opposite to the data stage data packets, we thus need
* to select the OUT/IN endpoint for IN/OUT transfers.
*/
- isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) |
- (dir == USB_DIR_IN ? 0 : DC_EPDIR));
- isp1760_udc_write(udc, DC_CTRLFUNC, DC_STATUS);
+ if (dir == USB_DIR_IN)
+ isp1760_udc_clear(udc, DC_EPDIR);
+ else
+ isp1760_udc_set(udc, DC_EPDIR);
+
+ isp1760_udc_write(udc, DC_ENDPIDX, 1);
+ isp1760_udc_set(udc, DC_STATUS);
/*
* The hardware will terminate the request automatically and go back to
@@ -157,10 +215,10 @@ static void isp1760_udc_ctrl_send_stall(struct isp1760_ep *ep)
spin_lock_irqsave(&udc->lock, flags);
/* Stall both the IN and OUT endpoints. */
- __isp1760_udc_select_ep(ep, USB_DIR_OUT);
- isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL);
- __isp1760_udc_select_ep(ep, USB_DIR_IN);
- isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL);
+ __isp1760_udc_select_ep(udc, ep, USB_DIR_OUT);
+ isp1760_udc_set(udc, DC_STALL);
+ __isp1760_udc_select_ep(udc, ep, USB_DIR_IN);
+ isp1760_udc_set(udc, DC_STALL);
/* A protocol stall completes the control transaction. */
udc->ep0_state = ISP1760_CTRL_SETUP;
@@ -181,8 +239,8 @@ static bool isp1760_udc_receive(struct isp1760_ep *ep,
u32 *buf;
int i;
- isp1760_udc_select_ep(ep);
- len = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK;
+ isp1760_udc_select_ep(udc, ep);
+ len = isp1760_udc_read(udc, DC_BUFLEN);
dev_dbg(udc->isp->dev, "%s: received %u bytes (%u/%u done)\n",
__func__, len, req->req.actual, req->req.length);
@@ -198,7 +256,7 @@ static bool isp1760_udc_receive(struct isp1760_ep *ep,
* datasheet doesn't clearly document how this should be
* handled.
*/
- isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+ isp1760_udc_set(udc, DC_CLBUF);
return false;
}
@@ -209,9 +267,9 @@ static bool isp1760_udc_receive(struct isp1760_ep *ep,
* the next packet might be removed from the FIFO.
*/
for (i = len; i > 2; i -= 4, ++buf)
- *buf = le32_to_cpu(isp1760_udc_read(udc, DC_DATAPORT));
+ *buf = isp1760_udc_read_raw(udc, ISP176x_DC_DATAPORT);
if (i > 0)
- *(u16 *)buf = le16_to_cpu(readw(udc->regs + DC_DATAPORT));
+ *(u16 *)buf = isp1760_udc_read_raw16(udc, ISP176x_DC_DATAPORT);
req->req.actual += len;
@@ -253,7 +311,7 @@ static void isp1760_udc_transmit(struct isp1760_ep *ep,
__func__, req->packet_size, req->req.actual,
req->req.length);
- __isp1760_udc_select_ep(ep, USB_DIR_IN);
+ __isp1760_udc_select_ep(udc, ep, USB_DIR_IN);
if (req->packet_size)
isp1760_udc_write(udc, DC_BUFLEN, req->packet_size);
@@ -265,14 +323,14 @@ static void isp1760_udc_transmit(struct isp1760_ep *ep,
* the FIFO for this kind of conditions, but doesn't seem to work.
*/
for (i = req->packet_size; i > 2; i -= 4, ++buf)
- isp1760_udc_write(udc, DC_DATAPORT, cpu_to_le32(*buf));
+ isp1760_udc_write_raw(udc, ISP176x_DC_DATAPORT, *buf);
if (i > 0)
- writew(cpu_to_le16(*(u16 *)buf), udc->regs + DC_DATAPORT);
+ isp1760_udc_write_raw16(udc, ISP176x_DC_DATAPORT, *(u16 *)buf);
if (ep->addr == 0)
- isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+ isp1760_udc_set(udc, DC_DSEN);
if (!req->packet_size)
- isp1760_udc_write(udc, DC_CTRLFUNC, DC_VENDP);
+ isp1760_udc_set(udc, DC_VENDP);
}
static void isp1760_ep_rx_ready(struct isp1760_ep *ep)
@@ -408,19 +466,24 @@ static int __isp1760_udc_set_halt(struct isp1760_ep *ep, bool halt)
return -EINVAL;
}
- isp1760_udc_select_ep(ep);
- isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0);
+ isp1760_udc_select_ep(udc, ep);
+
+ if (halt)
+ isp1760_udc_set(udc, DC_STALL);
+ else
+ isp1760_udc_clear(udc, DC_STALL);
if (ep->addr == 0) {
/* When halting the control endpoint, stall both IN and OUT. */
- __isp1760_udc_select_ep(ep, USB_DIR_IN);
- isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0);
+ __isp1760_udc_select_ep(udc, ep, USB_DIR_IN);
+ if (halt)
+ isp1760_udc_set(udc, DC_STALL);
+ else
+ isp1760_udc_clear(udc, DC_STALL);
} else if (!halt) {
/* Reset the data PID by cycling the endpoint enable bit. */
- u16 eptype = isp1760_udc_read(udc, DC_EPTYPE);
-
- isp1760_udc_write(udc, DC_EPTYPE, eptype & ~DC_EPENABLE);
- isp1760_udc_write(udc, DC_EPTYPE, eptype);
+ isp1760_udc_clear(udc, DC_EPENABLE);
+ isp1760_udc_set(udc, DC_EPENABLE);
/*
* Disabling the endpoint emptied the transmit FIFO, fill it
@@ -479,12 +542,14 @@ static int isp1760_udc_get_status(struct isp1760_udc *udc,
return -EINVAL;
}
- isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) | DC_EPDIR);
+ isp1760_udc_set(udc, DC_EPDIR);
+ isp1760_udc_write(udc, DC_ENDPIDX, 1);
+
isp1760_udc_write(udc, DC_BUFLEN, 2);
- writew(cpu_to_le16(status), udc->regs + DC_DATAPORT);
+ isp1760_udc_write_raw16(udc, ISP176x_DC_DATAPORT, status);
- isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+ isp1760_udc_set(udc, DC_DSEN);
dev_dbg(udc->isp->dev, "%s: status 0x%04x\n", __func__, status);
@@ -508,7 +573,8 @@ static int isp1760_udc_set_address(struct isp1760_udc *udc, u16 addr)
usb_gadget_set_state(&udc->gadget, addr ? USB_STATE_ADDRESS :
USB_STATE_DEFAULT);
- isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN | addr);
+ isp1760_udc_write(udc, DC_DEVADDR, addr);
+ isp1760_udc_set(udc, DC_DEVEN);
spin_lock(&udc->lock);
isp1760_udc_ctrl_send_status(&udc->ep[0], USB_DIR_OUT);
@@ -650,9 +716,9 @@ static void isp1760_ep0_setup(struct isp1760_udc *udc)
spin_lock(&udc->lock);
- isp1760_udc_write(udc, DC_EPINDEX, DC_EP0SETUP);
+ isp1760_udc_set(udc, DC_EP0SETUP);
- count = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK;
+ count = isp1760_udc_read(udc, DC_BUFLEN);
if (count != sizeof(req)) {
spin_unlock(&udc->lock);
@@ -663,8 +729,8 @@ static void isp1760_ep0_setup(struct isp1760_udc *udc)
return;
}
- req.data[0] = isp1760_udc_read(udc, DC_DATAPORT);
- req.data[1] = isp1760_udc_read(udc, DC_DATAPORT);
+ req.data[0] = isp1760_udc_read_raw(udc, ISP176x_DC_DATAPORT);
+ req.data[1] = isp1760_udc_read_raw(udc, ISP176x_DC_DATAPORT);
if (udc->ep0_state != ISP1760_CTRL_SETUP) {
spin_unlock(&udc->lock);
@@ -732,13 +798,13 @@ static int isp1760_ep_enable(struct usb_ep *ep,
switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_ISOC:
- type = DC_ENDPTYP_ISOC;
+ type = ISP176x_DC_ENDPTYP_ISOC;
break;
case USB_ENDPOINT_XFER_BULK:
- type = DC_ENDPTYP_BULK;
+ type = ISP176x_DC_ENDPTYP_BULK;
break;
case USB_ENDPOINT_XFER_INT:
- type = DC_ENDPTYP_INTERRUPT;
+ type = ISP176x_DC_ENDPTYP_INTERRUPT;
break;
case USB_ENDPOINT_XFER_CONTROL:
default:
@@ -755,10 +821,13 @@ static int isp1760_ep_enable(struct usb_ep *ep,
uep->halted = false;
uep->wedged = false;
- isp1760_udc_select_ep(uep);
- isp1760_udc_write(udc, DC_EPMAXPKTSZ, uep->maxpacket);
+ isp1760_udc_select_ep(udc, uep);
+
+ isp1760_udc_write(udc, DC_FFOSZ, uep->maxpacket);
isp1760_udc_write(udc, DC_BUFLEN, uep->maxpacket);
- isp1760_udc_write(udc, DC_EPTYPE, DC_EPENABLE | type);
+
+ isp1760_udc_write(udc, DC_ENDPTYP, type);
+ isp1760_udc_set(udc, DC_EPENABLE);
spin_unlock_irqrestore(&udc->lock, flags);
@@ -786,8 +855,9 @@ static int isp1760_ep_disable(struct usb_ep *ep)
uep->desc = NULL;
uep->maxpacket = 0;
- isp1760_udc_select_ep(uep);
- isp1760_udc_write(udc, DC_EPTYPE, 0);
+ isp1760_udc_select_ep(udc, uep);
+ isp1760_udc_clear(udc, DC_EPENABLE);
+ isp1760_udc_clear(udc, DC_ENDPTYP);
/* TODO Synchronize with the IRQ handler */
@@ -864,8 +934,8 @@ static int isp1760_ep_queue(struct usb_ep *ep, struct usb_request *_req,
case ISP1760_CTRL_DATA_OUT:
list_add_tail(&req->queue, &uep->queue);
- __isp1760_udc_select_ep(uep, USB_DIR_OUT);
- isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+ __isp1760_udc_select_ep(udc, uep, USB_DIR_OUT);
+ isp1760_udc_set(udc, DC_DSEN);
break;
case ISP1760_CTRL_STATUS:
@@ -1025,14 +1095,14 @@ static void isp1760_ep_fifo_flush(struct usb_ep *ep)
spin_lock_irqsave(&udc->lock, flags);
- isp1760_udc_select_ep(uep);
+ isp1760_udc_select_ep(udc, uep);
/*
* Set the CLBUF bit twice to flush both buffers in case double
* buffering is enabled.
*/
- isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
- isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+ isp1760_udc_set(udc, DC_CLBUF);
+ isp1760_udc_set(udc, DC_CLBUF);
spin_unlock_irqrestore(&udc->lock, flags);
}
@@ -1082,6 +1152,10 @@ static void isp1760_udc_disconnect(struct isp1760_udc *udc)
static void isp1760_udc_init_hw(struct isp1760_udc *udc)
{
+ u32 intconf = udc->is_isp1763 ? ISP1763_DC_INTCONF : ISP176x_DC_INTCONF;
+ u32 intena = udc->is_isp1763 ? ISP1763_DC_INTENABLE :
+ ISP176x_DC_INTENABLE;
+
/*
* The device controller currently shares its interrupt with the host
* controller, the DC_IRQ polarity and signaling mode are ignored. Set
@@ -1091,19 +1165,22 @@ static void isp1760_udc_init_hw(struct isp1760_udc *udc)
* ACK tokens only (and NYET for the out pipe). The default
* configuration also generates an interrupt on the first NACK token.
*/
- isp1760_udc_write(udc, DC_INTCONF, DC_CDBGMOD_ACK | DC_DDBGMODIN_ACK |
- DC_DDBGMODOUT_ACK_NYET);
-
- isp1760_udc_write(udc, DC_INTENABLE, DC_IEPRXTX(7) | DC_IEPRXTX(6) |
- DC_IEPRXTX(5) | DC_IEPRXTX(4) | DC_IEPRXTX(3) |
- DC_IEPRXTX(2) | DC_IEPRXTX(1) | DC_IEPRXTX(0) |
- DC_IEP0SETUP | DC_IEVBUS | DC_IERESM | DC_IESUSP |
- DC_IEHS_STA | DC_IEBRST);
+ isp1760_reg_write(udc->regs, intconf,
+ ISP176x_DC_CDBGMOD_ACK | ISP176x_DC_DDBGMODIN_ACK |
+ ISP176x_DC_DDBGMODOUT_ACK);
+
+ isp1760_reg_write(udc->regs, intena, DC_IEPRXTX(7) |
+ DC_IEPRXTX(6) | DC_IEPRXTX(5) | DC_IEPRXTX(4) |
+ DC_IEPRXTX(3) | DC_IEPRXTX(2) | DC_IEPRXTX(1) |
+ DC_IEPRXTX(0) | ISP176x_DC_IEP0SETUP |
+ ISP176x_DC_IEVBUS | ISP176x_DC_IERESM |
+ ISP176x_DC_IESUSP | ISP176x_DC_IEHS_STA |
+ ISP176x_DC_IEBRST);
if (udc->connected)
isp1760_set_pullup(udc->isp, true);
- isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN);
+ isp1760_udc_set(udc, DC_DEVEN);
}
static void isp1760_udc_reset(struct isp1760_udc *udc)
@@ -1152,7 +1229,7 @@ static int isp1760_udc_get_frame(struct usb_gadget *gadget)
{
struct isp1760_udc *udc = gadget_to_udc(gadget);
- return isp1760_udc_read(udc, DC_FRAMENUM) & ((1 << 11) - 1);
+ return isp1760_udc_read(udc, DC_FRAMENUM);
}
static int isp1760_udc_wakeup(struct usb_gadget *gadget)
@@ -1219,7 +1296,7 @@ static int isp1760_udc_start(struct usb_gadget *gadget,
usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED);
/* DMA isn't supported yet, don't enable the DMA clock. */
- isp1760_udc_write(udc, DC_MODE, DC_GLINTENA);
+ isp1760_udc_set(udc, DC_GLINTENA);
isp1760_udc_init_hw(udc);
@@ -1232,13 +1309,14 @@ static int isp1760_udc_start(struct usb_gadget *gadget,
static int isp1760_udc_stop(struct usb_gadget *gadget)
{
struct isp1760_udc *udc = gadget_to_udc(gadget);
+ u32 mode_reg = udc->is_isp1763 ? ISP1763_DC_MODE : ISP176x_DC_MODE;
unsigned long flags;
dev_dbg(udc->isp->dev, "%s\n", __func__);
del_timer_sync(&udc->vbus_timer);
- isp1760_udc_write(udc, DC_MODE, 0);
+ isp1760_reg_write(udc->regs, mode_reg, 0);
spin_lock_irqsave(&udc->lock, flags);
udc->driver = NULL;
@@ -1260,15 +1338,30 @@ static const struct usb_gadget_ops isp1760_udc_ops = {
* Interrupt Handling
*/
+static u32 isp1760_udc_irq_get_status(struct isp1760_udc *udc)
+{
+ u32 status;
+
+ if (udc->is_isp1763) {
+ status = isp1760_reg_read(udc->regs, ISP1763_DC_INTERRUPT)
+ & isp1760_reg_read(udc->regs, ISP1763_DC_INTENABLE);
+ isp1760_reg_write(udc->regs, ISP1763_DC_INTERRUPT, status);
+ } else {
+ status = isp1760_reg_read(udc->regs, ISP176x_DC_INTERRUPT)
+ & isp1760_reg_read(udc->regs, ISP176x_DC_INTENABLE);
+ isp1760_reg_write(udc->regs, ISP176x_DC_INTERRUPT, status);
+ }
+
+ return status;
+}
+
static irqreturn_t isp1760_udc_irq(int irq, void *dev)
{
struct isp1760_udc *udc = dev;
unsigned int i;
u32 status;
- status = isp1760_udc_read(udc, DC_INTERRUPT)
- & isp1760_udc_read(udc, DC_INTENABLE);
- isp1760_udc_write(udc, DC_INTERRUPT, status);
+ status = isp1760_udc_irq_get_status(udc);
if (status & DC_IEVBUS) {
dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__);
@@ -1313,7 +1406,7 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev)
dev_dbg(udc->isp->dev, "%s(SUSP)\n", __func__);
spin_lock(&udc->lock);
- if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT))
+ if (!isp1760_udc_is_set(udc, DC_VBUSSTAT))
isp1760_udc_disconnect(udc);
else
isp1760_udc_suspend(udc);
@@ -1335,7 +1428,7 @@ static void isp1760_udc_vbus_poll(struct timer_list *t)
spin_lock_irqsave(&udc->lock, flags);
- if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT))
+ if (!(isp1760_udc_is_set(udc, DC_VBUSSTAT)))
isp1760_udc_disconnect(udc);
else if (udc->gadget.state >= USB_STATE_POWERED)
mod_timer(&udc->vbus_timer,
@@ -1403,6 +1496,7 @@ static void isp1760_udc_init_eps(struct isp1760_udc *udc)
static int isp1760_udc_init(struct isp1760_udc *udc)
{
+ u32 mode_reg = udc->is_isp1763 ? ISP1763_DC_MODE : ISP176x_DC_MODE;
u16 scratch;
u32 chipid;
@@ -1413,7 +1507,8 @@ static int isp1760_udc_init(struct isp1760_udc *udc)
* and scratch register contents must match the expected values.
*/
isp1760_udc_write(udc, DC_SCRATCH, 0xbabe);
- chipid = isp1760_udc_read(udc, DC_CHIPID);
+ chipid = isp1760_udc_read(udc, DC_CHIP_ID_HIGH) << 16;
+ chipid |= isp1760_udc_read(udc, DC_CHIP_ID_LOW);
scratch = isp1760_udc_read(udc, DC_SCRATCH);
if (scratch != 0xbabe) {
@@ -1423,15 +1518,16 @@ static int isp1760_udc_init(struct isp1760_udc *udc)
return -ENODEV;
}
- if (chipid != 0x00011582 && chipid != 0x00158210) {
+ if (chipid != 0x00011582 && chipid != 0x00158210 &&
+ chipid != 0x00176320) {
dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid);
return -ENODEV;
}
/* Reset the device controller. */
- isp1760_udc_write(udc, DC_MODE, DC_SFRESET);
+ isp1760_udc_set(udc, DC_SFRESET);
usleep_range(10000, 11000);
- isp1760_udc_write(udc, DC_MODE, 0);
+ isp1760_reg_write(udc->regs, mode_reg, 0);
usleep_range(10000, 11000);
return 0;
@@ -1445,7 +1541,6 @@ int isp1760_udc_register(struct isp1760_device *isp, int irq,
udc->irq = -1;
udc->isp = isp;
- udc->regs = isp->regs;
spin_lock_init(&udc->lock);
timer_setup(&udc->vbus_timer, isp1760_udc_vbus_poll, 0);
diff --git a/drivers/usb/isp1760/isp1760-udc.h b/drivers/usb/isp1760/isp1760-udc.h
index d2df650d54e9..22044e86bc0e 100644
--- a/drivers/usb/isp1760/isp1760-udc.h
+++ b/drivers/usb/isp1760/isp1760-udc.h
@@ -2,10 +2,12 @@
/*
* Driver for the NXP ISP1761 device controller
*
+ * Copyright 2021 Linaro, Rui Miguel Silva
* Copyright 2014 Ideas on Board Oy
*
* Contacts:
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Rui Miguel Silva <rui.silva@linaro.org>
*/
#ifndef _ISP1760_UDC_H_
@@ -17,6 +19,8 @@
#include <linux/timer.h>
#include <linux/usb/gadget.h>
+#include "isp1760-regs.h"
+
struct isp1760_device;
struct isp1760_udc;
@@ -48,7 +52,7 @@ struct isp1760_ep {
* struct isp1760_udc - UDC state information
* irq: IRQ number
* irqname: IRQ name (as passed to request_irq)
- * regs: Base address of the UDC registers
+ * regs: regmap for UDC registers
* driver: Gadget driver
* gadget: Gadget device
* lock: Protects driver, vbus_timer, ep, ep0_*, DC_EPINDEX register
@@ -59,12 +63,13 @@ struct isp1760_ep {
* connected: Tracks gadget driver bus connection state
*/
struct isp1760_udc {
-#ifdef CONFIG_USB_ISP1761_UDC
struct isp1760_device *isp;
int irq;
char *irqname;
- void __iomem *regs;
+
+ struct regmap *regs;
+ struct regmap_field *fields[DC_FIELD_MAX];
struct usb_gadget_driver *driver;
struct usb_gadget gadget;
@@ -79,9 +84,9 @@ struct isp1760_udc {
u16 ep0_length;
bool connected;
+ bool is_isp1763;
unsigned int devstatus;
-#endif
};
#ifdef CONFIG_USB_ISP1761_UDC
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index 8a3d9c0c8d8b..e5a8fcdbb78e 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -2098,7 +2098,6 @@ more:{
} else
d += sprintf(d, " ..");
bytes_read += 1;
- continue;
}
goto more;
} else if (packet_bytes > 1) {
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index aef0a0bba25a..5546b868b08b 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -10,6 +10,7 @@
#ifndef __MTU3_H__
#define __MTU3_H__
+#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dmapool.h>
#include <linux/extcon.h>
@@ -21,6 +22,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
+#include <linux/usb/role.h>
struct mtu3;
struct mtu3_ep;
@@ -88,6 +90,8 @@ struct mtu3_request;
*/
#define EP0_RESPONSE_BUF 6
+#define BULK_CLKS_CNT 4
+
/* device operated link and speed got from DEVICE_CONF register */
enum mtu3_speed {
MTU3_SPEED_INACTIVE = 0,
@@ -192,13 +196,9 @@ struct mtu3_gpd_ring {
/**
* @vbus: vbus 5V used by host mode
* @edev: external connector used to detect vbus and iddig changes
-* @vbus_nb: notifier for vbus detection
-* @vbus_work : work of vbus detection notifier, used to avoid sleep in
-* notifier callback which is atomic context
-* @vbus_event : event of vbus detecion notifier
* @id_nb : notifier for iddig(idpin) detection
-* @id_work : work of iddig detection notifier
-* @id_event : event of iddig detecion notifier
+* @dr_work : work for drd mode switch, used to avoid sleep in atomic context
+* @desired_role : role desired to switch
* @role_sw : use USB Role Switch to support dual-role switch, can't use
* extcon at the same time, and extcon is deprecated.
* @role_sw_used : true when the USB Role Switch is used.
@@ -209,12 +209,9 @@ struct mtu3_gpd_ring {
struct otg_switch_mtk {
struct regulator *vbus;
struct extcon_dev *edev;
- struct notifier_block vbus_nb;
- struct work_struct vbus_work;
- unsigned long vbus_event;
struct notifier_block id_nb;
- struct work_struct id_work;
- unsigned long id_event;
+ struct work_struct dr_work;
+ enum usb_role desired_role;
struct usb_role_switch *role_sw;
bool role_sw_used;
bool is_u3_drd;
@@ -225,10 +222,6 @@ struct otg_switch_mtk {
* @mac_base: register base address of device MAC, exclude xHCI's
* @ippc_base: register base address of IP Power and Clock interface (IPPC)
* @vusb33: usb3.3V shared by device/host IP
- * @sys_clk: system clock of mtu3, shared by device/host IP
- * @ref_clk: reference clock
- * @mcu_clk: mcu_bus_ck clock for AHB bus etc
- * @dma_clk: dma_bus_ck clock for AXI bus etc
* @dr_mode: works in which mode:
* host only, device only or dual-role mode
* @u2_ports: number of usb2.0 host ports
@@ -250,10 +243,7 @@ struct ssusb_mtk {
int num_phys;
/* common power & clock */
struct regulator *vusb33;
- struct clk *sys_clk;
- struct clk *ref_clk;
- struct clk *mcu_clk;
- struct clk *dma_clk;
+ struct clk_bulk_data clks[BULK_CLKS_CNT];
/* otg */
struct otg_switch_mtk otg_switch;
enum usb_dr_mode dr_mode;
@@ -422,11 +412,9 @@ int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
int interval, int burst, int mult);
void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep);
void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set);
-void mtu3_ep0_setup(struct mtu3 *mtu);
void mtu3_start(struct mtu3 *mtu);
void mtu3_stop(struct mtu3 *mtu);
void mtu3_dev_on_off(struct mtu3 *mtu, int is_on);
-void mtu3_set_speed(struct mtu3 *mtu, enum usb_device_speed speed);
int mtu3_gadget_setup(struct mtu3 *mtu);
void mtu3_gadget_cleanup(struct mtu3 *mtu);
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index b3b459937566..562f4357831e 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -207,7 +207,7 @@ static void mtu3_intr_enable(struct mtu3 *mtu)
mtu3_writel(mbase, U3D_DEV_LINK_INTR_ENABLE, SSUSB_DEV_SPEED_CHG_INTR);
}
-void mtu3_set_speed(struct mtu3 *mtu, enum usb_device_speed speed)
+static void mtu3_set_speed(struct mtu3 *mtu, enum usb_device_speed speed)
{
void __iomem *mbase = mtu->mac_base;
@@ -334,6 +334,10 @@ void mtu3_start(struct mtu3 *mtu)
mtu3_readl(mbase, U3D_DEVICE_CONTROL));
mtu3_clrbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+ if (mtu->is_u3_ip)
+ mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN);
+
+ mtu3_clrbits(mtu->ippc_base, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN);
mtu3_csr_init(mtu);
mtu3_set_speed(mtu, mtu->speed);
@@ -356,6 +360,11 @@ void mtu3_stop(struct mtu3 *mtu)
mtu3_dev_on_off(mtu, 0);
mtu->is_active = 0;
+
+ if (mtu->is_u3_ip)
+ mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN);
+
+ mtu3_setbits(mtu->ippc_base, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN);
mtu3_setbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
}
@@ -536,7 +545,7 @@ static void get_ep_fifo_config(struct mtu3 *mtu)
rx_fifo->base, rx_fifo->limit);
}
-void mtu3_ep0_setup(struct mtu3 *mtu)
+static void mtu3_ep0_setup(struct mtu3 *mtu)
{
u32 maxpacket = mtu->g.ep0->maxpacket;
u32 csr;
@@ -921,16 +930,15 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb)
device_init_wakeup(dev, true);
+ /* power down device IP for power saving by default */
+ mtu3_stop(mtu);
+
ret = mtu3_gadget_setup(mtu);
if (ret) {
dev_err(dev, "mtu3 gadget init failed:%d\n", ret);
goto gadget_err;
}
- /* init as host mode, power down device IP for power saving */
- if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG)
- mtu3_stop(mtu);
-
ssusb_dev_debugfs_init(ssusb);
dev_dbg(dev, " %s() done...\n", __func__);
diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c
index 7537bfd651af..d27de647c86a 100644
--- a/drivers/usb/mtu3/mtu3_debugfs.c
+++ b/drivers/usb/mtu3/mtu3_debugfs.c
@@ -30,6 +30,7 @@ static const struct debugfs_reg32 mtu3_ippc_regs[] = {
dump_register(SSUSB_IP_PW_CTRL1),
dump_register(SSUSB_IP_PW_CTRL2),
dump_register(SSUSB_IP_PW_CTRL3),
+ dump_register(SSUSB_IP_PW_STS1),
dump_register(SSUSB_OTG_STS),
dump_register(SSUSB_IP_XHCI_CAP),
dump_register(SSUSB_IP_DEV_CAP),
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index 04f666e85731..318fbc618137 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -7,8 +7,6 @@
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
-#include <linux/usb/role.h>
-
#include "mtu3.h"
#include "mtu3_dr.h"
#include "mtu3_debug.h"
@@ -16,27 +14,9 @@
#define USB2_PORT 2
#define USB3_PORT 3
-enum mtu3_vbus_id_state {
- MTU3_ID_FLOAT = 1,
- MTU3_ID_GROUND,
- MTU3_VBUS_OFF,
- MTU3_VBUS_VALID,
-};
-
-static char *mailbox_state_string(enum mtu3_vbus_id_state state)
+static inline struct ssusb_mtk *otg_sx_to_ssusb(struct otg_switch_mtk *otg_sx)
{
- switch (state) {
- case MTU3_ID_FLOAT:
- return "ID_FLOAT";
- case MTU3_ID_GROUND:
- return "ID_GROUND";
- case MTU3_VBUS_OFF:
- return "VBUS_OFF";
- case MTU3_VBUS_VALID:
- return "VBUS_VALID";
- default:
- return "UNKNOWN";
- }
+ return container_of(otg_sx, struct ssusb_mtk, otg_switch);
}
static void toggle_opstate(struct ssusb_mtk *ssusb)
@@ -123,8 +103,7 @@ static void switch_port_to_device(struct ssusb_mtk *ssusb)
int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
{
- struct ssusb_mtk *ssusb =
- container_of(otg_sx, struct ssusb_mtk, otg_switch);
+ struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
struct regulator *vbus = otg_sx->vbus;
int ret;
@@ -147,99 +126,72 @@ int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
return 0;
}
-/*
- * switch to host: -> MTU3_VBUS_OFF --> MTU3_ID_GROUND
- * switch to device: -> MTU3_ID_FLOAT --> MTU3_VBUS_VALID
- */
-static void ssusb_set_mailbox(struct otg_switch_mtk *otg_sx,
- enum mtu3_vbus_id_state status)
+static void ssusb_mode_sw_work(struct work_struct *work)
{
- struct ssusb_mtk *ssusb =
- container_of(otg_sx, struct ssusb_mtk, otg_switch);
+ struct otg_switch_mtk *otg_sx =
+ container_of(work, struct otg_switch_mtk, dr_work);
+ struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
struct mtu3 *mtu = ssusb->u3d;
+ enum usb_role desired_role = otg_sx->desired_role;
+ enum usb_role current_role;
+
+ current_role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+
+ if (desired_role == USB_ROLE_NONE)
+ desired_role = USB_ROLE_HOST;
- dev_dbg(ssusb->dev, "mailbox %s\n", mailbox_state_string(status));
- mtu3_dbg_trace(ssusb->dev, "mailbox %s", mailbox_state_string(status));
+ if (current_role == desired_role)
+ return;
+
+ dev_dbg(ssusb->dev, "set role : %s\n", usb_role_string(desired_role));
+ mtu3_dbg_trace(ssusb->dev, "set role : %s", usb_role_string(desired_role));
- switch (status) {
- case MTU3_ID_GROUND:
+ switch (desired_role) {
+ case USB_ROLE_HOST:
+ ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
+ mtu3_stop(mtu);
switch_port_to_host(ssusb);
ssusb_set_vbus(otg_sx, 1);
ssusb->is_host = true;
break;
- case MTU3_ID_FLOAT:
+ case USB_ROLE_DEVICE:
+ ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE);
ssusb->is_host = false;
ssusb_set_vbus(otg_sx, 0);
switch_port_to_device(ssusb);
- break;
- case MTU3_VBUS_OFF:
- mtu3_stop(mtu);
- pm_relax(ssusb->dev);
- break;
- case MTU3_VBUS_VALID:
- /* avoid suspend when works as device */
- pm_stay_awake(ssusb->dev);
mtu3_start(mtu);
break;
+ case USB_ROLE_NONE:
default:
- dev_err(ssusb->dev, "invalid state\n");
+ dev_err(ssusb->dev, "invalid role\n");
}
}
-static void ssusb_id_work(struct work_struct *work)
+static void ssusb_set_mode(struct otg_switch_mtk *otg_sx, enum usb_role role)
{
- struct otg_switch_mtk *otg_sx =
- container_of(work, struct otg_switch_mtk, id_work);
+ struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
- if (otg_sx->id_event)
- ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
- else
- ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
-}
-
-static void ssusb_vbus_work(struct work_struct *work)
-{
- struct otg_switch_mtk *otg_sx =
- container_of(work, struct otg_switch_mtk, vbus_work);
+ if (ssusb->dr_mode != USB_DR_MODE_OTG)
+ return;
- if (otg_sx->vbus_event)
- ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
- else
- ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
+ otg_sx->desired_role = role;
+ queue_work(system_freezable_wq, &otg_sx->dr_work);
}
-/*
- * @ssusb_id_notifier is called in atomic context, but @ssusb_set_mailbox
- * may sleep, so use work queue here
- */
static int ssusb_id_notifier(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct otg_switch_mtk *otg_sx =
container_of(nb, struct otg_switch_mtk, id_nb);
- otg_sx->id_event = event;
- schedule_work(&otg_sx->id_work);
-
- return NOTIFY_DONE;
-}
-
-static int ssusb_vbus_notifier(struct notifier_block *nb,
- unsigned long event, void *ptr)
-{
- struct otg_switch_mtk *otg_sx =
- container_of(nb, struct otg_switch_mtk, vbus_nb);
-
- otg_sx->vbus_event = event;
- schedule_work(&otg_sx->vbus_work);
+ ssusb_set_mode(otg_sx, event ? USB_ROLE_HOST : USB_ROLE_DEVICE);
return NOTIFY_DONE;
}
static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
{
- struct ssusb_mtk *ssusb =
- container_of(otg_sx, struct ssusb_mtk, otg_switch);
+ struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
struct extcon_dev *edev = otg_sx->edev;
int ret;
@@ -247,14 +199,6 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
if (!edev)
return 0;
- otg_sx->vbus_nb.notifier_call = ssusb_vbus_notifier;
- ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB,
- &otg_sx->vbus_nb);
- if (ret < 0) {
- dev_err(ssusb->dev, "failed to register notifier for USB\n");
- return ret;
- }
-
otg_sx->id_nb.notifier_call = ssusb_id_notifier;
ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB_HOST,
&otg_sx->id_nb);
@@ -263,15 +207,12 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
return ret;
}
- dev_dbg(ssusb->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n",
- extcon_get_state(edev, EXTCON_USB),
- extcon_get_state(edev, EXTCON_USB_HOST));
+ ret = extcon_get_state(edev, EXTCON_USB_HOST);
+ dev_dbg(ssusb->dev, "EXTCON_USB_HOST: %d\n", ret);
/* default as host, switch to device mode if needed */
- if (extcon_get_state(edev, EXTCON_USB_HOST) == false)
- ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
- if (extcon_get_state(edev, EXTCON_USB) == true)
- ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
+ if (!ret)
+ ssusb_set_mode(otg_sx, USB_ROLE_DEVICE);
return 0;
}
@@ -286,15 +227,7 @@ void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host)
{
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
- if (to_host) {
- ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
- ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
- ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
- } else {
- ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE);
- ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
- ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
- }
+ ssusb_set_mode(otg_sx, to_host ? USB_ROLE_HOST : USB_ROLE_DEVICE);
}
void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
@@ -323,13 +256,9 @@ void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
static int ssusb_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
{
struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw);
- bool to_host = false;
-
- if (role == USB_ROLE_HOST)
- to_host = true;
+ struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
- if (to_host ^ ssusb->is_host)
- ssusb_mode_switch(ssusb, to_host);
+ ssusb_set_mode(otg_sx, role);
return 0;
}
@@ -337,18 +266,14 @@ static int ssusb_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
static enum usb_role ssusb_role_sw_get(struct usb_role_switch *sw)
{
struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw);
- enum usb_role role;
-
- role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
- return role;
+ return ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
}
static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx)
{
struct usb_role_switch_desc role_sx_desc = { 0 };
- struct ssusb_mtk *ssusb =
- container_of(otg_sx, struct ssusb_mtk, otg_switch);
+ struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
if (!otg_sx->role_sw_used)
return 0;
@@ -367,8 +292,7 @@ int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
int ret = 0;
- INIT_WORK(&otg_sx->id_work, ssusb_id_work);
- INIT_WORK(&otg_sx->vbus_work, ssusb_vbus_work);
+ INIT_WORK(&otg_sx->dr_work, ssusb_mode_sw_work);
if (otg_sx->manual_drd_enabled)
ssusb_dr_debugfs_init(ssusb);
@@ -384,7 +308,6 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
{
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
- cancel_work_sync(&otg_sx->id_work);
- cancel_work_sync(&otg_sx->vbus_work);
+ cancel_work_sync(&otg_sx->dr_work);
usb_role_switch_unregister(otg_sx->role_sw);
}
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index 38f17d66d5bc..5e21ba05ebf0 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -577,7 +577,7 @@ mtu3_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed)
dev_dbg(mtu->dev, "%s %s\n", __func__, usb_speed_string(speed));
spin_lock_irqsave(&mtu->lock, flags);
- mtu3_set_speed(mtu, speed);
+ mtu->speed = speed;
spin_unlock_irqrestore(&mtu->lock, flags);
}
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
index 0a8cd446cf1b..93a1a4c11e1e 100644
--- a/drivers/usb/mtu3/mtu3_host.c
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -213,8 +213,6 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
static void ssusb_host_setup(struct ssusb_mtk *ssusb)
{
- struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
-
host_ports_num_get(ssusb);
/*
@@ -222,9 +220,7 @@ static void ssusb_host_setup(struct ssusb_mtk *ssusb)
* if support OTG, gadget driver will switch port0 to device mode
*/
ssusb_host_enable(ssusb);
-
- if (otg_sx->manual_drd_enabled)
- ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
+ ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
/* if port0 supports dual-role, works as host mode by default */
ssusb_set_vbus(&ssusb->otg_switch, 1);
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index 7786a95a874e..c0615f6e5cce 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -5,7 +5,6 @@
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
-#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -101,54 +100,6 @@ static void ssusb_phy_power_off(struct ssusb_mtk *ssusb)
phy_power_off(ssusb->phys[i]);
}
-static int ssusb_clks_enable(struct ssusb_mtk *ssusb)
-{
- int ret;
-
- ret = clk_prepare_enable(ssusb->sys_clk);
- if (ret) {
- dev_err(ssusb->dev, "failed to enable sys_clk\n");
- goto sys_clk_err;
- }
-
- ret = clk_prepare_enable(ssusb->ref_clk);
- if (ret) {
- dev_err(ssusb->dev, "failed to enable ref_clk\n");
- goto ref_clk_err;
- }
-
- ret = clk_prepare_enable(ssusb->mcu_clk);
- if (ret) {
- dev_err(ssusb->dev, "failed to enable mcu_clk\n");
- goto mcu_clk_err;
- }
-
- ret = clk_prepare_enable(ssusb->dma_clk);
- if (ret) {
- dev_err(ssusb->dev, "failed to enable dma_clk\n");
- goto dma_clk_err;
- }
-
- return 0;
-
-dma_clk_err:
- clk_disable_unprepare(ssusb->mcu_clk);
-mcu_clk_err:
- clk_disable_unprepare(ssusb->ref_clk);
-ref_clk_err:
- clk_disable_unprepare(ssusb->sys_clk);
-sys_clk_err:
- return ret;
-}
-
-static void ssusb_clks_disable(struct ssusb_mtk *ssusb)
-{
- clk_disable_unprepare(ssusb->dma_clk);
- clk_disable_unprepare(ssusb->mcu_clk);
- clk_disable_unprepare(ssusb->ref_clk);
- clk_disable_unprepare(ssusb->sys_clk);
-}
-
static int ssusb_rscs_init(struct ssusb_mtk *ssusb)
{
int ret = 0;
@@ -159,7 +110,7 @@ static int ssusb_rscs_init(struct ssusb_mtk *ssusb)
goto vusb33_err;
}
- ret = ssusb_clks_enable(ssusb);
+ ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, ssusb->clks);
if (ret)
goto clks_err;
@@ -180,7 +131,7 @@ static int ssusb_rscs_init(struct ssusb_mtk *ssusb)
phy_err:
ssusb_phy_exit(ssusb);
phy_init_err:
- ssusb_clks_disable(ssusb);
+ clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
clks_err:
regulator_disable(ssusb->vusb33);
vusb33_err:
@@ -189,7 +140,7 @@ vusb33_err:
static void ssusb_rscs_exit(struct ssusb_mtk *ssusb)
{
- ssusb_clks_disable(ssusb);
+ clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
regulator_disable(ssusb->vusb33);
ssusb_phy_power_off(ssusb);
ssusb_phy_exit(ssusb);
@@ -215,6 +166,7 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
{
struct device_node *node = pdev->dev.of_node;
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+ struct clk_bulk_data *clks = ssusb->clks;
struct device *dev = &pdev->dev;
int i;
int ret;
@@ -225,23 +177,13 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
return PTR_ERR(ssusb->vusb33);
}
- ssusb->sys_clk = devm_clk_get(dev, "sys_ck");
- if (IS_ERR(ssusb->sys_clk)) {
- dev_err(dev, "failed to get sys clock\n");
- return PTR_ERR(ssusb->sys_clk);
- }
-
- ssusb->ref_clk = devm_clk_get_optional(dev, "ref_ck");
- if (IS_ERR(ssusb->ref_clk))
- return PTR_ERR(ssusb->ref_clk);
-
- ssusb->mcu_clk = devm_clk_get_optional(dev, "mcu_ck");
- if (IS_ERR(ssusb->mcu_clk))
- return PTR_ERR(ssusb->mcu_clk);
-
- ssusb->dma_clk = devm_clk_get_optional(dev, "dma_ck");
- if (IS_ERR(ssusb->dma_clk))
- return PTR_ERR(ssusb->dma_clk);
+ clks[0].id = "sys_ck";
+ clks[1].id = "ref_ck";
+ clks[2].id = "mcu_ck";
+ clks[3].id = "dma_ck";
+ ret = devm_clk_bulk_get_optional(dev, BULK_CLKS_CNT, clks);
+ if (ret)
+ return ret;
ssusb->num_phys = of_count_phandle_with_args(node,
"phys", "#phy-cells");
@@ -299,11 +241,14 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
of_property_read_bool(node, "enable-manual-drd");
otg_sx->role_sw_used = of_property_read_bool(node, "usb-role-switch");
- if (!otg_sx->role_sw_used && of_property_read_bool(node, "extcon")) {
+ if (otg_sx->role_sw_used || otg_sx->manual_drd_enabled)
+ goto out;
+
+ if (of_property_read_bool(node, "extcon")) {
otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0);
if (IS_ERR(otg_sx->edev)) {
- dev_err(ssusb->dev, "couldn't get extcon device\n");
- return PTR_ERR(otg_sx->edev);
+ return dev_err_probe(dev, PTR_ERR(otg_sx->edev),
+ "couldn't get extcon device\n");
}
}
@@ -461,7 +406,7 @@ static int __maybe_unused mtu3_suspend(struct device *dev)
ssusb_host_disable(ssusb, true);
ssusb_phy_power_off(ssusb);
- ssusb_clks_disable(ssusb);
+ clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
ssusb_wakeup_set(ssusb, true);
return 0;
@@ -478,7 +423,7 @@ static int __maybe_unused mtu3_resume(struct device *dev)
return 0;
ssusb_wakeup_set(ssusb, false);
- ret = ssusb_clks_enable(ssusb);
+ ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, ssusb->clks);
if (ret)
goto clks_err;
@@ -491,7 +436,7 @@ static int __maybe_unused mtu3_resume(struct device *dev)
return 0;
phy_err:
- ssusb_clks_disable(ssusb);
+ clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
clks_err:
return ret;
}
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 4c8f0112481f..f7b1d5993f8c 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -480,9 +480,7 @@ int musb_set_host(struct musb *musb)
devctl = musb_read_devctl(musb);
if (!(devctl & MUSB_DEVCTL_BDEVICE)) {
- dev_info(musb->controller,
- "%s: already in host mode: %02x\n",
- __func__, devctl);
+ trace_musb_state(musb, devctl, "Already in host mode");
goto init_data;
}
@@ -499,6 +497,9 @@ int musb_set_host(struct musb *musb)
return error;
}
+ devctl = musb_read_devctl(musb);
+ trace_musb_state(musb, devctl, "Host mode set");
+
init_data:
musb->is_active = 1;
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
@@ -526,10 +527,7 @@ int musb_set_peripheral(struct musb *musb)
devctl = musb_read_devctl(musb);
if (devctl & MUSB_DEVCTL_BDEVICE) {
- dev_info(musb->controller,
- "%s: already in peripheral mode: %02x\n",
- __func__, devctl);
-
+ trace_musb_state(musb, devctl, "Already in peripheral mode");
goto init_data;
}
@@ -546,6 +544,9 @@ int musb_set_peripheral(struct musb *musb)
return error;
}
+ devctl = musb_read_devctl(musb);
+ trace_musb_state(musb, devctl, "Peripheral mode set");
+
init_data:
musb->is_active = 0;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
@@ -1984,6 +1985,21 @@ ATTRIBUTE_GROUPS(musb);
#define MUSB_QUIRK_A_DISCONNECT_19 ((3 << MUSB_DEVCTL_VBUS_SHIFT) | \
MUSB_DEVCTL_SESSION)
+static bool musb_state_needs_recheck(struct musb *musb, u8 devctl,
+ const char *desc)
+{
+ if (musb->quirk_retries && !musb->flush_irq_work) {
+ trace_musb_state(musb, devctl, desc);
+ schedule_delayed_work(&musb->irq_work,
+ msecs_to_jiffies(1000));
+ musb->quirk_retries--;
+
+ return true;
+ }
+
+ return false;
+}
+
/*
* Check the musb devctl session bit to determine if we want to
* allow PM runtime for the device. In general, we want to keep things
@@ -2004,35 +2020,21 @@ static void musb_pm_runtime_check_session(struct musb *musb)
MUSB_DEVCTL_HR;
switch (devctl & ~s) {
case MUSB_QUIRK_B_DISCONNECT_99:
- if (musb->quirk_retries && !musb->flush_irq_work) {
- musb_dbg(musb, "Poll devctl in case of suspend after disconnect\n");
- schedule_delayed_work(&musb->irq_work,
- msecs_to_jiffies(1000));
- musb->quirk_retries--;
- }
+ musb_state_needs_recheck(musb, devctl,
+ "Poll devctl in case of suspend after disconnect");
break;
case MUSB_QUIRK_B_INVALID_VBUS_91:
- if (musb->quirk_retries && !musb->flush_irq_work) {
- musb_dbg(musb,
- "Poll devctl on invalid vbus, assume no session");
- schedule_delayed_work(&musb->irq_work,
- msecs_to_jiffies(1000));
- musb->quirk_retries--;
+ if (musb_state_needs_recheck(musb, devctl,
+ "Poll devctl on invalid vbus, assume no session"))
return;
- }
fallthrough;
case MUSB_QUIRK_A_DISCONNECT_19:
- if (musb->quirk_retries && !musb->flush_irq_work) {
- musb_dbg(musb,
- "Poll devctl on possible host mode disconnect");
- schedule_delayed_work(&musb->irq_work,
- msecs_to_jiffies(1000));
- musb->quirk_retries--;
+ if (musb_state_needs_recheck(musb, devctl,
+ "Poll devctl on possible host mode disconnect"))
return;
- }
if (!musb->session)
break;
- musb_dbg(musb, "Allow PM on possible host mode disconnect");
+ trace_musb_state(musb, devctl, "Allow PM on possible host mode disconnect");
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
musb->session = false;
@@ -2048,14 +2050,23 @@ static void musb_pm_runtime_check_session(struct musb *musb)
/* Block PM or allow PM? */
if (s) {
- musb_dbg(musb, "Block PM on active session: %02x", devctl);
+ trace_musb_state(musb, devctl, "Block PM on active session");
error = pm_runtime_get_sync(musb->controller);
if (error < 0)
dev_err(musb->controller, "Could not enable: %i\n",
error);
musb->quirk_retries = 3;
+
+ /*
+ * We can get a spurious MUSB_INTR_SESSREQ interrupt on start-up
+ * in B-peripheral mode with nothing connected and the session
+ * bit clears silently. Check status again in 3 seconds.
+ */
+ if (devctl & MUSB_DEVCTL_BDEVICE)
+ schedule_delayed_work(&musb->irq_work,
+ msecs_to_jiffies(3000));
} else {
- musb_dbg(musb, "Allow PM with no session: %02x", devctl);
+ trace_musb_state(musb, devctl, "Allow PM with no session");
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
}
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index ef374d4dd94a..98c0f4c1bffd 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -611,7 +611,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
* mode 0 only. So we do not get endpoint interrupts due to DMA
* completion. We only get interrupts from DMA controller.
*
- * We could operate in DMA mode 1 if we knew the size of the tranfer
+ * We could operate in DMA mode 1 if we knew the size of the transfer
* in advance. For mass storage class, request->length = what the host
* sends, so that'd work. But for pretty much everything else,
* request->length is routinely more than what the host sends. For
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 30c5e7de0761..9ff7d891b4b7 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -563,10 +563,9 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, u8 epnum)
ep->rx_reinit = 0;
}
-static void musb_tx_dma_set_mode_mentor(struct dma_controller *dma,
- struct musb_hw_ep *hw_ep, struct musb_qh *qh,
- struct urb *urb, u32 offset,
- u32 *length, u8 *mode)
+static void musb_tx_dma_set_mode_mentor(struct musb_hw_ep *hw_ep,
+ struct musb_qh *qh,
+ u32 *length, u8 *mode)
{
struct dma_channel *channel = hw_ep->tx_channel;
void __iomem *epio = hw_ep->regs;
@@ -602,12 +601,8 @@ static void musb_tx_dma_set_mode_mentor(struct dma_controller *dma,
musb_writew(epio, MUSB_TXCSR, csr);
}
-static void musb_tx_dma_set_mode_cppi_tusb(struct dma_controller *dma,
- struct musb_hw_ep *hw_ep,
- struct musb_qh *qh,
+static void musb_tx_dma_set_mode_cppi_tusb(struct musb_hw_ep *hw_ep,
struct urb *urb,
- u32 offset,
- u32 *length,
u8 *mode)
{
struct dma_channel *channel = hw_ep->tx_channel;
@@ -630,11 +625,10 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
u8 mode;
if (musb_dma_inventra(hw_ep->musb) || musb_dma_ux500(hw_ep->musb))
- musb_tx_dma_set_mode_mentor(dma, hw_ep, qh, urb, offset,
+ musb_tx_dma_set_mode_mentor(hw_ep, qh,
&length, &mode);
else if (is_cppi_enabled(hw_ep->musb) || tusb_dma_omap(hw_ep->musb))
- musb_tx_dma_set_mode_cppi_tusb(dma, hw_ep, qh, urb, offset,
- &length, &mode);
+ musb_tx_dma_set_mode_cppi_tusb(hw_ep, urb, &mode);
else
return false;
diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h
index 4804d4d85c15..63298bd233e0 100644
--- a/drivers/usb/musb/musb_host.h
+++ b/drivers/usb/musb/musb_host.h
@@ -61,10 +61,6 @@ extern void musb_host_tx(struct musb *, u8);
extern void musb_host_rx(struct musb *, u8);
extern void musb_root_disconnect(struct musb *musb);
extern void musb_host_free(struct musb *);
-extern void musb_host_cleanup(struct musb *);
-extern void musb_host_tx(struct musb *, u8);
-extern void musb_host_rx(struct musb *, u8);
-extern void musb_root_disconnect(struct musb *musb);
extern void musb_host_resume_root_hub(struct musb *musb);
extern void musb_host_poke_root_hub(struct musb *musb);
extern int musb_port_suspend(struct musb *musb, bool do_suspend);
diff --git a/drivers/usb/musb/musb_trace.h b/drivers/usb/musb/musb_trace.h
index 380ebc77eab1..ec28b5716796 100644
--- a/drivers/usb/musb/musb_trace.h
+++ b/drivers/usb/musb/musb_trace.h
@@ -37,6 +37,23 @@ TRACE_EVENT(musb_log,
TP_printk("%s: %s", __get_str(name), __get_str(msg))
);
+TRACE_EVENT(musb_state,
+ TP_PROTO(struct musb *musb, u8 devctl, const char *desc),
+ TP_ARGS(musb, devctl, desc),
+ TP_STRUCT__entry(
+ __string(name, dev_name(musb->controller))
+ __field(u8, devctl)
+ __string(desc, desc)
+ ),
+ TP_fast_assign(
+ __assign_str(name, dev_name(musb->controller));
+ __entry->devctl = devctl;
+ __assign_str(desc, desc);
+ ),
+ TP_printk("%s: devctl: %02x %s", __get_str(name), __entry->devctl,
+ __get_str(desc))
+);
+
DECLARE_EVENT_CLASS(musb_regb,
TP_PROTO(void *caller, const void __iomem *addr,
unsigned int offset, u8 data),
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 4232f1ce3fbf..640a46f0d118 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -33,6 +33,8 @@ struct omap2430_glue {
enum musb_vbus_id_status status;
struct work_struct omap_musb_mailbox_work;
struct device *control_otghs;
+ unsigned int is_runtime_suspended:1;
+ unsigned int needs_resume:1;
};
#define glue_to_musb(g) platform_get_drvdata(g->musb)
@@ -459,6 +461,8 @@ static int omap2430_runtime_suspend(struct device *dev)
phy_power_off(musb->phy);
phy_exit(musb->phy);
+ glue->is_runtime_suspended = 1;
+
return 0;
}
@@ -480,12 +484,40 @@ static int omap2430_runtime_resume(struct device *dev)
/* Wait for musb to get oriented. Otherwise we can get babble */
usleep_range(200000, 250000);
+ glue->is_runtime_suspended = 0;
+
return 0;
}
+static int omap2430_suspend(struct device *dev)
+{
+ struct omap2430_glue *glue = dev_get_drvdata(dev);
+
+ if (glue->is_runtime_suspended)
+ return 0;
+
+ glue->needs_resume = 1;
+
+ return omap2430_runtime_suspend(dev);
+}
+
+static int omap2430_resume(struct device *dev)
+{
+ struct omap2430_glue *glue = dev_get_drvdata(dev);
+
+ if (!glue->needs_resume)
+ return 0;
+
+ glue->needs_resume = 0;
+
+ return omap2430_runtime_resume(dev);
+}
+
static const struct dev_pm_ops omap2430_pm_ops = {
.runtime_suspend = omap2430_runtime_suspend,
.runtime_resume = omap2430_runtime_resume,
+ .suspend = omap2430_suspend,
+ .resume = omap2430_resume,
};
#define DEV_PM_OPS (&omap2430_pm_ops)
diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c
index 02bb7ddd4bd6..f3e9b3b6ac3e 100644
--- a/drivers/usb/phy/phy-isp1301-omap.c
+++ b/drivers/usb/phy/phy-isp1301-omap.c
@@ -555,7 +555,7 @@ pullup:
case OTG_STATE_A_PERIPHERAL:
if (otg_ctrl & OTG_PULLUP)
goto pullup;
- /* FALLTHROUGH */
+ fallthrough;
// case OTG_STATE_B_WAIT_ACON:
default:
pulldown:
diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c
index 6cf6fbd39237..ad3d57f1c273 100644
--- a/drivers/usb/phy/phy-isp1301.c
+++ b/drivers/usb/phy/phy-isp1301.c
@@ -142,24 +142,17 @@ static struct i2c_driver isp1301_driver = {
module_i2c_driver(isp1301_driver);
-static int match(struct device *dev, const void *data)
-{
- const struct device_node *node = (const struct device_node *)data;
- return (dev->of_node == node) &&
- (dev->driver == &isp1301_driver.driver);
-}
-
struct i2c_client *isp1301_get_client(struct device_node *node)
{
- if (node) { /* reference of ISP1301 I2C node via DT */
- struct device *dev = bus_find_device(&i2c_bus_type, NULL,
- node, match);
- if (!dev)
- return NULL;
- return to_i2c_client(dev);
- } else { /* non-DT: only one ISP1301 chip supported */
- return isp1301_i2c_client;
- }
+ struct i2c_client *client;
+
+ /* reference of ISP1301 I2C node via DT */
+ client = of_find_i2c_device_by_node(node);
+ if (client)
+ return client;
+
+ /* non-DT: only one ISP1301 chip supported */
+ return isp1301_i2c_client;
}
EXPORT_SYMBOL_GPL(isp1301_get_client);
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index a48452a6172b..c0f432d509aa 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -58,12 +58,12 @@
#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
#define USB_PHY_VBUS_SENSORS 0x404
-#define B_SESS_VLD_WAKEUP_EN BIT(6)
-#define B_VBUS_VLD_WAKEUP_EN BIT(14)
+#define B_SESS_VLD_WAKEUP_EN BIT(14)
#define A_SESS_VLD_WAKEUP_EN BIT(22)
#define A_VBUS_VLD_WAKEUP_EN BIT(30)
#define USB_PHY_VBUS_WAKEUP_ID 0x408
+#define VBUS_WAKEUP_STS BIT(10)
#define VBUS_WAKEUP_WAKEUP_EN BIT(30)
#define USB1_LEGACY_CTRL 0x410
@@ -544,7 +544,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
val = readl_relaxed(base + USB_PHY_VBUS_SENSORS);
val &= ~(A_VBUS_VLD_WAKEUP_EN | A_SESS_VLD_WAKEUP_EN);
- val &= ~(B_VBUS_VLD_WAKEUP_EN | B_SESS_VLD_WAKEUP_EN);
+ val &= ~(B_SESS_VLD_WAKEUP_EN);
writel_relaxed(val, base + USB_PHY_VBUS_SENSORS);
val = readl_relaxed(base + UTMIP_BAT_CHRG_CFG0);
@@ -642,6 +642,15 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy)
void __iomem *base = phy->regs;
u32 val;
+ /*
+ * Give hardware time to settle down after VBUS disconnection,
+ * otherwise PHY will immediately wake up from suspend.
+ */
+ if (phy->wakeup_enabled && phy->mode != USB_DR_MODE_HOST)
+ readl_relaxed_poll_timeout(base + USB_PHY_VBUS_WAKEUP_ID,
+ val, !(val & VBUS_WAKEUP_STS),
+ 5000, 100000);
+
utmi_phy_clk_disable(phy);
/* PHY won't resume if reset is asserted */
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index b47285f023cf..83ed5089475a 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -42,6 +42,12 @@ static const char *const usb_chger_type[] = {
[ACA_TYPE] = "USB_CHARGER_ACA_TYPE",
};
+static const char *const usb_chger_state[] = {
+ [USB_CHARGER_DEFAULT] = "USB_CHARGER_DEFAULT",
+ [USB_CHARGER_PRESENT] = "USB_CHARGER_PRESENT",
+ [USB_CHARGER_ABSENT] = "USB_CHARGER_ABSENT",
+};
+
static struct usb_phy *__usb_find_phy(struct list_head *list,
enum usb_phy_type type)
{
@@ -74,6 +80,18 @@ static struct usb_phy *__of_usb_find_phy(struct device_node *node)
return ERR_PTR(-EPROBE_DEFER);
}
+static struct usb_phy *__device_to_usb_phy(struct device *dev)
+{
+ struct usb_phy *usb_phy;
+
+ list_for_each_entry(usb_phy, &phy_list, head) {
+ if (usb_phy->dev == dev)
+ break;
+ }
+
+ return usb_phy;
+}
+
static void usb_phy_set_default_current(struct usb_phy *usb_phy)
{
usb_phy->chg_cur.sdp_min = DEFAULT_SDP_CUR_MIN;
@@ -105,9 +123,6 @@ static void usb_phy_set_default_current(struct usb_phy *usb_phy)
static void usb_phy_notify_charger_work(struct work_struct *work)
{
struct usb_phy *usb_phy = container_of(work, struct usb_phy, chg_work);
- char uchger_state[50] = { 0 };
- char uchger_type[50] = { 0 };
- char *envp[] = { uchger_state, uchger_type, NULL };
unsigned int min, max;
switch (usb_phy->chg_state) {
@@ -115,15 +130,11 @@ static void usb_phy_notify_charger_work(struct work_struct *work)
usb_phy_get_charger_current(usb_phy, &min, &max);
atomic_notifier_call_chain(&usb_phy->notifier, max, usb_phy);
- snprintf(uchger_state, ARRAY_SIZE(uchger_state),
- "USB_CHARGER_STATE=%s", "USB_CHARGER_PRESENT");
break;
case USB_CHARGER_ABSENT:
usb_phy_set_default_current(usb_phy);
atomic_notifier_call_chain(&usb_phy->notifier, 0, usb_phy);
- snprintf(uchger_state, ARRAY_SIZE(uchger_state),
- "USB_CHARGER_STATE=%s", "USB_CHARGER_ABSENT");
break;
default:
dev_warn(usb_phy->dev, "Unknown USB charger state: %d\n",
@@ -131,9 +142,30 @@ static void usb_phy_notify_charger_work(struct work_struct *work)
return;
}
+ kobject_uevent(&usb_phy->dev->kobj, KOBJ_CHANGE);
+}
+
+static int usb_phy_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct usb_phy *usb_phy;
+ char uchger_state[50] = { 0 };
+ char uchger_type[50] = { 0 };
+
+ usb_phy = __device_to_usb_phy(dev);
+
+ snprintf(uchger_state, ARRAY_SIZE(uchger_state),
+ "USB_CHARGER_STATE=%s", usb_chger_state[usb_phy->chg_state]);
+
snprintf(uchger_type, ARRAY_SIZE(uchger_type),
"USB_CHARGER_TYPE=%s", usb_chger_type[usb_phy->chg_type]);
- kobject_uevent_env(&usb_phy->dev->kobj, KOBJ_CHANGE, envp);
+
+ if (add_uevent_var(env, uchger_state))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, uchger_type))
+ return -ENOMEM;
+
+ return 0;
}
static void __usb_phy_get_charger_type(struct usb_phy *usb_phy)
@@ -661,6 +693,11 @@ out:
}
EXPORT_SYMBOL_GPL(usb_add_phy);
+static struct device_type usb_phy_dev_type = {
+ .name = "usb_phy",
+ .uevent = usb_phy_uevent,
+};
+
/**
* usb_add_phy_dev - declare the USB PHY
* @x: the USB phy to be used; or NULL
@@ -684,6 +721,8 @@ int usb_add_phy_dev(struct usb_phy *x)
if (ret)
return ret;
+ x->dev->type = &usb_phy_dev_type;
+
ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier);
spin_lock_irqsave(&phy_lock, flags);
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c
index 33b637d0d8d9..dfaed7eee94f 100644
--- a/drivers/usb/roles/class.c
+++ b/drivers/usb/roles/class.c
@@ -214,6 +214,15 @@ static const char * const usb_roles[] = {
[USB_ROLE_DEVICE] = "device",
};
+const char *usb_role_string(enum usb_role role)
+{
+ if (role < 0 || role >= ARRAY_SIZE(usb_roles))
+ return "unknown";
+
+ return usb_roles[role];
+}
+EXPORT_SYMBOL_GPL(usb_role_string);
+
static ssize_t
role_show(struct device *dev, struct device_attribute *attr, char *buf)
{
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index fcb812bc832c..09b845d0da41 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -247,9 +247,9 @@ struct cp210x_serial_private {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gc;
bool gpio_registered;
- u8 gpio_pushpull;
- u8 gpio_altfunc;
- u8 gpio_input;
+ u16 gpio_pushpull;
+ u16 gpio_altfunc;
+ u16 gpio_input;
#endif
u8 partnum;
u32 fw_version;
@@ -534,6 +534,48 @@ struct cp210x_single_port_config {
#define CP2104_GPIO1_RXLED_MODE BIT(1)
#define CP2104_GPIO2_RS485_MODE BIT(2)
+struct cp210x_quad_port_state {
+ __le16 gpio_mode_pb0;
+ __le16 gpio_mode_pb1;
+ __le16 gpio_mode_pb2;
+ __le16 gpio_mode_pb3;
+ __le16 gpio_mode_pb4;
+
+ __le16 gpio_lowpower_pb0;
+ __le16 gpio_lowpower_pb1;
+ __le16 gpio_lowpower_pb2;
+ __le16 gpio_lowpower_pb3;
+ __le16 gpio_lowpower_pb4;
+
+ __le16 gpio_latch_pb0;
+ __le16 gpio_latch_pb1;
+ __le16 gpio_latch_pb2;
+ __le16 gpio_latch_pb3;
+ __le16 gpio_latch_pb4;
+};
+
+/*
+ * CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0x49 bytes
+ * on a CP2108 chip.
+ *
+ * See https://www.silabs.com/documents/public/application-notes/an978-cp210x-usb-to-uart-api-specification.pdf
+ */
+struct cp210x_quad_port_config {
+ struct cp210x_quad_port_state reset_state;
+ struct cp210x_quad_port_state suspend_state;
+ u8 ipdelay_ifc[4];
+ u8 enhancedfxn_ifc[4];
+ u8 enhancedfxn_device;
+ u8 extclkfreq[4];
+} __packed;
+
+#define CP2108_EF_IFC_GPIO_TXLED 0x01
+#define CP2108_EF_IFC_GPIO_RXLED 0x02
+#define CP2108_EF_IFC_GPIO_RS485 0x04
+#define CP2108_EF_IFC_GPIO_RS485_LOGIC 0x08
+#define CP2108_EF_IFC_GPIO_CLOCK 0x10
+#define CP2108_EF_IFC_DYNAMIC_SUSPEND 0x40
+
/* CP2102N configuration array indices */
#define CP210X_2NCONFIG_CONFIG_VERSION_IDX 2
#define CP210X_2NCONFIG_GPIO_MODE_IDX 581
@@ -546,13 +588,25 @@ struct cp210x_single_port_config {
#define CP2102N_QFN20_GPIO1_RS485_MODE BIT(4)
#define CP2102N_QFN20_GPIO0_CLK_MODE BIT(6)
-/* CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x2 bytes. */
+/*
+ * CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x02 bytes
+ * for CP2102N, CP2103, CP2104 and CP2105.
+ */
struct cp210x_gpio_write {
u8 mask;
u8 state;
};
/*
+ * CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x04 bytes
+ * for CP2108.
+ */
+struct cp210x_gpio_write16 {
+ __le16 mask;
+ __le16 state;
+};
+
+/*
* Helper to get interface number when we only have struct usb_serial.
*/
static u8 cp210x_interface_num(struct usb_serial *serial)
@@ -1434,52 +1488,84 @@ static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct usb_serial *serial = gpiochip_get_data(gc);
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
- u8 req_type = REQTYPE_DEVICE_TO_HOST;
+ u8 req_type;
+ u16 mask;
int result;
- u8 buf;
-
- if (priv->partnum == CP210X_PARTNUM_CP2105)
- req_type = REQTYPE_INTERFACE_TO_HOST;
+ int len;
result = usb_autopm_get_interface(serial->interface);
if (result)
return result;
- result = cp210x_read_vendor_block(serial, req_type,
- CP210X_READ_LATCH, &buf, sizeof(buf));
+ switch (priv->partnum) {
+ case CP210X_PARTNUM_CP2105:
+ req_type = REQTYPE_INTERFACE_TO_HOST;
+ len = 1;
+ break;
+ case CP210X_PARTNUM_CP2108:
+ req_type = REQTYPE_INTERFACE_TO_HOST;
+ len = 2;
+ break;
+ default:
+ req_type = REQTYPE_DEVICE_TO_HOST;
+ len = 1;
+ break;
+ }
+
+ mask = 0;
+ result = cp210x_read_vendor_block(serial, req_type, CP210X_READ_LATCH,
+ &mask, len);
+
usb_autopm_put_interface(serial->interface);
+
if (result < 0)
return result;
- return !!(buf & BIT(gpio));
+ le16_to_cpus(&mask);
+
+ return !!(mask & BIT(gpio));
}
static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
{
struct usb_serial *serial = gpiochip_get_data(gc);
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ struct cp210x_gpio_write16 buf16;
struct cp210x_gpio_write buf;
+ u16 mask, state;
+ u16 wIndex;
int result;
if (value == 1)
- buf.state = BIT(gpio);
+ state = BIT(gpio);
else
- buf.state = 0;
+ state = 0;
- buf.mask = BIT(gpio);
+ mask = BIT(gpio);
result = usb_autopm_get_interface(serial->interface);
if (result)
goto out;
- if (priv->partnum == CP210X_PARTNUM_CP2105) {
+ switch (priv->partnum) {
+ case CP210X_PARTNUM_CP2105:
+ buf.mask = (u8)mask;
+ buf.state = (u8)state;
result = cp210x_write_vendor_block(serial,
REQTYPE_HOST_TO_INTERFACE,
CP210X_WRITE_LATCH, &buf,
sizeof(buf));
- } else {
- u16 wIndex = buf.state << 8 | buf.mask;
-
+ break;
+ case CP210X_PARTNUM_CP2108:
+ buf16.mask = cpu_to_le16(mask);
+ buf16.state = cpu_to_le16(state);
+ result = cp210x_write_vendor_block(serial,
+ REQTYPE_HOST_TO_INTERFACE,
+ CP210X_WRITE_LATCH, &buf16,
+ sizeof(buf16));
+ break;
+ default:
+ wIndex = state << 8 | mask;
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
CP210X_VENDOR_SPECIFIC,
@@ -1487,6 +1573,7 @@ static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
CP210X_WRITE_LATCH,
wIndex,
NULL, 0, USB_CTRL_SET_TIMEOUT);
+ break;
}
usb_autopm_put_interface(serial->interface);
@@ -1696,6 +1783,61 @@ static int cp2104_gpioconf_init(struct usb_serial *serial)
return 0;
}
+static int cp2108_gpio_init(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ struct cp210x_quad_port_config config;
+ u16 gpio_latch;
+ int result;
+ u8 i;
+
+ result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
+ CP210X_GET_PORTCONFIG, &config,
+ sizeof(config));
+ if (result < 0)
+ return result;
+
+ priv->gc.ngpio = 16;
+ priv->gpio_pushpull = le16_to_cpu(config.reset_state.gpio_mode_pb1);
+ gpio_latch = le16_to_cpu(config.reset_state.gpio_latch_pb1);
+
+ /*
+ * Mark all pins which are not in GPIO mode.
+ *
+ * Refer to table 9.1 "GPIO Mode alternate Functions" in the datasheet:
+ * https://www.silabs.com/documents/public/data-sheets/cp2108-datasheet.pdf
+ *
+ * Alternate functions of GPIO0 to GPIO3 are determine by enhancedfxn_ifc[0]
+ * and the similarly for the other pins; enhancedfxn_ifc[1]: GPIO4 to GPIO7,
+ * enhancedfxn_ifc[2]: GPIO8 to GPIO11, enhancedfxn_ifc[3]: GPIO12 to GPIO15.
+ */
+ for (i = 0; i < 4; i++) {
+ if (config.enhancedfxn_ifc[i] & CP2108_EF_IFC_GPIO_TXLED)
+ priv->gpio_altfunc |= BIT(i * 4);
+ if (config.enhancedfxn_ifc[i] & CP2108_EF_IFC_GPIO_RXLED)
+ priv->gpio_altfunc |= BIT((i * 4) + 1);
+ if (config.enhancedfxn_ifc[i] & CP2108_EF_IFC_GPIO_RS485)
+ priv->gpio_altfunc |= BIT((i * 4) + 2);
+ if (config.enhancedfxn_ifc[i] & CP2108_EF_IFC_GPIO_CLOCK)
+ priv->gpio_altfunc |= BIT((i * 4) + 3);
+ }
+
+ /*
+ * Like CP2102N, CP2108 has also no strict input and output pin
+ * modes. Do the same input mode emulation as CP2102N.
+ */
+ for (i = 0; i < priv->gc.ngpio; ++i) {
+ /*
+ * Set direction to "input" iff pin is open-drain and reset
+ * value is 1.
+ */
+ if (!(priv->gpio_pushpull & BIT(i)) && (gpio_latch & BIT(i)))
+ priv->gpio_input |= BIT(i);
+ }
+
+ return 0;
+}
+
static int cp2102n_gpioconf_init(struct usb_serial *serial)
{
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
@@ -1812,6 +1954,15 @@ static int cp210x_gpio_init(struct usb_serial *serial)
case CP210X_PARTNUM_CP2105:
result = cp2105_gpioconf_init(serial);
break;
+ case CP210X_PARTNUM_CP2108:
+ /*
+ * The GPIOs are not tied to any specific port so only register
+ * once for interface 0.
+ */
+ if (cp210x_interface_num(serial) != 0)
+ return 0;
+ result = cp2108_gpio_init(serial);
+ break;
case CP210X_PARTNUM_CP2102N_QFN28:
case CP210X_PARTNUM_CP2102N_QFN24:
case CP210X_PARTNUM_CP2102N_QFN20:
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index cf389224d528..51e5aac3bf4c 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -53,7 +53,7 @@ static int cyberjack_open(struct tty_struct *tty,
static void cyberjack_close(struct usb_serial_port *port);
static int cyberjack_write(struct tty_struct *tty,
struct usb_serial_port *port, const unsigned char *buf, int count);
-static int cyberjack_write_room(struct tty_struct *tty);
+static unsigned int cyberjack_write_room(struct tty_struct *tty);
static void cyberjack_read_int_callback(struct urb *urb);
static void cyberjack_read_bulk_callback(struct urb *urb);
static void cyberjack_write_bulk_callback(struct urb *urb);
@@ -240,7 +240,7 @@ static int cyberjack_write(struct tty_struct *tty,
return count;
}
-static int cyberjack_write_room(struct tty_struct *tty)
+static unsigned int cyberjack_write_room(struct tty_struct *tty)
{
/* FIXME: .... */
return CYBERJACK_LOCAL_BUF_SIZE;
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index 55f23df03e0b..6b18990258c3 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -122,14 +122,14 @@ static void cypress_dtr_rts(struct usb_serial_port *port, int on);
static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static void cypress_send(struct usb_serial_port *port);
-static int cypress_write_room(struct tty_struct *tty);
+static unsigned int cypress_write_room(struct tty_struct *tty);
static void cypress_earthmate_init_termios(struct tty_struct *tty);
static void cypress_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
static int cypress_tiocmget(struct tty_struct *tty);
static int cypress_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
-static int cypress_chars_in_buffer(struct tty_struct *tty);
+static unsigned int cypress_chars_in_buffer(struct tty_struct *tty);
static void cypress_throttle(struct tty_struct *tty);
static void cypress_unthrottle(struct tty_struct *tty);
static void cypress_set_dead(struct usb_serial_port *port);
@@ -789,18 +789,18 @@ send:
/* returns how much space is available in the soft buffer */
-static int cypress_write_room(struct tty_struct *tty)
+static unsigned int cypress_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct cypress_private *priv = usb_get_serial_port_data(port);
- int room = 0;
+ unsigned int room;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
room = kfifo_avail(&priv->write_fifo);
spin_unlock_irqrestore(&priv->lock, flags);
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, room);
return room;
}
@@ -955,18 +955,18 @@ static void cypress_set_termios(struct tty_struct *tty,
/* returns amount of data still left in soft buffer */
-static int cypress_chars_in_buffer(struct tty_struct *tty)
+static unsigned int cypress_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct cypress_private *priv = usb_get_serial_port_data(port);
- int chars = 0;
+ unsigned int chars;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
chars = kfifo_len(&priv->write_fifo);
spin_unlock_irqrestore(&priv->lock, flags);
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars);
return chars;
}
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 8b2f06539f2c..af65eb863d70 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -223,8 +223,8 @@ static int digi_tiocmset(struct tty_struct *tty, unsigned int set,
static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static void digi_write_bulk_callback(struct urb *urb);
-static int digi_write_room(struct tty_struct *tty);
-static int digi_chars_in_buffer(struct tty_struct *tty);
+static unsigned int digi_write_room(struct tty_struct *tty);
+static unsigned int digi_chars_in_buffer(struct tty_struct *tty);
static int digi_open(struct tty_struct *tty, struct usb_serial_port *port);
static void digi_close(struct usb_serial_port *port);
static void digi_dtr_rts(struct usb_serial_port *port, int on);
@@ -372,7 +372,7 @@ static int digi_write_oob_command(struct usb_serial_port *port,
int len;
struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port;
struct digi_port *oob_priv = usb_get_serial_port_data(oob_port);
- unsigned long flags = 0;
+ unsigned long flags;
dev_dbg(&port->dev,
"digi_write_oob_command: TOP: port=%d, count=%d\n",
@@ -430,7 +430,7 @@ static int digi_write_inb_command(struct usb_serial_port *port,
int len;
struct digi_port *priv = usb_get_serial_port_data(port);
unsigned char *data = port->write_urb->transfer_buffer;
- unsigned long flags = 0;
+ unsigned long flags;
dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n",
priv->dp_port_num, count);
@@ -511,8 +511,7 @@ static int digi_set_modem_signals(struct usb_serial_port *port,
struct usb_serial_port *oob_port = (struct usb_serial_port *) ((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port;
struct digi_port *oob_priv = usb_get_serial_port_data(oob_port);
unsigned char *data = oob_port->write_urb->transfer_buffer;
- unsigned long flags = 0;
-
+ unsigned long flags;
dev_dbg(&port->dev,
"digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x\n",
@@ -577,7 +576,7 @@ static int digi_transmit_idle(struct usb_serial_port *port,
int ret;
unsigned char buf[2];
struct digi_port *priv = usb_get_serial_port_data(port);
- unsigned long flags = 0;
+ unsigned long flags;
spin_lock_irqsave(&priv->dp_port_lock, flags);
priv->dp_transmit_idle = 0;
@@ -887,7 +886,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
int ret, data_len, new_len;
struct digi_port *priv = usb_get_serial_port_data(port);
unsigned char *data = port->write_urb->transfer_buffer;
- unsigned long flags = 0;
+ unsigned long flags;
dev_dbg(&port->dev, "digi_write: TOP: port=%d, count=%d\n",
priv->dp_port_num, count);
@@ -1020,12 +1019,12 @@ static void digi_write_bulk_callback(struct urb *urb)
tty_port_tty_wakeup(&port->port);
}
-static int digi_write_room(struct tty_struct *tty)
+static unsigned int digi_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct digi_port *priv = usb_get_serial_port_data(port);
- int room;
- unsigned long flags = 0;
+ unsigned long flags;
+ unsigned int room;
spin_lock_irqsave(&priv->dp_port_lock, flags);
@@ -1035,27 +1034,28 @@ static int digi_write_room(struct tty_struct *tty)
room = port->bulk_out_size - 2 - priv->dp_out_buf_len;
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
- dev_dbg(&port->dev, "digi_write_room: port=%d, room=%d\n", priv->dp_port_num, room);
+ dev_dbg(&port->dev, "digi_write_room: port=%d, room=%u\n", priv->dp_port_num, room);
return room;
}
-static int digi_chars_in_buffer(struct tty_struct *tty)
+static unsigned int digi_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct digi_port *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ unsigned int chars;
- if (priv->dp_write_urb_in_use) {
- dev_dbg(&port->dev, "digi_chars_in_buffer: port=%d, chars=%d\n",
- priv->dp_port_num, port->bulk_out_size - 2);
- /* return(port->bulk_out_size - 2); */
- return 256;
- } else {
- dev_dbg(&port->dev, "digi_chars_in_buffer: port=%d, chars=%d\n",
- priv->dp_port_num, priv->dp_out_buf_len);
- return priv->dp_out_buf_len;
- }
+ spin_lock_irqsave(&priv->dp_port_lock, flags);
+ if (priv->dp_write_urb_in_use)
+ chars = port->bulk_out_size - 2;
+ else
+ chars = priv->dp_out_buf_len;
+ spin_unlock_irqrestore(&priv->dp_port_lock, flags);
+ dev_dbg(&port->dev, "%s: port=%d, chars=%d\n", __func__,
+ priv->dp_port_num, chars);
+ return chars;
}
static void digi_dtr_rts(struct usb_serial_port *port, int on)
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 50e8bdc77e71..756d1ac7e96f 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -1113,7 +1113,7 @@ static int garmin_write(struct tty_struct *tty, struct usb_serial_port *port,
}
-static int garmin_write_room(struct tty_struct *tty)
+static unsigned int garmin_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
/*
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index d10aa3d2ee49..15b6dee3a8e5 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -230,11 +230,11 @@ int usb_serial_generic_write(struct tty_struct *tty,
}
EXPORT_SYMBOL_GPL(usb_serial_generic_write);
-int usb_serial_generic_write_room(struct tty_struct *tty)
+unsigned int usb_serial_generic_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
unsigned long flags;
- int room;
+ unsigned int room;
if (!port->bulk_out_size)
return 0;
@@ -243,15 +243,15 @@ int usb_serial_generic_write_room(struct tty_struct *tty)
room = kfifo_avail(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, room);
return room;
}
-int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
+unsigned int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
unsigned long flags;
- int chars;
+ unsigned int chars;
if (!port->bulk_out_size)
return 0;
@@ -260,7 +260,7 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
chars = kfifo_len(&port->write_fifo) + port->tx_bytes;
spin_unlock_irqrestore(&port->lock, flags);
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars);
return chars;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer);
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index e6fe3882bf69..ea4edf5eed27 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -1351,33 +1351,21 @@ exit_send:
/*****************************************************************************
* edge_write_room
* this function is called by the tty driver when it wants to know how
- * many bytes of data we can accept for a specific port. If successful,
- * we return the amount of room that we have for this port (the txCredits)
- * otherwise we return a negative error number.
+ * many bytes of data we can accept for a specific port.
*****************************************************************************/
-static int edge_write_room(struct tty_struct *tty)
+static unsigned int edge_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
- int room;
+ unsigned int room;
unsigned long flags;
- if (edge_port == NULL)
- return 0;
- if (edge_port->closePending)
- return 0;
-
- if (!edge_port->open) {
- dev_dbg(&port->dev, "%s - port not opened\n", __func__);
- return 0;
- }
-
/* total of both buffers is still txCredit */
spin_lock_irqsave(&edge_port->ep_lock, flags);
room = edge_port->txCredits - edge_port->txfifo.count;
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, room);
return room;
}
@@ -1387,33 +1375,20 @@ static int edge_write_room(struct tty_struct *tty)
* this function is called by the tty driver when it wants to know how
* many bytes of data we currently have outstanding in the port (data that
* has been written, but hasn't made it out the port yet)
- * If successful, we return the number of bytes left to be written in the
- * system,
- * Otherwise we return a negative error number.
*****************************************************************************/
-static int edge_chars_in_buffer(struct tty_struct *tty)
+static unsigned int edge_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
- int num_chars;
+ unsigned int num_chars;
unsigned long flags;
- if (edge_port == NULL)
- return 0;
- if (edge_port->closePending)
- return 0;
-
- if (!edge_port->open) {
- dev_dbg(&port->dev, "%s - port not opened\n", __func__);
- return 0;
- }
-
spin_lock_irqsave(&edge_port->ep_lock, flags);
num_chars = edge_port->maxTxCredits - edge_port->txCredits +
edge_port->txfifo.count;
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
if (num_chars) {
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, num_chars);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, num_chars);
}
return num_chars;
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 39503fdccebf..84b848d2794e 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -2067,11 +2067,11 @@ static void edge_send(struct usb_serial_port *port, struct tty_struct *tty)
tty_wakeup(tty);
}
-static int edge_write_room(struct tty_struct *tty)
+static unsigned int edge_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
- int room = 0;
+ unsigned int room;
unsigned long flags;
if (edge_port == NULL)
@@ -2083,15 +2083,15 @@ static int edge_write_room(struct tty_struct *tty)
room = kfifo_avail(&port->write_fifo);
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, room);
return room;
}
-static int edge_chars_in_buffer(struct tty_struct *tty)
+static unsigned int edge_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
- int chars = 0;
+ unsigned int chars;
unsigned long flags;
if (edge_port == NULL)
return 0;
@@ -2100,7 +2100,7 @@ static int edge_chars_in_buffer(struct tty_struct *tty)
chars = kfifo_len(&port->write_fifo);
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars);
return chars;
}
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 172261a908d8..7b44dbea95cd 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -47,7 +47,7 @@ static int xbof = -1;
static int ir_startup (struct usb_serial *serial);
static int ir_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
-static int ir_write_room(struct tty_struct *tty);
+static unsigned int ir_write_room(struct tty_struct *tty);
static void ir_write_bulk_callback(struct urb *urb);
static void ir_process_read_urb(struct urb *urb);
static void ir_set_termios(struct tty_struct *tty,
@@ -339,10 +339,10 @@ static void ir_write_bulk_callback(struct urb *urb)
usb_serial_port_softint(port);
}
-static int ir_write_room(struct tty_struct *tty)
+static unsigned int ir_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- int count = 0;
+ unsigned int count = 0;
if (port->bulk_out_size == 0)
return 0;
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index b04a029e3657..87b89c99d517 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -1453,13 +1453,13 @@ static void usa67_glocont_callback(struct urb *urb)
}
}
-static int keyspan_write_room(struct tty_struct *tty)
+static unsigned int keyspan_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct keyspan_port_private *p_priv;
const struct keyspan_device_details *d_details;
int flip;
- int data_len;
+ unsigned int data_len;
struct urb *this_urb;
p_priv = usb_get_serial_port_data(port);
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index a9bc546626ab..4ed8b8b0a361 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -53,7 +53,7 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port);
static void kobil_close(struct usb_serial_port *port);
static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
-static int kobil_write_room(struct tty_struct *tty);
+static unsigned int kobil_write_room(struct tty_struct *tty);
static int kobil_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
static int kobil_tiocmget(struct tty_struct *tty);
@@ -358,7 +358,7 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
}
-static int kobil_write_room(struct tty_struct *tty)
+static unsigned int kobil_write_room(struct tty_struct *tty)
{
/* FIXME */
return 8;
diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c
index f9ce9e7b9b80..30ab565e0738 100644
--- a/drivers/usb/serial/metro-usb.c
+++ b/drivers/usb/serial/metro-usb.c
@@ -109,9 +109,9 @@ static void metrousb_read_int_callback(struct urb *urb)
struct usb_serial_port *port = urb->context;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
+ unsigned long flags;
int throttled = 0;
int result = 0;
- unsigned long flags = 0;
dev_dbg(&port->dev, "%s\n", __func__);
@@ -171,7 +171,7 @@ static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
- unsigned long flags = 0;
+ unsigned long flags;
int result = 0;
/* Set the private data information for the port. */
@@ -268,7 +268,7 @@ static void metrousb_throttle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
- unsigned long flags = 0;
+ unsigned long flags;
/* Set the private information for the port to stop reading data. */
spin_lock_irqsave(&metro_priv->lock, flags);
@@ -281,7 +281,7 @@ static int metrousb_tiocmget(struct tty_struct *tty)
unsigned long control_state = 0;
struct usb_serial_port *port = tty->driver_data;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
- unsigned long flags = 0;
+ unsigned long flags;
spin_lock_irqsave(&metro_priv->lock, flags);
control_state = metro_priv->control_state;
@@ -296,7 +296,7 @@ static int metrousb_tiocmset(struct tty_struct *tty,
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
- unsigned long flags = 0;
+ unsigned long flags;
unsigned long control_state = 0;
dev_dbg(&port->dev, "%s - set=%d, clear=%d\n", __func__, set, clear);
@@ -323,7 +323,7 @@ static void metrousb_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
- unsigned long flags = 0;
+ unsigned long flags;
int result = 0;
/* Set the private information for the port to resume reading data. */
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 6ee83886e2c9..227f43d2bd56 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -945,27 +945,20 @@ static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port)
* this function is called by the tty driver when it wants to know how many
* bytes of data we currently have outstanding in the port (data that has
* been written, but hasn't made it out the port yet)
- * If successful, we return the number of bytes left to be written in the
- * system,
- * Otherwise we return a negative error number.
*/
-static int mos7720_chars_in_buffer(struct tty_struct *tty)
+static unsigned int mos7720_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
+ struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
int i;
- int chars = 0;
- struct moschip_port *mos7720_port;
-
- mos7720_port = usb_get_serial_port_data(port);
- if (mos7720_port == NULL)
- return 0;
+ unsigned int chars = 0;
for (i = 0; i < NUM_URBS; ++i) {
if (mos7720_port->write_urb_pool[i] &&
mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
chars += URB_TRANSFER_BUFFER_SIZE;
}
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars);
return chars;
}
@@ -1030,20 +1023,14 @@ static void mos7720_break(struct tty_struct *tty, int break_state)
* mos7720_write_room
* this function is called by the tty driver when it wants to know how many
* bytes of data we can accept for a specific port.
- * If successful, we return the amount of room that we have for this port
- * Otherwise we return a negative error number.
*/
-static int mos7720_write_room(struct tty_struct *tty)
+static unsigned int mos7720_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- struct moschip_port *mos7720_port;
- int room = 0;
+ struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
+ unsigned int room = 0;
int i;
- mos7720_port = usb_get_serial_port_data(port);
- if (mos7720_port == NULL)
- return 0;
-
/* FIXME: Locking */
for (i = 0; i < NUM_URBS; ++i) {
if (mos7720_port->write_urb_pool[i] &&
@@ -1051,7 +1038,7 @@ static int mos7720_write_room(struct tty_struct *tty)
room += URB_TRANSFER_BUFFER_SIZE;
}
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, room);
return room;
}
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 28e4093794e0..d7fe33ca73e4 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -730,17 +730,14 @@ err:
* this function is called by the tty driver when it wants to know how many
* bytes of data we currently have outstanding in the port (data that has
* been written, but hasn't made it out the port yet)
- * If successful, we return the number of bytes left to be written in the
- * system,
- * Otherwise we return zero.
*****************************************************************************/
-static int mos7840_chars_in_buffer(struct tty_struct *tty)
+static unsigned int mos7840_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
int i;
- int chars = 0;
+ unsigned int chars = 0;
unsigned long flags;
spin_lock_irqsave(&mos7840_port->pool_lock, flags);
@@ -751,7 +748,7 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty)
}
}
spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars);
return chars;
}
@@ -814,16 +811,14 @@ static void mos7840_break(struct tty_struct *tty, int break_state)
* mos7840_write_room
* this function is called by the tty driver when it wants to know how many
* bytes of data we can accept for a specific port.
- * If successful, we return the amount of room that we have for this port
- * Otherwise we return a negative error number.
*****************************************************************************/
-static int mos7840_write_room(struct tty_struct *tty)
+static unsigned int mos7840_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
int i;
- int room = 0;
+ unsigned int room = 0;
unsigned long flags;
spin_lock_irqsave(&mos7840_port->pool_lock, flags);
@@ -834,7 +829,7 @@ static int mos7840_write_room(struct tty_struct *tty)
spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1;
- dev_dbg(&mos7840_port->port->dev, "%s - returns %d\n", __func__, room);
+ dev_dbg(&mos7840_port->port->dev, "%s - returns %u\n", __func__, room);
return room;
}
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 40c713fae0c3..aed28c35caff 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -267,7 +267,7 @@ error_no_buffer:
return ret;
}
-static int opticon_write_room(struct tty_struct *tty)
+static unsigned int opticon_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct opticon_private *priv = usb_get_serial_port_data(port);
@@ -289,12 +289,12 @@ static int opticon_write_room(struct tty_struct *tty)
return 2048;
}
-static int opticon_chars_in_buffer(struct tty_struct *tty)
+static unsigned int opticon_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct opticon_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
- int count;
+ unsigned int count;
spin_lock_irqsave(&priv->lock, flags);
count = priv->outstanding_bytes;
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
index 65cd0341fa78..a5caedbe72e2 100644
--- a/drivers/usb/serial/oti6858.c
+++ b/drivers/usb/serial/oti6858.c
@@ -126,8 +126,8 @@ static void oti6858_read_bulk_callback(struct urb *urb);
static void oti6858_write_bulk_callback(struct urb *urb);
static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
-static int oti6858_write_room(struct tty_struct *tty);
-static int oti6858_chars_in_buffer(struct tty_struct *tty);
+static unsigned int oti6858_write_room(struct tty_struct *tty);
+static unsigned int oti6858_chars_in_buffer(struct tty_struct *tty);
static int oti6858_tiocmget(struct tty_struct *tty);
static int oti6858_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
@@ -363,10 +363,10 @@ static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port,
return count;
}
-static int oti6858_write_room(struct tty_struct *tty)
+static unsigned int oti6858_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- int room = 0;
+ unsigned int room;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
@@ -376,10 +376,10 @@ static int oti6858_write_room(struct tty_struct *tty)
return room;
}
-static int oti6858_chars_in_buffer(struct tty_struct *tty)
+static unsigned int oti6858_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- int chars = 0;
+ unsigned int chars;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index 067690dac24c..971907f083a3 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -870,12 +870,12 @@ static void qt2_update_lsr(struct usb_serial_port *port, unsigned char *ch)
}
-static int qt2_write_room(struct tty_struct *tty)
+static unsigned int qt2_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct qt2_port_private *port_priv;
- unsigned long flags = 0;
- int r;
+ unsigned long flags;
+ unsigned int r;
port_priv = usb_get_serial_port_data(port);
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index 54e16ffc30a0..4b49b7c33364 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -613,7 +613,7 @@ static void sierra_instat_callback(struct urb *urb)
}
}
-static int sierra_write_room(struct tty_struct *tty)
+static unsigned int sierra_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct sierra_port_private *portdata = usb_get_serial_port_data(port);
@@ -632,19 +632,19 @@ static int sierra_write_room(struct tty_struct *tty)
return 2048;
}
-static int sierra_chars_in_buffer(struct tty_struct *tty)
+static unsigned int sierra_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct sierra_port_private *portdata = usb_get_serial_port_data(port);
unsigned long flags;
- int chars;
+ unsigned int chars;
/* NOTE: This overcounts somewhat. */
spin_lock_irqsave(&portdata->lock, flags);
chars = portdata->outstanding_urbs * MAX_TRANSFER;
spin_unlock_irqrestore(&portdata->lock, flags);
- dev_dbg(&port->dev, "%s - %d\n", __func__, chars);
+ dev_dbg(&port->dev, "%s - %u\n", __func__, chars);
return chars;
}
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 310db5abea9d..18c0bd853392 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -308,8 +308,8 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port);
static void ti_close(struct usb_serial_port *port);
static int ti_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *data, int count);
-static int ti_write_room(struct tty_struct *tty);
-static int ti_chars_in_buffer(struct tty_struct *tty);
+static unsigned int ti_write_room(struct tty_struct *tty);
+static unsigned int ti_chars_in_buffer(struct tty_struct *tty);
static bool ti_tx_empty(struct usb_serial_port *port);
static void ti_throttle(struct tty_struct *tty);
static void ti_unthrottle(struct tty_struct *tty);
@@ -813,34 +813,34 @@ static int ti_write(struct tty_struct *tty, struct usb_serial_port *port,
}
-static int ti_write_room(struct tty_struct *tty)
+static unsigned int ti_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct ti_port *tport = usb_get_serial_port_data(port);
- int room = 0;
+ unsigned int room;
unsigned long flags;
spin_lock_irqsave(&tport->tp_lock, flags);
room = kfifo_avail(&port->write_fifo);
spin_unlock_irqrestore(&tport->tp_lock, flags);
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, room);
return room;
}
-static int ti_chars_in_buffer(struct tty_struct *tty)
+static unsigned int ti_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct ti_port *tport = usb_get_serial_port_data(port);
- int chars = 0;
+ unsigned int chars;
unsigned long flags;
spin_lock_irqsave(&tport->tp_lock, flags);
chars = kfifo_len(&port->write_fifo);
spin_unlock_irqrestore(&tport->tp_lock, flags);
- dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars);
return chars;
}
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
index b5331d03092f..519101945769 100644
--- a/drivers/usb/serial/usb-wwan.h
+++ b/drivers/usb/serial/usb-wwan.h
@@ -11,13 +11,13 @@ extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
extern void usb_wwan_close(struct usb_serial_port *port);
extern int usb_wwan_port_probe(struct usb_serial_port *port);
extern void usb_wwan_port_remove(struct usb_serial_port *port);
-extern int usb_wwan_write_room(struct tty_struct *tty);
+extern unsigned int usb_wwan_write_room(struct tty_struct *tty);
extern int usb_wwan_tiocmget(struct tty_struct *tty);
extern int usb_wwan_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
-extern int usb_wwan_chars_in_buffer(struct tty_struct *tty);
+extern unsigned int usb_wwan_chars_in_buffer(struct tty_struct *tty);
#ifdef CONFIG_PM
extern int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message);
extern int usb_wwan_resume(struct usb_serial *serial);
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 3eb72c59ede6..cb01283d4d15 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -278,12 +278,12 @@ static void usb_wwan_outdat_callback(struct urb *urb)
}
}
-int usb_wwan_write_room(struct tty_struct *tty)
+unsigned int usb_wwan_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_wwan_port_private *portdata;
int i;
- int data_len = 0;
+ unsigned int data_len = 0;
struct urb *this_urb;
portdata = usb_get_serial_port_data(port);
@@ -294,17 +294,17 @@ int usb_wwan_write_room(struct tty_struct *tty)
data_len += OUT_BUFLEN;
}
- dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
+ dev_dbg(&port->dev, "%s: %u\n", __func__, data_len);
return data_len;
}
EXPORT_SYMBOL(usb_wwan_write_room);
-int usb_wwan_chars_in_buffer(struct tty_struct *tty)
+unsigned int usb_wwan_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_wwan_port_private *portdata;
int i;
- int data_len = 0;
+ unsigned int data_len = 0;
struct urb *this_urb;
portdata = usb_get_serial_port_data(port);
@@ -316,7 +316,7 @@ int usb_wwan_chars_in_buffer(struct tty_struct *tty)
if (this_urb && test_bit(i, &portdata->out_busy))
data_len += this_urb->transfer_buffer_length;
}
- dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
+ dev_dbg(&port->dev, "%s: %u\n", __func__, data_len);
return data_len;
}
EXPORT_SYMBOL(usb_wwan_chars_in_buffer);
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index b9429c9f65f6..aeef453aa658 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -517,8 +517,10 @@ typec_register_altmode(struct device *parent,
int ret;
alt = kzalloc(sizeof(*alt), GFP_KERNEL);
- if (!alt)
+ if (!alt) {
+ altmode_id_remove(parent, id);
return ERR_PTR(-ENOMEM);
+ }
alt->adev.svid = desc->svid;
alt->adev.mode = desc->mode;
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
index 77dabd306ba8..c8340de0ed49 100644
--- a/drivers/usb/typec/mux.c
+++ b/drivers/usb/typec/mux.c
@@ -17,21 +17,12 @@
#include "class.h"
#include "mux.h"
-static bool dev_name_ends_with(struct device *dev, const char *suffix)
-{
- const char *name = dev_name(dev);
- const int name_len = strlen(name);
- const int suffix_len = strlen(suffix);
-
- if (suffix_len > name_len)
- return false;
-
- return strcmp(name + (name_len - suffix_len), suffix) == 0;
-}
-
static int switch_fwnode_match(struct device *dev, const void *fwnode)
{
- return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-switch");
+ if (!is_typec_switch(dev))
+ return 0;
+
+ return dev_fwnode(dev) == fwnode;
}
static void *typec_switch_match(struct fwnode_handle *fwnode, const char *id,
@@ -39,9 +30,22 @@ static void *typec_switch_match(struct fwnode_handle *fwnode, const char *id,
{
struct device *dev;
+ /*
+ * Device graph (OF graph) does not give any means to identify the
+ * device type or the device class of the remote port parent that @fwnode
+ * represents, so in order to identify the type or the class of @fwnode
+ * an additional device property is needed. With typec switches the
+ * property is named "orientation-switch" (@id). The value of the device
+ * property is ignored.
+ */
if (id && !fwnode_property_present(fwnode, id))
return NULL;
+ /*
+ * At this point we are sure that @fwnode is a typec switch in all
+ * cases. If the switch hasn't yet been registered for some reason, the
+ * function "defers probe" for now.
+ */
dev = class_find_device(&typec_mux_class, NULL, fwnode,
switch_fwnode_match);
@@ -90,7 +94,7 @@ static void typec_switch_release(struct device *dev)
kfree(to_typec_switch(dev));
}
-static const struct device_type typec_switch_dev_type = {
+const struct device_type typec_switch_dev_type = {
.name = "orientation_switch",
.release = typec_switch_release,
};
@@ -180,7 +184,10 @@ EXPORT_SYMBOL_GPL(typec_switch_get_drvdata);
static int mux_fwnode_match(struct device *dev, const void *fwnode)
{
- return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-mux");
+ if (!is_typec_mux(dev))
+ return 0;
+
+ return dev_fwnode(dev) == fwnode;
}
static void *typec_mux_match(struct fwnode_handle *fwnode, const char *id,
@@ -295,7 +302,7 @@ static void typec_mux_release(struct device *dev)
kfree(to_typec_mux(dev));
}
-static const struct device_type typec_mux_dev_type = {
+const struct device_type typec_mux_dev_type = {
.name = "mode_switch",
.release = typec_mux_release,
};
diff --git a/drivers/usb/typec/mux.h b/drivers/usb/typec/mux.h
index 4fd9426ee44f..b1d6e837cb74 100644
--- a/drivers/usb/typec/mux.h
+++ b/drivers/usb/typec/mux.h
@@ -18,4 +18,10 @@ struct typec_mux {
#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
+extern const struct device_type typec_switch_dev_type;
+extern const struct device_type typec_mux_dev_type;
+
+#define is_typec_switch(dev) ((dev)->type == &typec_switch_dev_type)
+#define is_typec_mux(dev) ((dev)->type == &typec_mux_dev_type)
+
#endif /* __USB_TYPEC_MUX__ */
diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c
index ffa8aa12d5f1..2cdd22130834 100644
--- a/drivers/usb/typec/mux/intel_pmc_mux.c
+++ b/drivers/usb/typec/mux/intel_pmc_mux.c
@@ -83,8 +83,6 @@ enum {
/*
* Input Output Manager (IOM) PORT STATUS
*/
-#define IOM_PORT_STATUS_OFFSET 0x560
-
#define IOM_PORT_STATUS_ACTIVITY_TYPE_MASK GENMASK(9, 6)
#define IOM_PORT_STATUS_ACTIVITY_TYPE_SHIFT 6
#define IOM_PORT_STATUS_ACTIVITY_TYPE_USB 0x03
@@ -144,6 +142,7 @@ struct pmc_usb {
struct pmc_usb_port *port;
struct acpi_device *iom_adev;
void __iomem *iom_base;
+ u32 iom_port_status_offset;
};
static void update_port_status(struct pmc_usb_port *port)
@@ -153,7 +152,8 @@ static void update_port_status(struct pmc_usb_port *port)
/* SoC expects the USB Type-C port numbers to start with 0 */
port_num = port->usb3_port - 1;
- port->iom_status = readl(port->pmc->iom_base + IOM_PORT_STATUS_OFFSET +
+ port->iom_status = readl(port->pmc->iom_base +
+ port->pmc->iom_port_status_offset +
port_num * sizeof(u32));
}
@@ -559,14 +559,32 @@ static int is_memory(struct acpi_resource *res, void *data)
return !acpi_dev_resource_memory(res, &r);
}
+/* IOM ACPI IDs and IOM_PORT_STATUS_OFFSET */
+static const struct acpi_device_id iom_acpi_ids[] = {
+ /* TigerLake */
+ { "INTC1072", 0x560, },
+
+ /* AlderLake */
+ { "INTC1079", 0x160, },
+ {}
+};
+
static int pmc_usb_probe_iom(struct pmc_usb *pmc)
{
struct list_head resource_list;
struct resource_entry *rentry;
- struct acpi_device *adev;
+ static const struct acpi_device_id *dev_id;
+ struct acpi_device *adev = NULL;
int ret;
- adev = acpi_dev_get_first_match_dev("INTC1072", NULL, -1);
+ for (dev_id = &iom_acpi_ids[0]; dev_id->id[0]; dev_id++) {
+ if (acpi_dev_present(dev_id->id, NULL, -1)) {
+ pmc->iom_port_status_offset = (u32)dev_id->driver_data;
+ adev = acpi_dev_get_first_match_dev(dev_id->id, NULL, -1);
+ break;
+ }
+ }
+
if (!adev)
return -ENODEV;
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 25b480752266..9858716698df 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -21,8 +21,12 @@
#define PD_RETRY_COUNT_DEFAULT 3
#define PD_RETRY_COUNT_3_0_OR_HIGHER 2
#define AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV 3500
-#define AUTO_DISCHARGE_PD_HEADROOM_MV 850
-#define AUTO_DISCHARGE_PPS_HEADROOM_MV 1250
+#define VSINKPD_MIN_IR_DROP_MV 750
+#define VSRC_NEW_MIN_PERCENT 95
+#define VSRC_VALID_MIN_MV 500
+#define VPPS_NEW_MIN_PERCENT 95
+#define VPPS_VALID_MIN_MV 100
+#define VSINKDISCONNECT_PD_MIN_PERCENT 90
#define tcpc_presenting_rd(reg, cc) \
(!(TCPC_ROLE_CTRL_DRP & (reg)) && \
@@ -115,6 +119,33 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
return 0;
}
+static int tcpci_apply_rc(struct tcpc_dev *tcpc, enum typec_cc_status cc,
+ enum typec_cc_polarity polarity)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, &reg);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * APPLY_RC state is when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2 and vbus autodischarge on
+ * disconnect is disabled. Bail out when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2.
+ */
+ if (((reg & (TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT)) >>
+ TCPC_ROLE_CTRL_CC2_SHIFT) !=
+ ((reg & (TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT)) >>
+ TCPC_ROLE_CTRL_CC1_SHIFT))
+ return 0;
+
+ return regmap_update_bits(tcpci->regmap, TCPC_ROLE_CTRL, polarity == TYPEC_POLARITY_CC1 ?
+ TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT :
+ TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT,
+ TCPC_ROLE_CTRL_CC_OPEN);
+}
+
static int tcpci_start_toggling(struct tcpc_dev *tcpc,
enum typec_port_type port_type,
enum typec_cc_status cc)
@@ -324,11 +355,13 @@ static int tcpci_set_auto_vbus_discharge_threshold(struct tcpc_dev *dev, enum ty
threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV;
} else if (mode == TYPEC_PWR_MODE_PD) {
if (pps_active)
- threshold = (95 * requested_vbus_voltage_mv / 100) -
- AUTO_DISCHARGE_PD_HEADROOM_MV;
+ threshold = ((VPPS_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) -
+ VSINKPD_MIN_IR_DROP_MV - VPPS_VALID_MIN_MV) *
+ VSINKDISCONNECT_PD_MIN_PERCENT / 100;
else
- threshold = (95 * requested_vbus_voltage_mv / 100) -
- AUTO_DISCHARGE_PPS_HEADROOM_MV;
+ threshold = ((VSRC_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) -
+ VSINKPD_MIN_IR_DROP_MV - VSRC_VALID_MIN_MV) *
+ VSINKDISCONNECT_PD_MIN_PERCENT / 100;
} else {
/* 3.5V for non-pd sink */
threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV;
@@ -728,6 +761,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
tcpci->tcpc.get_vbus = tcpci_get_vbus;
tcpci->tcpc.set_vbus = tcpci_set_vbus;
tcpci->tcpc.set_cc = tcpci_set_cc;
+ tcpci->tcpc.apply_rc = tcpci_apply_rc;
tcpci->tcpc.get_cc = tcpci_get_cc;
tcpci->tcpc.set_polarity = tcpci_set_polarity;
tcpci->tcpc.set_vconn = tcpci_set_vconn;
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 63470cf7f4cd..5b22a1c931a9 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -776,6 +776,34 @@ static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc)
port->tcpc->set_cc(port->tcpc, cc);
}
+static int tcpm_enable_auto_vbus_discharge(struct tcpm_port *port, bool enable)
+{
+ int ret = 0;
+
+ if (port->tcpc->enable_auto_vbus_discharge) {
+ ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, enable);
+ tcpm_log_force(port, "%s vbus discharge ret:%d", enable ? "enable" : "disable",
+ ret);
+ if (!ret)
+ port->auto_vbus_discharge_enabled = enable;
+ }
+
+ return ret;
+}
+
+static void tcpm_apply_rc(struct tcpm_port *port)
+{
+ /*
+ * TCPCI: Move to APPLY_RC state to prevent disconnect during PR_SWAP
+ * when Vbus auto discharge on disconnect is enabled.
+ */
+ if (port->tcpc->enable_auto_vbus_discharge && port->tcpc->apply_rc) {
+ tcpm_log(port, "Apply_RC");
+ port->tcpc->apply_rc(port->tcpc, port->cc_req, port->polarity);
+ tcpm_enable_auto_vbus_discharge(port, false);
+ }
+}
+
/*
* Determine RP value to set based on maximum current supported
* by a port if configured as source.
@@ -2576,6 +2604,11 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
} else {
next_state = SNK_WAIT_CAPABILITIES;
}
+
+ /* Threshold was relaxed before sending Request. Restore it back. */
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD,
+ port->pps_data.active,
+ port->supply_voltage);
tcpm_set_state(port, next_state, 0);
break;
case SNK_NEGOTIATE_PPS_CAPABILITIES:
@@ -2589,6 +2622,11 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
port->send_discover)
port->vdm_sm_running = true;
+ /* Threshold was relaxed before sending Request. Restore it back. */
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD,
+ port->pps_data.active,
+ port->supply_voltage);
+
tcpm_set_state(port, SNK_READY, 0);
break;
case DR_SWAP_SEND:
@@ -3308,6 +3346,12 @@ static int tcpm_pd_send_request(struct tcpm_port *port)
if (ret < 0)
return ret;
+ /*
+ * Relax the threshold as voltage will be adjusted after Accept Message plus tSrcTransition.
+ * It is safer to modify the threshold here.
+ */
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, 0);
+
memset(&msg, 0, sizeof(msg));
msg.header = PD_HEADER_LE(PD_DATA_REQUEST,
port->pwr_role,
@@ -3405,6 +3449,9 @@ static int tcpm_pd_send_pps_request(struct tcpm_port *port)
if (ret < 0)
return ret;
+ /* Relax the threshold as voltage will be adjusted right after Accept Message. */
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, 0);
+
memset(&msg, 0, sizeof(msg));
msg.header = PD_HEADER_LE(PD_DATA_REQUEST,
port->pwr_role,
@@ -3515,12 +3562,7 @@ static int tcpm_src_attach(struct tcpm_port *port)
if (ret < 0)
return ret;
- if (port->tcpc->enable_auto_vbus_discharge) {
- ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true);
- tcpm_log_force(port, "enable vbus discharge ret:%d", ret);
- if (!ret)
- port->auto_vbus_discharge_enabled = true;
- }
+ tcpm_enable_auto_vbus_discharge(port, true);
ret = tcpm_set_roles(port, true, TYPEC_SOURCE, tcpm_data_role_for_source(port));
if (ret < 0)
@@ -3597,14 +3639,7 @@ static void tcpm_set_partner_usb_comm_capable(struct tcpm_port *port, bool capab
static void tcpm_reset_port(struct tcpm_port *port)
{
- int ret;
-
- if (port->tcpc->enable_auto_vbus_discharge) {
- ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, false);
- tcpm_log_force(port, "Disable vbus discharge ret:%d", ret);
- if (!ret)
- port->auto_vbus_discharge_enabled = false;
- }
+ tcpm_enable_auto_vbus_discharge(port, false);
port->in_ams = false;
port->ams = NONE_AMS;
port->vdm_sm_running = false;
@@ -3672,13 +3707,7 @@ static int tcpm_snk_attach(struct tcpm_port *port)
if (ret < 0)
return ret;
- if (port->tcpc->enable_auto_vbus_discharge) {
- tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V);
- ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true);
- tcpm_log_force(port, "enable vbus discharge ret:%d", ret);
- if (!ret)
- port->auto_vbus_discharge_enabled = true;
- }
+ tcpm_enable_auto_vbus_discharge(port, true);
ret = tcpm_set_roles(port, true, TYPEC_SINK, tcpm_data_role_for_sink(port));
if (ret < 0)
@@ -4186,6 +4215,10 @@ static void run_state_machine(struct tcpm_port *port)
port->hard_reset_count = 0;
ret = tcpm_pd_send_request(port);
if (ret < 0) {
+ /* Restore back to the original state */
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD,
+ port->pps_data.active,
+ port->supply_voltage);
/* Let the Source send capabilities again. */
tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0);
} else {
@@ -4196,6 +4229,10 @@ static void run_state_machine(struct tcpm_port *port)
case SNK_NEGOTIATE_PPS_CAPABILITIES:
ret = tcpm_pd_send_pps_request(port);
if (ret < 0) {
+ /* Restore back to the original state */
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD,
+ port->pps_data.active,
+ port->supply_voltage);
port->pps_status = ret;
/*
* If this was called due to updates to sink
@@ -4515,6 +4552,7 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, ready_state(port), 0);
break;
case PR_SWAP_START:
+ tcpm_apply_rc(port);
if (port->pwr_role == TYPEC_SOURCE)
tcpm_set_state(port, PR_SWAP_SRC_SNK_TRANSITION_OFF,
PD_T_SRC_TRANSITION);
@@ -4554,6 +4592,7 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_ON_PRS);
break;
case PR_SWAP_SRC_SNK_SINK_ON:
+ tcpm_enable_auto_vbus_discharge(port, true);
/* Set the vbus disconnect threshold for implicit contract */
tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V);
tcpm_set_state(port, SNK_STARTUP, 0);
@@ -4570,6 +4609,7 @@ static void run_state_machine(struct tcpm_port *port)
PD_T_PS_SOURCE_OFF);
break;
case PR_SWAP_SNK_SRC_SOURCE_ON:
+ tcpm_enable_auto_vbus_discharge(port, true);
tcpm_set_cc(port, tcpm_rp_cc(port));
tcpm_set_vbus(port, true);
/*
@@ -5198,6 +5238,10 @@ static void _tcpm_pd_vbus_vsafe0v(struct tcpm_port *port)
tcpm_set_state(port, SNK_UNATTACHED, 0);
}
break;
+ case PR_SWAP_SNK_SRC_SINK_OFF:
+ case PR_SWAP_SNK_SRC_SOURCE_ON:
+ /* Do nothing, vsafe0v is expected during transition */
+ break;
default:
if (port->pwr_role == TYPEC_SINK && port->auto_vbus_discharge_enabled)
tcpm_set_state(port, SNK_UNATTACHED, 0);
diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c
index 5d125339687a..20917d85d6f4 100644
--- a/drivers/usb/typec/tcpm/wcove.c
+++ b/drivers/usb/typec/tcpm/wcove.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* typec_wcove.c - WhiskeyCove PMIC USB Type-C PHY driver
*
* Copyright (C) 2017 Intel Corporation
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index b7d104c80d85..5ef5bd0e87cf 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -1219,7 +1219,7 @@ static int ucsi_init(struct ucsi *ucsi)
goto err_reset;
}
- /* Allocate the connectors. Released in ucsi_unregister_ppm() */
+ /* Allocate the connectors. Released in ucsi_unregister() */
ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1,
sizeof(*ucsi->connector), GFP_KERNEL);
if (!ucsi->connector) {
@@ -1280,7 +1280,7 @@ void *ucsi_get_drvdata(struct ucsi *ucsi)
EXPORT_SYMBOL_GPL(ucsi_get_drvdata);
/**
- * ucsi_get_drvdata - Assign private driver data pointer
+ * ucsi_set_drvdata - Assign private driver data pointer
* @ucsi: UCSI interface
* @data: Private data pointer
*/
diff --git a/include/linux/device.h b/include/linux/device.h
index 855fb95a1fa5..59940f1744c1 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -342,6 +342,22 @@ enum dl_dev_state {
};
/**
+ * enum device_removable - Whether the device is removable. The criteria for a
+ * device to be classified as removable is determined by its subsystem or bus.
+ * @DEVICE_REMOVABLE_NOT_SUPPORTED: This attribute is not supported for this
+ * device (default).
+ * @DEVICE_REMOVABLE_UNKNOWN: Device location is Unknown.
+ * @DEVICE_FIXED: Device is not removable by the user.
+ * @DEVICE_REMOVABLE: Device is removable by the user.
+ */
+enum device_removable {
+ DEVICE_REMOVABLE_NOT_SUPPORTED = 0, /* must be 0 */
+ DEVICE_REMOVABLE_UNKNOWN,
+ DEVICE_FIXED,
+ DEVICE_REMOVABLE,
+};
+
+/**
* struct dev_links_info - Device data related to device links.
* @suppliers: List of links to supplier devices.
* @consumers: List of links to consumer devices.
@@ -422,6 +438,9 @@ struct dev_links_info {
* device (i.e. the bus driver that discovered the device).
* @iommu_group: IOMMU group the device belongs to.
* @iommu: Per device generic IOMMU runtime data
+ * @removable: Whether the device can be removed from the system. This
+ * should be set by the subsystem / bus driver that discovered
+ * the device.
*
* @offline_disabled: If set, the device is permanently online.
* @offline: Set after successful invocation of bus type's .offline().
@@ -535,6 +554,8 @@ struct device {
struct iommu_group *iommu_group;
struct dev_iommu *iommu;
+ enum device_removable removable;
+
bool offline_disabled:1;
bool offline:1;
bool of_node_reused:1;
@@ -771,6 +792,22 @@ static inline bool dev_has_sync_state(struct device *dev)
return false;
}
+static inline void dev_set_removable(struct device *dev,
+ enum device_removable removable)
+{
+ dev->removable = removable;
+}
+
+static inline bool dev_is_removable(struct device *dev)
+{
+ return dev->removable == DEVICE_REMOVABLE;
+}
+
+static inline bool dev_removable_is_valid(struct device *dev)
+{
+ return dev->removable != DEVICE_REMOVABLE_NOT_SUPPORTED;
+}
+
/*
* High level routines for use by the bus drivers
*/
diff --git a/include/linux/phy/tegra/xusb.h b/include/linux/phy/tegra/xusb.h
index 71d956935405..3a35e74cdc61 100644
--- a/include/linux/phy/tegra/xusb.h
+++ b/include/linux/phy/tegra/xusb.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved.
*/
#ifndef PHY_TEGRA_XUSB_H
@@ -8,6 +8,7 @@
struct tegra_xusb_padctl;
struct device;
+enum usb_device_speed;
struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev);
void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl);
@@ -23,4 +24,11 @@ int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl,
int tegra_phy_xusb_utmi_port_reset(struct phy *phy);
int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl,
unsigned int port);
+int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy,
+ enum usb_device_speed speed);
+int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy);
+int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy);
+int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy);
+bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy);
+
#endif /* PHY_TEGRA_XUSB_H */
diff --git a/include/linux/usb.h b/include/linux/usb.h
index eaae24217e8a..7ccaa76a9a96 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -473,12 +473,6 @@ struct usb_dev_state;
struct usb_tt;
-enum usb_device_removable {
- USB_DEVICE_REMOVABLE_UNKNOWN = 0,
- USB_DEVICE_REMOVABLE,
- USB_DEVICE_FIXED,
-};
-
enum usb_port_connect_type {
USB_PORT_CONNECT_TYPE_UNKNOWN = 0,
USB_PORT_CONNECT_TYPE_HOT_PLUG,
@@ -703,7 +697,6 @@ struct usb_device {
#endif
struct wusb_dev *wusb_dev;
int slot_id;
- enum usb_device_removable removable;
struct usb2_lpm_parameters l1_params;
struct usb3_lpm_parameters u1_params;
struct usb3_lpm_parameters u2_params;
@@ -1485,7 +1478,7 @@ typedef void (*usb_complete_t)(struct urb *);
*
* Note that transfer_buffer must still be set if the controller
* does not support DMA (as indicated by hcd_uses_dma()) and when talking
- * to root hub. If you have to trasfer between highmem zone and the device
+ * to root hub. If you have to transfer between highmem zone and the device
* on such controller, create a bounce buffer or bail out with an error.
* If transfer_buffer cannot be set (is in highmem) and the controller is DMA
* capable, assign NULL to it, so that usbmon knows not to use the value.
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index c71150f2c639..9d2762279286 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -271,7 +271,7 @@ int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
* @bConfigurationValue: Copied into configuration descriptor.
* @iConfiguration: Copied into configuration descriptor.
* @bmAttributes: Copied into configuration descriptor.
- * @MaxPower: Power consumtion in mA. Used to compute bMaxPower in the
+ * @MaxPower: Power consumption in mA. Used to compute bMaxPower in the
* configuration descriptor after considering the bus speed.
* @cdev: assigned by @usb_add_config() before calling @bind(); this is
* the device associated with this configuration.
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index ee04ef214ce8..75c7538e350a 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -197,7 +197,7 @@ struct usb_ep_caps {
* @name:identifier for the endpoint, such as "ep-a" or "ep9in-bulk"
* @ops: Function pointers used to access hardware-specific operations.
* @ep_list:the gadget's ep_list holds all of its endpoints
- * @caps:The structure describing types and directions supported by endoint.
+ * @caps:The structure describing types and directions supported by endpoint.
* @enabled: The current endpoint enabled/disabled state.
* @claimed: True if this endpoint is claimed by a function.
* @maxpacket:The maximum packet size used on this endpoint. The initial
@@ -325,6 +325,7 @@ struct usb_gadget_ops {
void (*udc_set_speed)(struct usb_gadget *, enum usb_device_speed);
void (*udc_set_ssp_rate)(struct usb_gadget *gadget,
enum usb_ssp_rate rate);
+ void (*udc_async_callbacks)(struct usb_gadget *gadget, bool enable);
struct usb_ep *(*match_ep)(struct usb_gadget *,
struct usb_endpoint_descriptor *,
struct usb_ss_ep_comp_descriptor *);
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 96281cd50ff6..548a028f2dab 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -59,7 +59,7 @@
* USB Host Controller Driver (usb_hcd) framework
*
* Since "struct usb_bus" is so thin, you can't share much code in it.
- * This framework is a layer over that, and should be more sharable.
+ * This framework is a layer over that, and should be more shareable.
*/
/*-------------------------------------------------------------------------*/
@@ -299,7 +299,7 @@ struct hc_driver {
* (optional) these hooks allow an HCD to override the default DMA
* mapping and unmapping routines. In general, they shouldn't be
* necessary unless the host controller has special DMA requirements,
- * such as alignment contraints. If these are not specified, the
+ * such as alignment constraints. If these are not specified, the
* general usb_hcd_(un)?map_urb_for_dma functions will be used instead
* (and it may be a good idea to call these functions in your HCD
* implementation)
@@ -409,7 +409,10 @@ struct hc_driver {
int (*find_raw_port_number)(struct usb_hcd *, int);
/* Call for power on/off the port if necessary */
int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
-
+ /* Call for SINGLE_STEP_SET_FEATURE Test for USB2 EH certification */
+#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
+ int (*submit_single_step_set_feature)(struct usb_hcd *,
+ struct urb *, int);
};
static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
@@ -474,6 +477,14 @@ int usb_hcd_setup_local_mem(struct usb_hcd *hcd, phys_addr_t phys_addr,
struct platform_device;
extern void usb_hcd_platform_shutdown(struct platform_device *dev);
+#ifdef CONFIG_USB_HCD_TEST_MODE
+extern int ehset_single_step_set_feature(struct usb_hcd *hcd, int port);
+#else
+static inline int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
+{
+ return 0;
+}
+#endif /* CONFIG_USB_HCD_TEST_MODE */
#ifdef CONFIG_USB_PCI
struct pci_dev;
diff --git a/include/linux/usb/isp1760.h b/include/linux/usb/isp1760.h
deleted file mode 100644
index b75ded28db81..000000000000
--- a/include/linux/usb/isp1760.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * board initialization should put one of these into dev->platform_data
- * and place the isp1760 onto platform_bus named "isp1760-hcd".
- */
-
-#ifndef __LINUX_USB_ISP1760_H
-#define __LINUX_USB_ISP1760_H
-
-struct isp1760_platform_data {
- unsigned is_isp1761:1; /* Chip is ISP1761 */
- unsigned bus_width_16:1; /* 16/32-bit data bus width */
- unsigned port1_otg:1; /* Port 1 supports OTG */
- unsigned analog_oc:1; /* Analog overcurrent */
- unsigned dack_polarity_high:1; /* DACK active high */
- unsigned dreq_polarity_high:1; /* DREQ active high */
-};
-
-#endif /* __LINUX_USB_ISP1760_H */
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index e78eb577d0fa..3aee78dda16d 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -98,7 +98,7 @@ enum otg_fsm_timer {
* @b_bus_req: TRUE during the time that the Application running on the
* B-device wants to use the bus
*
- * Auxilary inputs (OTG v1.3 only. Obsolete now.)
+ * Auxiliary inputs (OTG v1.3 only. Obsolete now.)
* @a_sess_vld: TRUE if the A-device detects that VBUS is above VA_SESS_VLD
* @b_bus_suspend: TRUE when the A-device detects that the B-device has put
* the bus into suspend
@@ -153,7 +153,7 @@ struct otg_fsm {
int a_bus_req;
int b_bus_req;
- /* Auxilary inputs */
+ /* Auxiliary inputs */
int a_sess_vld;
int b_bus_resume;
int b_bus_suspend;
@@ -177,7 +177,7 @@ struct otg_fsm {
int a_bus_req_inf;
int a_clr_err_inf;
int b_bus_req_inf;
- /* Auxilary informative variables */
+ /* Auxiliary informative variables */
int a_suspend_req_inf;
/* Timeout indicator for timers */
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 69f1b6328532..7ceeecbb9e02 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -125,7 +125,7 @@ enum usb_dr_mode {
* @dev: Pointer to the given device
*
* The function gets phy interface string from property 'dr_mode',
- * and returns the correspondig enum usb_dr_mode
+ * and returns the corresponding enum usb_dr_mode
*/
extern enum usb_dr_mode usb_get_dr_mode(struct device *dev);
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 5e4c497f54d6..eeb7c2157c72 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -32,7 +32,7 @@
#define USB_QUIRK_DELAY_INIT BIT(6)
/*
- * For high speed and super speed interupt endpoints, the USB 2.0 and
+ * For high speed and super speed interrupt endpoints, the USB 2.0 and
* USB 3.0 spec require the interval in microframes
* (1 microframe = 125 microseconds) to be calculated as
* interval = 2 ^ (bInterval-1).
diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h
index 0164fed31b06..031f148ab373 100644
--- a/include/linux/usb/role.h
+++ b/include/linux/usb/role.h
@@ -65,6 +65,7 @@ void usb_role_switch_unregister(struct usb_role_switch *sw);
void usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data);
void *usb_role_switch_get_drvdata(struct usb_role_switch *sw);
+const char *usb_role_string(enum usb_role role);
#else
static inline int usb_role_switch_set_role(struct usb_role_switch *sw,
enum usb_role role)
@@ -109,6 +110,11 @@ static inline void *usb_role_switch_get_drvdata(struct usb_role_switch *sw)
return NULL;
}
+static inline const char *usb_role_string(enum usb_role role)
+{
+ return "unknown";
+}
+
#endif
#endif /* __LINUX_USB_ROLE_H */
diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
index 8c63fa9bfc74..16ea5a4cc586 100644
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -276,7 +276,7 @@ struct usb_serial_driver {
int (*write)(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
/* Called only by the tty layer */
- int (*write_room)(struct tty_struct *tty);
+ unsigned int (*write_room)(struct tty_struct *tty);
int (*ioctl)(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
void (*get_serial)(struct tty_struct *tty, struct serial_struct *ss);
@@ -284,7 +284,7 @@ struct usb_serial_driver {
void (*set_termios)(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
void (*break_ctl)(struct tty_struct *tty, int break_state);
- int (*chars_in_buffer)(struct tty_struct *tty);
+ unsigned int (*chars_in_buffer)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, long timeout);
bool (*tx_empty)(struct usb_serial_port *port);
void (*throttle)(struct tty_struct *tty);
@@ -347,8 +347,8 @@ int usb_serial_generic_write(struct tty_struct *tty, struct usb_serial_port *por
const unsigned char *buf, int count);
void usb_serial_generic_close(struct usb_serial_port *port);
int usb_serial_generic_resume(struct usb_serial *serial);
-int usb_serial_generic_write_room(struct tty_struct *tty);
-int usb_serial_generic_chars_in_buffer(struct tty_struct *tty);
+unsigned int usb_serial_generic_write_room(struct tty_struct *tty);
+unsigned int usb_serial_generic_chars_in_buffer(struct tty_struct *tty);
void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout);
void usb_serial_generic_read_bulk_callback(struct urb *urb);
void usb_serial_generic_write_bulk_callback(struct urb *urb);
@@ -395,7 +395,7 @@ static inline void usb_serial_debug_data(struct device *dev,
}
/*
- * Macro for reporting errors in write path to avoid inifinite loop
+ * Macro for reporting errors in write path to avoid infinite loop
* when port is used as a console.
*/
#define dev_err_console(usport, fmt, ...) \
diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h
index 42fcfbe10590..bffc8d3e14ad 100644
--- a/include/linux/usb/tcpm.h
+++ b/include/linux/usb/tcpm.h
@@ -66,6 +66,8 @@ enum tcpm_transmit_type {
* For example, some tcpcs may include BC1.2 charger detection
* and use that in this case.
* @set_cc: Called to set value of CC pins
+ * @apply_rc: Optional; Needed to move TCPCI based chipset to APPLY_RC state
+ * as stated by the TCPCI specification.
* @get_cc: Called to read current CC pin values
* @set_polarity:
* Called to set polarity
@@ -120,6 +122,8 @@ struct tcpc_dev {
int (*get_vbus)(struct tcpc_dev *dev);
int (*get_current_limit)(struct tcpc_dev *dev);
int (*set_cc)(struct tcpc_dev *dev, enum typec_cc_status cc);
+ int (*apply_rc)(struct tcpc_dev *dev, enum typec_cc_status cc,
+ enum typec_cc_polarity polarity);
int (*get_cc)(struct tcpc_dev *dev, enum typec_cc_status *cc1,
enum typec_cc_status *cc2);
int (*set_polarity)(struct tcpc_dev *dev,
diff --git a/include/linux/usb/typec_dp.h b/include/linux/usb/typec_dp.h
index fc4c7edb2e8a..cfb916cccd31 100644
--- a/include/linux/usb/typec_dp.h
+++ b/include/linux/usb/typec_dp.h
@@ -97,7 +97,7 @@ enum {
#define DP_CONF_PIN_ASSIGNEMENT_SHIFT 8
#define DP_CONF_PIN_ASSIGNEMENT_MASK GENMASK(15, 8)
-/* Helper for setting/getting the pin assignement value to the configuration */
+/* Helper for setting/getting the pin assignment value to the configuration */
#define DP_CONF_SET_PIN_ASSIGN(_a_) ((_a_) << 8)
#define DP_CONF_GET_PIN_ASSIGN(_conf_) (((_conf_) & GENMASK(15, 8)) >> 8)