diff options
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, ®s); 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, ®); + 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) |